diff --git a/DEPS b/DEPS
index 5841d2e..dfa530b 100644
--- a/DEPS
+++ b/DEPS
@@ -177,7 +177,7 @@
   # luci-go CIPD package version.
   # Make sure the revision is uploaded by infra-packagers builder.
   # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console
-  'luci_go': 'git_revision:d8815e36ea7b66a4b8c9d69fcc2322012d25715f',
+  'luci_go': 'git_revision:3e796d36914b6ddf5311374284e3ffa06c24fc7e',
 
   # This can be overridden, e.g. with custom_vars, to build clang from HEAD
   # instead of downloading the prebuilt pinned revision.
@@ -209,7 +209,7 @@
   # 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': '72fb3fcdf0a7a7b093271eb8766c7728c17bea1d',
+  'skia_revision': '03e783020013caa8fd8fb070a6ea37bb2f756200',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -221,11 +221,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '9459456b09abc2e75c0c9a67e4b653273087e1eb',
+  'angle_revision': 'd0dc22fd07e40beb5a771840a8a0d43134aa20c4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '0cfdf0c272cd1e1ac40e95d797baf216721b9026',
+  'swiftshader_revision': 'c1e4abc1bcfeae3ea17279b1f3d419406769c77d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -244,7 +244,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling googletest
   # and whatever else without interference from each other.
-  'googletest_revision': 'a3460d1aeeaa43fdf137a6adefef10ba0b59fe4b',
+  'googletest_revision': '53495a2a7d6ba7e0691a7f3602e9a5324bba6e45',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling lighttpd
   # and whatever else without interference from each other.
@@ -280,7 +280,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '053e386fd4017d799b5b9130fa7a0b00b0fb0229',
+  'catapult_revision': 'ec690bb8debdc5c03a90654a9eb6d8d762dd26ee',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -288,7 +288,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': '8402fc332631f51cddd1c7562a488e900b0fe59d',
+  'devtools_frontend_revision': 'a6df3ab32f4aafc308236619a04a99317dd22b06',
   # 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.
@@ -328,7 +328,7 @@
   # 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': '59e16c9b02a0ef2a094bef900b83f2785b3c2a43',
+  'dawn_revision': '1413b351fac11876fcead0a95f04deb17f5630a5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -372,11 +372,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.
-  'libcxxabi_revision':    'da3e6cbc62dd1e3df45f522ba313d1c581867094',
+  'libcxxabi_revision':    '7e3b76855b76ce7b4c975df50674734357056612',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'libunwind_revision':    'c0776fc6490f549d1f34a677cb74c3d3181ad0fc',
+  'libunwind_revision':    '950faeeabc1ee25569e62ec4ce749f013169482d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -730,7 +730,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '5wEAJbMDQJnCxXbN6hMn66IR4akg1G25HQtc_8_7Vz0C',
+          'version': '8d-gGcc4KVhOnn2B-Od7eR421Q-sNZQ0U7dMrNz_VX4C',
       },
     ],
     'condition': 'checkout_android',
@@ -961,12 +961,12 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'b29188be3069888800b409486c1e47bcf36a8120',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '23db6947e3198a27f3fcebc9dbac4d1b025c2935',
       'condition': 'checkout_linux',
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'cd3696cf7ee983de86583b898785cea56ffd07fe',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c8f63d390cfda6001a67316869914dca977770b7',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1349,7 +1349,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '34eb6a14728a870046c2637dbfb909d02936b84c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '8545284860afe328993751b80a9cf856872463df',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1601,7 +1601,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': '8A8GVNDvQNly-61PpEBfFpqpG9cscGA9DrHNNoc8tSQC',
+          'version': 'SIbSqtgKfmhBASeojfVyHGkIx2ZItagJYLeJt9yef1oC',
         },
       ],
       'dep_type': 'cipd',
@@ -1611,7 +1611,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'SufM9WryJm4lk56xFxO5xa0VKBqk-rQ7kcVCJl111ecC',
+          'version': 'BL1QYX6LBgah6_XEB3KHs9jZiFQtq2z8PUZRswCFvN8C',
         },
       ],
       'dep_type': 'cipd',
@@ -1621,7 +1621,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'iqXjvpHQMsiYHTsmCxuGGRSb6zp776WuTrwp1k9p_A4C',
+          'version': 'WB42GE3e_7-dR5RnBBCICQtMkfpOoJvlT9tMG_6Fj1UC',
         },
       ],
       'dep_type': 'cipd',
@@ -1635,7 +1635,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3051bb1cf2055b65b92a93c9f84cf6f189cc6998',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d86de93cc34524c6dfd3a2e52ff8eaec1bb7ecf4',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 84a4516e..7af6ca7 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1094,7 +1094,8 @@
                   'ash/system/holding_space|'\
                   'chrome/browser/lacros/.*holding_space.*|'\
                   'chrome/browser/ui/ash/holding_space|'\
-                  'chromeos/crosapi/mojom/.*holding_space.*'
+                  'chromeos/crosapi/mojom/.*holding_space.*|'\
+                  'tools/metrics/histograms/histograms_xml/holding_space'
     },
     'i18n': {
       'filepath': 'base/i18n/|base/string|l10n|icu|'\
diff --git a/android_webview/java/strings/translations/android_webview_strings_hy.xtb b/android_webview/java/strings/translations/android_webview_strings_hy.xtb
index 9b01c94f..c79fab3 100644
--- a/android_webview/java/strings/translations/android_webview_strings_hy.xtb
+++ b/android_webview/java/strings/translations/android_webview_strings_hy.xtb
@@ -1,6 +1,6 @@
 <?xml version="1.0" ?>
 <!DOCTYPE translationbundle>
 <translationbundle lang="hy">
-<translation id="3572484393913897457">Համակարգի WebView արտոնագրեր</translation>
+<translation id="3572484393913897457">Համակարգի WebView լիցենզիաներ</translation>
 <translation id="8916631167640856213">Այս գործառույթը չի աջակցվում Android-ի այս տարբերակում:</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/android_webview/java/strings/translations/android_webview_strings_ru.xtb b/android_webview/java/strings/translations/android_webview_strings_ru.xtb
index 4092e1a2..8a3f139 100644
--- a/android_webview/java/strings/translations/android_webview_strings_ru.xtb
+++ b/android_webview/java/strings/translations/android_webview_strings_ru.xtb
@@ -1,6 +1,6 @@
 <?xml version="1.0" ?>
 <!DOCTYPE translationbundle>
 <translationbundle lang="ru">
-<translation id="3572484393913897457">Лицензии системного WebView</translation>
+<translation id="3572484393913897457">Лицензии System WebView</translation>
 <translation id="8916631167640856213">Эта функция не поддерживается в данной версии Android.</translation>
 </translationbundle>
\ No newline at end of file
diff --git a/ash/app_list/BUILD.gn b/ash/app_list/BUILD.gn
index fbf8e3d..c5fa9c7b 100644
--- a/ash/app_list/BUILD.gn
+++ b/ash/app_list/BUILD.gn
@@ -24,16 +24,16 @@
     "app_list_util.cc",
     "app_list_util.h",
     "app_list_view_delegate.h",
+    "bubble/app_list_bubble_apps_page.cc",
+    "bubble/app_list_bubble_apps_page.h",
+    "bubble/app_list_bubble_assistant_page.cc",
+    "bubble/app_list_bubble_assistant_page.h",
     "bubble/app_list_bubble_event_filter.cc",
     "bubble/app_list_bubble_event_filter.h",
+    "bubble/app_list_bubble_search_page.cc",
+    "bubble/app_list_bubble_search_page.h",
     "bubble/app_list_bubble_view.cc",
     "bubble/app_list_bubble_view.h",
-    "bubble/bubble_apps_page.cc",
-    "bubble/bubble_apps_page.h",
-    "bubble/bubble_assistant_page.cc",
-    "bubble/bubble_assistant_page.h",
-    "bubble/bubble_search_page.cc",
-    "bubble/bubble_search_page.h",
     "bubble/scrollable_apps_grid_view.cc",
     "bubble/scrollable_apps_grid_view.h",
     "home_launcher_animation_info.h",
diff --git a/ash/app_list/bubble/bubble_apps_page.cc b/ash/app_list/bubble/app_list_bubble_apps_page.cc
similarity index 94%
rename from ash/app_list/bubble/bubble_apps_page.cc
rename to ash/app_list/bubble/app_list_bubble_apps_page.cc
index 97e70b2..8fb47a1 100644
--- a/ash/app_list/bubble/bubble_apps_page.cc
+++ b/ash/app_list/bubble/app_list_bubble_apps_page.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 "ash/app_list/bubble/bubble_apps_page.h"
+#include "ash/app_list/bubble/app_list_bubble_apps_page.h"
 
 #include <limits>
 #include <memory>
@@ -32,7 +32,8 @@
 
 }  // namespace
 
-BubbleAppsPage::BubbleAppsPage(AppListViewDelegate* view_delegate) {
+AppListBubbleAppsPage::AppListBubbleAppsPage(
+    AppListViewDelegate* view_delegate) {
   DCHECK(view_delegate);
 
   SetUseDefaultFillLayout(true);
@@ -97,6 +98,6 @@
   scroll->SetContents(std::move(scroll_contents));
 }
 
-BubbleAppsPage::~BubbleAppsPage() = default;
+AppListBubbleAppsPage::~AppListBubbleAppsPage() = default;
 
 }  // namespace ash
diff --git a/ash/app_list/bubble/app_list_bubble_apps_page.h b/ash/app_list/bubble/app_list_bubble_apps_page.h
new file mode 100644
index 0000000..30ebb62
--- /dev/null
+++ b/ash/app_list/bubble/app_list_bubble_apps_page.h
@@ -0,0 +1,30 @@
+// 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 ASH_APP_LIST_BUBBLE_APP_LIST_BUBBLE_APPS_PAGE_H_
+#define ASH_APP_LIST_BUBBLE_APP_LIST_BUBBLE_APPS_PAGE_H_
+
+#include "ash/ash_export.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+class AppListViewDelegate;
+
+// The default page for the app list bubble / clamshell launcher. Contains a
+// scroll view with:
+// - Continue section with recent tasks and recent apps
+// - Grid of all apps
+// Does not include the search box, which is owned by a parent view.
+class ASH_EXPORT AppListBubbleAppsPage : public views::View {
+ public:
+  explicit AppListBubbleAppsPage(AppListViewDelegate* view_delegate);
+  AppListBubbleAppsPage(const AppListBubbleAppsPage&) = delete;
+  AppListBubbleAppsPage& operator=(const AppListBubbleAppsPage&) = delete;
+  ~AppListBubbleAppsPage() override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_APP_LIST_BUBBLE_APP_LIST_BUBBLE_APPS_PAGE_H_
diff --git a/ash/app_list/bubble/bubble_assistant_page.cc b/ash/app_list/bubble/app_list_bubble_assistant_page.cc
similarity index 82%
rename from ash/app_list/bubble/bubble_assistant_page.cc
rename to ash/app_list/bubble/app_list_bubble_assistant_page.cc
index 6079c70..fa511b0 100644
--- a/ash/app_list/bubble/bubble_assistant_page.cc
+++ b/ash/app_list/bubble/app_list_bubble_assistant_page.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 "ash/app_list/bubble/bubble_assistant_page.h"
+#include "ash/app_list/bubble/app_list_bubble_assistant_page.h"
 
 #include <memory>
 #include <utility>
@@ -14,7 +14,7 @@
 
 namespace ash {
 
-BubbleAssistantPage::BubbleAssistantPage() {
+AppListBubbleAssistantPage::AppListBubbleAssistantPage() {
   SetLayoutManager(
       std::make_unique<BoxLayout>(BoxLayout::Orientation::kVertical));
 
@@ -27,6 +27,6 @@
   AddChildView(std::make_unique<views::Label>(u"Assistant"));
 }
 
-BubbleAssistantPage::~BubbleAssistantPage() = default;
+AppListBubbleAssistantPage::~AppListBubbleAssistantPage() = default;
 
 }  // namespace ash
diff --git a/ash/app_list/bubble/app_list_bubble_assistant_page.h b/ash/app_list/bubble/app_list_bubble_assistant_page.h
new file mode 100644
index 0000000..6a3cb50
--- /dev/null
+++ b/ash/app_list/bubble/app_list_bubble_assistant_page.h
@@ -0,0 +1,25 @@
+// 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 ASH_APP_LIST_BUBBLE_APP_LIST_BUBBLE_ASSISTANT_PAGE_H_
+#define ASH_APP_LIST_BUBBLE_APP_LIST_BUBBLE_ASSISTANT_PAGE_H_
+
+#include "ash/ash_export.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+// The assistant page for the app list bubble / clamshell launcher.
+class ASH_EXPORT AppListBubbleAssistantPage : public views::View {
+ public:
+  AppListBubbleAssistantPage();
+  AppListBubbleAssistantPage(const AppListBubbleAssistantPage&) = delete;
+  AppListBubbleAssistantPage& operator=(const AppListBubbleAssistantPage&) =
+      delete;
+  ~AppListBubbleAssistantPage() override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_APP_LIST_BUBBLE_APP_LIST_BUBBLE_ASSISTANT_PAGE_H_
diff --git a/ash/app_list/bubble/bubble_search_page.cc b/ash/app_list/bubble/app_list_bubble_search_page.cc
similarity index 90%
rename from ash/app_list/bubble/bubble_search_page.cc
rename to ash/app_list/bubble/app_list_bubble_search_page.cc
index 5c0e836..a7a813e2 100644
--- a/ash/app_list/bubble/bubble_search_page.cc
+++ b/ash/app_list/bubble/app_list_bubble_search_page.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 "ash/app_list/bubble/bubble_search_page.h"
+#include "ash/app_list/bubble/app_list_bubble_search_page.h"
 
 #include <limits>
 #include <memory>
@@ -18,7 +18,7 @@
 
 namespace ash {
 
-BubbleSearchPage::BubbleSearchPage() {
+AppListBubbleSearchPage::AppListBubbleSearchPage() {
   SetUseDefaultFillLayout(true);
 
   // The entire page scrolls.
@@ -50,6 +50,6 @@
   scroll->SetContents(std::move(scroll_contents));
 }
 
-BubbleSearchPage::~BubbleSearchPage() = default;
+AppListBubbleSearchPage::~AppListBubbleSearchPage() = default;
 
 }  // namespace ash
diff --git a/ash/app_list/bubble/app_list_bubble_search_page.h b/ash/app_list/bubble/app_list_bubble_search_page.h
new file mode 100644
index 0000000..3fb6024
--- /dev/null
+++ b/ash/app_list/bubble/app_list_bubble_search_page.h
@@ -0,0 +1,26 @@
+// 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 ASH_APP_LIST_BUBBLE_APP_LIST_BUBBLE_SEARCH_PAGE_H_
+#define ASH_APP_LIST_BUBBLE_APP_LIST_BUBBLE_SEARCH_PAGE_H_
+
+#include "ash/ash_export.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+// The search results page for the app list bubble / clamshell launcher.
+// Contains a scrolling list of search results. Does not include the search box,
+// which is owned by a parent view.
+class ASH_EXPORT AppListBubbleSearchPage : public views::View {
+ public:
+  AppListBubbleSearchPage();
+  AppListBubbleSearchPage(const AppListBubbleSearchPage&) = delete;
+  AppListBubbleSearchPage& operator=(const AppListBubbleSearchPage&) = delete;
+  ~AppListBubbleSearchPage() override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_APP_LIST_BUBBLE_APP_LIST_BUBBLE_SEARCH_PAGE_H_
diff --git a/ash/app_list/bubble/app_list_bubble_view.cc b/ash/app_list/bubble/app_list_bubble_view.cc
index 1983031..1146cc0 100644
--- a/ash/app_list/bubble/app_list_bubble_view.cc
+++ b/ash/app_list/bubble/app_list_bubble_view.cc
@@ -6,9 +6,9 @@
 
 #include <memory>
 
-#include "ash/app_list/bubble/bubble_apps_page.h"
-#include "ash/app_list/bubble/bubble_assistant_page.h"
-#include "ash/app_list/bubble/bubble_search_page.h"
+#include "ash/app_list/bubble/app_list_bubble_apps_page.h"
+#include "ash/app_list/bubble/app_list_bubble_assistant_page.h"
+#include "ash/app_list/bubble/app_list_bubble_search_page.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shelf/shelf.h"
@@ -97,12 +97,14 @@
       base::BindRepeating(&AppListBubbleView::FlipPage, base::Unretained(this)),
       u"Flip page"));
 
-  apps_page_ = AddChildView(std::make_unique<BubbleAppsPage>(view_delegate));
+  apps_page_ =
+      AddChildView(std::make_unique<AppListBubbleAppsPage>(view_delegate));
 
-  search_page_ = AddChildView(std::make_unique<BubbleSearchPage>());
+  search_page_ = AddChildView(std::make_unique<AppListBubbleSearchPage>());
   search_page_->SetVisible(false);
 
-  assistant_page_ = AddChildView(std::make_unique<BubbleAssistantPage>());
+  assistant_page_ =
+      AddChildView(std::make_unique<AppListBubbleAssistantPage>());
   assistant_page_->SetVisible(false);
 }
 
diff --git a/ash/app_list/bubble/app_list_bubble_view.h b/ash/app_list/bubble/app_list_bubble_view.h
index 07eb8ac..59a8e6c 100644
--- a/ash/app_list/bubble/app_list_bubble_view.h
+++ b/ash/app_list/bubble/app_list_bubble_view.h
@@ -14,10 +14,10 @@
 
 namespace ash {
 
+class AppListBubbleAppsPage;
+class AppListBubbleAssistantPage;
+class AppListBubbleSearchPage;
 class AppListViewDelegate;
-class BubbleAppsPage;
-class BubbleAssistantPage;
-class BubbleSearchPage;
 enum class ShelfAlignment;
 
 // Contains the views for the bubble version of the launcher.
@@ -43,9 +43,9 @@
   // TODO(https://crbug.com/1204551): Delete this when search box is hooked up.
   int visible_page_ = 0;
 
-  BubbleAppsPage* apps_page_ = nullptr;
-  BubbleSearchPage* search_page_ = nullptr;
-  BubbleAssistantPage* assistant_page_ = nullptr;
+  AppListBubbleAppsPage* apps_page_ = nullptr;
+  AppListBubbleSearchPage* search_page_ = nullptr;
+  AppListBubbleAssistantPage* assistant_page_ = nullptr;
 };
 
 }  // namespace ash
diff --git a/ash/app_list/bubble/bubble_apps_page.h b/ash/app_list/bubble/bubble_apps_page.h
deleted file mode 100644
index c97c3a6..0000000
--- a/ash/app_list/bubble/bubble_apps_page.h
+++ /dev/null
@@ -1,30 +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 ASH_APP_LIST_BUBBLE_BUBBLE_APPS_PAGE_H_
-#define ASH_APP_LIST_BUBBLE_BUBBLE_APPS_PAGE_H_
-
-#include "ash/ash_export.h"
-#include "ui/views/view.h"
-
-namespace ash {
-
-class AppListViewDelegate;
-
-// The default page for the app list bubble / clamshell launcher. Contains a
-// scroll view with:
-// - Continue section with recent tasks and recent apps
-// - Grid of all apps
-// Does not include the search box, which is owned by a parent view.
-class ASH_EXPORT BubbleAppsPage : public views::View {
- public:
-  explicit BubbleAppsPage(AppListViewDelegate* view_delegate);
-  BubbleAppsPage(const BubbleAppsPage&) = delete;
-  BubbleAppsPage& operator=(const BubbleAppsPage&) = delete;
-  ~BubbleAppsPage() override;
-};
-
-}  // namespace ash
-
-#endif  // ASH_APP_LIST_BUBBLE_BUBBLE_APPS_PAGE_H_
diff --git a/ash/app_list/bubble/bubble_assistant_page.h b/ash/app_list/bubble/bubble_assistant_page.h
deleted file mode 100644
index ab3c2f82..0000000
--- a/ash/app_list/bubble/bubble_assistant_page.h
+++ /dev/null
@@ -1,24 +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 ASH_APP_LIST_BUBBLE_BUBBLE_ASSISTANT_PAGE_H_
-#define ASH_APP_LIST_BUBBLE_BUBBLE_ASSISTANT_PAGE_H_
-
-#include "ash/ash_export.h"
-#include "ui/views/view.h"
-
-namespace ash {
-
-// The assistant page for the app list bubble / clamshell launcher.
-class ASH_EXPORT BubbleAssistantPage : public views::View {
- public:
-  BubbleAssistantPage();
-  BubbleAssistantPage(const BubbleAssistantPage&) = delete;
-  BubbleAssistantPage& operator=(const BubbleAssistantPage&) = delete;
-  ~BubbleAssistantPage() override;
-};
-
-}  // namespace ash
-
-#endif  // ASH_APP_LIST_BUBBLE_BUBBLE_ASSISTANT_PAGE_H_
diff --git a/ash/app_list/bubble/bubble_search_page.h b/ash/app_list/bubble/bubble_search_page.h
deleted file mode 100644
index d55acbb..0000000
--- a/ash/app_list/bubble/bubble_search_page.h
+++ /dev/null
@@ -1,26 +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 ASH_APP_LIST_BUBBLE_BUBBLE_SEARCH_PAGE_H_
-#define ASH_APP_LIST_BUBBLE_BUBBLE_SEARCH_PAGE_H_
-
-#include "ash/ash_export.h"
-#include "ui/views/view.h"
-
-namespace ash {
-
-// The search results page for the app list bubble / clamshell launcher.
-// Contains a scrolling list of search results. Does not include the search box,
-// which is owned by a parent view.
-class ASH_EXPORT BubbleSearchPage : public views::View {
- public:
-  BubbleSearchPage();
-  BubbleSearchPage(const BubbleSearchPage&) = delete;
-  BubbleSearchPage& operator=(const BubbleSearchPage&) = delete;
-  ~BubbleSearchPage() override;
-};
-
-}  // namespace ash
-
-#endif  // ASH_APP_LIST_BUBBLE_BUBBLE_SEARCH_PAGE_H_
diff --git a/ash/app_list/bubble/scrollable_apps_grid_view.cc b/ash/app_list/bubble/scrollable_apps_grid_view.cc
index 5358020..97bdfc7 100644
--- a/ash/app_list/bubble/scrollable_apps_grid_view.cc
+++ b/ash/app_list/bubble/scrollable_apps_grid_view.cc
@@ -82,8 +82,9 @@
   return grid_size;
 }
 
-void ScrollableAppsGridView::MaybeCreateGradientMask() {
-  // Scrollable apps grid does not have a gradient.
+int ScrollableAppsGridView::GetPaddingBetweenPages() const {
+  // The scrollable apps grid does not use pages.
+  return 0;
 }
 
 void ScrollableAppsGridView::CalculateIdealBounds() {
diff --git a/ash/app_list/bubble/scrollable_apps_grid_view.h b/ash/app_list/bubble/scrollable_apps_grid_view.h
index 9591b3b..58602e2 100644
--- a/ash/app_list/bubble/scrollable_apps_grid_view.h
+++ b/ash/app_list/bubble/scrollable_apps_grid_view.h
@@ -29,8 +29,8 @@
   gfx::Size GetTileViewSize() const override;
   gfx::Insets GetTilePadding() const override;
   gfx::Size GetTileGridSize() const override;
+  int GetPaddingBetweenPages() const override;
   void CalculateIdealBounds() override;
-  void MaybeCreateGradientMask() override;
 };
 
 }  // namespace ash
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index 4c7e2b2..a607356 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -21,8 +21,8 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "base/auto_reset.h"
 #include "base/bind.h"
+#include "base/check.h"
 #include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
 #include "cc/paint/paint_flags.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -258,8 +258,7 @@
 
 AppListItemView::AppListItemView(AppsGridView* apps_grid_view,
                                  AppListItem* item,
-                                 AppListViewDelegate* delegate,
-                                 bool is_in_folder)
+                                 AppListViewDelegate* delegate)
     : Button(),
       is_folder_(item->GetItemType() == AppListFolderItem::kItemType),
       item_weak_(item),
@@ -267,6 +266,7 @@
       apps_grid_view_(apps_grid_view),
       is_notification_indicator_enabled_(
           features::IsNotificationIndicatorEnabled()) {
+  DCHECK(delegate_);
   SetFocusBehavior(FocusBehavior::ALWAYS);
 
   auto title = std::make_unique<views::Label>();
@@ -283,7 +283,7 @@
     // Set background blur for folder icon and use mask layer to clip it into
     // circle. Note that blur is only enabled in tablet mode to improve dragging
     // smoothness.
-    if (apps_grid_view_->IsTabletMode())
+    if (delegate_->IsInTabletMode())
       SetBackgroundBlurEnabled(true);
     icon_->SetExtendedState(GetAppListConfig(), false /*extended*/,
                             false /*animate*/);
@@ -592,7 +592,7 @@
       source_type, metric_params, AppListMenuModelAdapter::FULLSCREEN_APP_GRID,
       base::BindOnce(&AppListItemView::OnMenuClosed,
                      weak_ptr_factory_.GetWeakPtr()),
-      apps_grid_view_->IsTabletMode());
+      delegate_->IsInTabletMode());
   context_menu_->Run(anchor_rect, views::MenuAnchorPosition::kBubbleRight,
                      run_types);
   apps_grid_view_->SetSelectedView(this);
diff --git a/ash/app_list/views/app_list_item_view.h b/ash/app_list/views/app_list_item_view.h
index 5322e86..2e25287b 100644
--- a/ash/app_list/views/app_list_item_view.h
+++ b/ash/app_list/views/app_list_item_view.h
@@ -43,8 +43,7 @@
 
   AppListItemView(AppsGridView* apps_grid_view,
                   AppListItem* item,
-                  AppListViewDelegate* delegate,
-                  bool is_in_folder);
+                  AppListViewDelegate* delegate);
   AppListItemView(const AppListItemView&) = delete;
   AppListItemView& operator=(const AppListItemView&) = delete;
   ~AppListItemView() override;
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 977beed9..050bc25 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -33,7 +33,6 @@
 #include "ash/public/cpp/app_list/app_list_switches.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "ash/public/cpp/pagination/pagination_controller.h"
-#include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/guid.h"
@@ -47,7 +46,6 @@
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/compositor/animation_throughput_reporter.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/display.h"
@@ -56,7 +54,6 @@
 #include "ui/gfx/animation/animation.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/geometry/vector2d_conversions.h"
-#include "ui/gfx/transform_util.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/border.h"
@@ -87,15 +84,6 @@
 // The drag and drop proxy should get scaled by this factor.
 constexpr float kDragAndDropProxyScale = 1.2f;
 
-// Vertical padding between the apps grid pages in cardified state.
-constexpr int kCardifiedPaddingBetweenPages = 12;
-
-// Horizontal padding of the apps grid page in cardified state.
-constexpr int kCardifiedHorizontalPadding = 16;
-
-// The radius of the corner of the background cards in the apps grid.
-constexpr int kBackgroundCardCornerRadius = 12;
-
 // Delays in milliseconds to show re-order preview.
 constexpr int kReorderDelay = 120;
 
@@ -105,12 +93,6 @@
 // Maximum vertical and horizontal spacing between tiles.
 constexpr int kMaximumTileSpacing = 96;
 
-// The duration in ms for most of the apps grid view animations.
-constexpr int kDefaultAnimationDuration = 200;
-
-// The opacity for the background cards when hidden.
-constexpr float kBackgroundCardOpacityHide = 0.0f;
-
 // Animation curve used for fading in the target page when opening or closing
 // a folder.
 constexpr gfx::Tween::Type kFolderFadeInTweenType = gfx::Tween::EASE_IN_2;
@@ -120,10 +102,6 @@
 constexpr gfx::Tween::Type kFolderFadeOutTweenType =
     gfx::Tween::FAST_OUT_LINEAR_IN;
 
-// Animation curve used for entering and exiting cardified state.
-constexpr gfx::Tween::Type kCardifiedStateTweenType =
-    gfx::Tween::LINEAR_OUT_SLOW_IN;
-
 // RowMoveAnimationDelegate is used when moving an item into a different row.
 // Before running the animation, the item's layer is re-created and kept in
 // the original position, then the item is moved to just before its target
@@ -195,28 +173,6 @@
   DISALLOW_COPY_AND_ASSIGN(ItemRemoveAnimationDelegate);
 };
 
-// CardifiedAnimationObserver is used to observe the animation for toggling the
-// cardified state of the apps grid view. We used this to ensure app icons are
-// repainted with the correct bounds and scale.
-class CardifiedAnimationObserver : public ui::ImplicitAnimationObserver {
- public:
-  explicit CardifiedAnimationObserver(base::OnceClosure callback)
-      : callback_(std::move(callback)) {}
-  ~CardifiedAnimationObserver() override = default;
-
-  // ui::ImplicitAnimationObserver:
-  void OnImplicitAnimationsCompleted() override {
-    if (callback_)
-      std::move(callback_).Run();
-    delete this;
-  }
-
- private:
-  base::OnceClosure callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(CardifiedAnimationObserver);
-};
-
 // ItemMoveAnimationDelegate observes when an item finishes animating when it is
 // not moving between rows. This is to ensure an item is repainted for the
 // "zoom out" case when releasing an item being dragged.
@@ -312,6 +268,9 @@
 // static
 constexpr float AppsGridView::kCardifiedScale;
 
+// static
+constexpr int AppsGridView::kDefaultAnimationDuration;
+
 AppsGridView::AppsGridView(ContentsView* contents_view,
                            AppListViewDelegate* app_list_view_delegate,
                            AppsGridViewFolderDelegate* folder_delegate)
@@ -425,14 +384,6 @@
                        kMaximumTileSpacing * (rows_per_page - 1));
 }
 
-int AppsGridView::GetPaddingBetweenPages() const {
-  // In cardified state, padding between pages should be fixed  and it should
-  // include background card padding.
-  return cardified_state_
-             ? kCardifiedPaddingBetweenPages + 2 * vertical_tile_padding_
-             : GetAppListConfig().page_spacing();
-}
-
 void AppsGridView::ResetForShowApps() {
   ClearDragState();
   layer()->SetOpacity(1.0f);
@@ -574,8 +525,7 @@
   if (!drag_view_)
     return false;  // Drag canceled.
 
-  if (!cardified_state_)
-    StartAppsGridCardifiedView();
+  MaybeStartCardifiedView();
 
   gfx::Point drag_point_in_grid_view;
   ExtractDragLocation(event.root_location(), &drag_point_in_grid_view);
@@ -804,7 +754,7 @@
     // Compensate drag_source_bounds for the translation of the items_container
     // during AnimateCardifiedState().
     gfx::Point start_position = items_container_->origin();
-    EndAppsGridCardifiedView();
+    MaybeEndCardifiedView();
     drag_source_bounds.Offset(
         0, start_position.y() - items_container_->origin().y());
   }
@@ -899,8 +849,7 @@
   // Set the flag in root level grid view.
   dragging_for_reparent_item_ = true;
 
-  if (!cardified_state_)
-    StartAppsGridCardifiedView();
+  MaybeStartCardifiedView();
 }
 
 void AppsGridView::UpdateDragFromReparentItem(Pointer pointer,
@@ -1134,10 +1083,9 @@
 }
 
 std::unique_ptr<AppListItemView> AppsGridView::CreateViewForItem(
-    AppListItem* item,
-    bool is_in_folder) {
-  std::unique_ptr<AppListItemView> view = std::make_unique<AppListItemView>(
-      this, item, app_list_view_delegate_, is_in_folder);
+    AppListItem* item) {
+  std::unique_ptr<AppListItemView> view =
+      std::make_unique<AppListItemView>(this, item, app_list_view_delegate_);
   view->SetCallback(base::BindRepeating(&AppsGridView::OnAppListItemViewPressed,
                                         base::Unretained(this),
                                         base::Unretained(view.get())));
@@ -1150,7 +1098,7 @@
   // may have one more item than |item_list_|.
   DCHECK_LE(index, item_list_->item_count());
   auto* item = item_list_->item_at(index);
-  return CreateViewForItem(item, item->IsInFolder());
+  return CreateViewForItem(item);
 }
 
 void AppsGridView::EnsureViewVisible(const GridIndex& index) {
@@ -1801,8 +1749,7 @@
     SetViewHidden(drag_view_, false /* show */, true /* no animate */);
   }
 
-  if (cardified_state_)
-    EndAppsGridCardifiedView();
+  MaybeEndCardifiedView();
   SetAsFolderDroppingTarget(drop_target_, false);
 
   AppListItemView* released_drag_view = nullptr;
@@ -1936,161 +1883,6 @@
   return contents_view_->app_list_view()->GetAppListConfig();
 }
 
-void AppsGridView::StartAppsGridCardifiedView() {
-  if (!app_list_features::IsNewDragSpecInLauncherEnabled())
-    return;
-  if (folder_delegate_)
-    return;
-  DCHECK(!cardified_state_);
-  StopObservingImplicitAnimations();
-  RemoveAllBackgroundCards();
-  // Calculate background bounds for a normal grid so it animates from the
-  // normal to the cardified bounds with the icons.
-  // Add an extra card for the peeking page in the last page. This hints users
-  // that apps can be dragged past the last existing page.
-  for (int i = 0; i < pagination_model_.total_pages() + 1; i++)
-    AppendBackgroundCard();
-  cardified_state_ = true;
-  UpdateTilePadding();
-  MaybeCreateGradientMask();
-  AnimateCardifiedState();
-}
-
-void AppsGridView::EndAppsGridCardifiedView() {
-  if (!app_list_features::IsNewDragSpecInLauncherEnabled())
-    return;
-  if (folder_delegate_)
-    return;
-  DCHECK(cardified_state_);
-  StopObservingImplicitAnimations();
-  cardified_state_ = false;
-  // Update the padding between tiles, so we can animate back the apps grid
-  // elements to their original positions.
-  UpdateTilePadding();
-  AnimateCardifiedState();
-  layer()->SetClipRect(gfx::Rect());
-}
-
-void AppsGridView::AnimateCardifiedState() {
-  if (GetWidget()) {
-    // Normally Layout() cancels any animations. At this point there may be a
-    // pending Layout(), force it now so that one isn't triggered part way
-    // through the animation. Further, ignore this layout so that the position
-    // isn't reset.
-    DCHECK(!ignore_layout_);
-    base::AutoReset<bool> auto_reset(&ignore_layout_, true);
-    GetWidget()->LayoutRootViewIfNecessary();
-  }
-
-  CalculateIdealBounds();
-  // Cache the current item container position, as RecenterItemsContainer() may
-  // change it.
-  gfx::Point start_position = items_container_->origin();
-  RecenterItemsContainer();
-  gfx::Vector2d translate_offset(
-      0, start_position.y() - items_container_->origin().y());
-  if (cardified_state_) {
-    // The drag view is translated when the items container is recentered.
-    // Reposition the drag view to compensate for the translation offset.
-    drag_view_start_ += translate_offset;
-    drag_view_->SetPosition(drag_view_start_);
-  }
-  // Drag view can be nullptr by EndDrag.
-  const int number_of_views_to_animate =
-      view_model_.view_size() - (drag_view_ ? 1 : 0);
-
-  base::RepeatingClosure on_bounds_animator_callback;
-  if (number_of_views_to_animate > 0) {
-    on_bounds_animator_callback = base::BarrierClosure(
-        number_of_views_to_animate,
-        base::BindOnce(&AppsGridView::MaybeCallOnBoundsAnimatorDone,
-                       weak_ptr_factory_.GetWeakPtr()));
-    bounds_animation_for_cardified_state_in_progress_++;
-  }
-
-  for (int i = 0; i < view_model_.view_size(); ++i) {
-    AppListItemView* entry_view = view_model_.view_at(i);
-    // We don't animate bounds for the dragged view.
-    if (entry_view == drag_view_)
-      continue;
-    // Reposition view bounds to compensate for the translation offset.
-    gfx::Rect current_bounds = entry_view->bounds();
-    current_bounds.Offset(translate_offset);
-
-    entry_view->EnsureLayer();
-
-    if (cardified_state_)
-      entry_view->EnterCardifyState();
-    else
-      entry_view->ExitCardifyState();
-
-    gfx::Rect target_bounds(view_model_.ideal_bounds(i));
-    entry_view->SetBoundsRect(target_bounds);
-
-    // View bounds are currently |target_bounds|. Transform the view so it
-    // appears in |current_bounds|.
-    gfx::Transform transform = gfx::TransformBetweenRects(
-        gfx::RectF(target_bounds), gfx::RectF(current_bounds));
-    entry_view->layer()->SetTransform(transform);
-
-    ui::ScopedLayerAnimationSettings animator(
-        entry_view->layer()->GetAnimator());
-    animator.SetPreemptionStrategy(
-        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-    animator.SetTweenType(kCardifiedStateTweenType);
-    if (!cardified_state_) {
-      animator.SetTransitionDuration(
-          base::TimeDelta::FromMilliseconds(kDefaultAnimationDuration));
-    }
-    // When the animations are done, discard the layer and reset view to
-    // proper scale.
-    animator.AddObserver(
-        new CardifiedAnimationObserver(on_bounds_animator_callback));
-    entry_view->layer()->SetTransform(gfx::Transform());
-  }
-
-  for (size_t i = 0; i < background_cards_.size(); i++) {
-    auto& background_card = background_cards_[i];
-    // Reposition card bounds to compensate for the translation offset.
-    gfx::Rect background_bounds = background_card->bounds();
-    background_bounds.Offset(translate_offset);
-    background_card->SetBounds(background_bounds);
-    ui::ScopedLayerAnimationSettings animator(background_card->GetAnimator());
-    animator.SetTweenType(kCardifiedStateTweenType);
-    animator.SetPreemptionStrategy(
-        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-    if (!cardified_state_) {
-      animator.SetTransitionDuration(
-          base::TimeDelta::FromMilliseconds(kDefaultAnimationDuration));
-    }
-    animator.AddObserver(this);
-    ui::AnimationThroughputReporter reporter(
-        background_card->GetAnimator(),
-        metrics_util::ForSmoothness(
-            base::BindRepeating(&ReportCardifiedSmoothness, cardified_state_)));
-    if (cardified_state_) {
-      const bool is_active_page =
-          background_cards_[pagination_model_.selected_page()] ==
-          background_card;
-      background_card->SetColor(
-          GetAppListConfig().GetCardifiedBackgroundColor(is_active_page));
-    } else {
-      background_card->SetOpacity(kBackgroundCardOpacityHide);
-    }
-    background_card->SetBounds(BackgroundCardBounds(i));
-  }
-  highlighted_page_ = pagination_model_.selected_page();
-}
-
-void AppsGridView::RecenterItemsContainer() {
-  const int pages = pagination_model_.total_pages();
-  const int current_page = pagination_model_.selected_page();
-  const int page_height = GetTileGridSize().height() + GetPaddingBetweenPages();
-  items_container_->SetBoundsRect(gfx::Rect(0, -page_height * current_page,
-                                            GetContentsBounds().width(),
-                                            page_height * pages));
-}
-
 bool AppsGridView::FirePageFlipTimerForTest() {
   if (!page_flip_timer_.IsRunning())
     return false;
@@ -2667,64 +2459,6 @@
     Layout();
 }
 
-gfx::Rect AppsGridView::BackgroundCardBounds(int new_page_index) {
-  // The size of the grid excluding the outer padding.
-  const gfx::Size grid_size = GetTileGridSize();
-  // The size for the background card that will be displayed. The outer padding
-  // of the grid need to be added.
-  const gfx::Size background_card_size =
-      grid_size +
-      gfx::Size(2 * horizontal_tile_padding_, 2 * vertical_tile_padding_);
-  const int padding_between_pages = GetPaddingBetweenPages();
-  // The space that each page occupies in the items container. This is the size
-  // of the grid without outer padding plus the padding between pages.
-  const int grid_size_height = grid_size.height() + padding_between_pages;
-  // We position a new card in the last place in items container view.
-  const int vertical_page_start_offset = grid_size_height * new_page_index;
-  // Add a padding on the sides to make space for pagination preview.
-  const int horizontal_padding =
-      (GetContentsBounds().width() - background_card_size.width()) / 2 +
-      kCardifiedHorizontalPadding;
-  // The vertical padding should account for the fadeout mask.
-  const int vertical_padding =
-      (GetContentsBounds().height() - background_card_size.height()) / 2 +
-      GetAppListConfig().grid_fadeout_mask_height();
-  return gfx::Rect(
-      horizontal_padding, vertical_padding + vertical_page_start_offset,
-      background_card_size.width() - 2 * kCardifiedHorizontalPadding,
-      background_card_size.height());
-}
-
-void AppsGridView::AppendBackgroundCard() {
-  background_cards_.push_back(
-      std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR));
-  ui::Layer* current_layer = background_cards_.back().get();
-  current_layer->SetBounds(BackgroundCardBounds(background_cards_.size() - 1));
-  current_layer->SetVisible(true);
-  current_layer->SetRoundedCornerRadius(
-      gfx::RoundedCornersF(kBackgroundCardCornerRadius));
-  items_container_->layer()->Add(current_layer);
-}
-
-void AppsGridView::RemoveBackgroundCard() {
-  items_container_->layer()->Remove(background_cards_.back().get());
-  background_cards_.pop_back();
-}
-
-void AppsGridView::MaskContainerToBackgroundBounds() {
-  DCHECK(!background_cards_.empty());
-  // Mask apps grid container layer to the background card width.
-  layer()->SetClipRect(gfx::Rect(background_cards_[0]->bounds().x(), 0,
-                                 background_cards_[0]->bounds().width(),
-                                 layer()->bounds().height()));
-}
-
-void AppsGridView::RemoveAllBackgroundCards() {
-  for (auto& card : background_cards_)
-    items_container_->layer()->Remove(card.get());
-  background_cards_.clear();
-}
-
 void AppsGridView::OnAppListModelStatusChanged() {
   UpdatePulsingBlockViews();
   Layout();
@@ -2748,6 +2482,8 @@
 void AppsGridView::OnImplicitAnimationsCompleted() {
   if (layer()->opacity() == 0.0f)
     SetVisible(false);
+  // TODO(crbug.com/1211608): Refactor animation code so multiple animations
+  // don't share this method, then move code below to PagedAppsGridView.
   if (cardified_state_) {
     MaskContainerToBackgroundBounds();
     return;
@@ -2772,12 +2508,6 @@
     entry.view->DestroyLayer();
 }
 
-void AppsGridView::MaybeCallOnBoundsAnimatorDone() {
-  --bounds_animation_for_cardified_state_in_progress_;
-  if (bounds_animation_for_cardified_state_in_progress_ == 0)
-    OnBoundsAnimatorDone(/*animator=*/nullptr);
-}
-
 GridIndex AppsGridView::GetNearestTileIndexForPoint(
     const gfx::Point& point) const {
   gfx::Rect bounds = GetContentsBounds();
@@ -3222,25 +2952,6 @@
                             kMaxAppListAppMovingType);
 }
 
-// TODO(crbug.com/1211608): Move to PagedAppsGridView.
-void AppsGridView::UpdateTilePadding() {
-  gfx::Size content_size = GetContentsBounds().size();
-  const gfx::Size tile_size = GetTileViewSize();
-  if (cardified_state_)
-    content_size = gfx::ScaleToRoundedSize(content_size, kCardifiedScale);
-
-  // Item tiles should be evenly distributed in this view.
-  horizontal_tile_padding_ =
-      cols_ > 1 ? (content_size.width() - cols_ * tile_size.width()) /
-                      ((cols_ - 1) * 2)
-                : 0;
-  vertical_tile_padding_ =
-      rows_per_page_ > 1
-          ? (content_size.height() - rows_per_page_ * tile_size.height()) /
-                ((rows_per_page_ - 1) * 2)
-          : 0;
-}
-
 int AppsGridView::GetItemsNumOfPage(int page) const {
   if (page < 0 || page >= pagination_model_.total_pages())
     return 0;
@@ -3483,21 +3194,4 @@
   return new_page_flip_target;
 }
 
-void AppsGridView::SetHighlightedBackgroundCard(int new_highlighted_page) {
-  if (!IsValidPageFlipTarget(new_highlighted_page))
-    return;
-
-  if (new_highlighted_page != highlighted_page_) {
-    background_cards_[highlighted_page_]->SetColor(
-        GetAppListConfig().GetCardifiedBackgroundColor(
-            /*is_active=*/false));
-    if (static_cast<int>(background_cards_.size()) == new_highlighted_page)
-      AppendBackgroundCard();
-    background_cards_[new_highlighted_page]->SetColor(
-        GetAppListConfig().GetCardifiedBackgroundColor(/*is_active=*/true));
-
-    highlighted_page_ = new_highlighted_page;
-  }
-}
-
 }  // namespace ash
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index efe581d..405de59 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -20,7 +20,6 @@
 #include "ash/app_list/views/app_list_view.h"
 #include "ash/ash_export.h"
 #include "ash/public/cpp/pagination/pagination_model.h"
-#include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "ui/base/models/list_model_observer.h"
@@ -118,9 +117,6 @@
   // Returns the maximum size of the entire tile grid.
   gfx::Size GetMaximumTileGridSize(int cols, int rows_per_page) const;
 
-  // Returns the padding between each page of the apps grid.
-  int GetPaddingBetweenPages() const;
-
   // This resets the grid view to a fresh state for showing the app list.
   void ResetForShowApps();
 
@@ -288,29 +284,6 @@
   // view hierarchy.
   const AppListConfig& GetAppListConfig() const;
 
-  // Helper functions to start the Apps Grid Cardified state.
-  // The cardified state scales down apps and is shown when the user drags an
-  // app in the AppList.
-  void StartAppsGridCardifiedView();
-  // Ends the Apps Grid Cardified state and sets it to normal.
-  void EndAppsGridCardifiedView();
-  // Animates individual elements of the apps grid to and from cardified state.
-  void AnimateCardifiedState();
-  // Translates the items container view to center the current page in the apps
-  // grid.
-  void RecenterItemsContainer();
-  // Calculates the background bounds for the grid depending on the value of
-  // |cardified_state_|
-  gfx::Rect BackgroundCardBounds(int new_page_index);
-  // Appends a background card to the back of |background_cards_|.
-  void AppendBackgroundCard();
-  // Removes the background card at the end of |background_cards_|.
-  void RemoveBackgroundCard();
-  // Masks the apps grid container to background cards bounds.
-  void MaskContainerToBackgroundBounds();
-  // Removes all background cards from |background_cards_|.
-  void RemoveAllBackgroundCards();
-
   // Return the view model.
   views::ViewModelT<AppListItemView>* view_model() { return &view_model_; }
 
@@ -349,14 +322,13 @@
     return bounds_animator_.get();
   }
 
-  bool cardified_state_for_testing() const { return cardified_state_; }
-
-  int BackgroundCardCountForTesting() const { return background_cards_.size(); }
-
  protected:
   // The cardified apps grid should be scaled down by this factor.
   static constexpr float kCardifiedScale = 0.84f;
 
+  // The duration in ms for most of the apps grid view animations.
+  static constexpr int kDefaultAnimationDuration = 200;
+
   // Returns the size of a tile view excluding its padding.
   virtual gfx::Size GetTileViewSize() const = 0;
 
@@ -366,12 +338,22 @@
   // Returns the size of the entire tile grid.
   virtual gfx::Size GetTileGridSize() const = 0;
 
-  // Creates a layer mask for gradient alpha when the feature is enabled. The
-  // gradient appears at the top and bottom of the apps grid to create a
-  // "fade out" effect when
-  // TODO(crbug.com/1211608): Move to PagedAppsGridView when cardified view
-  // methods move.
-  virtual void MaybeCreateGradientMask() = 0;
+  // Returns the padding between each page of the apps grid, or zero if the grid
+  // does not use pages.
+  virtual int GetPaddingBetweenPages() const = 0;
+
+  // Starts the "cardified" state if the subclass supports it.
+  virtual void MaybeStartCardifiedView() {}
+
+  // Ends the "cardified" state if the subclass supports it.
+  virtual void MaybeEndCardifiedView() {}
+
+  // TODO(crbug.com/1211608): Remove these methods. They exist to allow this
+  // class to call into PagedAppsGridView from UpdateDrag() and
+  // OnImplicitAnimationsCompleted().
+  virtual void SetHighlightedBackgroundCard(int new_highlighted_page) {}
+  virtual void MaskContainerToBackgroundBounds() {}
+  virtual void RemoveAllBackgroundCards() {}
 
   // Calculates the item views' bounds for non-folder.
   virtual void CalculateIdealBounds();
@@ -379,9 +361,6 @@
   // Calculates the item views' bounds for folder.
   void CalculateIdealBoundsForFolder();
 
-  // Update the padding of tile view based on the contents bounds.
-  void UpdateTilePadding();
-
   // Gets the bounds of the tile located at |index|, where |index| contains the
   // page/slot info.
   gfx::Rect GetExpectedTileBounds(const GridIndex& index) const;
@@ -400,10 +379,17 @@
   // Cancels any context menus showing for app items on the current page.
   void CancelContextMenusOnCurrentPage();
 
+  // Returns true if the page is the right target to flip to.
+  bool IsValidPageFlipTarget(int page) const;
+
   // Starts the page flip timer if |drag_point| is in left/right side page flip
   // zone or is over page switcher.
   void MaybeStartPageFlipTimer(const gfx::Point& drag_point);
 
+  // views::BoundsAnimatorObserver:
+  void OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) override;
+  void OnBoundsAnimatorDone(views::BoundsAnimator* animator) override;
+
   bool ignore_layout() const { return ignore_layout_; }
   views::BoundsAnimator* bounds_animator() { return bounds_animator_.get(); }
   views::View* items_container() { return items_container_; }
@@ -411,8 +397,6 @@
     return pulsing_blocks_model_;
   }
   int reorder_placeholder_slot() const { return reorder_placeholder_.slot; }
-  int vertical_tile_padding() const { return vertical_tile_padding_; }
-  int horizontal_tile_padding() const { return horizontal_tile_padding_; }
   const gfx::Point& last_drag_point() const { return last_drag_point_; }
   bool handling_keyboard_move() const { return handling_keyboard_move_; }
 
@@ -436,13 +420,20 @@
   // The location of |drag_view_| when the drag started.
   gfx::Point drag_view_start_;
 
-  // True if the AppList is in cardified state.
+  // If true, Layout() does nothing. See where set for details.
+  bool ignore_layout_ = false;
+
+  // True if the AppList is in cardified state. "Cardified" means showing a
+  // rounded rectangle background "card" behind each page during a drag. The
+  // grid icons are reduced in size in this state.
   // TODO(crbug.com/1211608): Move cardified state members to PagedAppsGridView.
   bool cardified_state_ = false;
 
-  // Layer array for apps grid background cards. Used to display the background
-  // card during cardified state.
-  std::vector<std::unique_ptr<ui::Layer>> background_cards_;
+  int bounds_animation_for_cardified_state_in_progress_ = 0;
+
+  // Tile spacing between the tile views.
+  int horizontal_tile_padding_ = 0;
+  int vertical_tile_padding_ = 0;
 
  private:
   friend class test::AppsGridViewTestApi;
@@ -470,8 +461,7 @@
   // number of apps.
   void UpdatePulsingBlockViews();
 
-  std::unique_ptr<AppListItemView> CreateViewForItem(AppListItem* item,
-                                                     bool is_in_folder = false);
+  std::unique_ptr<AppListItemView> CreateViewForItem(AppListItem* item);
 
   std::unique_ptr<AppListItemView> CreateViewForItemAtIndex(size_t index);
 
@@ -598,13 +588,6 @@
   // ui::ImplicitAnimationObserver overrides:
   void OnImplicitAnimationsCompleted() override;
 
-  // views::BoundsAnimatorObserver:
-  void OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) override;
-  void OnBoundsAnimatorDone(views::BoundsAnimator* animator) override;
-
-  // Call OnBoundsAnimatorDone when all layer animations finish.
-  void MaybeCallOnBoundsAnimatorDone();
-
   // Hide a given view temporarily without losing (mouse) events and / or
   // changing the size of it. If |immediate| is set the change will be
   // immediately applied - otherwise it will change gradually.
@@ -712,9 +695,6 @@
   // can be moved.
   bool IsValidReorderTargetIndex(const GridIndex& index) const;
 
-  // Returns true if the page is the right target to flip to.
-  bool IsValidPageFlipTarget(int page) const;
-
   // Returns model index of the item view of the specified item.
   int GetModelIndexOfItem(const AppListItem* item) const;
 
@@ -795,9 +775,6 @@
   // Obtains the target page to flip for |drag_point|.
   int GetPageFlipTargetForDrag(const gfx::Point& drag_point);
 
-  // Updates the highlighted background card. Used only for cardified state.
-  void SetHighlightedBackgroundCard(int new_highlighted_page);
-
   AppListModel* model_ = nullptr;         // Owned by AppListView.
   AppListItemList* item_list_ = nullptr;  // Not owned.
 
@@ -908,10 +885,6 @@
   // non-folder.
   bool extra_page_opened_ = false;
 
-  // Tile spacing between the tile views.
-  int horizontal_tile_padding_ = 0;
-  int vertical_tile_padding_ = 0;
-
   // The drop location of the most recent reorder related accessibility event.
   GridIndex last_reorder_a11y_event_location_;
 
@@ -923,16 +896,6 @@
 
   GhostImageView* current_ghost_view_ = nullptr;
   GhostImageView* last_ghost_view_ = nullptr;
-
-  // If true, Layout() does nothing. See where set for details.
-  bool ignore_layout_ = false;
-
-  // The highlighted page during cardified state.
-  int highlighted_page_ = -1;
-
-  int bounds_animation_for_cardified_state_in_progress_ = 0;
-
-  base::WeakPtrFactory<AppsGridView> weak_ptr_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index 635a0ab..c1c0491 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -175,7 +175,7 @@
     set_display_type(ash::SearchResultDisplayType::kChip);
     set_is_recommendation(true);
   }
-  ~TestSuggestedSearchResult() override {}
+  ~TestSuggestedSearchResult() override = default;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestSuggestedSearchResult);
@@ -412,10 +412,10 @@
     apps_grid_view_->MoveItemInModel(item_view, target);
   }
 
-  TestAppListColorProvider color_provider_;  // Needed by AppListView.
-  AppListView* app_list_view_ = nullptr;     // Owned by native widget.
-  AppsGridView* apps_grid_view_ = nullptr;   // Owned by |app_list_view_|.
-  ContentsView* contents_view_ = nullptr;    // Owned by |app_list_view_|.
+  TestAppListColorProvider color_provider_;      // Needed by AppListView.
+  AppListView* app_list_view_ = nullptr;         // Owned by native widget.
+  PagedAppsGridView* apps_grid_view_ = nullptr;  // Owned by |app_list_view_|.
+  ContentsView* contents_view_ = nullptr;        // Owned by |app_list_view_|.
   SearchResultContainerView* suggestions_container_ =
       nullptr;                                    // Owned by |apps_grid_view_|.
   ExpandArrowView* expand_arrow_view_ = nullptr;  // Owned by |apps_grid_view_|.
diff --git a/ash/app_list/views/paged_apps_grid_view.cc b/ash/app_list/views/paged_apps_grid_view.cc
index 3ae00b6e..c4c944e 100644
--- a/ash/app_list/views/paged_apps_grid_view.cc
+++ b/ash/app_list/views/paged_apps_grid_view.cc
@@ -5,20 +5,27 @@
 #include "ash/app_list/views/paged_apps_grid_view.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "ash/app_list/views/app_list_item_view.h"
 #include "ash/app_list/views/app_list_main_view.h"
 #include "ash/app_list/views/contents_view.h"
 #include "ash/public/cpp/app_list/app_list_config.h"
+#include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/pagination/pagination_controller.h"
 #include "ash/public/cpp/pagination/pagination_model.h"
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/callback.h"
 #include "base/check.h"
 #include "base/metrics/histogram_macros.h"
 #include "cc/paint/paint_flags.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/compositor/animation_throughput_reporter.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/paint_recorder.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/events/event.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/canvas.h"
@@ -30,6 +37,7 @@
 #include "ui/gfx/geometry/vector2d_f.h"
 #include "ui/gfx/skia_paint_util.h"
 #include "ui/gfx/transform.h"
+#include "ui/gfx/transform_util.h"
 #include "ui/views/paint_info.h"
 #include "ui/views/view.h"
 #include "ui/views/view_model_utils.h"
@@ -49,6 +57,45 @@
     "Apps.PaginationTransition.DragScroll.PresentationTime.MaxLatency."
     "TabletMode";
 
+// Vertical padding between the apps grid pages in cardified state.
+constexpr int kCardifiedPaddingBetweenPages = 12;
+
+// Horizontal padding of the apps grid page in cardified state.
+constexpr int kCardifiedHorizontalPadding = 16;
+
+// The radius of the corner of the background cards in the apps grid.
+constexpr int kBackgroundCardCornerRadius = 12;
+
+// The opacity for the background cards when hidden.
+constexpr float kBackgroundCardOpacityHide = 0.0f;
+
+// Animation curve used for entering and exiting cardified state.
+constexpr gfx::Tween::Type kCardifiedStateTweenType =
+    gfx::Tween::LINEAR_OUT_SLOW_IN;
+
+// CardifiedAnimationObserver is used to observe the animation for toggling the
+// cardified state of the apps grid view. We used this to ensure app icons are
+// repainted with the correct bounds and scale.
+class CardifiedAnimationObserver : public ui::ImplicitAnimationObserver {
+ public:
+  explicit CardifiedAnimationObserver(base::OnceClosure callback)
+      : callback_(std::move(callback)) {}
+  CardifiedAnimationObserver(const CardifiedAnimationObserver&) = delete;
+  CardifiedAnimationObserver& operator=(const CardifiedAnimationObserver&) =
+      delete;
+  ~CardifiedAnimationObserver() override = default;
+
+  // ui::ImplicitAnimationObserver:
+  void OnImplicitAnimationsCompleted() override {
+    if (callback_)
+      std::move(callback_).Run();
+    delete this;
+  }
+
+ private:
+  base::OnceClosure callback_;
+};
+
 }  // namespace
 
 // A layer delegate used for PagedAppsGridView's mask layer, with top and bottom
@@ -408,7 +455,7 @@
         GetAppListConfig().grid_tile_spacing_in_folder() / 2;
     return gfx::Insets(-tile_padding_in_folder, -tile_padding_in_folder);
   }
-  return gfx::Insets(-vertical_tile_padding(), -horizontal_tile_padding());
+  return gfx::Insets(-vertical_tile_padding_, -horizontal_tile_padding_);
 }
 
 gfx::Size PagedAppsGridView::GetTileGridSize() const {
@@ -419,22 +466,22 @@
   return rect.size();
 }
 
-void PagedAppsGridView::MaybeCreateGradientMask() {
-  if (!is_in_folder() && features::IsBackgroundBlurEnabled()) {
-    // TODO(newcomer): Improve implementation of the mask layer so we can
-    // enable it on all devices https://crbug.com/765292.
-    if (!layer()->layer_mask_layer()) {
-      // Always create a new layer. The layer may be recreated by animation,
-      // and using the mask layer used by the detached layer can lead to
-      // crash. b/118822974.
-      if (!fadeout_layer_delegate_) {
-        fadeout_layer_delegate_ = std::make_unique<FadeoutLayerDelegate>(
-            GetAppListConfig().grid_fadeout_mask_height());
-        fadeout_layer_delegate_->layer()->SetBounds(layer()->bounds());
-      }
-      layer()->SetMaskLayer(fadeout_layer_delegate_->layer());
-    }
-  }
+int PagedAppsGridView::GetPaddingBetweenPages() const {
+  // In cardified state, padding between pages should be fixed  and it should
+  // include background card padding.
+  return cardified_state_
+             ? kCardifiedPaddingBetweenPages + 2 * vertical_tile_padding_
+             : GetAppListConfig().page_spacing();
+}
+
+void PagedAppsGridView::MaybeStartCardifiedView() {
+  if (!cardified_state_)
+    StartAppsGridCardifiedView();
+}
+
+void PagedAppsGridView::MaybeEndCardifiedView() {
+  if (cardified_state_)
+    EndAppsGridCardifiedView();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -617,4 +664,276 @@
   return true;
 }
 
+void PagedAppsGridView::MaybeCreateGradientMask() {
+  if (!is_in_folder() && features::IsBackgroundBlurEnabled()) {
+    // TODO(newcomer): Improve implementation of the mask layer so we can
+    // enable it on all devices https://crbug.com/765292.
+    if (!layer()->layer_mask_layer()) {
+      // Always create a new layer. The layer may be recreated by animation,
+      // and using the mask layer used by the detached layer can lead to
+      // crash. b/118822974.
+      if (!fadeout_layer_delegate_) {
+        fadeout_layer_delegate_ = std::make_unique<FadeoutLayerDelegate>(
+            GetAppListConfig().grid_fadeout_mask_height());
+        fadeout_layer_delegate_->layer()->SetBounds(layer()->bounds());
+      }
+      layer()->SetMaskLayer(fadeout_layer_delegate_->layer());
+    }
+  }
+}
+
+void PagedAppsGridView::StartAppsGridCardifiedView() {
+  if (!app_list_features::IsNewDragSpecInLauncherEnabled())
+    return;
+  if (is_in_folder())
+    return;
+  DCHECK(!cardified_state_);
+  StopObservingImplicitAnimations();
+  RemoveAllBackgroundCards();
+  // Calculate background bounds for a normal grid so it animates from the
+  // normal to the cardified bounds with the icons.
+  // Add an extra card for the peeking page in the last page. This hints users
+  // that apps can be dragged past the last existing page.
+  for (int i = 0; i < pagination_model_.total_pages() + 1; i++)
+    AppendBackgroundCard();
+  cardified_state_ = true;
+  UpdateTilePadding();
+  MaybeCreateGradientMask();
+  AnimateCardifiedState();
+}
+
+void PagedAppsGridView::EndAppsGridCardifiedView() {
+  if (!app_list_features::IsNewDragSpecInLauncherEnabled())
+    return;
+  if (is_in_folder())
+    return;
+  DCHECK(cardified_state_);
+  StopObservingImplicitAnimations();
+  cardified_state_ = false;
+  // Update the padding between tiles, so we can animate back the apps grid
+  // elements to their original positions.
+  UpdateTilePadding();
+  AnimateCardifiedState();
+  layer()->SetClipRect(gfx::Rect());
+}
+
+void PagedAppsGridView::AnimateCardifiedState() {
+  if (GetWidget()) {
+    // Normally Layout() cancels any animations. At this point there may be a
+    // pending Layout(), force it now so that one isn't triggered part way
+    // through the animation. Further, ignore this layout so that the position
+    // isn't reset.
+    DCHECK(!ignore_layout_);
+    base::AutoReset<bool> auto_reset(&ignore_layout_, true);
+    GetWidget()->LayoutRootViewIfNecessary();
+  }
+
+  CalculateIdealBounds();
+  // Cache the current item container position, as RecenterItemsContainer() may
+  // change it.
+  gfx::Point start_position = items_container()->origin();
+  RecenterItemsContainer();
+  gfx::Vector2d translate_offset(
+      0, start_position.y() - items_container()->origin().y());
+  if (cardified_state_) {
+    // The drag view is translated when the items container is recentered.
+    // Reposition the drag view to compensate for the translation offset.
+    drag_view_start_ += translate_offset;
+    drag_view_->SetPosition(drag_view_start_);
+  }
+  // Drag view can be nullptr by EndDrag.
+  const int number_of_views_to_animate =
+      view_model()->view_size() - (drag_view_ ? 1 : 0);
+
+  base::RepeatingClosure on_bounds_animator_callback;
+  if (number_of_views_to_animate > 0) {
+    on_bounds_animator_callback = base::BarrierClosure(
+        number_of_views_to_animate,
+        base::BindOnce(&PagedAppsGridView::MaybeCallOnBoundsAnimatorDone,
+                       weak_ptr_factory_.GetWeakPtr()));
+    bounds_animation_for_cardified_state_in_progress_++;
+  }
+
+  for (int i = 0; i < view_model()->view_size(); ++i) {
+    AppListItemView* entry_view = view_model()->view_at(i);
+    // We don't animate bounds for the dragged view.
+    if (entry_view == drag_view_)
+      continue;
+    // Reposition view bounds to compensate for the translation offset.
+    gfx::Rect current_bounds = entry_view->bounds();
+    current_bounds.Offset(translate_offset);
+
+    entry_view->EnsureLayer();
+
+    if (cardified_state_)
+      entry_view->EnterCardifyState();
+    else
+      entry_view->ExitCardifyState();
+
+    gfx::Rect target_bounds(view_model()->ideal_bounds(i));
+    entry_view->SetBoundsRect(target_bounds);
+
+    // View bounds are currently |target_bounds|. Transform the view so it
+    // appears in |current_bounds|.
+    gfx::Transform transform = gfx::TransformBetweenRects(
+        gfx::RectF(target_bounds), gfx::RectF(current_bounds));
+    entry_view->layer()->SetTransform(transform);
+
+    ui::ScopedLayerAnimationSettings animator(
+        entry_view->layer()->GetAnimator());
+    animator.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+    animator.SetTweenType(kCardifiedStateTweenType);
+    if (!cardified_state_) {
+      animator.SetTransitionDuration(
+          base::TimeDelta::FromMilliseconds(kDefaultAnimationDuration));
+    }
+    // When the animations are done, discard the layer and reset view to
+    // proper scale.
+    animator.AddObserver(
+        new CardifiedAnimationObserver(on_bounds_animator_callback));
+    entry_view->layer()->SetTransform(gfx::Transform());
+  }
+
+  for (size_t i = 0; i < background_cards_.size(); i++) {
+    auto& background_card = background_cards_[i];
+    // Reposition card bounds to compensate for the translation offset.
+    gfx::Rect background_bounds = background_card->bounds();
+    background_bounds.Offset(translate_offset);
+    background_card->SetBounds(background_bounds);
+    ui::ScopedLayerAnimationSettings animator(background_card->GetAnimator());
+    animator.SetTweenType(kCardifiedStateTweenType);
+    animator.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+    if (!cardified_state_) {
+      animator.SetTransitionDuration(
+          base::TimeDelta::FromMilliseconds(kDefaultAnimationDuration));
+    }
+    animator.AddObserver(this);
+    ui::AnimationThroughputReporter reporter(
+        background_card->GetAnimator(),
+        metrics_util::ForSmoothness(
+            base::BindRepeating(&ReportCardifiedSmoothness, cardified_state_)));
+    if (cardified_state_) {
+      const bool is_active_page =
+          background_cards_[pagination_model_.selected_page()] ==
+          background_card;
+      background_card->SetColor(
+          GetAppListConfig().GetCardifiedBackgroundColor(is_active_page));
+    } else {
+      background_card->SetOpacity(kBackgroundCardOpacityHide);
+    }
+    background_card->SetBounds(BackgroundCardBounds(i));
+  }
+  highlighted_page_ = pagination_model_.selected_page();
+}
+
+void PagedAppsGridView::MaybeCallOnBoundsAnimatorDone() {
+  --bounds_animation_for_cardified_state_in_progress_;
+  if (bounds_animation_for_cardified_state_in_progress_ == 0)
+    OnBoundsAnimatorDone(/*animator=*/nullptr);
+}
+
+void PagedAppsGridView::RecenterItemsContainer() {
+  const int pages = pagination_model_.total_pages();
+  const int current_page = pagination_model_.selected_page();
+  const int page_height = GetTileGridSize().height() + GetPaddingBetweenPages();
+  items_container()->SetBoundsRect(gfx::Rect(0, -page_height * current_page,
+                                             GetContentsBounds().width(),
+                                             page_height * pages));
+}
+
+gfx::Rect PagedAppsGridView::BackgroundCardBounds(int new_page_index) {
+  // The size of the grid excluding the outer padding.
+  const gfx::Size grid_size = GetTileGridSize();
+  // The size for the background card that will be displayed. The outer padding
+  // of the grid need to be added.
+  const gfx::Size background_card_size =
+      grid_size +
+      gfx::Size(2 * horizontal_tile_padding_, 2 * vertical_tile_padding_);
+  const int padding_between_pages = GetPaddingBetweenPages();
+  // The space that each page occupies in the items container. This is the size
+  // of the grid without outer padding plus the padding between pages.
+  const int grid_size_height = grid_size.height() + padding_between_pages;
+  // We position a new card in the last place in items container view.
+  const int vertical_page_start_offset = grid_size_height * new_page_index;
+  // Add a padding on the sides to make space for pagination preview.
+  const int horizontal_padding =
+      (GetContentsBounds().width() - background_card_size.width()) / 2 +
+      kCardifiedHorizontalPadding;
+  // The vertical padding should account for the fadeout mask.
+  const int vertical_padding =
+      (GetContentsBounds().height() - background_card_size.height()) / 2 +
+      GetAppListConfig().grid_fadeout_mask_height();
+  return gfx::Rect(
+      horizontal_padding, vertical_padding + vertical_page_start_offset,
+      background_card_size.width() - 2 * kCardifiedHorizontalPadding,
+      background_card_size.height());
+}
+
+void PagedAppsGridView::AppendBackgroundCard() {
+  background_cards_.push_back(
+      std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR));
+  ui::Layer* current_layer = background_cards_.back().get();
+  current_layer->SetBounds(BackgroundCardBounds(background_cards_.size() - 1));
+  current_layer->SetVisible(true);
+  current_layer->SetRoundedCornerRadius(
+      gfx::RoundedCornersF(kBackgroundCardCornerRadius));
+  items_container()->layer()->Add(current_layer);
+}
+
+void PagedAppsGridView::RemoveBackgroundCard() {
+  items_container()->layer()->Remove(background_cards_.back().get());
+  background_cards_.pop_back();
+}
+
+void PagedAppsGridView::MaskContainerToBackgroundBounds() {
+  DCHECK(!background_cards_.empty());
+  // Mask apps grid container layer to the background card width.
+  layer()->SetClipRect(gfx::Rect(background_cards_[0]->bounds().x(), 0,
+                                 background_cards_[0]->bounds().width(),
+                                 layer()->bounds().height()));
+}
+
+void PagedAppsGridView::RemoveAllBackgroundCards() {
+  for (auto& card : background_cards_)
+    items_container()->layer()->Remove(card.get());
+  background_cards_.clear();
+}
+
+void PagedAppsGridView::SetHighlightedBackgroundCard(int new_highlighted_page) {
+  if (!IsValidPageFlipTarget(new_highlighted_page))
+    return;
+
+  if (new_highlighted_page != highlighted_page_) {
+    background_cards_[highlighted_page_]->SetColor(
+        GetAppListConfig().GetCardifiedBackgroundColor(
+            /*is_active=*/false));
+    if (static_cast<int>(background_cards_.size()) == new_highlighted_page)
+      AppendBackgroundCard();
+    background_cards_[new_highlighted_page]->SetColor(
+        GetAppListConfig().GetCardifiedBackgroundColor(/*is_active=*/true));
+
+    highlighted_page_ = new_highlighted_page;
+  }
+}
+
+void PagedAppsGridView::UpdateTilePadding() {
+  gfx::Size content_size = GetContentsBounds().size();
+  const gfx::Size tile_size = GetTileViewSize();
+  if (cardified_state_)
+    content_size = gfx::ScaleToRoundedSize(content_size, kCardifiedScale);
+
+  // Item tiles should be evenly distributed in this view.
+  horizontal_tile_padding_ =
+      cols() > 1 ? (content_size.width() - cols() * tile_size.width()) /
+                       ((cols() - 1) * 2)
+                 : 0;
+  vertical_tile_padding_ =
+      rows_per_page() > 1
+          ? (content_size.height() - rows_per_page() * tile_size.height()) /
+                ((rows_per_page() - 1) * 2)
+          : 0;
+}
+
 }  // namespace ash
diff --git a/ash/app_list/views/paged_apps_grid_view.h b/ash/app_list/views/paged_apps_grid_view.h
index 3c52026..e4b98b3 100644
--- a/ash/app_list/views/paged_apps_grid_view.h
+++ b/ash/app_list/views/paged_apps_grid_view.h
@@ -6,10 +6,12 @@
 #define ASH_APP_LIST_VIEWS_PAGED_APPS_GRID_VIEW_H_
 
 #include <memory>
+#include <vector>
 
 #include "ash/app_list/views/apps_grid_view.h"
 #include "ash/ash_export.h"
 #include "ash/public/cpp/pagination/pagination_model_observer.h"
+#include "base/memory/ref_counted.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/compositor/throughput_tracker.h"
 #include "ui/events/types/event_type.h"
@@ -19,6 +21,10 @@
 class Vector2d;
 }  // namespace gfx
 
+namespace ui {
+class Layer;
+}  // namespace ui
+
 namespace ash {
 
 class ContentsView;
@@ -56,7 +62,9 @@
   gfx::Size GetTileViewSize() const override;
   gfx::Insets GetTilePadding() const override;
   gfx::Size GetTileGridSize() const override;
-  void MaybeCreateGradientMask() override;
+  int GetPaddingBetweenPages() const override;
+  void MaybeStartCardifiedView() override;
+  void MaybeEndCardifiedView() override;
 
   // PaginationModelObserver:
   void TotalPagesChanged(int previous_page_count, int new_page_count) override;
@@ -68,6 +76,9 @@
   void ScrollStarted() override;
   void ScrollEnded() override;
 
+  bool cardified_state_for_testing() const { return cardified_state_; }
+  int BackgroundCardCountForTesting() const { return background_cards_.size(); }
+
  private:
   class FadeoutLayerDelegate;
 
@@ -75,6 +86,42 @@
   // handled by PagedAppsGridView.
   bool ShouldHandleDragEvent(const ui::LocatedEvent& event);
 
+  // Creates a layer mask for gradient alpha when the feature is enabled. The
+  // gradient appears at the top and bottom of the apps grid to create a
+  // "fade out" effect when
+  void MaybeCreateGradientMask();
+
+  // Helper functions to start the Apps Grid Cardified state.
+  // The cardified state scales down apps and is shown when the user drags an
+  // app in the AppList.
+  void StartAppsGridCardifiedView();
+  // Ends the Apps Grid Cardified state and sets it to normal.
+  void EndAppsGridCardifiedView();
+  // Animates individual elements of the apps grid to and from cardified state.
+  void AnimateCardifiedState();
+  // Call OnBoundsAnimatorDone when all layer animations finish.
+  void MaybeCallOnBoundsAnimatorDone();
+  // Translates the items container view to center the current page in the apps
+  // grid.
+  void RecenterItemsContainer();
+  // Calculates the background bounds for the grid depending on the value of
+  // |cardified_state_|
+  gfx::Rect BackgroundCardBounds(int new_page_index);
+  // Appends a background card to the back of |background_cards_|.
+  void AppendBackgroundCard();
+  // Removes the background card at the end of |background_cards_|.
+  void RemoveBackgroundCard();
+  // Masks the apps grid container to background cards bounds.
+  // TODO(crbug.com/1211608): Remove "override" from these methods.
+  void MaskContainerToBackgroundBounds() override;
+  // Removes all background cards from |background_cards_|.
+  void RemoveAllBackgroundCards() override;
+  // Updates the highlighted background card. Used only for cardified state.
+  void SetHighlightedBackgroundCard(int new_highlighted_page) override;
+
+  // Update the padding of tile view based on the contents bounds.
+  void UpdateTilePadding();
+
   // Created by AppListMainView, owned by views hierarchy.
   ContentsView* const contents_view_;
 
@@ -100,6 +147,15 @@
 
   // Records the presentation time for apps grid dragging.
   std::unique_ptr<PresentationTimeRecorder> presentation_time_recorder_;
+
+  // The highlighted page during cardified state.
+  int highlighted_page_ = -1;
+
+  // Layer array for apps grid background cards. Used to display the background
+  // card during cardified state.
+  std::vector<std::unique_ptr<ui::Layer>> background_cards_;
+
+  base::WeakPtrFactory<PagedAppsGridView> weak_ptr_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/clipboard/clipboard_history_controller_impl.cc b/ash/clipboard/clipboard_history_controller_impl.cc
index d6782a5..b1122e9 100644
--- a/ash/clipboard/clipboard_history_controller_impl.cc
+++ b/ash/clipboard/clipboard_history_controller_impl.cc
@@ -359,6 +359,8 @@
       }
     }
     item_value.SetKey("id", base::Value(item.id().ToString()));
+    item_value.SetKey("timeCopied",
+                      base::Value(item.time_copied().ToJsTimeIgnoringNull()));
     item_results.Append(std::move(item_value));
   }
 
diff --git a/ash/clipboard/clipboard_history_item.cc b/ash/clipboard/clipboard_history_item.cc
index 11fb9c0..2d72aa6 100644
--- a/ash/clipboard/clipboard_history_item.cc
+++ b/ash/clipboard/clipboard_history_item.cc
@@ -7,7 +7,9 @@
 namespace ash {
 
 ClipboardHistoryItem::ClipboardHistoryItem(ui::ClipboardData data)
-    : id_(base::UnguessableToken::Create()), data_(std::move(data)) {}
+    : id_(base::UnguessableToken::Create()),
+      data_(std::move(data)),
+      time_copied_(base::Time::Now()) {}
 
 ClipboardHistoryItem::ClipboardHistoryItem(const ClipboardHistoryItem&) =
     default;
diff --git a/ash/clipboard/clipboard_history_item.h b/ash/clipboard/clipboard_history_item.h
index 704c787..7c949c7 100644
--- a/ash/clipboard/clipboard_history_item.h
+++ b/ash/clipboard/clipboard_history_item.h
@@ -6,6 +6,9 @@
 #define ASH_CLIPBOARD_CLIPBOARD_HISTORY_ITEM_H_
 
 #include "ash/ash_export.h"
+#include "base/i18n/time_formatting.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "base/unguessable_token.h"
 #include "ui/base/clipboard/clipboard_data.h"
 
@@ -27,11 +30,13 @@
 
   const base::UnguessableToken& id() const { return id_; }
   const ui::ClipboardData& data() const { return data_; }
+  const base::Time time_copied() const { return time_copied_; }
 
  private:
   // Unique identifier.
   base::UnguessableToken id_;
   ui::ClipboardData data_;
+  base::Time time_copied_;
 };
 
 }  // namespace ash
diff --git a/ash/content/shimless_rma/resources/fake_data.js b/ash/content/shimless_rma/resources/fake_data.js
index d603c1f..ac132e8 100644
--- a/ash/content/shimless_rma/resources/fake_data.js
+++ b/ash/content/shimless_rma/resources/fake_data.js
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {Component, ComponentRepairState, ComponentType, RmadErrorCode, RmaState, State} from './shimless_rma_types.js';
+import {Component, ComponentRepairState, ComponentType, RmadErrorCode, RmaState, StateResult} from './shimless_rma_types.js';
 
-/** @type {!Array<!State>} */
+/** @type {!Array<!StateResult>} */
 export const fakeStates = [
   {state: RmaState.kWelcomeScreen, error: RmadErrorCode.kOk},
   {state: RmaState.kUpdateChrome, error: RmadErrorCode.kOk},
diff --git a/ash/content/shimless_rma/resources/fake_shimless_rma_service.js b/ash/content/shimless_rma/resources/fake_shimless_rma_service.js
index 70748644..b32b11be 100644
--- a/ash/content/shimless_rma/resources/fake_shimless_rma_service.js
+++ b/ash/content/shimless_rma/resources/fake_shimless_rma_service.js
@@ -6,7 +6,7 @@
 import {FakeObservables} from 'chrome://resources/ash/common/fake_observables.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 
-import {CalibrationComponent, CalibrationObserver, Component, ComponentRepairState, ComponentType, CurrentState, ErrorObserver, HardwareWriteProtectionStateObserver, NextState, PowerCableStateObserver, PrevState, ProvisioningObserver, ProvisioningStep, RmadErrorCode, RmaState, ShimlessRmaServiceInterface, State} from './shimless_rma_types.js';
+import {CalibrationComponent, CalibrationObserver, Component, ComponentRepairState, ComponentType, ErrorObserver, HardwareWriteProtectionStateObserver, PowerCableStateObserver, ProvisioningObserver, ProvisioningStep, RmadErrorCode, RmaState, ShimlessRmaServiceInterface, StateResult} from './shimless_rma_types.js';
 
 /** @implements {ShimlessRmaServiceInterface} */
 export class FakeShimlessRmaService {
@@ -16,7 +16,7 @@
 
     /**
      * The list of states for this RMA flow.
-     * @private {!Array<!State>}
+     * @private {!Array<!StateResult>}
      */
     this.states_ = [];
 
@@ -81,7 +81,7 @@
    * list, and return kTransitionFailed if it would move off either end.
    * getCurrentState always return the state at the current index.
    *
-   * @param {!Array<!State>} states
+   * @param {!Array<!StateResult>} states
    */
   setStates(states) {
     this.states_ = states;
@@ -89,7 +89,7 @@
   }
 
   /**
-   * @return {!Promise<!CurrentState>}
+   * @return {!Promise<!StateResult>}
    */
   getCurrentState() {
     // As getNextState and getPrevState can modify the result of this function
@@ -108,7 +108,7 @@
   }
 
   /**
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   getNextState() {
     // As getNextState and getPrevState can modify the result of this function
@@ -130,7 +130,7 @@
   }
 
   /**
-   * @return {!Promise<!PrevState>}
+   * @return {!Promise<!StateResult>}
    */
   getPrevState() {
     // As getNextState and getPrevState can modify the result of this function
@@ -210,7 +210,7 @@
   }
 
   /**
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   setSameOwner() {
     return this.getNextStateForMethod_(
@@ -218,7 +218,7 @@
   }
 
   /**
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   setDifferentOwner() {
     return this.getNextStateForMethod_(
@@ -241,7 +241,7 @@
   }
 
   /**
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   chooseManuallyDisableWriteProtect() {
     return this.getNextStateForMethod_(
@@ -250,7 +250,7 @@
   }
 
   /**
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   chooseRsuDisableWriteProtect() {
     return this.getNextStateForMethod_(
@@ -260,7 +260,7 @@
 
   /**
    * @param {string} code
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   setRsuDisableWriteProtectCode(code) {
     // TODO(gavindodd): Send the code over mojo.
@@ -285,7 +285,7 @@
 
   /**
    * @param {!Array<!Component>} components
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   setComponentsRepairState(components) {
     return this.getNextStateForMethod_(
@@ -293,7 +293,7 @@
   }
 
   /**
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   reworkMainboard() {
     return this.getNextStateForMethod_(
@@ -315,7 +315,7 @@
   }
 
   /**
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   reimageSkipped() {
     return this.getNextStateForMethod_(
@@ -323,7 +323,7 @@
   }
 
   /**
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   reimageFromDownload() {
     return this.getNextStateForMethod_(
@@ -331,7 +331,7 @@
   }
 
   /**
-   * @return {!Promise<!NextState>}
+   * @return {!Promise<!StateResult>}
    */
   reimageFromUsb() {
     return this.getNextStateForMethod_(
@@ -741,29 +741,29 @@
    * @private
    * @param {string} method
    * @param {!RmaState} expectedState
-   * @returns {!Promise<!NextState>}
+   * @returns {!Promise<!StateResult>}
    */
   getNextStateForMethod_(method, expectedState) {
     if (this.states_.length === 0) {
-      this.setFakeNextStateForMethod_(
+      this.setFakeStateForMethod_(
           method, RmaState.kUnknown, RmadErrorCode.kRmaNotRequired);
     } else if (this.stateIndex_ >= this.states_.length - 1) {
       // It should not be possible for stateIndex_ to be out of range unless
       // there is a bug in the fake.
       assert(this.stateIndex_ < this.states_.length);
       let state = this.states_[this.stateIndex_];
-      this.setFakeNextStateForMethod_(
+      this.setFakeStateForMethod_(
           method, state.state, RmadErrorCode.kTransitionFailed);
     } else if (this.states_[this.stateIndex_].state !== expectedState) {
       // Error: Called in wrong state.
       let state = this.states_[this.stateIndex_];
-      this.setFakeNextStateForMethod_(
+      this.setFakeStateForMethod_(
           method, state.state, RmadErrorCode.kRequestInvalid);
     } else {
       // Success.
       this.stateIndex_++;
       let state = this.states_[this.stateIndex_];
-      this.setFakeNextStateForMethod_(method, state.state, state.error);
+      this.setFakeStateForMethod_(method, state.state, state.error);
     }
     return this.methods_.resolveMethod(method);
   }
@@ -775,20 +775,7 @@
    * @param {!RmadErrorCode} error
    */
   setFakeCurrentState_(state, error) {
-    this.methods_.setResult(
-        'getCurrentState', {currentState: state, error: error});
-  }
-
-  /**
-   * Sets the value that will be returned when calling state specific functions
-   * that progress state. e.g. setSameOwner()
-   * @private
-   * @param {string} method
-   * @param {!RmaState} state
-   * @param {!RmadErrorCode} error
-   */
-  setFakeNextStateForMethod_(method, state, error) {
-    this.methods_.setResult(method, {nextState: state, error: error});
+    this.setFakeStateForMethod_('getCurrentState', state, error);
   }
 
   /**
@@ -798,7 +785,7 @@
    * @param {!RmadErrorCode} error
    */
   setFakeNextState_(state, error) {
-    this.methods_.setResult('getNextState', {nextState: state, error: error});
+    this.setFakeStateForMethod_('getNextState', state, error);
   }
 
   /**
@@ -808,6 +795,19 @@
    * @param {!RmadErrorCode} error
    */
   setFakePrevState_(state, error) {
-    this.methods_.setResult('getPrevState', {prevState: state, error: error});
+    this.setFakeStateForMethod_('getPrevState', state, error);
+  }
+
+  /**
+   * Sets the value that will be returned when calling state specific functions
+   * that update state. e.g. setSameOwner()
+   * @private
+   * @param {string} method
+   * @param {!RmaState} state
+   * @param {!RmadErrorCode} error
+   */
+  setFakeStateForMethod_(method, state, error) {
+    this.methods_.setResult(
+        method, /** @type {!StateResult} */ ({state: state, error: error}));
   }
 }
diff --git a/ash/content/shimless_rma/resources/shimless_rma.js b/ash/content/shimless_rma/resources/shimless_rma.js
index 5757b7fa..e9d244e 100644
--- a/ash/content/shimless_rma/resources/shimless_rma.js
+++ b/ash/content/shimless_rma/resources/shimless_rma.js
@@ -16,7 +16,7 @@
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getShimlessRmaService} from './mojo_interface_provider.js';
-import {CurrentState, NextState, PrevState, RmadErrorCode, RmaState, ShimlessRmaServiceInterface} from './shimless_rma_types.js'
+import {RmadErrorCode, RmaState, ShimlessRmaServiceInterface, StateResult} from './shimless_rma_types.js'
 
 /**
  * Enum for button states.
@@ -150,9 +150,10 @@
     this.shimlessRmaService_ = getShimlessRmaService();
 
     // Get the initial state.
-    this.fetchState_().then((state) => {
-      this.initialState_ = state.currentState;
-      this.loadState_(state.currentState);
+    this.fetchState_().then((stateResult) => {
+      // TODO(gavindodd): Handle stateResult.error
+      this.initialState_ = stateResult.state;
+      this.loadState_(stateResult.state);
     });
   }
 
@@ -246,8 +247,8 @@
 
   /** @protected */
   onBackButtonClicked_() {
-    // TODO(joonbug): error handling based on state.error
-    this.fetchPrevState_().then((state) => this.loadState_(state.prevState));
+    this.fetchPrevState_().then(
+        (stateResult) => this.loadState_(stateResult.state));
   }
 
   /** @protected */
@@ -256,15 +257,17 @@
     assert(page);
 
     // Acquire promise to check whether current page is ready for next page.
-    const prepPageAdvance =
-        page.onNextButtonClick || (() => Promise.resolve(RmaState.kUnknown));
+    const prepPageAdvance = page.onNextButtonClick ||
+        (() => Promise.resolve(
+             {state: RmaState.kUnknown, error: RmadErrorCode.kOk}));
     assert(typeof prepPageAdvance === 'function');
 
+    // TODO(gavindodd): Handle stateResult.error
     prepPageAdvance()
         .then(
-            (rmaState) => !!rmaState ? Promise.resolve({nextState: rmaState}) :
-                                       this.fetchNextState_())
-        .then((state) => this.loadState_(state.nextState))
+            (stateResult) => !!stateResult ? Promise.resolve(stateResult) :
+                                             this.fetchNextState_())
+        .then((stateResult) => this.loadState_(stateResult.state))
         .catch((err) => void 0);
   }
 
diff --git a/ash/content/shimless_rma/resources/shimless_rma_types.js b/ash/content/shimless_rma/resources/shimless_rma_types.js
index 493bb6fd..8f992b1 100644
--- a/ash/content/shimless_rma/resources/shimless_rma_types.js
+++ b/ash/content/shimless_rma/resources/shimless_rma_types.js
@@ -10,6 +10,13 @@
  * re-aliased to the corresponding mojo types, or replaced by them.
  */
 
+/**
+ * Return type from state progression methods.
+ * Convenience type as mojo-lite does not define types for method results and
+ * this is used frequently.
+ * @typedef {{state: !RmaState, error: !RmadErrorCode}}
+ */
+export let StateResult;
 
 /**
  * @enum {number}
@@ -131,29 +138,6 @@
  */
 export let Component;
 
-// TODO(gavindodd): Change the mojo interface to always use
-// (RmaState state, RmadErrorCode error)
-// as the method return signature so *State types are consolidated.
-/**
- * @typedef {{state: !RmaState, error: !RmadErrorCode}}
- */
-export let State;
-
-/**
- * @typedef {{currentState: !RmaState, error: !RmadErrorCode}}
- */
-export let CurrentState;
-
-/**
- * @typedef {{nextState: !RmaState, error: !RmadErrorCode}}
- */
-export let NextState;
-
-/**
- * @typedef {{prevState: !RmaState, error: !RmadErrorCode}}
- */
-export let PrevState;
-
 /**
  * Type alias for ErrorObserver.
  * @typedef {{onError: !function(!RmadErrorCode)}}
diff --git a/base/BUILD.gn b/base/BUILD.gn
index fad1f1b0..06bc03e 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2297,11 +2297,13 @@
       "tracing/tracing_tls.h",
     ]
 
-    public_deps += [ "//third_party/perfetto:libperfetto" ]
+    public_deps += [
+      "//base/tracing/protos:chrome_track_event_zero",
+      "//third_party/perfetto:libperfetto",
+    ]
 
     deps += [
       "//base/tracing/protos:chrome_track_event",
-      "//base/tracing/protos:chrome_track_event_zero",
       "//third_party/perfetto/include/perfetto/protozero",
     ]
 
diff --git a/base/tracing/protos/chrome_track_event.proto b/base/tracing/protos/chrome_track_event.proto
index 54e11ce..311ccd6 100644
--- a/base/tracing/protos/chrome_track_event.proto
+++ b/base/tracing/protos/chrome_track_event.proto
@@ -34,7 +34,8 @@
 }
 
 message ChromeBrowserContext {
-  optional fixed64 ptr = 1;
+  reserved 1;
+  optional string id = 2;
 }
 
 message ChromeProfileDestroyer {
@@ -142,6 +143,50 @@
   optional uint32 resource_id = 1;
 }
 
+// Information about RenderProcessHost.
+message RenderProcessHost {
+  // Unique Id to identify the RenderProcessHost. This is the browser-side,
+  // persistent id for this RenderProcessHost that stays constant even across OS
+  // layer processes managed by this RenderProcessHost.
+  optional uint32 id = 1;
+  // See ProcessLock::ToString().
+  optional string process_lock = 2;
+  // The PID of the child process.
+  optional int32 child_process_id = 3;
+
+  // Details about the associated browser context.
+  optional ChromeBrowserContext browser_context = 4;
+}
+
+message RenderProcessHostListener {
+  // Routing ID of the listener to the RenderProcessHost, recorded when a new ID
+  // is added or when an ID is removed.
+  optional uint32 routing_id = 1;
+}
+
+message RenderProcessHostCleanup {
+  // Number of IPC listeners registered to the host when Cleanup() was called.
+  optional uint32 listener_count = 1;
+  // Number of "keep alive" references active in the RenderProcessHost, recorded
+  // when Cleanup() was called.
+  optional uint32 keep_alive_ref_count = 2;
+}
+
+message ChildProcessLauncherPriority {
+  // True if the new priority set to background.
+  optional bool is_backgrounded = 1;
+  // True if the renderer proecss has pending views.
+  optional bool has_pending_views = 2;
+
+  // Importance of the child process in Android.
+  enum Importance {
+    IMPORTANCE_NORMAL = 1;
+    IMPORTANCE_MODERATE = 2;
+    IMPORTANCE_IMPORTANT = 3;
+  }
+  optional Importance importance = 3;
+}
+
 message ChromeTrackEvent {
   // Extension range for Chrome: 1000-1999
   // Next ID: 1017
@@ -173,7 +218,12 @@
 
     optional ChromeHashedPerformanceMark chrome_hashed_performance_mark = 1011;
 
-    // reserved 1012 to 1015.
+    optional RenderProcessHost render_process_host = 1012;
+    optional RenderProcessHostCleanup render_process_host_cleanup = 1013;
+    optional RenderProcessHostListener render_process_host_listener_changed =
+        1014;
+    optional ChildProcessLauncherPriority child_process_launcher_priority =
+        1015;
 
     optional ResourceBundle resource_bundle = 1016;
   }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index e2ba3a48..3f50e2d 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-4.20210602.1.1
+4.20210602.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index e2ba3a48..3f50e2d 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-4.20210602.1.1
+4.20210602.3.1
diff --git a/build/lacros/test_runner.py b/build/lacros/test_runner.py
index 982c045..7ec91cf 100755
--- a/build/lacros/test_runner.py
+++ b/build/lacros/test_runner.py
@@ -102,7 +102,8 @@
     # are allowed. For now we only enable crosapi in targets that run tests
     # serially.
     'interactive_ui_tests',
-    'lacros_chrome_browsertests'
+    'lacros_chrome_browsertests',
+    'lacros_chrome_browsertests_run_in_series'
 ]
 
 
diff --git a/build/toolchain/apple/toolchain.gni b/build/toolchain/apple/toolchain.gni
index 80fa7b3..1ca6339 100644
--- a/build/toolchain/apple/toolchain.gni
+++ b/build/toolchain/apple/toolchain.gni
@@ -17,12 +17,6 @@
 assert((target_os == "ios" && host_os == "mac") || host_os != "win")
 
 declare_args() {
-  # This makes the linker set timestamps in Mach-O files to 0. This isn't
-  # enabled by default because this breaks Xcode's lldb. This has been fixed in
-  # https://reviews.llvm.org/rL368199 but that has not yet made it into a public
-  # lldb release.
-  mac_deterministic_build = false
-
   # This controls whether whole module optimization is enabled when building
   # Swift modules. If enabled, the compiler will compile the module as one
   # unit, generating just one single object file. Otherwise, it will generate
@@ -156,9 +150,8 @@
     _strippath = invoker.bin_path + "strip"
     linker_driver += " -Wcrl,strippath," + _strippath
 
-    if (mac_deterministic_build) {
-      linker_driver += " --deterministic"
-    }
+    # This makes the linker set timestamps in Mach-O files to 0.
+    linker_driver += " --deterministic"
 
     # On iOS, the final applications are assembled using lipo (to support fat
     # builds). The correct flags are passed to the linker_driver.py script
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index 811a994a..4a479a8 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -1777,37 +1777,32 @@
     const int sdk = base::android::BuildInfo::GetInstance()->sdk_int();
     if (sdk <= base::android::SDK_VERSION_MARSHMALLOW) {
       error_pixels_percentage = 10.f;
-      max_abs_error = 16;
+      max_abs_error = 20;
     } else {
       // Newer OSes occasionally have smaller flakes when using the real GPU
       error_pixels_percentage = 1.5f;
       max_abs_error = 2;
     }
-#elif defined(OS_MAC) || defined(OS_WIN)
-    // Mac and Windows need very small tolerances only under complex transforms
-    if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kComplex) {
-      error_pixels_percentage = 0.2f;
-      max_abs_error = 2;
-    }
 #endif
-    // Regardless of OS, perspective triggers path rendering for each glyph,
-    // which produces its own set of pixel differences.
-    if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kPerspective) {
-      error_pixels_percentage = 4.f;
-      max_abs_error = 36;
-    }
+    // Many platforms need very small tolerances under complex transforms,
+    // and higher tolerances for perspective, since it triggers path rendering
+    // for each glyph. Additionally, record filters require higher tolerance
+    // because oop-r converts raster-at-scale to fixed-scale.
     float avg_error = max_abs_error;
-    // And if the text is stored in a PaintFilter, allow for more differences
-    // since OOP-R converts to a fixed scale image before the transform. We
-    // could convert GPU-raster to fixed scale for direct comparison, but this
-    // ensures that both scaling modes produce approximately the same image.
-    if (GetTextBlobStrategy(GetParam()) == TextBlobStrategy::kRecordFilter &&
-        (GetMatrixStrategy(GetParam()) == MatrixStrategy::kPerspective ||
-         GetMatrixStrategy(GetParam()) == MatrixStrategy::kComplex)) {
-      error_pixels_percentage = 12.f;
-      max_abs_error = 220;
-      avg_error = 50.f;
+    const bool is_record_filter =
+        GetTextBlobStrategy(GetParam()) == TextBlobStrategy::kRecordFilter;
+    if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kComplex) {
+      error_pixels_percentage =
+          std::max(is_record_filter ? 12.f : 0.2f, error_pixels_percentage);
+      max_abs_error = std::max(is_record_filter ? 220 : 2, max_abs_error);
+      avg_error = std::max(is_record_filter ? 50.f : 2.f, avg_error);
+    } else if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kPerspective) {
+      error_pixels_percentage =
+          std::max(is_record_filter ? 12.f : 4.f, error_pixels_percentage);
+      max_abs_error = std::max(is_record_filter ? 255 : 36, max_abs_error);
+      avg_error = std::max(is_record_filter ? 60.f : 36.f, avg_error);
     }
+
     FuzzyPixelComparator comparator(
         /*discard_alpha=*/false,
         /*error_pixels_percentage_limit=*/error_pixels_percentage,
@@ -1995,12 +1990,7 @@
 };
 
 TEST_P(OopTextBlobPixelTest, Config) {
-#if defined(OS_ANDROID) && defined(ARCH_CPU_X86_FAMILY)
-  // Broken on emulators: https://crbug.com/1189284
-  GTEST_SKIP();
-#else
   RunTest();
-#endif
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc
index b6c84a81..5dd5670 100644
--- a/cc/trees/draw_property_utils.cc
+++ b/cc/trees/draw_property_utils.cc
@@ -12,6 +12,7 @@
 #include "base/containers/adapters.h"
 #include "base/containers/stack.h"
 #include "base/logging.h"
+#include "build/build_config.h"
 #include "cc/base/math_util.h"
 #include "cc/layers/draw_properties.h"
 #include "cc/layers/layer.h"
@@ -1121,18 +1122,57 @@
 void UpdateElasticOverscroll(
     PropertyTrees* property_trees,
     TransformNode* overscroll_elasticity_transform_node,
-    const gfx::Vector2dF& elastic_overscroll) {
+    const gfx::Vector2dF& elastic_overscroll,
+    const ScrollNode* inner_viewport) {
   if (!overscroll_elasticity_transform_node) {
     DCHECK(elastic_overscroll.IsZero());
     return;
   }
 
+#if defined(OS_ANDROID)
+
+  // On android, elastic overscroll is implemented by stretching the content
+  // from the overscrolled edge.
+  // TODO(https://crbug.com/1213217): Use a non-linear stretch rather than a
+  // simple scale transformation for the overscroll effect.
+  overscroll_elasticity_transform_node->local.MakeIdentity();
+  overscroll_elasticity_transform_node->origin.SetPoint(0.f, 0.f, 0.f);
+  overscroll_elasticity_transform_node->to_screen_is_potentially_animated =
+      !elastic_overscroll.IsZero();
+
+  if (!elastic_overscroll.IsZero() && inner_viewport) {
+    overscroll_elasticity_transform_node->local.Scale(
+        1.f + std::abs(elastic_overscroll.x()) /
+                  inner_viewport->container_bounds.width(),
+        1.f + std::abs(elastic_overscroll.y()) /
+                  inner_viewport->container_bounds.height());
+
+    // If overscrolling to the right, stretch from right.
+    if (elastic_overscroll.x() > 0.f) {
+      overscroll_elasticity_transform_node->origin.set_x(
+          inner_viewport->container_bounds.width());
+    }
+
+    // If overscrolling off the bottom, stretch from bottom.
+    if (elastic_overscroll.y() > 0.f) {
+      overscroll_elasticity_transform_node->origin.set_y(
+          inner_viewport->container_bounds.height());
+    }
+  }
+
+#else  // defined(OS_ANDROID)
+
+  // On other platforms, we modify the translation offset to match the
+  // overscroll amount.
   if (overscroll_elasticity_transform_node->scroll_offset ==
       gfx::ScrollOffset(elastic_overscroll))
     return;
 
   overscroll_elasticity_transform_node->scroll_offset =
       gfx::ScrollOffset(elastic_overscroll);
+
+#endif  // defined(OS_ANDROID)
+
   overscroll_elasticity_transform_node->needs_local_transform_update = true;
   property_trees->transform_tree.set_needs_update(true);
 }
@@ -1448,7 +1488,8 @@
                         layer_tree_impl->current_page_scale_factor());
   UpdateElasticOverscroll(property_trees,
                           layer_tree_impl->OverscrollElasticityTransformNode(),
-                          layer_tree_impl->current_elastic_overscroll());
+                          layer_tree_impl->current_elastic_overscroll(),
+                          layer_tree_impl->InnerViewportScrollNode());
   // Similarly, the device viewport and device transform are shared
   // by both trees.
   property_trees->clip_tree.SetViewportClip(
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc
index 26d6d288..d858c5ba 100644
--- a/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -308,8 +308,7 @@
           .InsertBeforeExtensionASCII(GetRendererSuffix()));
 }
 
-TEST_P(LayerTreeHostMaskPixelTestWithLayerList,
-       DISABLED_MaskWithEffectDifferentSize) {
+TEST_P(LayerTreeHostMaskPixelTestWithLayerList, MaskWithEffectDifferentSize) {
   mask_bounds_ = gfx::Size(25, 25);
   MaskContentLayerClient client(mask_bounds_);
   mask_layer_ = PictureLayer::Create(&client);
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index 0cc5ae85f..f2011d4 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -5720,34 +5720,57 @@
     }
   }
 
+  void VerifyOverscroll(const gfx::Vector2dF& stretch_amount,
+                        const gfx::Transform& transform) {
+#if defined(OS_ANDROID)
+    gfx::Vector2dF scale = transform.Scale2d();
+    // On android, overscroll stretches the content. We don't assert the amount
+    // of stretch but there should be some stretch for overscroll and no stretch
+    // without it.
+    if (stretch_amount.x() == 0.f)
+      EXPECT_EQ(1.f, scale.x());
+    else
+      EXPECT_GT(scale.x(), 1.f);
+    if (stretch_amount.y() == 0.f)
+      EXPECT_EQ(1.f, scale.y());
+    else
+      EXPECT_GT(scale.y(), 1.f);
+#else   // defined(OS_ANDROID)
+    gfx::Transform expected_draw_transform;
+    expected_draw_transform.Translate(-stretch_amount);
+    EXPECT_EQ(expected_draw_transform, transform);
+#endif  // defined(OS_ANDROID)
+  }
+
   void DrawLayersOnThread(LayerTreeHostImpl* host_impl) override {
     num_draws_++;
     LayerImpl* content_layer_impl =
         host_impl->active_tree()->LayerById(content_layer_id_);
-    gfx::Transform expected_draw_transform;
     switch (num_draws_) {
       case 1:
         // Initially, there's no overscroll.
-        EXPECT_EQ(expected_draw_transform, content_layer_impl->DrawTransform());
+        VerifyOverscroll(gfx::Vector2dF(), content_layer_impl->DrawTransform());
 
         // Begin overscrolling. This should be reflected in the draw transform
         // the next time we draw.
         scroll_elasticity_helper_->SetStretchAmount(gfx::Vector2dF(5.f, 6.f));
         break;
       case 2:
-        expected_draw_transform.Translate(-5.0, -6.0);
-        EXPECT_EQ(expected_draw_transform, content_layer_impl->DrawTransform());
+        // We should have some overscroll.
+        VerifyOverscroll(gfx::Vector2dF(5.f, 6.f),
+                         content_layer_impl->DrawTransform());
 
         scroll_elasticity_helper_->SetStretchAmount(gfx::Vector2dF(3.f, 2.f));
         break;
       case 3:
-        expected_draw_transform.Translate(-3.0, -2.0);
-        EXPECT_EQ(expected_draw_transform, content_layer_impl->DrawTransform());
+        VerifyOverscroll(gfx::Vector2dF(3.f, 2.f),
+                         content_layer_impl->DrawTransform());
 
         scroll_elasticity_helper_->SetStretchAmount(gfx::Vector2dF());
         break;
       case 4:
-        EXPECT_EQ(expected_draw_transform, content_layer_impl->DrawTransform());
+        // In the final frame there is no more overscroll.
+        VerifyOverscroll(gfx::Vector2dF(), content_layer_impl->DrawTransform());
         EndTest();
         break;
       default:
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 97a4f17f..488a510 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1156,6 +1156,7 @@
   "java/src/org/chromium/chrome/browser/share/ShareDelegateImpl.java",
   "java/src/org/chromium/chrome/browser/share/ShareDelegateSupplier.java",
   "java/src/org/chromium/chrome/browser/share/ShareHelper.java",
+  "java/src/org/chromium/chrome/browser/share/ShareRegistrationCoordinator.java",
   "java/src/org/chromium/chrome/browser/share/ShareUtils.java",
   "java/src/org/chromium/chrome/browser/sharing/SharingAdapter.java",
   "java/src/org/chromium/chrome/browser/sharing/SharingJNIBridge.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index e5aa81c..cdd91a2 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -200,6 +200,7 @@
   "junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotificationTest.java",
   "junit/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapterTest.java",
   "junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java",
+  "junit/src/org/chromium/chrome/browser/share/ShareRegistrationCoordinatorTest.java",
   "junit/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandlerTest.java",
   "junit/src/org/chromium/chrome/browser/signin/SigninBridgeTest.java",
   "junit/src/org/chromium/chrome/browser/signin/SigninManagerImplTest.java",
diff --git a/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected b/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected
index 55b8825..236f59a 100644
--- a/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected
+++ b/chrome/android/expectations/monochrome_public_bundle.proguard_flags.expected
@@ -646,16 +646,6 @@
     <init>();
 }
 
-# File: obj/third_party/androidx/androidx_startup_startup_runtime_java/proguard.txt
-# This Proguard rule ensures that ComponentInitializers are are neither shrunk nor obfuscated.
-# This is because they are discovered and instantiated during application initialization.
--keep class * extends androidx.startup.Initializer {
-    # Keep the public no-argument constructor while allowing other methods to be optimized.
-    <init>();
-}
-
--assumenosideeffects class androidx.startup.StartupLogger
-
 # File: obj/third_party/androidx/androidx_transition_transition_java/proguard.txt
 # Copyright (C) 2017 The Android Open Source Project
 #
diff --git a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
index 292ef651..71021c2 100644
--- a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
+++ b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
@@ -59,8 +59,10 @@
     private static final int SIGN_REQUEST_CODE = 2;
 
     private static final int CTAP2_OK = 0;
+    private static final int CTAP2_ERR_CREDENTIAL_EXCLUDED = 0x19;
     private static final int CTAP2_ERR_OPERATION_DENIED = 0x27;
     private static final int CTAP2_ERR_UNSUPPORTED_OPTION = 0x2D;
+    private static final int CTAP2_ERR_NO_CREDENTIALS = 0x2E;
     private static final int CTAP2_ERR_OTHER = 0x7F;
 
     // sOwnBluetooth is true if this class owns the fact that Bluetooth is enabled and needs to
@@ -316,7 +318,6 @@
         Log.e(TAG, "OK.");
 
         if (data.hasExtra(Fido.FIDO2_KEY_ERROR_EXTRA)) {
-            Log.e(TAG, "error extra");
             AuthenticatorErrorResponse error = AuthenticatorErrorResponse.deserializeFromBytes(
                     data.getByteArrayExtra(Fido.FIDO2_KEY_ERROR_EXTRA));
             Log.i(TAG,
@@ -324,9 +325,14 @@
                             + String.valueOf(error.getErrorCodeAsInt()));
 
             // ErrorCode represents DOMErrors not CTAP status codes.
-            // TODO: figure out translation of the remaining codes
             int ctap_status;
             switch (error.getErrorCode()) {
+                case INVALID_STATE_ERR:
+                    // Assumed to be caused by a matching excluded credential.
+                    // (It's possible to match the error string to be sure,
+                    // but that's fragile.)
+                    ctap_status = CTAP2_ERR_CREDENTIAL_EXCLUDED;
+                    break;
                 case NOT_ALLOWED_ERR:
                     ctap_status = CTAP2_ERR_OPERATION_DENIED;
                     break;
@@ -364,7 +370,6 @@
         Log.e(TAG, "OK.");
 
         if (data.hasExtra(Fido.FIDO2_KEY_ERROR_EXTRA)) {
-            Log.e(TAG, "error extra");
             AuthenticatorErrorResponse error = AuthenticatorErrorResponse.deserializeFromBytes(
                     data.getByteArrayExtra(Fido.FIDO2_KEY_ERROR_EXTRA));
             Log.i(TAG,
@@ -372,9 +377,14 @@
                             + String.valueOf(error.getErrorCodeAsInt()));
 
             // ErrorCode represents DOMErrors not CTAP status codes.
-            // TODO: figure out translation of the remaining codes
             int ctap_status;
             switch (error.getErrorCode()) {
+                case INVALID_STATE_ERR:
+                    // Assumed to be because none of the credentials were
+                    // recognised. (It's possible to match the error string to
+                    // be sure, but that's fragile.)
+                    ctap_status = CTAP2_ERR_NO_CREDENTIALS;
+                    break;
                 case NOT_ALLOWED_ERR:
                     ctap_status = CTAP2_ERR_OPERATION_DENIED;
                     break;
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
index 5d816ae..e9b14ad 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponentBridge.java
@@ -1,3 +1,4 @@
+
 // 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.
@@ -146,6 +147,13 @@
     }
 
     @CalledByNative
+    private void showAccessorySheetTab(int tabType) {
+        if (getManualFillingComponent() != null) {
+            getManualFillingComponent().showAccessorySheetTab(tabType);
+        }
+    }
+
+    @CalledByNative
     private void addOptionToggleToAccessorySheetData(Object objAccessorySheetData,
             String displayText, boolean enabled, @AccessoryAction int accessoryAction) {
         ((AccessorySheetData) objAccessorySheetData)
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingCoordinator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingCoordinator.java
index 769d7a9..62defef 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingCoordinator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingCoordinator.java
@@ -127,6 +127,11 @@
     }
 
     @Override
+    public void showAccessorySheetTab(@AccessoryTabType int tabType) {
+        mMediator.showAccessorySheetTab(tabType);
+    }
+
+    @Override
     public void onResume() {
         mMediator.resume();
     }
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
index fdc18e7..23fa0a86 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingMediator.java
@@ -282,8 +282,7 @@
     void dismiss() {
         if (!isInitialized()) return;
         pause();
-        ViewGroup contentView = getContentView();
-        if (contentView != null) mSoftKeyboardDelegate.hideSoftKeyboardOnly(contentView);
+        hideSoftKeyboard();
     }
 
     void notifyPopupOpened(DropdownPopupWindow popup) {
@@ -302,6 +301,13 @@
         mModel.set(KEYBOARD_EXTENSION_STATE, HIDDEN);
     }
 
+    void showAccessorySheetTab(@AccessoryTabType int tabType) {
+        if (!isInitialized()) return;
+        mModel.set(SHOW_WHEN_VISIBLE, true);
+        if (is(HIDDEN)) mModel.set(KEYBOARD_EXTENSION_STATE, REPLACING_KEYBOARD);
+        mKeyboardAccessory.setActiveTab(tabType);
+    }
+
     void pause() {
         if (!isInitialized()) return;
         mConfirmationHelper.dismiss();
@@ -588,7 +594,7 @@
         int newSheetHeight = insetObserver != null
                 ? insetObserver.getSystemWindowInsetsBottom()
                 : mSoftKeyboardDelegate.calculateSoftKeyboardHeight(rootView);
-        newSheetHeight = Math.max(minimalSheetHeight, newSheetHeight);
+        newSheetHeight = Math.max(newSheetHeight, minimalSheetHeight);
         return newSheetHeight;
     }
 
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/README.md b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/README.md
index fc60379..7d39111 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/README.md
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/README.md
@@ -72,7 +72,7 @@
 
 |   ID   | State              | Accessory Bar            | Fallback Sheet                          | Floats  | Transition into*
 |--------|--------------------|--------------------------|-----------------------------------------|---------|-
-| 0x0100 | HIDDEN             | Hidden                   | Hidden                                  | N/A     | FLOATING_BAR
+| 0x0100 | HIDDEN             | Hidden                   | Hidden                                  | N/A     | FLOATING_BAR, REPLACING_KEYBOARD
 | 0x0101 | EXTENDING_KEYBOARD | **Visible**              | Hidden                                  | No      | WAITING_TO_REPLACE
 | 0x0001 | WAITING_TO_REPLACE | **Visible**              | N/A — waits for keyboard to (dis)appear | No      | REPLACING_KEYBOARD
 | 0x0011 | REPLACING_KEYBOARD | **Visible** as title bar | **Visible**                             | No      | FLOATING_SHEET
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java
index 94d1f32f..6e37d68b 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryCoordinator.java
@@ -15,6 +15,7 @@
 import androidx.viewpager.widget.ViewPager;
 
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.keyboard_accessory.AccessoryTabType;
 import org.chromium.chrome.browser.keyboard_accessory.bar_component.KeyboardAccessoryProperties.BarItem;
 import org.chromium.chrome.browser.keyboard_accessory.bar_component.KeyboardAccessoryViewBinder.BarItemViewHolder;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
@@ -92,6 +93,12 @@
         void closeActiveTab();
 
         /**
+         * Set the currently active tab to the given tabType.
+         * @param tabType The type of the tab that should be selected.
+         */
+        void setActiveTab(@AccessoryTabType int tabType);
+
+        /**
          * Returns whether active tab or null if no tab is currently active. The returned property
          * reflects the latest change while the view might still be in progress of being updated.
          * @return The active {@link KeyboardAccessoryData.Tab}, null otherwise.
@@ -169,6 +176,10 @@
         mTabLayout.getTabSwitchingDelegate().setTabs(tabs);
     }
 
+    public void setActiveTab(@AccessoryTabType int tabType) {
+        mTabLayout.getTabSwitchingDelegate().setActiveTab(tabType);
+    }
+
     /**
      * Allows any {@link Provider} to communicate with the
      * {@link KeyboardAccessoryMediator} of this component.
diff --git a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutMediator.java b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutMediator.java
index 7153278..5f28def 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutMediator.java
+++ b/chrome/android/features/keyboard_accessory/internal/java/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutMediator.java
@@ -14,9 +14,11 @@
 
 import com.google.android.material.tabs.TabLayout;
 
+import org.chromium.chrome.browser.keyboard_accessory.AccessoryTabType;
 import org.chromium.chrome.browser.keyboard_accessory.bar_component.KeyboardAccessoryCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
 import org.chromium.chrome.browser.keyboard_accessory.tab_layout_component.KeyboardAccessoryTabLayoutCoordinator.AccessoryTabObserver;
+import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyObservable;
@@ -107,6 +109,20 @@
     }
 
     @Override
+    public void setActiveTab(@AccessoryTabType int tabType) {
+        ListModel<KeyboardAccessoryData.Tab> tabs = mModel.get(TABS);
+        int tabPosition = 0;
+        while (tabPosition < tabs.size()) {
+            if (tabs.get(tabPosition).getRecordingType() == tabType) {
+                break;
+            }
+            tabPosition++;
+        }
+        assert tabPosition < tabs.size() : "No tab found for the given tabType: " + tabType;
+        mModel.set(ACTIVE_TAB, tabPosition);
+    }
+
+    @Override
     public @Nullable KeyboardAccessoryData.Tab getActiveTab() {
         if (mModel.get(ACTIVE_TAB) == null) return null;
         return mModel.get(TABS).get(mModel.get(ACTIVE_TAB));
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingIntegrationTest.java
index 3b502401..2a01cb10 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingIntegrationTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingIntegrationTest.java
@@ -143,6 +143,24 @@
 
     @Test
     @SmallTest
+    @EnableFeatures({ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID})
+    public void testAccessorySheetShown() throws TimeoutException {
+        mHelper.loadTestPage(false);
+        // Register a sheet data provider so that sheet is available when needed.
+        mHelper.registerSheetDataProvider(AccessoryTabType.CREDIT_CARDS);
+
+        // Show the passwords accessory sheet without focusing on any fields.
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> mHelper.getManualFillingCoordinator().showAccessorySheetTab(
+                                AccessoryTabType.CREDIT_CARDS));
+
+        // Verify that the accessory sheet is shown.
+        whenDisplayed(withChild(withId(R.id.keyboard_accessory_sheet)));
+    }
+
+    @Test
+    @SmallTest
     @DisableIf.
     Build(sdk_is_greater_than = VERSION_CODES.LOLLIPOP_MR1, sdk_is_less_than = VERSION_CODES.N,
             message = "Flaky on Marshmallow https://crbug.com/1102302")
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java
index 0dc6e9a91..4eded381 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingTestHelper.java
@@ -50,6 +50,7 @@
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.keyboard_accessory.bar_component.KeyboardAccessoryCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData;
+import org.chromium.chrome.browser.keyboard_accessory.data.KeyboardAccessoryData.AccessorySheetData;
 import org.chromium.chrome.browser.keyboard_accessory.data.PropertyProvider;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AddressAccessorySheetCoordinator;
 import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.CreditCardAccessorySheetCoordinator;
@@ -510,4 +511,10 @@
                     mActivityTestRule.getWebContents(), available);
         });
     }
+
+    public void registerSheetDataProvider(@AccessoryTabType int tabType) {
+        PropertyProvider<AccessorySheetData> sheetDataProvider = new PropertyProvider<>();
+        getManualFillingCoordinator().registerSheetDataProvider(
+                mWebContentsRef.get(), tabType, sheetDataProvider);
+    }
 }
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
index 06dc719d..c651c66d 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -1085,6 +1085,30 @@
                 .showConfirmation("Suggestion", "Delete it?", R.string.ok, testRunnable);
     }
 
+    @Test
+    public void testShowAccessorySheetTab() {
+        // Prepare a tab and register a new tab, so there is a reason to display the bar.
+        addBrowserTab(mMediator, 1111, null);
+        mController.registerSheetDataProvider(
+                mLastMockWebContents, AccessoryTabType.PASSWORDS, new PropertyProvider<>());
+        assertThat(mModel.get(SHOW_WHEN_VISIBLE), is(false));
+        assertThat(mModel.get(KEYBOARD_EXTENSION_STATE), is(HIDDEN));
+
+        mController.showAccessorySheetTab(AccessoryTabType.PASSWORDS);
+
+        // Verify that the states are updated correctly and the active tab is set.
+        assertThat(mModel.get(SHOW_WHEN_VISIBLE), is(true));
+        assertThat(mModel.get(KEYBOARD_EXTENSION_STATE), is(REPLACING_KEYBOARD));
+        verify(mMockKeyboardAccessory, times(1)).setActiveTab(AccessoryTabType.PASSWORDS);
+
+        // Simulate the callback once active tab is set.
+        mMediator.onChangeAccessorySheet(0);
+
+        // Assert tha the keyboard extension state continues to be REPLACING_KEYBOARD as we're
+        // showing the sheet.
+        assertThat(mModel.get(KEYBOARD_EXTENSION_STATE), is(REPLACING_KEYBOARD));
+    }
+
     /**
      * Creates a tab and calls the observer events as if it was just created and switched to.
      * @param mediator The {@link ManualFillingMediator} whose observers should be triggered.
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutControllerTest.java
index 3c62218..23a5c78 100644
--- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutControllerTest.java
+++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/tab_layout_component/KeyboardAccessoryTabLayoutControllerTest.java
@@ -151,4 +151,27 @@
         verifyNoMoreInteractions(
                 mMockPropertyObserver, mMockTabListObserver, mMockAccessoryTabObserver);
     }
+
+    @Test
+    public void testSetActiveTab() {
+        mModel.addObserver(mMockPropertyObserver);
+        assertThat(mModel.get(ACTIVE_TAB), is(nullValue()));
+        mCoordinator.getTabSwitchingDelegate().addTab(mTestTab);
+
+        // Set the active tab type to 0 which is the recording_type of |mTestTab|.
+        mCoordinator.getTabSwitchingDelegate().setActiveTab(0);
+
+        verify(mMockPropertyObserver).onPropertyChanged(mModel, ACTIVE_TAB);
+        assertThat(mModel.get(ACTIVE_TAB), is(0));
+    }
+
+    @Test(expected = AssertionError.class)
+    public void testSetActiveTab_tabTypeNotFound_throwsException() {
+        mModel.addObserver(mMockPropertyObserver);
+        assertThat(mModel.get(ACTIVE_TAB), is(nullValue()));
+        mCoordinator.getTabSwitchingDelegate().addTab(mTestTab);
+
+        // Set the active tab type to 1 which is different from the recording_type of |mTestTab|.
+        mCoordinator.getTabSwitchingDelegate().setActiveTab(1);
+    }
 }
diff --git a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java
index 6171a01..434032c 100644
--- a/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java
+++ b/chrome/android/features/keyboard_accessory/public/java/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingComponent.java
@@ -178,6 +178,12 @@
     void hide();
 
     /**
+     * Commands the accessory to show and set the currently active tab to the given |tabType|.
+     * @param tabType the tab that should be selected by default.
+     */
+    void showAccessorySheetTab(@AccessoryTabType int tabType);
+
+    /**
      * Notifies the component that the activity it's living in was resumed.
      */
     void onResume();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ActivityUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/ActivityUtils.java
index 555caf5..9ed56cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ActivityUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ActivityUtils.java
@@ -44,4 +44,15 @@
         return (useLowEndTheme ? R.style.Theme_Chromium_WithWindowAnimation_LowEnd
                                : R.style.Theme_Chromium_WithWindowAnimation);
     }
+
+    /**
+     * Returns whether the activity is finishing or destroyed.
+     * @param activity The activity to check.
+     * @return Whether the activity is finishing or destroyed. Also returns true if the activity is
+     *         null.
+     */
+    public static boolean isActivityFinishingOrDestroyed(Activity activity) {
+        if (activity == null) return true;
+        return activity.isDestroyed() || activity.isFinishing();
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeAccessorActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeAccessorActivity.java
index e6bd329..3d9d65a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeAccessorActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeAccessorActivity.java
@@ -4,22 +4,21 @@
 
 package org.chromium.chrome.browser;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 
 import androidx.appcompat.app.AppCompatActivity;
 
-import org.chromium.base.ApplicationStatus;
 import org.chromium.base.IntentUtils;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.share.ShareHelper;
-import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
+import org.chromium.chrome.browser.share.ShareRegistrationCoordinator.ShareBroadcastReceiver;
 
 /**
  * {@code ChromeActivityAccessor} is the base class for share options, which
  * are activities that are shown in the share chooser. Activities subclassing
- * ChromeAccessorActivity override handleAction.
+ * ChromeAccessorActivity need to:
+ * - Override #getBroadcastAction.
+ * - Register to receive that broadcast in ShareRegistrationCoordinator.
  */
 public abstract class ChromeAccessorActivity extends AppCompatActivity {
     @Override
@@ -32,39 +31,17 @@
             if (!Intent.ACTION_SEND.equals(intent.getAction())) return;
             if (!IntentUtils.safeHasExtra(intent, ShareHelper.EXTRA_TASK_ID)) return;
 
-            ChromeActivity triggeringActivity = getTriggeringActivity();
-            if (triggeringActivity == null) return;
-
-            handleAction(/* triggeringActivity= */ triggeringActivity,
-                    /* menuOrKeyboardActionController= */ triggeringActivity);
+            ShareBroadcastReceiver.sendShareBroadcastWithAction(
+                    intent.getIntExtra(ShareHelper.EXTRA_TASK_ID, 0), getBroadcastAction());
         } finally {
             finish();
         }
     }
 
     /**
-     * Returns the ChromeActivity that called the share intent picker.
+     * Return a unique action string which is used to register to receive the broadcast in
+     * ShareRegistrationController. Usually, the best option is to use the stringified class name
+     * with the "BroadcastAction" postfix.
      */
-    private ChromeActivity getTriggeringActivity() {
-        int triggeringTaskId =
-                IntentUtils.safeGetIntExtra(getIntent(), ShareHelper.EXTRA_TASK_ID, 0);
-        for (Activity activity : ApplicationStatus.getRunningActivities()) {
-            if (activity.getTaskId() == triggeringTaskId && activity instanceof ChromeActivity) {
-                return (ChromeActivity) activity;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Completes the share action.
-     *
-     * Override this activity to implement desired share functionality.  This activity
-     * will be destroyed immediately after this method is called.
-     *
-     * @param triggeringActivity The {@link Activity} that triggered the share.
-     * @param menuOrKeyboardActionController Handles menu or keyboard actions.
-     */
-    protected abstract void handleAction(Activity triggeringActivity,
-            MenuOrKeyboardActionController menuOrKeyboardActionController);
+    protected abstract String getBroadcastAction();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index b34a404..1825f9c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1847,7 +1847,7 @@
 
             LauncherShortcutActivity.updateIncognitoShortcut(ChromeTabbedActivity.this);
 
-            ChromeSurveyController.initialize(mTabModelSelectorImpl);
+            ChromeSurveyController.initialize(mTabModelSelectorImpl, getLifecycleDispatcher());
 
             if (mStartSurfaceSupplier.get() != null && mOverviewShownOnStart) {
                 mStartSurfaceSupplier.get().onOverviewShownAtLaunch(getOnCreateTimestampMs());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
index b509cc63..2056347 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
@@ -14,9 +14,6 @@
 specific_include_rules = {
   # Exceptions to the ChromeActivity dependency restriction. These will all eventually be removed
   # new code should rely on acceptable dependency aquisition patterns.
-  "ChromeAccessorActivity\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "ChromeActivitySessionTracker\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
@@ -65,9 +62,6 @@
   "DownloadInfoBarController\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
-  "ExploreSitesIPH\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "ScreenshotTask\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
@@ -98,9 +92,6 @@
   "ChromePaymentRequestService\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
-  "SendTabToSelfShareActivity\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "InterceptNavigationDelegateClientImpl\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
index c84206d..cb392b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -288,7 +288,7 @@
         int GOOGLE_APPS = 1;
         // This should not be used, it's a fallback for Chrome features that didn't identify
         // themselves. Please see {@link
-        // IncognitoCustomTabIntentDataProvider#addIncongitoExtrasForChromeFeatures}
+        // IncognitoCustomTabIntentDataProvider#addIncognitoExtrasForChromeFeatures}
         int OTHER_CHROME_FEATURES = 2;
 
         // Chrome Features
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 693bb66a4..467cd4c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -54,6 +54,7 @@
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.base.supplier.OneshotSupplierImpl;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.base.supplier.UnownedUserDataSupplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ActivityTabProvider;
@@ -139,12 +140,14 @@
 import org.chromium.chrome.browser.page_info.ChromePageInfo;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
 import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.chrome.browser.printing.PrintShareActivity;
 import org.chromium.chrome.browser.printing.TabPrinter;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.share.ShareDelegateImpl;
 import org.chromium.chrome.browser.share.ShareDelegateSupplier;
+import org.chromium.chrome.browser.share.ShareRegistrationCoordinator;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.tab.AccessibilityVisibilityHandler;
 import org.chromium.chrome.browser.tab.RequestDesktopUtils;
@@ -368,6 +371,8 @@
     private List<MenuOrKeyboardActionController.MenuOrKeyboardActionHandler> mMenuActionHandlers =
             new ArrayList<>();
 
+    private ShareRegistrationCoordinator mShareRegistrationCoordinator;
+
     protected ChromeActivity() {
         mIntentHandler = new IntentHandler(this, createIntentHandlerDelegate());
         mManualFillingComponentSupplier.set(ManualFillingComponentFactory.createComponent());
@@ -567,6 +572,12 @@
                     mManualFillingComponentSupplier.get().getBottomInsetSupplier());
 
             // Should be called after TabModels are initialized.
+            mShareRegistrationCoordinator = new ShareRegistrationCoordinator(
+                    this, mActivityTabProvider, mRootUiCoordinator.getBottomSheetController());
+            // Some share types are registered in the coorindator itself.
+            mShareRegistrationCoordinator.registerShareType(PrintShareActivity.BROADCAST_ACTION,
+                    () -> doPrintShare(this, mActivityTabProvider));
+
             ShareDelegate shareDelegate =
                     new ShareDelegateImpl(mRootUiCoordinator.getBottomSheetController(),
                             getLifecycleDispatcher(), getActivityTabProvider(),
@@ -1464,6 +1475,7 @@
             mBookmarkBridgeSupplier = null;
         }
 
+        if (mShareRegistrationCoordinator != null) mShareRegistrationCoordinator.destroy();
         if (mShareDelegateSupplier != null) {
             mShareDelegateSupplier.destroy();
         }
@@ -2364,18 +2376,8 @@
         }
 
         if (id == R.id.print_id) {
-            // TODO(crbug.com/1155789): Move this logic to PrintShareActivity once the current tab
-            // is available as UnownedUserData.
-            PrintingController printingController = PrintingControllerImpl.getInstance();
-            if (printingController != null && !printingController.isBusy()
-                    && UserPrefs.get(Profile.getLastUsedRegularProfile())
-                               .getBoolean(Pref.PRINTING_ENABLED)) {
-                printingController.startPrint(
-                        new TabPrinter(currentTab), new PrintManagerDelegateImpl(this));
-                RecordUserAction.record("MobileMenuPrint");
-                return true;
-            }
-            return false;
+            RecordUserAction.record("MobileMenuPrint");
+            return doPrintShare(this, mActivityTabProvider);
         }
 
         if (id == R.id.add_to_homescreen_id) {
@@ -2732,4 +2734,18 @@
     public DisplayAndroidObserver getDisplayAndroidObserverForTesting() {
         return mDisplayAndroidObserver;
     }
+
+    /** Returns whether the print action was successfully started. */
+    private boolean doPrintShare(Activity activity, Supplier<Tab> currentTabSupplier) {
+        PrintingController printingController = PrintingControllerImpl.getInstance();
+
+        if (!currentTabSupplier.hasValue()) return false;
+        if (printingController == null || printingController.isBusy()) return false;
+        if (!UserPrefs.get(Profile.getLastUsedRegularProfile()).getBoolean(Pref.PRINTING_ENABLED)) {
+            return false;
+        }
+        printingController.startPrint(
+                new TabPrinter(currentTabSupplier.get()), new PrintManagerDelegateImpl(activity));
+        return true;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
index ed52a10..d6732a46 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillPopupBridge.java
@@ -183,8 +183,17 @@
             String label, String sublabel, String itemTag, int iconId, boolean isIconAtStart,
             int suggestionId, boolean isDeletable, boolean isLabelMultiline, boolean isLabelBold) {
         int drawableId = iconId == 0 ? DropdownItem.NO_ICON : iconId;
-        array[index] = new AutofillSuggestion(label, sublabel, itemTag, drawableId, isIconAtStart,
-                suggestionId, isDeletable, isLabelMultiline, isLabelBold, /* featureForIPH= */ "");
+        array[index] = new AutofillSuggestion.Builder()
+                               .setLabel(label)
+                               .setSubLabel(sublabel)
+                               .setItemTag(itemTag)
+                               .setIconId(drawableId)
+                               .setIsIconAtStart(isIconAtStart)
+                               .setSuggestionId(suggestionId)
+                               .setIsDeletable(isDeletable)
+                               .setIsMultiLineLabel(isLabelMultiline)
+                               .setIsBoldLabel(isLabelBold)
+                               .build();
     }
 
     @NativeMethods
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
index e72c5bc..eb9851b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
@@ -621,6 +621,11 @@
             // Note that keyboard will not be shown for dropdown field since it's not necessary.
             if (getCurrentFocus() != null) {
                 KeyboardVisibilityDelegate.getInstance().showKeyboard(getCurrentFocus());
+                // Put the cursor to the end of the text.
+                if (getCurrentFocus() instanceof EditText) {
+                    EditText focusedEditText = (EditText) getCurrentFocus();
+                    focusedEditText.setSelection(focusedEditText.getText().length());
+                }
             }
             if (sObserverForTest != null) sObserverForTest.onEditorReadyToEdit();
         });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
index 13cdeda..b6901b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
@@ -481,7 +481,7 @@
 
         // Extras for incognito CCT.
         if (isOffTheRecord) {
-            IncognitoCustomTabIntentDataProvider.addIncongitoExtrasForChromeFeatures(
+            IncognitoCustomTabIntentDataProvider.addIncognitoExtrasForChromeFeatures(
                     intent, IncognitoCCTCallerId.READ_LATER);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabIntentDataProvider.java
index 40439ce0..936147d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabIntentDataProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabIntentDataProvider.java
@@ -159,7 +159,7 @@
         return CustomTabsConnection.getInstance().getClientPackageNameForSession(sessionToken);
     }
 
-    public static void addIncongitoExtrasForChromeFeatures(
+    public static void addIncognitoExtrasForChromeFeatures(
             Intent intent, @IntentHandler.IncognitoCCTCallerId int chromeCallerId) {
         intent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, true);
         intent.putExtra(IntentHandler.EXTRA_INCOGNITO_CCT_CALLER_ID, chromeCallerId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 4bd363029..4d126fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -503,7 +503,7 @@
         // Use Incognito CCT if the source page is in Incognito mode. This is gated by
         // flag ChromeFeatureList.CCT_INCOGNITO.
         if (mTab.isIncognito()) {
-            IncognitoCustomTabIntentDataProvider.addIncongitoExtrasForChromeFeatures(
+            IncognitoCustomTabIntentDataProvider.addIncognitoExtrasForChromeFeatures(
                     customTabsIntent.intent, IntentHandler.IncognitoCCTCallerId.READER_MODE);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesIPH.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesIPH.java
index 0cd42e9..8b1ee88d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesIPH.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesIPH.java
@@ -4,11 +4,13 @@
 
 package org.chromium.chrome.browser.explore_sites;
 
+import android.app.Activity;
 import android.content.Context;
 import android.graphics.Rect;
 import android.view.View;
 
-import org.chromium.chrome.browser.app.ChromeActivity;
+import org.chromium.base.ContextUtils;
+import org.chromium.chrome.browser.ActivityUtils;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
@@ -28,11 +30,10 @@
 public class ExploreSitesIPH {
     public static void configureIPH(TileView tileView, Profile profile) {
         Context context = tileView.getContext();
-        if (!(context instanceof ChromeActivity)) {
-            return;
-        }
+        if (context == null) return;
+        Activity activity = ContextUtils.activityFromContext(context);
+        if (activity == null) return;
 
-        ChromeActivity activity = (ChromeActivity) context;
         if (tileView.isAttachedToWindow()) {
             maybeShowIPH(tileView, profile, activity);
         } else {
@@ -48,8 +49,8 @@
         }
     }
 
-    private static void maybeShowIPH(TileView tileView, Profile profile, ChromeActivity activity) {
-        if (activity.isActivityFinishingOrDestroyed()) return;
+    private static void maybeShowIPH(TileView tileView, Profile profile, Activity activity) {
+        if (ActivityUtils.isActivityFinishingOrDestroyed(activity)) return;
 
         final String contentString =
                 tileView.getContext().getString(org.chromium.chrome.R.string.explore_sites_iph);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
index 2eac2c6..77c5ee21 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBar.java
@@ -181,8 +181,9 @@
         mClicked = true;
         mDelegate.onSurveyTriggered();
 
-        SurveyController.getInstance().showSurveyIfAvailable(
-                TabUtils.getActivity(tab), mSiteId, mShowAsBottomSheet, mDisplayLogoResId);
+        // TODO(https://crbug.com/1212754): Move to ChromeSurveyController.
+        SurveyController.getInstance().showSurveyIfAvailable(TabUtils.getActivity(tab), mSiteId,
+                mShowAsBottomSheet, mDisplayLogoResId, mDelegate.getLifecycleDispatcher());
         super.onCloseButtonClicked();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBarDelegate.java
index e42276f..cb123a4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBarDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/SurveyInfoBarDelegate.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.infobar;
 
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+
 /**
  * Delegate for survey info bar actions.
  */
@@ -36,4 +38,10 @@
      * @return The string that will be displayed on the info bar.
      */
     String getSurveyPromptString();
+
+    /**
+     * Called to supply the survey info bar with lifecycle dispatcher used to show survey.
+     * @return The lifecycle dispatcher used to dispatch signals from the activity.
+     * */
+    ActivityLifecycleDispatcher getLifecycleDispatcher();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeActivityNativeDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeActivityNativeDelegate.java
index b90e8ee..d472be3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeActivityNativeDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeActivityNativeDelegate.java
@@ -43,7 +43,8 @@
     void onStopWithNative();
 
     /**
-     * @return Whether the activity linked to the delegate has been destroyed or is finishing.
+     * @return Whether the activity linked to the delegate has been destroyed or is finishing. The
+     *         majority of clients should prefer the method in {@link ActivityUtils}.
      */
     boolean isActivityFinishingOrDestroyed();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/printing/PrintShareActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/printing/PrintShareActivity.java
index d55a014..2d57252 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/printing/PrintShareActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/printing/PrintShareActivity.java
@@ -4,14 +4,10 @@
 
 package org.chromium.chrome.browser.printing;
 
-import android.app.Activity;
-
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeAccessorActivity;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.printing.PrintingController;
 import org.chromium.printing.PrintingControllerImpl;
@@ -20,10 +16,11 @@
  * A simple activity that allows Chrome to expose print as an option in the share menu.
  */
 public class PrintShareActivity extends ChromeAccessorActivity {
+    public static final String BROADCAST_ACTION = "PrintShareActivityBroadcastAction";
+
     @Override
-    protected void handleAction(Activity triggeringActivity,
-            MenuOrKeyboardActionController menuOrKeyboardActionController) {
-        menuOrKeyboardActionController.onMenuOrKeyboardAction(R.id.print_id, true);
+    protected String getBroadcastAction() {
+        return BROADCAST_ACTION;
     }
 
     public static boolean featureIsAvailable(Tab currentTab) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java
index b5579f4..e3efdada 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java
@@ -4,68 +4,22 @@
 
 package org.chromium.chrome.browser.send_tab_to_self;
 
-import android.app.Activity;
-
-import androidx.annotation.VisibleForTesting;
-
 import org.chromium.chrome.browser.ChromeAccessorActivity;
-import org.chromium.chrome.browser.app.ChromeActivity;
-import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
 import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridge;
-import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfCoordinator;
-import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
-import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
-import org.chromium.content_public.browser.NavigationEntry;
-import org.chromium.ui.base.WindowAndroid;
 
 /**
  * A simple activity that allows Chrome to expose send tab to self as an option in the share menu.
  */
 public class SendTabToSelfShareActivity extends ChromeAccessorActivity {
-    private static BottomSheetController sBottomSheetControllerForTesting;
+    public static final String BROADCAST_ACTION = "SendTabToSelfShareActivityBroadcastAction";
 
     @Override
-    public void handleAction(Activity triggeringActivity,
-            MenuOrKeyboardActionController menuOrKeyboardActionController) {
-        // TODO(crbug.com/1175155): Remove ChromeActivity reference once the activity tab is
-        // available via UnownedUserData.
-        ChromeActivity chromeActivity = (ChromeActivity) triggeringActivity;
-        Tab tab = chromeActivity.getActivityTabProvider().get();
-        if (tab == null) return;
-        NavigationEntry entry = tab.getWebContents().getNavigationController().getVisibleEntry();
-        if (entry == null) return;
-        BottomSheetController controller =
-                getBottomSheetController(chromeActivity.getWindowAndroid());
-        if (controller == null) {
-            return;
-        }
-
-        boolean isSyncEnabled =
-                ProfileSyncService.get() != null && ProfileSyncService.get().isSyncRequested();
-        controller.requestShowContent(
-                SendTabToSelfCoordinator.createBottomSheetContent(triggeringActivity,
-                        entry.getUrl().getSpec(), entry.getTitle(), entry.getTimestamp(),
-                        controller, new SettingsLauncherImpl(), isSyncEnabled),
-                true);
-        // TODO(crbug.com/968246): Remove the need to call this explicitly and instead have it
-        // automatically show since PeekStateEnabled is set to false.
-        controller.expandSheet();
+    protected String getBroadcastAction() {
+        return BROADCAST_ACTION;
     }
 
     public static boolean featureIsAvailable(Tab currentTab) {
         return SendTabToSelfAndroidBridge.isFeatureAvailable(currentTab.getWebContents());
     }
-
-    private BottomSheetController getBottomSheetController(WindowAndroid window) {
-        if (sBottomSheetControllerForTesting != null) return sBottomSheetControllerForTesting;
-        return BottomSheetControllerProvider.from(window);
-    }
-
-    @VisibleForTesting
-    public static void setBottomSheetControllerForTesting(BottomSheetController controller) {
-        sBottomSheetControllerForTesting = controller;
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareRegistrationCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareRegistrationCoordinator.java
new file mode 100644
index 0000000..ad97f053
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareRegistrationCoordinator.java
@@ -0,0 +1,221 @@
+// 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.share;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.IntentUtils;
+import org.chromium.base.Log;
+import org.chromium.base.supplier.Supplier;
+import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity;
+import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfCoordinator;
+import org.chromium.chrome.browser.sync.ProfileSyncService;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.content_public.browser.NavigationEntry;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Glues the sub-class of ChromeAccessorActivity to the relevant share action.
+ *
+ * When adding a new share action to Chrome,
+ * 1. Add a new subclass of {@link ChromeAccessorActivity}.
+ * 2. Register that activity and the proper intent-filter in AndroidManifest.xml.
+ * 3. Register a {@link BroadcastReceiver} here to catch the action broadcasted from (1).
+ * 4. Implement the sharing logic in a function inside this class and call it from (3).
+ **/
+public class ShareRegistrationCoordinator {
+    private static final String TAG = "ShareRegCoord";
+
+    /** Handles receiving share-specific internal broadcasts. */
+    public static class ShareBroadcastReceiver extends BroadcastReceiver {
+        /** A token used to verify that the broadcast is a Chrome-internal one. */
+        private static final String EXTRA_TOKEN = "receiver_token";
+
+        /** The type to pass along to the receiver, used to route the request. */
+        @VisibleForTesting
+        static final String EXTRA_TYPE = "share_type";
+
+        /** Top-level intent action that allows the share actions to be grouped in one intent. */
+        private static final String RECEIVER_ACTION = "ShareBroadcastReceiverBroadcastAction";
+
+        private static final Map<Integer, ShareBroadcastReceiver> sReceiverMap = new HashMap<>();
+
+        /**
+         * Send a share broadcast with the given action.
+         * @param taskId The Activity task id for the broadcast destination,.
+         * @param action The share action to be broadcast.
+         */
+        public static void sendShareBroadcastWithAction(int taskId, String action) {
+            sendShareBroadcastWithAction(taskId, action, ContextUtils.getApplicationContext());
+        }
+
+        @VisibleForTesting
+        static void sendShareBroadcastWithAction(int taskId, String action, Context context) {
+            ShareBroadcastReceiver receiver = sReceiverMap.get(taskId);
+            if (receiver == null) {
+                Log.e(TAG,
+                        "Attempt to send share broadcast before reciever was registered: \""
+                                + action + "\"");
+                return;
+            }
+
+            Intent intent = new Intent(RECEIVER_ACTION);
+            // Attach the parent ShareRegistrationCoordinator's hashcode to verify the intent.
+            intent.putExtra(EXTRA_TOKEN, receiver.getHashCodeToken());
+            intent.putExtra(EXTRA_TYPE, action);
+            intent.putExtra(ShareHelper.EXTRA_TASK_ID, taskId);
+
+            context.sendBroadcast(intent);
+        }
+
+        private final Map<String, Runnable> mShareMap = new HashMap<>();
+        private final int mTaskId;
+        private final int mHashCodeToken;
+        private Context mContext;
+        private boolean mIsDestroyed;
+
+        /**
+         * @param activity The activity to associate with this receiver.
+         */
+        public ShareBroadcastReceiver(Activity activity) {
+            this(activity.getTaskId(), ContextUtils.getApplicationContext());
+        }
+
+        @VisibleForTesting
+        ShareBroadcastReceiver(int taskId, Context context) {
+            mTaskId = taskId;
+            mContext = context;
+            // We do this so the token is durable over the lifetime of the app.
+            mHashCodeToken = hashCode();
+
+            sReceiverMap.put(mTaskId, this);
+            mContext.registerReceiver(this, new IntentFilter(RECEIVER_ACTION));
+        }
+
+        /** Destroy the receiver. */
+        public void destroy() {
+            mIsDestroyed = true;
+            sReceiverMap.remove(mTaskId);
+            mContext.unregisterReceiver(this);
+
+            mContext = null;
+        }
+
+        /**
+         * Register this share type.
+         * @param type The share type to register.
+         * @param runnable The runnable to run when the share type is broadcasted.
+         */
+        public void registerShareType(String type, Runnable runnable) {
+            if (mIsDestroyed) {
+                Log.e(TAG, "Attempted to register type after destruction: \"" + type + "\".");
+                return;
+            }
+
+            if (mShareMap.containsKey(type)) {
+                throw new IllegalStateException(
+                        "Only one instance of a share type should be registered at a time.");
+            }
+
+            mShareMap.put(type, runnable);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String type = IntentUtils.safeGetStringExtra(intent, EXTRA_TYPE);
+            if (mIsDestroyed) {
+                Log.e(TAG, "Broadcast received after destruction: \"" + type + "\".");
+                return;
+            }
+
+            boolean hasToken = intent.hasExtra(EXTRA_TOKEN)
+                    && intent.getIntExtra(EXTRA_TOKEN, 0) == mHashCodeToken;
+            boolean hasTaskId = intent.hasExtra(ShareHelper.EXTRA_TASK_ID)
+                    && intent.getIntExtra(ShareHelper.EXTRA_TASK_ID, 0) == mTaskId;
+            if (!hasToken || !hasTaskId) return;
+
+            if (!mShareMap.containsKey(type)) {
+                Log.e(TAG, "Unidentified type receieved: \"" + type + "\".");
+                return;
+            }
+
+            mShareMap.get(type).run();
+        }
+
+        private int getHashCodeToken() {
+            return mHashCodeToken;
+        }
+    }
+
+    private final ShareBroadcastReceiver mShareBroadcastReceiver;
+
+    /** ShareRegistrationCoordinator constructor. */
+    public ShareRegistrationCoordinator(Activity activity, Supplier<Tab> currentTabSupplier,
+            BottomSheetController bottomSheetController) {
+        mShareBroadcastReceiver = new ShareBroadcastReceiver(activity);
+
+        mShareBroadcastReceiver.registerShareType(
+                SendTabToSelfShareActivity.BROADCAST_ACTION, () -> {
+                    NavigationEntry entry = currentTabSupplier.hasValue()
+                            ? currentTabSupplier.get()
+                                      .getWebContents()
+                                      .getNavigationController()
+                                      .getVisibleEntry()
+                            : null;
+                    doSendTabToSelfShare(activity, entry, bottomSheetController);
+                });
+    }
+
+    /**
+     * Register the type runnable pair.
+     * @param type The share type to register.
+     * @param runnable The runnable to invoke for the given share type.
+     */
+    public void registerShareType(String type, Runnable runnable) {
+        mShareBroadcastReceiver.registerShareType(type, runnable);
+    }
+
+    /** Destroys this component */
+    public void destroy() {
+        mShareBroadcastReceiver.destroy();
+    }
+
+    /**
+     * Starts a send tab to self share action.
+     *
+     * @param context The current application context.
+     * @param entry The current {@link NavigationEntry}, null if the current tab isn't available or
+     *              doesn't have a visible entry.
+     * @param bottomSheetController Controls what's shown in the bottom sheet.
+     */
+    @VisibleForTesting
+    void doSendTabToSelfShare(@NonNull Context context, @Nullable NavigationEntry entry,
+            @NonNull BottomSheetController bottomSheetController) {
+        if (entry == null) return;
+        boolean isSyncEnabled =
+                ProfileSyncService.get() != null && ProfileSyncService.get().isSyncRequested();
+        bottomSheetController.requestShowContent(
+                SendTabToSelfCoordinator.createBottomSheetContent(context, entry.getUrl().getSpec(),
+                        entry.getTitle(), entry.getTimestamp(), bottomSheetController,
+                        new SettingsLauncherImpl(), isSyncEnabled),
+                true);
+        // TODO(crbug.com/968246): Remove the need to call this explicitly and instead have it
+        // automatically show since PeekStateEnabled is set to false.
+        bottomSheetController.expandSheet();
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java b/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java
index a342139..d28c2af5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java
@@ -9,6 +9,7 @@
 import android.text.TextUtils;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.CommandLine;
@@ -25,6 +26,7 @@
 import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
 import org.chromium.chrome.browser.infobar.SurveyInfoBar;
 import org.chromium.chrome.browser.infobar.SurveyInfoBarDelegate;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
@@ -51,15 +53,15 @@
  */
 public class ChromeSurveyController implements InfoBarAnimationListener {
     private static final String TAG = "ChromeSurveyCtrler";
-    private static final long REQUIRED_VISIBILITY_DURATION_MS = 5000;
 
     @VisibleForTesting
+    static final long REQUIRED_VISIBILITY_DURATION_MS = 5000;
+    @VisibleForTesting
     public static final String COMMAND_LINE_PARAM_NAME = "survey_override_site_id";
     @VisibleForTesting
     static final String MAX_NUMBER = "max-number";
     @VisibleForTesting
     static final String SITE_ID_PARAM_NAME = "site-id";
-
     private static boolean sForceUmaEnabledForTesting;
 
     /**
@@ -108,12 +110,15 @@
 
     private final String mTriggerId;
     private final String mPrefKeyPromptDisplayed;
+    private final @Nullable ActivityLifecycleDispatcher mLifecycleDispatcher;
 
     @VisibleForTesting
-    ChromeSurveyController(String triggerId) {
+    ChromeSurveyController(
+            String triggerId, @Nullable ActivityLifecycleDispatcher lifecycleDispatcher) {
         mTriggerId = triggerId;
         mPrefKeyPromptDisplayed =
                 ChromePreferenceKeys.CHROME_SURVEY_PROMPT_DISPLAYED_TIMESTAMP.createKey(mTriggerId);
+        mLifecycleDispatcher = lifecycleDispatcher;
     }
 
     /**
@@ -121,11 +126,12 @@
      * @param tabModelSelector The tab model selector to access the tab on which the survey will be
      *                         shown.
      */
-    public static void initialize(TabModelSelector tabModelSelector) {
+    public static void initialize(TabModelSelector tabModelSelector,
+            @Nullable ActivityLifecycleDispatcher lifecycleDispatcher) {
         assert tabModelSelector != null;
         if (!isSurveyEnabled() || TextUtils.isEmpty(getTriggerId())) return;
         new StartDownloadIfEligibleTask(
-                new ChromeSurveyController(getTriggerId()), tabModelSelector)
+                new ChromeSurveyController(getTriggerId(), lifecycleDispatcher), tabModelSelector)
                 .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
@@ -387,6 +393,11 @@
                 return ContextUtils.getApplicationContext().getString(
                         R.string.chrome_survey_prompt);
             }
+
+            @Override
+            public ActivityLifecycleDispatcher getLifecycleDispatcher() {
+                return mLifecycleDispatcher;
+            }
         };
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/survey/SurveyController.java b/chrome/android/java/src/org/chromium/chrome/browser/survey/SurveyController.java
index 0c6698ee..2fe61ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/survey/SurveyController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/survey/SurveyController.java
@@ -7,9 +7,11 @@
 import android.app.Activity;
 import android.content.Context;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.chrome.browser.AppHooks;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 
 /**
  * Class that controls retrieving and displaying surveys. Clients should call #downloadSurvey() and
@@ -72,15 +74,26 @@
             Runnable onFailureRunnable) {}
 
     /**
+     * @deprecated Use #showSurveyIfAvailable(Activity, String, boolean, int,
+     *         ActivityLifecycleDispatcher).
+     */
+    @Deprecated
+    public void showSurveyIfAvailable(
+            Activity activity, String siteId, boolean showAsBottomSheet, int displayLogoResId) {
+        this.showSurveyIfAvailable(activity, siteId, showAsBottomSheet, displayLogoResId, null);
+    }
+
+    /**
      * Show the survey.
      * @param activity The client activity for the survey request.
      * @param siteId The id of the site from where the survey will be downloaded.
      * @param showAsBottomSheet Whether the survey should be presented as a bottom sheet or not.
      * @param displayLogoResId Optional resource id of the logo to be displayed on the survey.
      *                         Pass 0 for no logo.
+     * @param lifecycleDispatcher LifecycleDispatcher that will dispatch different activity signals.
      */
-    public void showSurveyIfAvailable(
-            Activity activity, String siteId, boolean showAsBottomSheet, int displayLogoResId) {}
+    public void showSurveyIfAvailable(Activity activity, String siteId, boolean showAsBottomSheet,
+            int displayLogoResId, @Nullable ActivityLifecycleDispatcher lifecycleDispatcher) {}
 
     /**
      * Clears the survey cache containing responses and history.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoMetricTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoMetricTest.java
index b1bae5e..549a670 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoMetricTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoMetricTest.java
@@ -85,7 +85,7 @@
         assertEquals(0, RecordHistogram.getHistogramTotalCountForTesting(UMA_KEY));
         Intent intent = createMinimalIncognitoCustomTabIntent();
         CustomTabIntentDataProvider.addReaderModeUIExtras(intent);
-        IncognitoCustomTabIntentDataProvider.addIncongitoExtrasForChromeFeatures(
+        IncognitoCustomTabIntentDataProvider.addIncognitoExtrasForChromeFeatures(
                 intent, IntentHandler.IncognitoCCTCallerId.READER_MODE);
 
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
index 260ab27..179f9d6 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
@@ -373,7 +373,7 @@
     public void ensureAddCustomMenuItemIsEnabledForReaderMode() throws Exception {
         Intent intent = createMinimalIncognitoCustomTabIntent();
         CustomTabIntentDataProvider.addReaderModeUIExtras(intent);
-        IncognitoCustomTabIntentDataProvider.addIncongitoExtrasForChromeFeatures(
+        IncognitoCustomTabIntentDataProvider.addIncognitoExtrasForChromeFeatures(
                 intent, IntentHandler.IncognitoCCTCallerId.READER_MODE);
         CustomTabActivity activity = launchIncognitoCustomTab(intent);
         CustomTabsTestUtils.openAppMenuAndAssertMenuShown(activity);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerIntegrationTest.java
index d9d616b..3664e86 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerIntegrationTest.java
@@ -26,6 +26,7 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
 import org.chromium.chrome.browser.infobar.SurveyInfoBar;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.survey.ChromeSurveyController.InfoBarClosingState;
@@ -182,8 +183,8 @@
     }
 
     private static class AlwaysSuccessfulSurveyController extends SurveyController {
-        public CallbackHelper downloadCallbackHelper = new CallbackHelper();
-        public CallbackHelper showSurveyCallbackHelper = new CallbackHelper();
+        public final CallbackHelper downloadCallbackHelper = new CallbackHelper();
+        public final CallbackHelper showSurveyCallbackHelper = new CallbackHelper();
 
         @Override
         public void downloadSurvey(Context context, String triggerId, Runnable onSuccessRunnable,
@@ -195,8 +196,9 @@
         }
 
         @Override
-        public void showSurveyIfAvailable(
-                Activity activity, String siteId, boolean showAsBottomSheet, int displayLogoResId) {
+        public void showSurveyIfAvailable(Activity activity, String siteId,
+                boolean showAsBottomSheet, int displayLogoResId,
+                ActivityLifecycleDispatcher lifecycleDispatcher) {
             showSurveyCallbackHelper.notifyCalled();
         }
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ui/system/StatusBarColorControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ui/system/StatusBarColorControllerTest.java
index 0d70dbca..4a1374a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ui/system/StatusBarColorControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ui/system/StatusBarColorControllerTest.java
@@ -75,7 +75,7 @@
     @Feature({"StatusBar"})
     @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP_MR1)
     @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) // Status bar is always black on tablets
-    public void testColorToggleIncongitoInOverview() throws Exception {
+    public void testColorToggleIncognitoInOverview() throws Exception {
         ChromeTabbedActivity activity = mActivityTestRule.getActivity();
         Resources resources = activity.getResources();
         final int expectedOverviewStandardColor = defaultColorFallbackToBlack(
@@ -87,13 +87,13 @@
                 "about:blank", true /* incognito */, TabLaunchType.FROM_CHROME_UI);
         TabModelSelector tabModelSelector = activity.getTabModelSelector();
         TestThreadUtils.runOnUiThreadBlocking(
-                () -> { tabModelSelector.selectModel(true /* incongito */); });
+                () -> { tabModelSelector.selectModel(true /* incognito */); });
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> { activity.getLayoutManager().showOverview(false /* animate */); });
 
         waitForStatusBarColor(activity, expectedOverviewIncognitoColor);
         TestThreadUtils.runOnUiThreadBlocking(
-                () -> { tabModelSelector.selectModel(false /* incongito */); });
+                () -> { tabModelSelector.selectModel(false /* incognito */); });
         ThemeTestUtils.assertStatusBarColor(activity, expectedOverviewStandardColor);
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
index 441b09e9..0535f86 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
@@ -4,9 +4,7 @@
 
 package org.chromium.chrome.browser.send_tab_to_self;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.support.test.filters.SmallTest;
@@ -17,66 +15,36 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.ActivityTabProvider;
-import org.chromium.chrome.browser.app.ChromeActivity;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.profiles.ProfileJni;
 import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridge;
 import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridgeJni;
-import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfCoordinator;
-import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
-import org.chromium.content_public.browser.NavigationController;
-import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.url.JUnitTestGURLs;
 
-/** Tests for SendTabToSelfShareActivityTest */
+/** Tests for SendTabToSelfShareActivity */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 public class SendTabToSelfShareActivityTest {
     @Rule
     public JniMocker mocker = new JniMocker();
-    @Mock
-    public Profile.Natives mMockProfileNatives;
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock
     SendTabToSelfAndroidBridge.Natives mNativeMock;
     @Mock
     private Tab mTab;
     @Mock
-    private ChromeActivity mChromeActivity;
-    @Mock
-    private ActivityTabProvider mActivityTabProvider;
-    @Mock
     private WebContents mWebContents;
-    @Mock
-    private NavigationController mNavigationController;
-    @Mock
-    private NavigationEntry mNavigationEntry;
-    @Mock
-    private BottomSheetContent mBottomSheetContent;
-    @Mock
-    private BottomSheetController mBottomSheetController;
-
-    @Mock
-    private ProfileSyncService mProfileSyncService;
-
-    private Profile mProfile;
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
         mocker.mock(SendTabToSelfAndroidBridgeJni.TEST_HOOKS, mNativeMock);
-        mocker.mock(ProfileJni.TEST_HOOKS, mMockProfileNatives);
     }
 
     @Test
@@ -89,33 +57,4 @@
         boolean actual = SendTabToSelfShareActivity.featureIsAvailable(mTab);
         Assert.assertEquals(expected, actual);
     }
-
-    @Test
-    @SmallTest
-    public void testHandleShareAction() {
-        // Setup the mocked object chain to get to the profile.
-        when(mChromeActivity.getActivityTabProvider()).thenReturn(mActivityTabProvider);
-        when(mActivityTabProvider.get()).thenReturn(mTab);
-        when(mMockProfileNatives.fromWebContents(eq(mWebContents))).thenReturn(mProfile);
-
-        // Setup the mocked object chain to get to the url, title and timestamp.
-        when(mTab.getWebContents()).thenReturn(mWebContents);
-        when(mWebContents.getNavigationController()).thenReturn(mNavigationController);
-        when(mNavigationController.getVisibleEntry()).thenReturn(mNavigationEntry);
-        when(mNavigationEntry.getUrl())
-                .thenReturn(JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL));
-
-        // Setup the mocked object for sync settings.
-        when(mProfileSyncService.isSyncRequested()).thenReturn(true);
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> ProfileSyncService.overrideForTests(mProfileSyncService));
-
-        // Setup the mocked object chain to get the bottom controller.
-        SendTabToSelfShareActivity shareActivity = new SendTabToSelfShareActivity();
-        SendTabToSelfCoordinator.setBottomSheetContentForTesting(mBottomSheetContent);
-        SendTabToSelfShareActivity.setBottomSheetControllerForTesting(mBottomSheetController);
-        shareActivity.handleAction(/* triggeringActivity= */ mChromeActivity,
-                /* menuOrKeyboardActionController= */ mChromeActivity);
-        verify(mBottomSheetController).requestShowContent(any(BottomSheetContent.class), eq(true));
-    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/share/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/share/OWNERS
new file mode 100644
index 0000000..f1b7df2
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/share/OWNERS
@@ -0,0 +1 @@
+file://chrome/android/java/src/org/chromium/chrome/browser/share/OWNERS
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/share/ShareRegistrationCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/share/ShareRegistrationCoordinatorTest.java
new file mode 100644
index 0000000..9d133d5
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/share/ShareRegistrationCoordinatorTest.java
@@ -0,0 +1,188 @@
+// 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.share;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.supplier.Supplier;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.share.ShareRegistrationCoordinator.ShareBroadcastReceiver;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridge;
+import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfAndroidBridgeJni;
+import org.chromium.chrome.browser.sync.ProfileSyncService;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.content_public.browser.NavigationEntry;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.url.JUnitTestGURLs;
+
+/** Tests for ShareRegistrationCoordinator. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ShareRegistrationCoordinatorTest {
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule
+    public JniMocker mocker = new JniMocker();
+
+    @Mock
+    SendTabToSelfAndroidBridge.Natives mNativeMock;
+
+    @Mock
+    private Profile mProfile;
+    @Mock
+    private Tab mTab;
+    @Mock
+    private NavigationEntry mNavigationEntry;
+    @Mock
+    private BottomSheetController mBottomSheetController;
+    @Mock
+    private ProfileSyncService mProfileSyncService;
+    @Mock
+    private Context mContext;
+
+    @Captor
+    private ArgumentCaptor<Intent> mIntentCaptor;
+
+    @Spy
+    private Activity mActivity;
+
+    private Supplier<Tab> mCurrentTabSupplier;
+    private ShareRegistrationCoordinator mShareRegistrationCoordinator;
+
+    @Before
+    public void setUp() {
+        mocker.mock(SendTabToSelfAndroidBridgeJni.TEST_HOOKS, mNativeMock);
+        Profile.setLastUsedProfileForTesting(mProfile);
+        mActivity = Mockito.spy(Robolectric.buildActivity(Activity.class).create().get());
+
+        mCurrentTabSupplier = () -> mTab;
+        mShareRegistrationCoordinator = new ShareRegistrationCoordinator(
+                mActivity, mCurrentTabSupplier, mBottomSheetController);
+    }
+
+    @Test
+    @SmallTest
+    public void doSendTabToSelfShare() {
+        // Setup the mocked object chain to get to the url, title and timestamp.
+        when(mNavigationEntry.getUrl())
+                .thenReturn(JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL));
+
+        // Setup the mocked object for sync settings.
+        when(mProfileSyncService.isSyncRequested()).thenReturn(true);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> ProfileSyncService.overrideForTests(mProfileSyncService));
+
+        mShareRegistrationCoordinator.doSendTabToSelfShare(
+                mActivity, mNavigationEntry, mBottomSheetController);
+        verify(mBottomSheetController).requestShowContent(any(BottomSheetContent.class), eq(true));
+    }
+
+    @Test
+    @SmallTest
+    public void sendShareBroadcastWithAction() {
+        ShareBroadcastReceiver receiver = new ShareBroadcastReceiver(mActivity);
+        Runnable runnable = Mockito.mock(Runnable.class);
+        receiver.registerShareType("foobar", runnable);
+
+        ShareBroadcastReceiver.sendShareBroadcastWithAction(mActivity.getTaskId(), "foobar");
+        verify(runnable).run();
+
+        receiver.destroy();
+    }
+
+    @Test
+    @SmallTest
+    public void sendShareBroadcastWithAction_NoRegisteredReceiver() {
+        ShareBroadcastReceiver.sendShareBroadcastWithAction(mActivity.getTaskId(), "foobar");
+        verify(mContext, times(0)).sendBroadcast(mIntentCaptor.capture());
+    }
+
+    @Test
+    @SmallTest
+    public void sendShareBroadcastWithAction_ReplaceShareTypeRunnable() {
+        ShareBroadcastReceiver receiver = new ShareBroadcastReceiver(mActivity);
+        Runnable runnable1 = Mockito.mock(Runnable.class);
+        Runnable runnable2 = Mockito.mock(Runnable.class);
+        receiver.registerShareType("foobar", runnable1);
+        try {
+            receiver.registerShareType("foobar", runnable2);
+            Assert.assertTrue("Expected exception to be thrown", false);
+        } catch (IllegalStateException e) {
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void sendShareBroadcastWithAction_RegisterShareTypeAfterDestruction() {
+        ShareBroadcastReceiver receiver = new ShareBroadcastReceiver(mActivity);
+        receiver.destroy();
+        Runnable runnable = Mockito.mock(Runnable.class);
+        receiver.registerShareType("foobar", runnable);
+
+        ShareBroadcastReceiver.sendShareBroadcastWithAction(mActivity.getTaskId(), "foobar");
+        verify(runnable, times(0)).run();
+    }
+
+    @Test
+    @SmallTest
+    public void sendShareBroadcastWithAction_SendBroadcastAfterDestruction() {
+        ShareBroadcastReceiver receiver = new ShareBroadcastReceiver(mActivity);
+        Runnable runnable = Mockito.mock(Runnable.class);
+        receiver.registerShareType("foobar", runnable);
+        receiver.destroy();
+
+        ShareBroadcastReceiver.sendShareBroadcastWithAction(mActivity.getTaskId(), "foobar");
+        verify(runnable, times(0)).run();
+    }
+
+    @Test
+    @SmallTest
+    public void sendShareBroadcastWithAction_DifferentActivity() {
+        ShareBroadcastReceiver receiver1 = new ShareBroadcastReceiver(mActivity);
+        Runnable runnable1 = Mockito.mock(Runnable.class);
+        receiver1.registerShareType("foobar", runnable1);
+
+        doReturn(123).when(mActivity).getTaskId();
+        ShareBroadcastReceiver receiver2 = new ShareBroadcastReceiver(mActivity);
+        Runnable runnable2 = Mockito.mock(Runnable.class);
+        receiver2.registerShareType("foobar", runnable2);
+
+        ShareBroadcastReceiver.sendShareBroadcastWithAction(123, "foobar");
+        verify(runnable1, times(0)).run();
+        verify(runnable2).run();
+
+        receiver1.destroy();
+        receiver2.destroy();
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerFlowTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerFlowTest.java
index b628920..db64006 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerFlowTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerFlowTest.java
@@ -7,6 +7,7 @@
 import static org.mockito.ArgumentMatchers.any;
 
 import android.content.Context;
+import android.os.Looper;
 
 import androidx.annotation.Nullable;
 
@@ -20,6 +21,7 @@
 import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
@@ -28,18 +30,23 @@
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.CommandLine;
+import org.chromium.base.ContextUtils;
 import org.chromium.base.metrics.test.ShadowRecordHistogram;
 import org.chromium.base.task.test.BackgroundShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.JniMocker;
+import org.chromium.base.test.util.PayloadCallbackHelper;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.infobar.SurveyInfoBar;
 import org.chromium.chrome.browser.infobar.SurveyInfoBarDelegate;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.chrome.browser.survey.ChromeSurveyController.InfoBarClosingState;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -48,6 +55,7 @@
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * "Integration" style unit tests for {@link ChromeSurveyController} that mocks most of the
@@ -96,14 +104,14 @@
 
     @Implements(SurveyInfoBar.class)
     static class ShadowSurveyInfoBar {
-        static CallbackHelper sShowInfoBarCallback;
+        static PayloadCallbackHelper<SurveyInfoBarDelegate> sShowInfoBarCallback;
 
         @Implementation
         public static void showSurveyInfoBar(WebContents webContents, String siteId,
                 boolean showAsBottomSheet, int displayLogoResId,
                 SurveyInfoBarDelegate surveyInfoBarDelegate) {
             Assert.assertNotNull("sShowInfoBarCallback is null.", sShowInfoBarCallback);
-            sShowInfoBarCallback.notifyCalled();
+            sShowInfoBarCallback.notifyCalled(surveyInfoBarDelegate);
         }
     }
 
@@ -131,6 +139,8 @@
     WebContents mMockWebContent;
     @Mock
     InfoBarContainer mMockInfoBarContainer;
+    @Mock
+    ActivityLifecycleDispatcher mMockLifecycleDispatcher;
 
     private final TestSurveyController mTestSurveyController = new TestSurveyController();
 
@@ -147,7 +157,7 @@
                 ChromeSurveyController.SITE_ID_PARAM_NAME, TEST_TRIGGER_ID);
         ShadowChromeFeatureList.sParamValues.put(ChromeSurveyController.MAX_NUMBER, "99");
         ShadowInfoBarContainer.sInfoBarContainer = mMockInfoBarContainer;
-        ShadowSurveyInfoBar.sShowInfoBarCallback = new CallbackHelper();
+        ShadowSurveyInfoBar.sShowInfoBarCallback = new PayloadCallbackHelper<>();
 
         // Set user is selected and by pass the rate limit. The rate limiting logic is tested in
         // ChromeSurveyControllerTest.
@@ -252,17 +262,8 @@
     }
 
     @Test
-    public void testPresentSurvey_ValidTab() {
-        setupTabMocks();
-        initializeChromeSurveyController();
-        assertCallbackAssignedInSurveyController();
-
-        // Verify the survey should be attempted to present on a valid tab.
-        mockTabReady();
-        mTestSurveyController.onDownloadSuccessRunnable.run();
-
-        Assert.assertEquals("presentSurvey should be triggered.", 1,
-                ShadowSurveyInfoBar.sShowInfoBarCallback.getCallCount());
+    public void testPresentSurvey_ValidTab_SurveyInfobarDelegate() {
+        presentSurveyInfoBarInValidTab();
     }
 
     @Test
@@ -275,15 +276,13 @@
         Mockito.doReturn(true).when(mMockTab).isLoading();
         mTestSurveyController.onDownloadSuccessRunnable.run();
 
-        Assert.assertEquals("presentSurvey should not be triggered.", 0,
-                ShadowSurveyInfoBar.sShowInfoBarCallback.getCallCount());
+        assertSurveyInfoBarShown(false);
         Assert.assertNotNull("Tab observer should be registered.", mTabObserver);
 
         // Assume tab loading is complete.
         mockTabReady();
         mTabObserver.onPageLoadFinished(mMockTab, null);
-        Assert.assertEquals("presentSurvey should be triggered.", 1,
-                ShadowSurveyInfoBar.sShowInfoBarCallback.getCallCount());
+        assertSurveyInfoBarShown(true);
     }
 
     @Test
@@ -296,15 +295,13 @@
         Mockito.doReturn(false).when(mMockTab).isUserInteractable();
         mTestSurveyController.onDownloadSuccessRunnable.run();
 
-        Assert.assertEquals("presentSurvey should not be triggered.", 0,
-                ShadowSurveyInfoBar.sShowInfoBarCallback.getCallCount());
+        assertSurveyInfoBarShown(false);
         Assert.assertNotNull("Tab observer should be registered.", mTabObserver);
 
         // Assume tab loading is complete.
         mockTabReady();
         mTabObserver.onInteractabilityChanged(mMockTab, true);
-        Assert.assertEquals("presentSurvey should br triggered.", 1,
-                ShadowSurveyInfoBar.sShowInfoBarCallback.getCallCount());
+        assertSurveyInfoBarShown(true);
     }
 
     @Test
@@ -317,21 +314,120 @@
         Mockito.when(mMockModelSelector.getCurrentTab()).thenReturn(null);
         mTestSurveyController.onDownloadSuccessRunnable.run();
 
-        Assert.assertEquals("presentSurvey should not be triggered.", 0,
-                ShadowSurveyInfoBar.sShowInfoBarCallback.getCallCount());
+        assertSurveyInfoBarShown(false);
         Assert.assertNotNull(
                 "TabModelSelectorObserver should be registered.", mTabModelSelectorObserver);
 
-        // Assume tab selector can provide a tab (e.g. switch to a fully loaded tab)
+        // Assume tab selector can provide a tab (e.g. switch to a fully loaded tab).
         mockTabReady();
         Mockito.when(mMockModelSelector.getCurrentTab()).thenReturn(mMockTab);
         mTabModelSelectorObserver.onChange();
-        Assert.assertEquals("presentSurvey should triggered.", 1,
-                ShadowSurveyInfoBar.sShowInfoBarCallback.getCallCount());
+        assertSurveyInfoBarShown(true);
+    }
+
+    @Test
+    public void testSurveyInfoBarDelegate_getLifecycleDispatcher() {
+        presentSurveyInfoBarInValidTab();
+        SurveyInfoBarDelegate surveyInfoBarDelegate =
+                ShadowSurveyInfoBar.sShowInfoBarCallback.getOnlyPayloadBlocking();
+        Assert.assertEquals("#getLifecycleDispatcher is different.", mMockLifecycleDispatcher,
+                surveyInfoBarDelegate.getLifecycleDispatcher());
+    }
+
+    @Test
+    public void testSurveyInfoBarDelegate_getSurveyPromptString() {
+        presentSurveyInfoBarInValidTab();
+        SurveyInfoBarDelegate surveyInfoBarDelegate =
+                ShadowSurveyInfoBar.sShowInfoBarCallback.getOnlyPayloadBlocking();
+
+        Assert.assertEquals("#getPromptString is different.",
+                ContextUtils.getApplicationContext().getString(R.string.chrome_survey_prompt),
+                surveyInfoBarDelegate.getSurveyPromptString());
+    }
+
+    @Test
+    public void testSurveyInfoBarDelegate_onSurveyTriggered() {
+        presentSurveyInfoBarInValidTab();
+        SurveyInfoBarDelegate surveyInfoBarDelegate =
+                ShadowSurveyInfoBar.sShowInfoBarCallback.getOnlyPayloadBlocking();
+
+        surveyInfoBarDelegate.onSurveyTriggered();
+        assertInfoBarClosingStateRecorded(InfoBarClosingState.ACCEPTED_SURVEY);
+        assertInfoBarDisplayedRecorded();
+    }
+
+    @Test
+    public void testSurveyInfoBarDelegate_onSurveyInfoBarClosed() {
+        presentSurveyInfoBarInValidTab();
+        SurveyInfoBarDelegate surveyInfoBarDelegate =
+                ShadowSurveyInfoBar.sShowInfoBarCallback.getOnlyPayloadBlocking();
+
+        surveyInfoBarDelegate.onSurveyInfoBarClosed(
+                /*viaCloseButton=*/false, /*visibleWhenClosed=*/true);
+        assertInfoBarClosingStateRecorded(InfoBarClosingState.VISIBLE_INDIRECT);
+        assertInfoBarDisplayedNotRecorded("onSurveyInfoBarClosed with VISIBLE_INDIRECT "
+                + "should not result in info bar displayed being recorded.");
+
+        surveyInfoBarDelegate.onSurveyInfoBarClosed(
+                /*viaCloseButton=*/false, /*visibleWhenClosed=*/false);
+        assertInfoBarClosingStateRecorded(InfoBarClosingState.HIDDEN_INDIRECT);
+        assertInfoBarDisplayedNotRecorded("onSurveyInfoBarClosed with HIDDEN_INDIRECT "
+                + "should not result in info bar displayed being recorded.");
+
+        // #onSurveyInfoBarClosed(true, false) is not a valid case, so skipped in test.
+        surveyInfoBarDelegate.onSurveyInfoBarClosed(
+                /*viaCloseButton=*/true, /*visibleWhenClosed=*/true);
+        assertInfoBarClosingStateRecorded(InfoBarClosingState.CLOSE_BUTTON);
+        assertInfoBarDisplayedRecorded();
+    }
+
+    @Test
+    public void testSurveyInfoBarDelegate_onSurveyInfoBarTabBecomeInteractable() {
+        presentSurveyInfoBarInValidTab();
+        SurveyInfoBarDelegate surveyInfoBarDelegate =
+                ShadowSurveyInfoBar.sShowInfoBarCallback.getOnlyPayloadBlocking();
+
+        surveyInfoBarDelegate.onSurveyInfoBarTabInteractabilityChanged(true);
+        Shadows.shadowOf(Looper.myLooper())
+                .idleFor(ChromeSurveyController.REQUIRED_VISIBILITY_DURATION_MS,
+                        TimeUnit.MILLISECONDS);
+        assertInfoBarDisplayedRecorded();
+    }
+
+    @Test
+    public void testSurveyInfoBarDelegate_onSurveyInfoBarTabBecomeNotInteractable() {
+        presentSurveyInfoBarInValidTab();
+        SurveyInfoBarDelegate surveyInfoBarDelegate =
+                ShadowSurveyInfoBar.sShowInfoBarCallback.getOnlyPayloadBlocking();
+
+        surveyInfoBarDelegate.onSurveyInfoBarTabInteractabilityChanged(true);
+        Shadows.shadowOf(Looper.myLooper())
+                .idleFor(ChromeSurveyController.REQUIRED_VISIBILITY_DURATION_MS - 1,
+                        TimeUnit.MILLISECONDS);
+        surveyInfoBarDelegate.onSurveyInfoBarTabInteractabilityChanged(false);
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        assertInfoBarDisplayedNotRecorded("Info bar should not be recorded as displayed "
+                + "if interactivity changed before minimum required visibility duration.");
+    }
+
+    @Test
+    public void testSurveyInfoBarDelegate_onSurveyInfoBarTabHidden() {
+        presentSurveyInfoBarInValidTab();
+        SurveyInfoBarDelegate surveyInfoBarDelegate =
+                ShadowSurveyInfoBar.sShowInfoBarCallback.getOnlyPayloadBlocking();
+
+        surveyInfoBarDelegate.onSurveyInfoBarTabInteractabilityChanged(true);
+        Shadows.shadowOf(Looper.myLooper())
+                .idleFor(ChromeSurveyController.REQUIRED_VISIBILITY_DURATION_MS - 1,
+                        TimeUnit.MILLISECONDS);
+        surveyInfoBarDelegate.onSurveyInfoBarTabHidden();
+        Shadows.shadowOf(Looper.myLooper()).runToEndOfTasks();
+        assertInfoBarDisplayedNotRecorded("Info bar should not be recorded as displayed "
+                + "if hidden before minimum required visibility duration.");
     }
 
     private void initializeChromeSurveyController() {
-        ChromeSurveyController.initialize(mMockModelSelector);
+        ChromeSurveyController.initialize(mMockModelSelector, mMockLifecycleDispatcher);
         try {
             BackgroundShadowAsyncTask.runBackgroundTasks();
         } catch (Exception e) {
@@ -340,6 +436,17 @@
         ShadowLooper.runUiThreadTasks();
     }
 
+    private void presentSurveyInfoBarInValidTab() {
+        setupTabMocks();
+        initializeChromeSurveyController();
+        assertCallbackAssignedInSurveyController();
+
+        // Verify the survey should be attempted to present on a valid tab.
+        mockTabReady();
+        mTestSurveyController.onDownloadSuccessRunnable.run();
+        assertSurveyInfoBarShown(true);
+    }
+
     private void mockTabReady() {
         Mockito.doReturn(false).when(mMockTab).isLoading();
         Mockito.doReturn(true).when(mMockTab).isUserInteractable();
@@ -373,6 +480,32 @@
                 mTestSurveyController.onDownloadFailureRunnable);
     }
 
+    private void assertSurveyInfoBarShown(boolean shown) {
+        Assert.assertEquals("presentSurvey should triggered.", shown ? 1 : 0,
+                ShadowSurveyInfoBar.sShowInfoBarCallback.getCallCount());
+        if (shown) {
+            Assert.assertNotNull("SurveyInfoBarDelegate is null.",
+                    ShadowSurveyInfoBar.sShowInfoBarCallback.getOnlyPayloadBlocking());
+        }
+    }
+
+    private void assertInfoBarClosingStateRecorded(@InfoBarClosingState int state) {
+        int count = ShadowRecordHistogram.getHistogramValueCountForTesting(
+                "Android.Survey.InfoBarClosingState", state);
+        Assert.assertEquals(
+                String.format("InfoBarClosingState for state <%d> is not recorded.", state), 1,
+                count);
+    }
+
+    private void assertInfoBarDisplayedRecorded() {
+        Assert.assertTrue("SharedPreference for InfoBarShown is not recorded.",
+                mSharedPreferencesManager.contains(mPrefKey));
+    }
+
+    private void assertInfoBarDisplayedNotRecorded(String reason) {
+        Assert.assertFalse(reason, mSharedPreferencesManager.contains(mPrefKey));
+    }
+
     private static class TestSurveyController extends SurveyController {
         public final CallbackHelper downloadIfApplicableCallback = new CallbackHelper();
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java
index 5fb93a2..49492ecc 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java
@@ -249,7 +249,7 @@
         private int mMaxNumber;
 
         RiggedSurveyController(int randomNumberToReturn, int dayOfYear, int maxNumber) {
-            super(TEST_SURVEY_TRIGGER_ID);
+            super(TEST_SURVEY_TRIGGER_ID, null);
             mRandomNumberToReturn = randomNumberToReturn;
             mDayOfYear = dayOfYear;
             mMaxNumber = maxNumber;
@@ -271,11 +271,11 @@
         }
     }
 
-    class TestChromeSurveyController extends ChromeSurveyController {
+    static class TestChromeSurveyController extends ChromeSurveyController {
         private Tab mTab;
 
         public TestChromeSurveyController(String triggerId) {
-            super(triggerId);
+            super(triggerId, null);
         }
 
         @Override
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index cd8dd25f..100eb78 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5215,8 +5215,8 @@
       </message>
 
       <!-- Experimental Lacros Infobar-->
-      <message name="IDS_EXPERIMENTAL_LACROS_WARNING_MESSAGE" desc="Message shown when user launches Lacros, an experimental feature, for the first time.">
-        Lacros is an experimental Browser. Please report issues with: Help > "Report an issue...".
+      <message name="IDS_EXPERIMENTAL_LACROS_WARNING_MESSAGE" desc="Message shown when user launches Lacros, an experimental feature.">
+        Experimental browser! May lose data or compromise privacy or security. Please report issues with: Help > "Report an issue...".
       </message>
 
       <message name="IDS_EXPERIMENTAL_LACROS_WARNING_MESSAGE_PRIMARY" desc="Addendum which is sometimes added to the initial warning message.">
@@ -7639,8 +7639,8 @@
       <message name="IDS_SHARING_REMOTE_COPY_NOTIFICATION_DESCRIPTION" desc="Second line of text displayed in a Remote Copy notification.">
         Press <ph name="MODIFIER_KEY_DESCRIPTION">$1<ex>Ctrl+V</ex></ph> to paste
       </message>
-      <message name="IDS_OMNIBOX_TOOLTIP_SMS_REMOTE_FETCHER" translateable="false" desc="The label of item for sms remote fetcher in the omnibox for showing device name when one device is available.">
-        Tap the notification on <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph> to verify your phone number
+      <message name="IDS_OMNIBOX_TOOLTIP_SMS_REMOTE_FETCHER" translateable="false" desc="The label of item for sms remote fetcher in the omnibox for showing confirmation of an interaction on the remote device.">
+        Chrome has sent the verification code to the website upon your approval on <ph name="DEVICE_NAME">$1<ex>Jimmy's Pixel</ex></ph>
       </message>
       <if expr="not use_titlecase">
         <message name="IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES" desc="The label of item for click to call in context menu when multiple devices are available.">
diff --git a/chrome/app/generated_resources_grd/IDS_EXPERIMENTAL_LACROS_WARNING_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXPERIMENTAL_LACROS_WARNING_MESSAGE.png.sha1
index cfc5da4..e4cd29c 100644
--- a/chrome/app/generated_resources_grd/IDS_EXPERIMENTAL_LACROS_WARNING_MESSAGE.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_EXPERIMENTAL_LACROS_WARNING_MESSAGE.png.sha1
@@ -1 +1 @@
-8fb028bfd6f53770392671b73d3fc9ab4ace0bc0
\ No newline at end of file
+851ecde2849c4846c62fdc1cb43379460bf57a35
\ No newline at end of file
diff --git a/chrome/app/resources/generated_resources_ar.xtb b/chrome/app/resources/generated_resources_ar.xtb
index 4d6e53c..4344493 100644
--- a/chrome/app/resources/generated_resources_ar.xtb
+++ b/chrome/app/resources/generated_resources_ar.xtb
@@ -3806,7 +3806,7 @@
 <translation id="5125751979347152379">‏عنوان URL غير صالح.</translation>
 <translation id="5126611267288187364">عرض التغييرات</translation>
 <translation id="5127620150973591153">رمز إتمام الاتصال الآمن: <ph name="TOKEN" /></translation>
-<translation id="5127805178023152808">المزامنة غير مفعّلة</translation>
+<translation id="5127805178023152808">المزامنة متوقفة.</translation>
 <translation id="5127881134400491887">إدارة اتصالات الشبكة</translation>
 <translation id="512903556749061217">متصل</translation>
 <translation id="5130675701626084557">تعذَّر تنزيل الملف الشخصي. يُرجى إعادة المحاولة لاحقًا أو التواصل مع مشغّل شبكة الجوّال للحصول على المساعدة.</translation>
diff --git a/chrome/app/resources/generated_resources_hy.xtb b/chrome/app/resources/generated_resources_hy.xtb
index e33a144d..f62567f 100644
--- a/chrome/app/resources/generated_resources_hy.xtb
+++ b/chrome/app/resources/generated_resources_hy.xtb
@@ -383,7 +383,7 @@
 <translation id="1415708812149920388">Սեղմատախտակի ընթերցումը մերժվեց</translation>
 <translation id="1415990189994829608">Այս տեսակի աշխատաշրջանում <ph name="EXTENSION_NAME" />-ը (ընդլայնման ID «<ph name="EXTENSION_ID" />») չի թույլատրվում:</translation>
 <translation id="1418954524306642206">Ընտրել տպիչի PPD ֆայլ</translation>
-<translation id="1420834118113404499">Մեդիա արտոնագրեր</translation>
+<translation id="1420834118113404499">Մեդիա լիցենզիաներ</translation>
 <translation id="1420920093772172268"><ph name="TURN_ON_BLUETOOTH_LINK" />՝ զուգակցումը թույլատրելու համար</translation>
 <translation id="1422159345171879700">Բեռնել վտանգավոր հրահանգաշարերը</translation>
 <translation id="1423716227250567100">Այս գործողության արդյունքում տեղի կունենա հետևյալը․
@@ -5659,7 +5659,7 @@
 <translation id="7186303001964993981"><ph name="ORIGIN" /> կայքը չի կարող բացել այս պանակը, քանի որ այն պարունակում է համակարգի ֆայլեր</translation>
 <translation id="7187428571767585875">Գրանցամատյանի գրառումներ, որոնք ենթակա են հեռացման կամ փոփոխման՝</translation>
 <translation id="7189234443051076392">Համոզվեք, որ ձեր սարքում բավարար տարածք կա</translation>
-<translation id="7189451821249468368">Ձեր արտոնագրերը բավարար չեն այս սարքը գրանցելու համար։ Լրացուցիչ արտոնագրեր գնելու համար դիմեք վաճառքների բաժին։ Եթե կարծում եք, որ այս հաղորդագրությունը սխալմամբ է ձեզ ուղարկվել, դիմեք աջակցման կենտրոն։</translation>
+<translation id="7189451821249468368">Ձեր լիցենզիաները բավարար չեն այս սարքը գրանցելու համար։ Լրացուցիչ լիցենզիաներ գնելու համար դիմեք վաճառքների բաժին։ Եթե կարծում եք, որ այս հաղորդագրությունը սխալմամբ է ձեզ ուղարկվել, դիմեք աջակցման կենտրոն։</translation>
 <translation id="7189965711416741966">Մատնահետքն ավելացված է:</translation>
 <translation id="7191159667348037">Անհայտ տպիչ (USB)</translation>
 <translation id="7193051357671784796">Այս հավելվածն ավելացվել է ձեր կազմակերպության կողմից: Վերագործարկեք հավելվածը՝ տեղադրումն ավարտելու համար:</translation>
diff --git a/chrome/app/resources/generated_resources_ja.xtb b/chrome/app/resources/generated_resources_ja.xtb
index 253e46ac..8d6af7f49 100644
--- a/chrome/app/resources/generated_resources_ja.xtb
+++ b/chrome/app/resources/generated_resources_ja.xtb
@@ -4211,10 +4211,10 @@
 <translation id="5583640892426849032">Backspace</translation>
 <translation id="5584088138253955452">ユーザー名を保存しますか?</translation>
 <translation id="5584091888252706332">起動時</translation>
-<translation id="5584915726528712820"><ph name="BEGIN_PARAGRAPH1" />これは、お使いのデバイスとその使用状況(電池残量、システムとアプリのアクティビティ、エラーなど)に関する一般的な情報です。このデータは Android の改善に使用されます。また一部の集計情報は、Google のアプリのほか、Android デベロッパーなどのパートナーが開発するアプリやサービスの品質改善にも役立てられます。<ph name="END_PARAGRAPH1" />
+<translation id="5584915726528712820"><ph name="BEGIN_PARAGRAPH1" />送信されるのは、お使いのデバイスとその使用状況(電池残量、システムとアプリのアクティビティ、エラーなど)に関する一般的な情報です。この情報は Android の改善に使用されます。また一部の集計情報は、Google のアプリのほか、Android デベロッパーなどのパートナーが開発するアプリやサービスの品質改善にも役立てられます。<ph name="END_PARAGRAPH1" />
     <ph name="BEGIN_PARAGRAPH2" />この機能をオフにした場合でも、システム アップデートやセキュリティなどの重要なサービスで必要となる情報は、デバイスからこれまでどおり送信されます。<ph name="END_PARAGRAPH2" />
     <ph name="BEGIN_PARAGRAPH3" />デバイスの所有者は、[設定] &gt; [詳細設定] &gt; [診断と使用状況のデータを Google に自動送信する] でこの機能を管理できます。<ph name="END_PARAGRAPH3" />
-    <ph name="BEGIN_PARAGRAPH4" />これに加えて [ウェブとアプリのアクティビティ] の設定もオンにしている場合、このデータは Google アカウントにも保存される可能性があります。account.google.com でアカウント設定を閲覧、削除、変更することができます。<ph name="END_PARAGRAPH4" /></translation>
+    <ph name="BEGIN_PARAGRAPH4" />[ウェブとアプリのアクティビティ] の設定もオンにしている場合、診断と使用状況のデータは Google アカウントにも保存される可能性があります。account.google.com でアカウント設定を閲覧、削除、変更することができます。<ph name="END_PARAGRAPH4" /></translation>
 <translation id="5585019845078534178">カード</translation>
 <translation id="5585118885427931890">ブックマーク フォルダを作成できませんでした。</translation>
 <translation id="558563010977877295">特定のページまたはページセットを開く</translation>
@@ -6680,10 +6680,10 @@
 <translation id="826511437356419340">ウィンドウ概観モードに切り替わりました。移動するにはスワイプするか、キーボードの場合は Tab キーを押してください。</translation>
 <translation id="8266947622852630193">すべての入力方法</translation>
 <translation id="8267539814046467575">プリンタの追加</translation>
-<translation id="8267961145111171918"><ph name="BEGIN_PARAGRAPH1" />これは、お使いのデバイスとその使用状況(電池残量、システムとアプリのアクティビティ、エラーなど)に関する一般的な情報です。このデータは、Android の改善に使用されます。また一部の集計情報は、Google のアプリのほか、Android デベロッパーなどのパートナーが開発するアプリやサービスの品質改善にも役立てられます。<ph name="END_PARAGRAPH1" />
+<translation id="8267961145111171918"><ph name="BEGIN_PARAGRAPH1" />送信されるのは、お使いのデバイスとその使用状況(電池残量、システムとアプリのアクティビティ、エラーなど)に関する一般的な情報です。この情報は、Android の改善に使用されます。また一部の集計情報は、Google のアプリのほか、Android デベロッパーなどのパートナーが開発するアプリやサービスの品質改善にも役立てられます。<ph name="END_PARAGRAPH1" />
     <ph name="BEGIN_PARAGRAPH2" />この機能をオフにした場合でも、システム アップデートやセキュリティなどの重要なサービスで必要となる情報は、デバイスからこれまでどおり送信されます。<ph name="END_PARAGRAPH2" />
     <ph name="BEGIN_PARAGRAPH3" />デバイスの所有者は、[設定] &gt; [詳細設定] &gt; [診断と使用状況のデータを Google に自動送信する] でこの機能を管理できます。<ph name="END_PARAGRAPH3" />
-    <ph name="BEGIN_PARAGRAPH4" />これに加えて、お子様のアカウントで [ウェブとアプリのアクティビティ] の設定もオンにしている場合、このデータはお子様の Google アカウントにも保存される可能性があります。これらの設定の詳細と設定を調整する方法については、families.google.com をご覧ください。<ph name="END_PARAGRAPH4" /></translation>
+    <ph name="BEGIN_PARAGRAPH4" />お子様のアカウントで [ウェブとアプリのアクティビティ] の設定もオンにしている場合、診断と使用状況のデータはお子様の Google アカウントにも保存される可能性があります。これらの設定の詳細と設定を調整する方法については、families.google.com をご覧ください。<ph name="END_PARAGRAPH4" /></translation>
 <translation id="826905130698769948">クライアント証明書が無効です</translation>
 <translation id="8270242299912238708">PDF ドキュメント</translation>
 <translation id="827097179112817503">ホームボタンを表示する</translation>
@@ -7090,7 +7090,7 @@
 <translation id="8737914367566358838">ページを翻訳する言語を選択してください</translation>
 <translation id="8740247629089392745">この Chromebook を <ph name="SUPERVISED_USER_NAME" /> に渡す準備ができました。設定はあと少しで完了です。</translation>
 <translation id="8741944563400125534">スイッチ アクセス設定ガイド</translation>
-<translation id="8742998548129056176">これは、お使いのデバイスと使用状況に関する一般的な情報(電池残量、システムやアプリのアクティビティ、エラーなど)です。このデータは Android の改善に使用され、統計情報の一部は、Google アプリや、Android デベロッパーなどのパートナーが開発するアプリやサービスの品質改善にも役立てられます。</translation>
+<translation id="8742998548129056176">送信されるのは、お使いのデバイスと使用状況に関する一般的な情報(電池残量、システムやアプリのアクティビティ、エラーなど)です。この情報は Android の改善に使用され、統計情報の一部は、Google アプリや、Android デベロッパーなどのパートナーが開発するアプリやサービスの品質改善にも役立てられます。</translation>
 <translation id="8743164338060742337"><ph name="NETWORK_INDEX" /> 番目のネットワーク(全 <ph name="NETWORK_COUNT" /> 件)、<ph name="NETWORK_NAME" />、<ph name="NETWORK_PROVIDER_NAME" />、信号強度 <ph name="SIGNAL_STRENGTH" />%、管理者によって管理、接続</translation>
 <translation id="8746654918629346731">「<ph name="EXTENSION_NAME" />」はリクエスト済みです</translation>
 <translation id="874689135111202667">{0,plural, =1{1 個のファイルをこのサイトにアップロードしますか?}other{# 個のファイルをこのサイトにアップロードしますか?}}</translation>
diff --git a/chrome/app/resources/generated_resources_mr.xtb b/chrome/app/resources/generated_resources_mr.xtb
index e975349..2923779 100644
--- a/chrome/app/resources/generated_resources_mr.xtb
+++ b/chrome/app/resources/generated_resources_mr.xtb
@@ -2541,7 +2541,7 @@
 <translation id="370415077757856453">JavaScript ब्लॉक केले</translation>
 <translation id="3704331259350077894">ऑपरेशनची समाप्ती</translation>
 <translation id="3705722231355495246">-</translation>
-<translation id="3706463572498736864">प्रति पत्रक पेज</translation>
+<translation id="3706463572498736864">प्रति शीट पेज</translation>
 <translation id="370649949373421643">Wi-fi सक्षम करा</translation>
 <translation id="370665806235115550">लोड करीत आहे...</translation>
 <translation id="3707163604290651814">सध्या <ph name="NAME" /> म्हणून साइन इन केले</translation>
diff --git a/chrome/app/resources/generated_resources_sw.xtb b/chrome/app/resources/generated_resources_sw.xtb
index 316636b..d1c2bdba 100644
--- a/chrome/app/resources/generated_resources_sw.xtb
+++ b/chrome/app/resources/generated_resources_sw.xtb
@@ -4187,7 +4187,7 @@
 <translation id="5543983818738093899">Inakagua hali...</translation>
 <translation id="554517701842997186">Kitekelezaji</translation>
 <translation id="5545335608717746497">{NUM_TABS,plural, =1{Weka kichupo kwenye kikundi}other{Weka vichupo kwenye kikundi}}</translation>
-<translation id="5546865291508181392">Pata</translation>
+<translation id="5546865291508181392">Tafuta</translation>
 <translation id="5548075230008247516">Umeacha kuchagua vipengee vyote, hali ya kuchagua imefungwa.</translation>
 <translation id="5548159762883465903">{NUM_OTHER_TABS,plural, =0{"<ph name="TAB_TITLE" />"}=1{"<ph name="TAB_TITLE" />" na Kichupo Kingine 1}other{"<ph name="TAB_TITLE" />" na Vichupo Vingine #}}</translation>
 <translation id="5548606607480005320">Angalizo la usalama</translation>
diff --git a/chrome/app/resources/generated_resources_th.xtb b/chrome/app/resources/generated_resources_th.xtb
index eaaa2429..db9013be 100644
--- a/chrome/app/resources/generated_resources_th.xtb
+++ b/chrome/app/resources/generated_resources_th.xtb
@@ -200,7 +200,7 @@
 <translation id="1203942045716040624">ผู้ปฏิบัติงานที่แชร์: <ph name="SCRIPT_URL" /></translation>
 <translation id="1209796539517632982">เซิร์ฟเวอร์ชื่ออัตโนมัติ</translation>
 <translation id="1211769675100312947">ทางลัดที่คุณเป็นผู้คัดสรร</translation>
-<translation id="1213254615020057352">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของบุตรหลานให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android เจ้าของเป็นผู้บังคับใช้การตั้งค่านี้ เจ้าของอาจเลือกที่จะส่งข้อมูลการวินิจฉัยและการใช้งานสำหรับอุปกรณ์นี้ไปยัง Google หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน</translation>
+<translation id="1213254615020057352">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของบุตรหลานให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android เจ้าของเป็นผู้บังคับใช้การตั้งค่านี้ เจ้าของอาจเลือกที่จะส่งข้อมูลการวินิจฉัยและการใช้งานสำหรับอุปกรณ์นี้ไปยัง Google หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน</translation>
 <translation id="121384500095351701">ดาวน์โหลดไฟล์นี้อย่างปลอดภัยไม่ได้</translation>
 <translation id="1215411991991485844">มีการเพิ่มแอปพลิเคชันที่ทำงานอยู่เบื้องหลังตัวใหม่</translation>
 <translation id="1216542092748365687">นำลายนิ้วมือออก</translation>
@@ -1838,7 +1838,7 @@
 <translation id="2942279350258725020">Android Messages</translation>
 <translation id="2942560570858569904">กำลังรอ...</translation>
 <translation id="2942581856830209953">ปรับแต่งหน้านี้</translation>
-<translation id="2944060181911631861">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของคุณให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ <ph name="BEGIN_LINK1" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK1" /></translation>
+<translation id="2944060181911631861">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของคุณให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ <ph name="BEGIN_LINK1" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK1" /></translation>
 <translation id="2946119680249604491">เพิ่มการเชื่อมต่อ</translation>
 <translation id="2946640296642327832">เปิดใช้งานบลูทูธ</translation>
 <translation id="2947605845283690091">การท่องเว็บควรจะเร็ว โปรดใช้เวลาสักครู่เพื่อ<ph name="BEGIN_LINK" />ตรวจสอบส่วนขยาย<ph name="END_LINK" />ตอนนี้เลย</translation>
@@ -2313,7 +2313,7 @@
 <translation id="3473479545200714844">แว่นขยายหน้าจอ</translation>
 <translation id="3474218480460386727">ใช้ตัวอักษรไม่เกิน 99 ตัวสำหรับคำใหม่</translation>
 <translation id="3475843873335999118">ขออภัย ระบบยังคงไม่รู้จักลายนิ้วมือของคุณ โปรดป้อนรหัสผ่าน</translation>
-<translation id="3476303763173086583">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของบุตรหลานให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android เจ้าของเป็นผู้บังคับใช้<ph name="BEGIN_LINK1" />การตั้งค่า<ph name="END_LINK1" />นี้ เจ้าของอาจเลือกที่จะส่งข้อมูลการวินิจฉัยและการใช้งานสำหรับอุปกรณ์นี้ไปยัง Google หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
+<translation id="3476303763173086583">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของบุตรหลานให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android เจ้าของเป็นผู้บังคับใช้<ph name="BEGIN_LINK1" />การตั้งค่า<ph name="END_LINK1" />นี้ เจ้าของอาจเลือกที่จะส่งข้อมูลการวินิจฉัยและการใช้งานสำหรับอุปกรณ์นี้ไปยัง Google หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
 <translation id="347670947055184738">อ๊ะ! ระบบไม่สามารถดึงข้อมูลนโยบายจากอุปกรณ์</translation>
 <translation id="347785443197175480">อนุญาตให้ <ph name="HOST" /> เข้าถึงกล้องถ่ายรูปและไมโครโฟนของคุณต่อไป</translation>
 <translation id="3479552764303398839">ไม่ใช่ตอนนี้</translation>
@@ -2662,7 +2662,7 @@
 <translation id="3834775135533257713">ไม่สามารถเพิ่มแอปพลิเคชัน "<ph name="TO_INSTALL_APP_NAME" />" เนื่องจากขัดแย้งกับ "<ph name="INSTALLED_APP_NAME" />"</translation>
 <translation id="3835233591525155343">การใช้งานอุปกรณ์ของคุณ</translation>
 <translation id="3835522725882634757">แย่แล้ว! เซิร์ฟเวอร์นี้กำลังส่งข้อมูลที่ <ph name="PRODUCT_NAME" /> ไม่เข้าใจ โปรด<ph name="BEGIN_LINK" />รายงานข้อบกพร่อง<ph name="END_LINK" /> และรวม<ph name="BEGIN2_LINK" />รายการที่ยังไม่ผ่านกระบวนการ<ph name="END2_LINK" />ไว้ด้วย</translation>
-<translation id="383669374481694771">นี่คือข้อมูลทั่วไปเกี่ยวกับอุปกรณ์และการใช้งานอุปกรณ์ (เช่น ระดับแบตเตอรี่ กิจกรรมในระบบและแอป ตลอดจนข้อผิดพลาด) ระบบจะใช้ข้อมูลเพื่อปรับปรุง Android และข้อมูลที่รวบรวมมาบางส่วนก็ยังจะช่วยให้แอปและพาร์ทเนอร์ของ Google เช่น นักพัฒนาซอฟต์แวร์ Android พัฒนาแอปและผลิตภัณฑ์ของตนให้ดีขึ้นด้วย</translation>
+<translation id="383669374481694771">นี่คือข้อมูลทั่วไปเกี่ยวกับอุปกรณ์และการใช้งานอุปกรณ์ (เช่น ระดับแบตเตอรี่ กิจกรรมในระบบและแอป ตลอดจนข้อผิดพลาด) ระบบจะใช้ข้อมูลเพื่อปรับปรุง Android และข้อมูลที่รวบรวมมาบางส่วนก็ยังจะช่วยให้แอปและพาร์ทเนอร์ของ Google เช่น นักพัฒนาแอป Android พัฒนาแอปและผลิตภัณฑ์ของตนให้ดีขึ้นด้วย</translation>
 <translation id="3838085852053358637">ไม่สามารถโหลดส่วนขยาย</translation>
 <translation id="3838486795898716504"><ph name="PAGE_TITLE" /> เพิ่มเติม</translation>
 <translation id="383891835335927981">ไม่มีการซูมเข้าหรือซูมออกเว็บไซต์ใดๆ</translation>
@@ -2998,7 +2998,7 @@
 <translation id="4209464433672152343">เอกสารจะ<ph name="BEGIN_LINK_HELP" />ส่งไปยัง Google<ph name="END_LINK_HELP" /> เพื่อเตรียมพิมพ์ ดู แก้ไข และจัดการเครื่องพิมพ์และประวัติการพิมพ์ได้ใน<ph name="BEGIN_LINK_DASHBOARD" />แดชบอร์ด Google Cloud Print<ph name="END_LINK_DASHBOARD" /></translation>
 <translation id="4210048056321123003">กำลังดาวน์โหลดเครื่องเสมือน</translation>
 <translation id="421182450098841253">&amp;แสดงแถบบุ๊กมาร์ก</translation>
-<translation id="4211851069413100178">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของคุณให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android เจ้าของเป็นผู้บังคับใช้<ph name="BEGIN_LINK1" />การตั้งค่า<ph name="END_LINK1" />นี้ เจ้าของอาจเลือกที่จะส่งข้อมูลการวินิจฉัยและการใช้งานสำหรับอุปกรณ์นี้ไปยัง Google หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
+<translation id="4211851069413100178">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของคุณให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android เจ้าของเป็นผู้บังคับใช้<ph name="BEGIN_LINK1" />การตั้งค่า<ph name="END_LINK1" />นี้ เจ้าของอาจเลือกที่จะส่งข้อมูลการวินิจฉัยและการใช้งานสำหรับอุปกรณ์นี้ไปยัง Google หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
 <translation id="42126664696688958">ส่งออก</translation>
 <translation id="42137655013211669">เซิร์ฟเวอร์ได้ห้ามการเข้าถึงทรัพยากรนี้</translation>
 <translation id="4217571870635786043">การพิมพ์ตามคำบอก</translation>
@@ -3713,7 +3713,7 @@
 <translation id="5017643436812738274">คุณไปยังส่วนต่างๆ ของหน้าได้ด้วยเคอร์เซอร์ข้อความ กด Ctrl+Search+7 เพื่อปิด</translation>
 <translation id="5017828934289857214">เตือนฉันภายหลัง</translation>
 <translation id="5018207570537526145">เปิดเว็บไซต์ส่วนขยาย</translation>
-<translation id="5018526990965779848">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของคุณให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ</translation>
+<translation id="5018526990965779848">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของคุณให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ</translation>
 <translation id="5021750053540820849">ยังไม่ได้อัปเดต</translation>
 <translation id="5026492829171796515">ลงชื่อเข้าใช้เพื่อเพิ่มบัญชี Google</translation>
 <translation id="5026806129670917316">เปิด Wi-Fi</translation>
@@ -3866,7 +3866,7 @@
 <translation id="5192062846343383368">เปิดแอป Family Link เพื่อดูการตั้งค่าการควบคุมดูแล</translation>
 <translation id="5193988420012215838">คัดลอกไปยังคลิปบอร์ดแล้ว</translation>
 <translation id="5197255632782567636">อินเทอร์เน็ต</translation>
-<translation id="5198430103906431024">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ</translation>
+<translation id="5198430103906431024">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ</translation>
 <translation id="5199729219167945352">การทดลอง</translation>
 <translation id="5203920255089865054">{NUM_EXTENSIONS,plural, =1{คลิกเพื่อดูส่วนขยาย}other{คลิกเพื่อดูส่วนขยายเหล่านี้}}</translation>
 <translation id="5204673965307125349">โปรดทำ Powerwash อุปกรณ์และลองอีกครั้ง</translation>
@@ -4211,7 +4211,7 @@
 <translation id="5583640892426849032">Backspace</translation>
 <translation id="5584088138253955452">บันทึกชื่อผู้ใช้ไหม</translation>
 <translation id="5584091888252706332">เริ่มต้น</translation>
-<translation id="5584915726528712820"><ph name="BEGIN_PARAGRAPH1" />นี่เป็นข้อมูลทั่วไปเกี่ยวกับอุปกรณ์ของคุณและวิธีที่คุณใช้อุปกรณ์ (เช่น ระดับแบตเตอรี่ กิจกรรมในระบบและแอป และข้อผิดพลาด) ระบบจะใช้ข้อมูลดังกล่าวเพื่อปรับปรุง Android และข้อมูลแบบสรุปรวมบางอย่างจะช่วยให้แอป Google และพาร์ทเนอร์ต่างๆ เช่น นักพัฒนาซอฟต์แวร์ Android ปรับปรุงแอปและผลิตภัณฑ์ของตนให้ดีขึ้นได้อีกด้วย<ph name="END_PARAGRAPH1" />
+<translation id="5584915726528712820"><ph name="BEGIN_PARAGRAPH1" />นี่เป็นข้อมูลทั่วไปเกี่ยวกับอุปกรณ์ของคุณและวิธีที่คุณใช้อุปกรณ์ (เช่น ระดับแบตเตอรี่ กิจกรรมในระบบและแอป และข้อผิดพลาด) ระบบจะใช้ข้อมูลดังกล่าวเพื่อปรับปรุง Android และข้อมูลแบบสรุปรวมบางอย่างจะช่วยให้แอป Google และพาร์ทเนอร์ต่างๆ เช่น นักพัฒนาแอป Android ปรับปรุงแอปและผลิตภัณฑ์ของตนให้ดีขึ้นได้อีกด้วย<ph name="END_PARAGRAPH1" />
     <ph name="BEGIN_PARAGRAPH2" />การปิดฟีเจอร์นี้จะไม่ส่งผลต่อความสามารถของอุปกรณ์ในการส่งข้อมูลที่จำเป็นต้องใช้ในบริการสำคัญอย่างเช่น การอัปเดตระบบ และความปลอดภัย<ph name="END_PARAGRAPH2" />
     <ph name="BEGIN_PARAGRAPH3" />เจ้าของจะควบคุมฟีเจอร์นี้ได้จากการตั้งค่า &gt; ขั้นสูง &gt; ส่งข้อมูลการวินิจฉัยและการใช้งานไปยัง Google โดยอัตโนมัติ<ph name="END_PARAGRAPH3" />
     <ph name="BEGIN_PARAGRAPH4" />หากการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปเปิดอยู่ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google คุณจะดูหรือลบข้อมูล และเปลี่ยนแปลงการตั้งค่าบัญชีได้ที่ account.google.com<ph name="END_PARAGRAPH4" /></translation>
@@ -5239,7 +5239,7 @@
 <translation id="6725073593266469338">บริการ UI</translation>
 <translation id="6725206449694821596">โปรโตคอลการพิมพ์ผ่านอินเทอร์เน็ต (IPP)</translation>
 <translation id="672609503628871915">ดูว่ามีอะไรใหม่</translation>
-<translation id="67269783048918309">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android เจ้าของเป็นผู้บังคับใช้<ph name="BEGIN_LINK1" />การตั้งค่า<ph name="END_LINK1" />นี้ หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
+<translation id="67269783048918309">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android เจ้าของเป็นผู้บังคับใช้<ph name="BEGIN_LINK1" />การตั้งค่า<ph name="END_LINK1" />นี้ หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
 <translation id="6727969043791803658">เชื่อมต่อแล้ว แบตเตอรี่ <ph name="BATTERY_PERCENTAGE" />%</translation>
 <translation id="6732087373923685049">กล้อง</translation>
 <translation id="6732801395666424405">ยังไม่ได้โหลดใบรับรอง</translation>
@@ -5352,7 +5352,7 @@
 <translation id="6847125920277401289">เพิ่มพื้นที่ว่างเพื่อดำเนินการต่อ</translation>
 <translation id="6848388270925200958">ขณะนี้คุณมีบัตรบางใบที่ใช้ได้ในอุปกรณ์นี้เท่านั้น</translation>
 <translation id="6850286078059909152">สีข้อความ</translation>
-<translation id="6851181413209322061">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android เจ้าของเป็นผู้บังคับใช้การตั้งค่านี้ หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน</translation>
+<translation id="6851181413209322061">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android เจ้าของเป็นผู้บังคับใช้การตั้งค่านี้ หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน</translation>
 <translation id="6851497530878285708">เปิดใช้แอปแล้ว</translation>
 <translation id="6853142139292753691">คืนค่าแอปและหน้าเว็บโดยค่าเริ่มต้นไหม</translation>
 <translation id="6853388645642883916">โปรแกรมอัปเดตอยู่ในโหมดสลีป</translation>
@@ -5673,7 +5673,7 @@
 <translation id="7203150201908454328">ขยาย</translation>
 <translation id="7206693748120342859">กำลังดาวน์โหลด <ph name="PLUGIN_NAME" />...</translation>
 <translation id="720715819012336933">{NUM_PAGES,plural, =1{หน้าที่ออกไป}other{หน้าที่ออกไป}}</translation>
-<translation id="7207457272187520234">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android เจ้าของเป็นผู้บังคับใช้การตั้งค่านี้ หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ</translation>
+<translation id="7207457272187520234">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android เจ้าของเป็นผู้บังคับใช้การตั้งค่านี้ หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ</translation>
 <translation id="7207631048330366454">ค้นหาแอป</translation>
 <translation id="7210499381659830293">เครื่องพิมพ์ที่สั่งการผ่านส่วนขยาย</translation>
 <translation id="7211769023302873228">กด “<ph name="CURRENTKEY" />” อีกครั้งเพื่อยืนยันการกำหนดและออก</translation>
@@ -6680,7 +6680,7 @@
 <translation id="826511437356419340">เข้าสู่โหมดภาพรวมหน้าต่างแล้ว เลื่อนเพื่อไปยังส่วนต่างๆ หรือกด Tab หากใช้แป้นพิมพ์</translation>
 <translation id="8266947622852630193">วิธีการป้อนข้อมูลทั้งหมด</translation>
 <translation id="8267539814046467575">เพิ่มเครื่องพิมพ์</translation>
-<translation id="8267961145111171918"><ph name="BEGIN_PARAGRAPH1" />นี่คือข้อมูลทั่วไปเกี่ยวกับอุปกรณ์นี้และวิธีใช้อุปกรณ์ (เช่น ระดับแบตเตอรี่ กิจกรรมในระบบและแอป และข้อผิดพลาด) ระบบจะใช้ข้อมูลดังกล่าวเพื่อปรับปรุง Android และข้อมูลแบบสรุปรวมบางอย่างจะช่วยให้แอป Google และพาร์ทเนอร์ต่างๆ เช่น นักพัฒนาซอฟต์แวร์ Android ปรับปรุงแอปและผลิตภัณฑ์ของตนให้ดีขึ้นได้อีกด้วย<ph name="END_PARAGRAPH1" />
+<translation id="8267961145111171918"><ph name="BEGIN_PARAGRAPH1" />นี่คือข้อมูลทั่วไปเกี่ยวกับอุปกรณ์นี้และวิธีใช้อุปกรณ์ (เช่น ระดับแบตเตอรี่ กิจกรรมในระบบและแอป และข้อผิดพลาด) ระบบจะใช้ข้อมูลดังกล่าวเพื่อปรับปรุง Android และข้อมูลแบบสรุปรวมบางอย่างจะช่วยให้แอป Google และพาร์ทเนอร์ต่างๆ เช่น นักพัฒนาแอป Android ปรับปรุงแอปและผลิตภัณฑ์ของตนให้ดีขึ้นได้อีกด้วย<ph name="END_PARAGRAPH1" />
     <ph name="BEGIN_PARAGRAPH2" />การปิดฟีเจอร์นี้จะไม่ส่งผลต่อความสามารถของอุปกรณ์ในการส่งข้อมูลที่จำเป็นต้องใช้ในบริการสำคัญอย่างเช่น การอัปเดตระบบ และความปลอดภัย<ph name="END_PARAGRAPH2" />
     <ph name="BEGIN_PARAGRAPH3" />เจ้าของจะควบคุมฟีเจอร์นี้ได้จากการตั้งค่า &gt; ขั้นสูง &gt; ส่งข้อมูลการวินิจฉัยและการใช้งานไปยัง Google โดยอัตโนมัติ<ph name="END_PARAGRAPH3" />
     <ph name="BEGIN_PARAGRAPH4" />หากการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปเปิดอยู่สำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน ดูข้อมูลเพิ่มเติมเกี่ยวกับการตั้งค่าเหล่านี้และวิธีปรับการตั้งค่าได้ที่ families.google.com<ph name="END_PARAGRAPH4" /></translation>
@@ -6941,7 +6941,7 @@
 <translation id="8591783563402255548">1 วินาที</translation>
 <translation id="8592141010104017453">ไม่ต้องแสดงการแจ้งเตือนใดๆ</translation>
 <translation id="859246725979739260">เว็บไซต์นี้ถูกบล็อกไม่ให้เข้าถึงตำแหน่งของคุณ</translation>
-<translation id="8593121833493516339">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของบุตรหลานให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน <ph name="BEGIN_LINK1" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK1" /></translation>
+<translation id="8593121833493516339">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของบุตรหลานให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน <ph name="BEGIN_LINK1" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK1" /></translation>
 <translation id="8594908476761052472">จับภาพวิดีโอ</translation>
 <translation id="8596540852772265699">ไฟล์ที่กำหนดเอง</translation>
 <translation id="8597845839771543242">รูปแบบของคุณสมบัติ:</translation>
@@ -6983,7 +6983,7 @@
 <translation id="863903787380594467">PIN ไม่ถูกต้อง คุณลองได้อีก <ph name="RETRIES" /> ครั้ง</translation>
 <translation id="8639047128869322042">กำลังตรวจหาซอฟต์แวร์ที่เป็นอันตราย...</translation>
 <translation id="8639391553632924850"><ph name="INPUT_LABEL" /> - พอร์ต</translation>
-<translation id="8639635302972078117">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน</translation>
+<translation id="8639635302972078117">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน</translation>
 <translation id="8642900771896232685">2 วินาที</translation>
 <translation id="8642947597466641025">ทำให้ข้อความใหญ่ขึ้น</translation>
 <translation id="8643443571868262066"><ph name="FILE_NAME" /> อาจเป็นอันตราย ส่งไปให้การปกป้องขั้นสูงของ Google สแกนไหม</translation>
@@ -7066,7 +7066,7 @@
 <translation id="8719653885894320876">การดาวน์โหลด <ph name="PLUGIN_NAME" /> ล้มเหลว</translation>
 <translation id="8720200012906404956">กำลังมองหาเครือข่ายมือถือ <ph name="BEGIN_LINK" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK" /></translation>
 <translation id="8720816553731218127">การเริ่มต้นแอตทริบิวต์เวลาการติดตั้งหมดเวลาแล้ว</translation>
-<translation id="8722912030556880711">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
+<translation id="8722912030556880711">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
 <translation id="8724405322205516354">เมื่อเห็นไอคอนนี้ ให้ใช้ลายนิ้วมือเพื่อระบุตัวตนหรือเพื่ออนุมัติการซื้อ</translation>
 <translation id="8724409975248965964">เพิ่มลายนิ้วมือแล้ว</translation>
 <translation id="8724859055372736596">&amp;แสดงในโฟลเดอร์</translation>
@@ -7090,7 +7090,7 @@
 <translation id="8737914367566358838">เลือกภาษาปลายทางของหน้าเว็บ</translation>
 <translation id="8740247629089392745">คุณส่งมอบ Chromebook เครื่องนี้ให้ <ph name="SUPERVISED_USER_NAME" /> ได้ การตั้งค่าใกล้เสร็จแล้ว จากนั้นก็ได้เวลาแห่งการสำรวจ</translation>
 <translation id="8741944563400125534">คู่มือการตั้งค่าการเข้าถึงด้วยสวิตช์</translation>
-<translation id="8742998548129056176">นี่คือข้อมูลทั่วไปเกี่ยวกับอุปกรณ์และวิธีที่คุณใช้งานอุปกรณ์ (เช่น ระดับแบตเตอรี่ กิจกรรมในระบบและแอป ตลอดจนข้อผิดพลาด) ระบบจะใช้ข้อมูลเพื่อปรับปรุง Android และข้อมูลที่รวบรวมมาบางส่วนจะช่วยให้แอปและพาร์ทเนอร์ของ Google เช่น นักพัฒนาซอฟต์แวร์ Android พัฒนาแอปและผลิตภัณฑ์ของตนให้ดีขึ้น</translation>
+<translation id="8742998548129056176">นี่คือข้อมูลทั่วไปเกี่ยวกับอุปกรณ์และวิธีที่คุณใช้งานอุปกรณ์ (เช่น ระดับแบตเตอรี่ กิจกรรมในระบบและแอป ตลอดจนข้อผิดพลาด) ระบบจะใช้ข้อมูลเพื่อปรับปรุง Android และข้อมูลที่รวบรวมมาบางส่วนจะช่วยให้แอปและพาร์ทเนอร์ของ Google เช่น นักพัฒนาแอป Android พัฒนาแอปและผลิตภัณฑ์ของตนให้ดีขึ้น</translation>
 <translation id="8743164338060742337">เครือข่ายที่ <ph name="NETWORK_INDEX" /> จาก <ph name="NETWORK_COUNT" />, <ph name="NETWORK_NAME" />, <ph name="NETWORK_PROVIDER_NAME" /> ความแรงสัญญาณ <ph name="SIGNAL_STRENGTH" />%, จัดการโดยผู้ดูแลระบบ, เชื่อมต่อ</translation>
 <translation id="8746654918629346731">คุณขอ "<ph name="EXTENSION_NAME" />" แล้ว</translation>
 <translation id="874689135111202667">{0,plural, =1{อัปโหลด 1 ไฟล์ไปยังเว็บไซต์นี้ใช่ไหม}other{อัปโหลด # ไฟล์ไปยังเว็บไซต์นี้ใช่ไหม}}</translation>
@@ -7276,7 +7276,7 @@
 <translation id="8929696694736010839">เซสชันไม่ระบุตัวตนปัจจุบันเท่านั้น</translation>
 <translation id="8930351635855238750">การตั้งค่าคุกกี้ใหม่จะมีผลหลังจากการโหลดหน้านี้ซ้ำ</translation>
 <translation id="8930622219860340959">ไร้สาย</translation>
-<translation id="8931076093143205651">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของคุณให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android เจ้าของเป็นผู้บังคับใช้การตั้งค่านี้ เจ้าของอาจเลือกที่จะส่งข้อมูลการวินิจฉัยและการใช้งานสำหรับอุปกรณ์นี้ไปยัง Google หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ</translation>
+<translation id="8931076093143205651">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของคุณให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android เจ้าของเป็นผู้บังคับใช้การตั้งค่านี้ เจ้าของอาจเลือกที่จะส่งข้อมูลการวินิจฉัยและการใช้งานสำหรับอุปกรณ์นี้ไปยัง Google หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ</translation>
 <translation id="8931475688782629595">จัดการข้อมูลที่คุณซิงค์</translation>
 <translation id="8932654652795262306">รายละเอียดการเชื่อมต่อ Wi-Fi ฮอตสปอตจากมือถือโดยอัตโนมัติ</translation>
 <translation id="8932894639908691771">ตัวเลือกการเข้าถึงด้วยสวิตช์</translation>
@@ -7510,9 +7510,9 @@
 <translation id="9170848237812810038">เ&amp;ลิกทำ</translation>
 <translation id="9170884462774788842">โปรแกรมอื่นในคอมพิวเตอร์ของคุณเพิ่มธีมที่อาจเปลี่ยนวิธีการทำงานของ Chrome</translation>
 <translation id="917350715406657904">คุณใช้งาน <ph name="APP_NAME" /> ถึงขีดจำกัดเวลาที่ผู้ปกครองตั้งไว้แล้ว พรุ่งนี้คุณจะใช้แอปได้ <ph name="TIME_LIMIT" /></translation>
-<translation id="9174401638287877180">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของบุตรหลานให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน</translation>
+<translation id="9174401638287877180">ส่งข้อมูลการใช้งานและการวินิจฉัย ช่วยปรับปรุงประสบการณ์การใช้งาน Android ของบุตรหลานให้ดีขึ้นด้วยการส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน</translation>
 <translation id="917510707618656279">ถามเมื่อเว็บไซต์ต้องการเข้าถึงอุปกรณ์บลูทูธ</translation>
-<translation id="9176476835295860688">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android เจ้าของเป็นผู้บังคับใช้<ph name="BEGIN_LINK1" />การตั้งค่า<ph name="END_LINK1" />นี้ หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
+<translation id="9176476835295860688">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ ซึ่งจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android เจ้าของเป็นผู้บังคับใช้<ph name="BEGIN_LINK1" />การตั้งค่า<ph name="END_LINK1" />นี้ หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปไว้ ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของคุณ <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
 <translation id="9176611096776448349"><ph name="WINDOW_TITLE" /> - เชื่อมต่ออุปกรณ์บลูทูธแล้ว</translation>
 <translation id="9179524979050048593">ชื่อผู้ใช้บนหน้าจอลงชื่อเข้าใช้</translation>
 <translation id="9180281769944411366">ขั้นตอนนี้อาจใช้เวลาสักครู่ กำลังเริ่มคอนเทนเนอร์ Linux</translation>
@@ -7520,7 +7520,7 @@
 <translation id="9182556968660520230">ไม่อนุญาตให้เว็บไซต์เล่นเนื้อหาที่มีการคุมครอง</translation>
 <translation id="918352324374649435">{COUNT,plural, =1{แอป}other{# แอป}}</translation>
 <translation id="9186963452600581158">ลงชื่อเข้าใช้ด้วยบัญชี Google ของบุตรหลาน</translation>
-<translation id="9188732951356337132">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาซอฟต์แวร์ Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
+<translation id="9188732951356337132">ส่งข้อมูลการใช้งานและการวินิจฉัย ปัจจุบันอุปกรณ์นี้ส่งข้อมูลการวินิจฉัย อุปกรณ์ และการใช้งานแอปไปยัง Google โดยอัตโนมัติ โดยจะไม่มีการใช้ข้อมูลนี้ในการระบุชื่อบุตรหลานของคุณ และจะช่วยให้แอปและระบบมีความเสถียร พร้อมทั้งปรับปรุงด้านอื่นๆ ข้อมูลที่รวบรวมมาบางส่วนจะมีประโยชน์ต่อแอปและพาร์ทเนอร์ของ Google ด้วย เช่น นักพัฒนาแอป Android หากเปิดการตั้งค่ากิจกรรมเพิ่มเติมบนเว็บและแอปสำหรับบุตรหลาน ระบบอาจบันทึกข้อมูลนี้ไว้ในบัญชี Google ของบุตรหลาน <ph name="BEGIN_LINK2" />ดูข้อมูลเพิ่มเติม<ph name="END_LINK2" /></translation>
 <translation id="9198090666959937775">ใช้โทรศัพท์ Android เป็นคีย์ความปลอดภัย</translation>
 <translation id="920045321358709304">ค้นหา <ph name="SEARCH_ENGINE" /></translation>
 <translation id="9201023452444595544">ระบบจะล้างข้อมูลออฟไลน์ทั้งหมด</translation>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8d9c15e1..bfd9846 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5243,6 +5243,12 @@
     if (use_x11) {
       deps += [ "//ui/gfx/x" ]
     }
+
+    # device_info_fetcher uses a "defined(USE_GIO)" macro, so if it's refactored
+    # away from this file make sure gio_config is still added to configs.
+    if (use_gio) {
+      configs += [ "//build/linux:gio_config" ]
+    }
   }
 
   if (is_linux || is_chromeos_lacros) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 798259c..e2eb9acdc 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -922,13 +922,23 @@
      heap_profiling::kMemlogSamplingRate5MB},
 };
 
-const FeatureEntry::FeatureVariation kMemoriesVariations[] = {{
-    "Persist Context",
-    (FeatureEntry::FeatureParam[]){
-        {"MemoriesPersistContextAnnotationsInHistoryDb", "true"}},
-    1,
-    nullptr,
-}};
+const FeatureEntry::FeatureVariation kMemoriesVariations[] = {
+    {
+        "Persist Context + Limit 1k",
+        (FeatureEntry::FeatureParam[]){
+            {"MemoriesPersistContextAnnotationsInHistoryDb", "true"}},
+        1,
+        nullptr,
+    },
+    {
+        "Persist Context + Limit 10k",
+        (FeatureEntry::FeatureParam[]){
+            {"MemoriesPersistContextAnnotationsInHistoryDb", "true"},
+            {"MemoriesMaxVisitsToCluster", "10000"}},
+        2,
+        nullptr,
+    },
+};
 
 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || \
     defined(OS_WIN)
@@ -4582,12 +4592,6 @@
 
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
     defined(OS_CHROMEOS)
-    {"direct-manipulation-stylus",
-     flag_descriptions::kDirectManipulationStylusName,
-     flag_descriptions::kDirectManipulationStylusDescription,
-     kOsWin | kOsMac | kOsLinux,
-     FEATURE_VALUE_TYPE(features::kDirectManipulationStylus)},
-
     {"webui-feedback", flag_descriptions::kWebuiFeedbackName,
      flag_descriptions::kWebuiFeedbackDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kWebUIFeedback)},
@@ -5971,10 +5975,10 @@
      flag_descriptions::kScrollUnificationDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kScrollUnification)},
 
-#if defined(OS_WIN)
-    {"elastic-overscroll-win", flag_descriptions::kElasticOverscrollWinName,
-     flag_descriptions::kElasticOverscrollWinDescription, kOsWin,
-     FEATURE_VALUE_TYPE(features::kElasticOverscrollWin)},
+#if defined(OS_WIN) || defined(OS_ANDROID)
+    {"elastic-overscroll", flag_descriptions::kElasticOverscrollName,
+     flag_descriptions::kElasticOverscrollDescription, kOsWin | kOsAndroid,
+     FEATURE_VALUE_TYPE(features::kElasticOverscroll)},
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index fb4b2adc..6622f6d 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -1767,8 +1767,12 @@
   }
 
   _lastProfile = profile;
-  _lastProfileKeepAlive = std::make_unique<ScopedProfileKeepAlive>(
-      _lastProfile, ProfileKeepAliveOrigin::kAppControllerMac);
+  if (profile->IsOffTheRecord()) {
+    _lastProfileKeepAlive.reset();
+  } else {
+    _lastProfileKeepAlive = std::make_unique<ScopedProfileKeepAlive>(
+        _lastProfile, ProfileKeepAliveOrigin::kAppControllerMac);
+  }
 }
 
 - (void)windowChangedToProfile:(Profile*)profile {
diff --git a/chrome/browser/ash/borealis/testing/callback_factory.h b/chrome/browser/ash/borealis/testing/callback_factory.h
index ab9de213..f34ee7c 100644
--- a/chrome/browser/ash/borealis/testing/callback_factory.h
+++ b/chrome/browser/ash/borealis/testing/callback_factory.h
@@ -17,6 +17,9 @@
 //
 // The callbacks created by this factory are 'naggy' in the sense that
 // uninteresting calls on it will cause a warning to be printed.
+//
+// Prefer to use the NiceCallbackFactory where possible (see
+// go/gmock-cookbook#NiceStrictNaggy)
 template <typename F>
 class NaggyCallbackFactory : public ::testing::MockFunction<F> {
  public:
@@ -39,10 +42,16 @@
 };
 
 // As above, but uninteresting calls will be errors.
+//
+// Prefer to use the NiceCallbackFactory where possible (see
+// go/gmock-cookbook#NiceStrictNaggy)
 template <typename F>
 using StrictCallbackFactory = ::testing::StrictMock<NaggyCallbackFactory<F>>;
 
 // As above, but uninteresting calls will be ignored.
+//
+// We recommend using this factory in preference to the others, see
+// go/gmock-cookbook#NiceStrictNaggy for more information.
 template <typename F>
 using NiceCallbackFactory = ::testing::NiceMock<NaggyCallbackFactory<F>>;
 
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/web_dialog_view.cc b/chrome/browser/ash/login/ui/login_screen_extension_ui/web_dialog_view.cc
index 90a44a6..347f6ed 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/web_dialog_view.cc
+++ b/chrome/browser/ash/login/ui/login_screen_extension_ui/web_dialog_view.cc
@@ -16,7 +16,7 @@
 
 WebDialogView::WebDialogView(
     content::BrowserContext* context,
-    DialogDelegate* delegate,
+    chromeos::login_screen_extension_ui::DialogDelegate* delegate,
     std::unique_ptr<ui::WebDialogWebContentsDelegate::WebContentsHandler>
         handler)
     : views::WebDialogView(context, delegate, std::move(handler)),
diff --git a/chrome/browser/ash/login/ui/login_screen_extension_ui/web_dialog_view.h b/chrome/browser/ash/login/ui/login_screen_extension_ui/web_dialog_view.h
index 2cb66ae..ff1490d 100644
--- a/chrome/browser/ash/login/ui/login_screen_extension_ui/web_dialog_view.h
+++ b/chrome/browser/ash/login/ui/login_screen_extension_ui/web_dialog_view.h
@@ -32,7 +32,7 @@
   METADATA_HEADER(WebDialogView);
   explicit WebDialogView(
       content::BrowserContext* context,
-      DialogDelegate* delegate,
+      chromeos::login_screen_extension_ui::DialogDelegate* delegate,
       std::unique_ptr<ui::WebDialogWebContentsDelegate::WebContentsHandler>
           handler);
   WebDialogView(const WebDialogView&) = delete;
@@ -46,7 +46,9 @@
   void OnFocusLeavingSystemTray(bool reverse) override;
 
  private:
-  DialogDelegate* delegate_ = nullptr;
+  // views::WebDialogView extends views::DialogDelegate, so fully qualified name
+  // is needed.
+  chromeos::login_screen_extension_ui::DialogDelegate* delegate_ = nullptr;
 };
 
 }  // namespace login_screen_extension_ui
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index 1bb487f..aa20c56 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -384,6 +384,10 @@
   return profile_->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled);
 }
 
+bool ChromeAutocompleteProviderClient::AllowDeletingBrowserHistory() const {
+  return profile_->GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory);
+}
+
 bool ChromeAutocompleteProviderClient::IsPersonalizedUrlDataCollectionActive()
     const {
   return url_consent_helper_->IsEnabled();
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
index 2f4e86a..bcac51a 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
@@ -71,6 +71,7 @@
   signin::IdentityManager* GetIdentityManager() const override;
   bool IsOffTheRecord() const override;
   bool SearchSuggestEnabled() const override;
+  bool AllowDeletingBrowserHistory() const override;
   bool IsPersonalizedUrlDataCollectionActive() const override;
   bool IsAuthenticated() const override;
   bool IsSyncActive() const override;
diff --git a/chrome/browser/autofill/manual_filling_controller.h b/chrome/browser/autofill/manual_filling_controller.h
index 03a0f70e..91b912e 100644
--- a/chrome/browser/autofill/manual_filling_controller.h
+++ b/chrome/browser/autofill/manual_filling_controller.h
@@ -98,6 +98,11 @@
   // action (given by an enum param) is available.
   virtual void OnAutomaticGenerationStatusChanged(bool available) = 0;
 
+  // Instructs the view to show the manual filling sheet for the given
+  // |tab_type|.
+  virtual void ShowAccessorySheetTab(
+      const autofill::AccessoryTabType& tab_type) = 0;
+
   // --------------------------
   // Methods called by UI code:
   // --------------------------
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.cc b/chrome/browser/autofill/manual_filling_controller_impl.cc
index b80bda10..7e0a7f3 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl.cc
@@ -157,6 +157,18 @@
   UpdateVisibility();
 }
 
+void ManualFillingControllerImpl::ShowAccessorySheetTab(
+    const autofill::AccessoryTabType& tab_type) {
+  if (tab_type == autofill::AccessoryTabType::CREDIT_CARDS) {
+    cc_controller_->RefreshSuggestions();
+  } else {
+    NOTIMPLEMENTED()
+        << "ShowAccessorySheetTab does not support the given TabType yet "
+        << tab_type;
+  }
+  view_->ShowAccessorySheetTab(tab_type);
+}
+
 void ManualFillingControllerImpl::UpdateSourceAvailability(
     FillingSource source,
     bool has_suggestions) {
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.h b/chrome/browser/autofill/manual_filling_controller_impl.h
index 650ddee1..70723d3 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.h
+++ b/chrome/browser/autofill/manual_filling_controller_impl.h
@@ -44,6 +44,8 @@
                                 bool has_suggestions) override;
   void Hide() override;
   void OnAutomaticGenerationStatusChanged(bool available) override;
+  void ShowAccessorySheetTab(
+      const autofill::AccessoryTabType& tab_type) override;
   void OnFillingTriggered(autofill::AccessoryTabType type,
                           const autofill::UserInfo::Field& selection) override;
   void OnOptionSelected(
diff --git a/chrome/browser/autofill/manual_filling_view_interface.h b/chrome/browser/autofill/manual_filling_view_interface.h
index 7eef076..bb933947 100644
--- a/chrome/browser/autofill/manual_filling_view_interface.h
+++ b/chrome/browser/autofill/manual_filling_view_interface.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "build/build_config.h"
+#include "components/autofill/core/browser/ui/accessory_sheet_enums.h"
 #include "url/gurl.h"
 
 class ManualFillingController;
@@ -75,6 +76,10 @@
   // Hides the accessory bar and the accessory sheet (if open).
   virtual void Hide() = 0;
 
+  // Shows the accessory sheet for the given |tab_type|.
+  virtual void ShowAccessorySheetTab(
+      const autofill::AccessoryTabType& tab_type) = 0;
+
  private:
   friend class ManualFillingControllerImpl;
   // Factory function used to create a concrete instance of this view.
diff --git a/chrome/browser/autofill/mock_manual_filling_controller.h b/chrome/browser/autofill/mock_manual_filling_controller.h
index 35d101e..cedce3a 100644
--- a/chrome/browser/autofill/mock_manual_filling_controller.h
+++ b/chrome/browser/autofill/mock_manual_filling_controller.h
@@ -24,6 +24,7 @@
                void(ManualFillingController::FillingSource, bool));
   MOCK_METHOD0(Hide, void());
   MOCK_METHOD1(OnAutomaticGenerationStatusChanged, void(bool));
+  MOCK_METHOD1(ShowAccessorySheetTab, void(const autofill::AccessoryTabType&));
   MOCK_METHOD2(OnFillingTriggered,
                void(autofill::AccessoryTabType type,
                     const autofill::UserInfo::Field&));
diff --git a/chrome/browser/autofill/mock_manual_filling_view.h b/chrome/browser/autofill/mock_manual_filling_view.h
index 4f220cd..fa463b9 100644
--- a/chrome/browser/autofill/mock_manual_filling_view.h
+++ b/chrome/browser/autofill/mock_manual_filling_view.h
@@ -7,6 +7,7 @@
 
 #include "chrome/browser/autofill/manual_filling_view_interface.h"
 #include "components/autofill/core/browser/ui/accessory_sheet_data.h"
+#include "components/autofill/core/browser/ui/accessory_sheet_enums.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 class MockManualFillingView : public ManualFillingViewInterface {
@@ -20,6 +21,7 @@
   MOCK_METHOD0(SwapSheetWithKeyboard, void());
   MOCK_METHOD0(ShowWhenKeyboardIsVisible, void());
   MOCK_METHOD0(Hide, void());
+  MOCK_METHOD1(ShowAccessorySheetTab, void(const autofill::AccessoryTabType&));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockManualFillingView);
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index 3d634db..a7657cb 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -35,11 +35,12 @@
 class NotificationUIManager;
 class PrefService;
 class ProfileManager;
+class SerialPolicyAllowedPorts;
+class StartupData;
 class StatusTray;
 class SystemNetworkContextManager;
 class WatchDogThread;
 class WebRtcLogUploader;
-class StartupData;
 
 #if !defined(OS_ANDROID)
 class IntranetRedirectDetector;
@@ -260,6 +261,12 @@
   virtual resource_coordinator::ResourceCoordinatorParts*
   resource_coordinator_parts() = 0;
 
+#if !defined(OS_ANDROID)
+  // Returns the object which keeps track of serial port permissions configured
+  // through the policy engine.
+  virtual SerialPolicyAllowedPorts* serial_policy_allowed_ports() = 0;
+#endif
+
   virtual BuildState* GetBuildState() = 0;
 
  private:
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 5e43450..54e8cca 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -157,6 +157,7 @@
 #include "chrome/browser/gcm/gcm_product_util.h"
 #include "chrome/browser/intranet_redirect_detector.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
+#include "chrome/browser/serial/serial_policy_allowed_ports.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "components/gcm_driver/gcm_client_factory.h"
@@ -895,6 +896,17 @@
   return resource_coordinator_parts_.get();
 }
 
+#if !defined(OS_ANDROID)
+SerialPolicyAllowedPorts* BrowserProcessImpl::serial_policy_allowed_ports() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!serial_policy_allowed_ports_) {
+    serial_policy_allowed_ports_ =
+        std::make_unique<SerialPolicyAllowedPorts>(local_state());
+  }
+  return serial_policy_allowed_ports_.get();
+}
+#endif
+
 BuildState* BrowserProcessImpl::GetBuildState() {
 #if !defined(OS_ANDROID)
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 0ee317e..e9c1273 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -200,6 +200,10 @@
   resource_coordinator::ResourceCoordinatorParts* resource_coordinator_parts()
       override;
 
+#if !defined(OS_ANDROID)
+  SerialPolicyAllowedPorts* serial_policy_allowed_ports() override;
+#endif
+
   BuildState* GetBuildState() override;
 
   static void RegisterPrefs(PrefRegistrySimple* registry);
@@ -403,6 +407,8 @@
   // Called to signal the process' main message loop to exit.
   base::OnceClosure quit_closure_;
 
+  std::unique_ptr<SerialPolicyAllowedPorts> serial_policy_allowed_ports_;
+
   BuildState build_state_;
 #endif
 
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index 54ff3062..65524fb9 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -13,7 +13,6 @@
 
 #include "base/bind.h"
 #include "base/containers/contains.h"
-#include "base/containers/flat_set.h"
 #include "base/files/file_util.h"
 #include "base/guid.h"
 #include "base/memory/ptr_util.h"
@@ -137,6 +136,8 @@
 #include "services/network/public/mojom/network_context.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/favicon_size.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 #if defined(OS_ANDROID)
 #include "chrome/browser/android/customtabs/origin_verifier.h"
@@ -170,6 +171,7 @@
 #endif  // BUILDFLAG(ENABLE_PLUGINS)
 
 #if BUILDFLAG(ENABLE_REPORTING)
+#include "base/containers/flat_map.h"
 #include "net/network_error_logging/network_error_logging_service.h"
 #include "net/reporting/reporting_browsing_data_remover.h"
 #include "net/reporting/reporting_policy.h"
@@ -833,6 +835,13 @@
 
   ~MockReportingService() override = default;
 
+  void SetDocumentReportingEndpoints(
+      const url::Origin& origin,
+      const net::NetworkIsolationKey& network_isolation_key,
+      const base::flat_map<std::string, std::string>& endpoints) override {
+    NOTREACHED();
+  }
+
   void QueueReport(const GURL& url,
                    const net::NetworkIsolationKey& network_isolation_key,
                    const std::string& user_agent,
@@ -850,13 +859,6 @@
     NOTREACHED();
   }
 
-  void ProcessReportingEndpointsHeader(
-      const url::Origin& origin,
-      const net::NetworkIsolationKey& network_isolation_key,
-      const std::string& header_value) override {
-    NOTREACHED();
-  }
-
   void RemoveBrowsingData(uint64_t data_type_mask,
                           const base::RepeatingCallback<bool(const GURL&)>&
                               origin_filter) override {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 2ffe9581..1834781 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -41,6 +41,7 @@
     "//chromeos/dbus:runtime_probe_proto",
     "//chromeos/dbus:seneschal_proto",
     "//chromeos/dbus:vm_applications_apps_proto",
+    "//chromeos/dbus:vm_disk_management_proto",
     "//chromeos/dbus:vm_permission_service_proto",
     "//chromeos/dbus:vm_sk_forwarding_proto",
     "//chromeos/dbus/power:power_manager_proto",
@@ -2169,6 +2170,8 @@
     "dbus/smb_fs_service_provider.h",
     "dbus/virtual_file_request_service_provider.cc",
     "dbus/virtual_file_request_service_provider.h",
+    "dbus/vm/vm_disk_management_service_provider.cc",
+    "dbus/vm/vm_disk_management_service_provider.h",
     "dbus/vm/vm_permission_service_provider.cc",
     "dbus/vm/vm_permission_service_provider.h",
     "dbus/vm/vm_sk_forwarding_service_provider.cc",
@@ -3318,6 +3321,7 @@
     "dbus/org.chromium.SmbFsService.conf",
     "dbus/org.chromium.VirtualFileRequestService.conf",
     "dbus/org.chromium.VmApplicationsService.conf",
+    "dbus/vm/org.chromium.VmDiskManagementService.conf",
     "dbus/vm/org.chromium.VmPermissionService.conf",
     "dbus/vm/org.chromium.VmSKForwardingService.conf",
   ]
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 10a6ba5..5142012a 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -112,6 +112,7 @@
 #include "chrome/browser/chromeos/dbus/screen_lock_service_provider.h"
 #include "chrome/browser/chromeos/dbus/smb_fs_service_provider.h"
 #include "chrome/browser/chromeos/dbus/virtual_file_request_service_provider.h"
+#include "chrome/browser/chromeos/dbus/vm/vm_disk_management_service_provider.h"
 #include "chrome/browser/chromeos/dbus/vm/vm_permission_service_provider.h"
 #include "chrome/browser/chromeos/dbus/vm/vm_sk_forwarding_service_provider.h"
 #include "chrome/browser/chromeos/dbus/vm_applications_service_provider.h"
@@ -354,6 +355,13 @@
         CrosDBusService::CreateServiceProviderList(
             std::make_unique<VmApplicationsServiceProvider>()));
 
+    vm_disk_management_service_ = CrosDBusService::Create(
+        system_bus, vm_tools::disk_management::kVmDiskManagementServiceName,
+        dbus::ObjectPath(
+            vm_tools::disk_management::kVmDiskManagementServicePath),
+        CrosDBusService::CreateServiceProviderList(
+            std::make_unique<VmDiskManagementServiceProvider>()));
+
     vm_sk_forwarding_service_ = CrosDBusService::Create(
         system_bus, vm_tools::sk_forwarding::kVmSKForwardingServiceName,
         dbus::ObjectPath(vm_tools::sk_forwarding::kVmSKForwardingServicePath),
@@ -465,6 +473,7 @@
     component_updater_service_.reset();
     chrome_features_service_.reset();
     vm_applications_service_.reset();
+    vm_disk_management_service_.reset();
     vm_sk_forwarding_service_.reset();
     vm_permission_service_.reset();
     drive_file_stream_service_.reset();
@@ -494,6 +503,7 @@
   std::unique_ptr<CrosDBusService> component_updater_service_;
   std::unique_ptr<CrosDBusService> chrome_features_service_;
   std::unique_ptr<CrosDBusService> vm_applications_service_;
+  std::unique_ptr<CrosDBusService> vm_disk_management_service_;
   std::unique_ptr<CrosDBusService> vm_sk_forwarding_service_;
   std::unique_ptr<CrosDBusService> vm_permission_service_;
   std::unique_ptr<CrosDBusService> drive_file_stream_service_;
diff --git a/chrome/browser/chromeos/dbus/vm/org.chromium.VmDiskManagementService.conf b/chrome/browser/chromeos/dbus/vm/org.chromium.VmDiskManagementService.conf
new file mode 100644
index 0000000..a98fae0
--- /dev/null
+++ b/chrome/browser/chromeos/dbus/vm/org.chromium.VmDiskManagementService.conf
@@ -0,0 +1,18 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<!--
+  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.
+-->
+
+<busconfig>
+  <policy user="chronos">
+    <allow own="org.chromium.VmDiskManagementService"/>
+  </policy>
+
+  <policy user="vm_cicerone">
+    <allow send_destination="org.chromium.VmDiskManagementService"
+           send_interface="org.chromium.VmDiskManagementService"/>
+  </policy>
+</busconfig>
\ No newline at end of file
diff --git a/chrome/browser/chromeos/dbus/vm/vm_disk_management_service_provider.cc b/chrome/browser/chromeos/dbus/vm/vm_disk_management_service_provider.cc
new file mode 100644
index 0000000..c05fc77
--- /dev/null
+++ b/chrome/browser/chromeos/dbus/vm/vm_disk_management_service_provider.cc
@@ -0,0 +1,231 @@
+// 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 "chrome/browser/chromeos/dbus/vm/vm_disk_management_service_provider.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "chrome/browser/ash/borealis/borealis_disk_manager_dispatcher.h"
+#include "chrome/browser/ash/borealis/borealis_service.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chromeos/dbus/vm_disk_management/disk_management.pb.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+VmDiskManagementServiceProvider::VmDiskManagementServiceProvider() = default;
+
+VmDiskManagementServiceProvider::~VmDiskManagementServiceProvider() = default;
+
+void VmDiskManagementServiceProvider::Start(
+    scoped_refptr<dbus::ExportedObject> exported_object) {
+  exported_object->ExportMethod(
+      vm_tools::disk_management::kVmDiskManagementServiceInterface,
+      vm_tools::disk_management::kVmDiskManagementServiceGetDiskInfoMethod,
+      base::BindRepeating(&VmDiskManagementServiceProvider::GetDiskInfo,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&VmDiskManagementServiceProvider::OnExported,
+                     weak_ptr_factory_.GetWeakPtr()));
+  exported_object->ExportMethod(
+      vm_tools::disk_management::kVmDiskManagementServiceInterface,
+      vm_tools::disk_management::kVmDiskManagementServiceRequestSpaceMethod,
+      base::BindRepeating(&VmDiskManagementServiceProvider::RequestSpace,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&VmDiskManagementServiceProvider::OnExported,
+                     weak_ptr_factory_.GetWeakPtr()));
+  exported_object->ExportMethod(
+      vm_tools::disk_management::kVmDiskManagementServiceInterface,
+      vm_tools::disk_management::kVmDiskManagementServiceReleaseSpaceMethod,
+      base::BindRepeating(&VmDiskManagementServiceProvider::ReleaseSpace,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&VmDiskManagementServiceProvider::OnExported,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void VmDiskManagementServiceProvider::OnExported(
+    const std::string& interface_name,
+    const std::string& method_name,
+    bool success) {
+  LOG_IF(ERROR, !success) << "Failed to export " << interface_name << "."
+                          << method_name;
+}
+
+void VmDiskManagementServiceProvider::GetDiskInfo(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+
+  vm_tools::disk_management::GetDiskInfoRequest request;
+  if (!reader.PopArrayOfBytesAsProto(&request)) {
+    constexpr char error_message[] =
+        "Unable to parse GetDiskInfoRequest from message";
+    LOG(ERROR) << error_message;
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, DBUS_ERROR_INVALID_ARGS, error_message));
+    return;
+  }
+
+  std::unique_ptr<dbus::Response> response =
+      dbus::Response::FromMethodCall(method_call);
+
+  if (request.origin().vm_name().empty() ||
+      request.origin().container_name().empty() ||
+      request.origin().owner_id().empty()) {
+    std::string error =
+        "GetDiskInfoRequest failed: request has missing or incomplete origin";
+    OnGetDiskInfo(
+        std::move(response), std::move(response_sender),
+        borealis::Expected<borealis::BorealisDiskManager::GetDiskInfoResponse,
+                           std::string>::Unexpected(std::move(error)));
+  }
+
+  borealis::BorealisService::GetForProfile(
+      ProfileManager::GetPrimaryUserProfile())
+      ->DiskManagerDispatcher()
+      .GetDiskInfo(
+          request.origin().vm_name(), request.origin().container_name(),
+          base::BindOnce(&VmDiskManagementServiceProvider::OnGetDiskInfo,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(response),
+                         std::move(response_sender)));
+}
+
+void VmDiskManagementServiceProvider::RequestSpace(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+
+  vm_tools::disk_management::RequestSpaceRequest request;
+  if (!reader.PopArrayOfBytesAsProto(&request)) {
+    constexpr char error_message[] =
+        "Unable to parse RequestSpaceRequest from message";
+    LOG(ERROR) << error_message;
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, DBUS_ERROR_INVALID_ARGS, error_message));
+    return;
+  }
+
+  std::unique_ptr<dbus::Response> response =
+      dbus::Response::FromMethodCall(method_call);
+
+  if (request.origin().vm_name().empty() ||
+      request.origin().container_name().empty() ||
+      request.origin().owner_id().empty()) {
+    std::string error =
+        "RequestSpaceRequest failed: request has missing or incomplete origin";
+    OnRequestSpace(std::move(response), std::move(response_sender),
+                   borealis::Expected<uint64_t, std::string>::Unexpected(
+                       std::move(error)));
+  }
+
+  borealis::BorealisService::GetForProfile(
+      ProfileManager::GetPrimaryUserProfile())
+      ->DiskManagerDispatcher()
+      .RequestSpace(
+          request.origin().vm_name(), request.origin().container_name(),
+          request.space_requested(),
+          base::BindOnce(&VmDiskManagementServiceProvider::OnRequestSpace,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(response),
+                         std::move(response_sender)));
+}
+
+void VmDiskManagementServiceProvider::ReleaseSpace(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+
+  vm_tools::disk_management::ReleaseSpaceRequest request;
+  if (!reader.PopArrayOfBytesAsProto(&request)) {
+    constexpr char error_message[] =
+        "Unable to parse ReleaseSpaceRequest from message";
+    LOG(ERROR) << error_message;
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, DBUS_ERROR_INVALID_ARGS, error_message));
+    return;
+  }
+
+  std::unique_ptr<dbus::Response> response =
+      dbus::Response::FromMethodCall(method_call);
+
+  if (request.origin().vm_name().empty() ||
+      request.origin().container_name().empty() ||
+      request.origin().owner_id().empty()) {
+    std::string error =
+        "ReleaseSpaceRequest failed: request has missing or incomplete origin";
+    OnReleaseSpace(std::move(response), std::move(response_sender),
+                   borealis::Expected<uint64_t, std::string>::Unexpected(
+                       std::move(error)));
+  }
+
+  borealis::BorealisService::GetForProfile(
+      ProfileManager::GetPrimaryUserProfile())
+      ->DiskManagerDispatcher()
+      .RequestSpace(
+          request.origin().vm_name(), request.origin().container_name(),
+          request.space_to_release(),
+          base::BindOnce(&VmDiskManagementServiceProvider::OnReleaseSpace,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(response),
+                         std::move(response_sender)));
+}
+
+void VmDiskManagementServiceProvider::OnGetDiskInfo(
+    std::unique_ptr<dbus::Response> response,
+    dbus::ExportedObject::ResponseSender response_sender,
+    borealis::Expected<borealis::BorealisDiskManager::GetDiskInfoResponse,
+                       std::string> response_or_error) {
+  vm_tools::disk_management::GetDiskInfoResponse payload;
+  if (!response_or_error) {
+    LOG(ERROR) << "GetDiskInfoRequest failed: " << response_or_error.Error();
+    payload.set_error(1);
+  } else {
+    payload.set_available_space(response_or_error.Value().available_bytes);
+    payload.set_expandable_space(response_or_error.Value().expandable_bytes);
+  }
+  // Reply to the original D-Bus method call.
+  dbus::MessageWriter writer(response.get());
+  writer.AppendProtoAsArrayOfBytes(payload);
+  std::move(response_sender).Run(std::move(response));
+}
+
+void VmDiskManagementServiceProvider::OnRequestSpace(
+    std::unique_ptr<dbus::Response> response,
+    dbus::ExportedObject::ResponseSender response_sender,
+    borealis::Expected<uint64_t, std::string> response_or_error) {
+  vm_tools::disk_management::RequestSpaceResponse payload;
+  if (!response_or_error) {
+    LOG(ERROR) << "RequestSpaceRequest failed: " << response_or_error.Error();
+    payload.set_error(1);
+  } else {
+    payload.set_space_granted(response_or_error.Value());
+  }
+  // Reply to the original D-Bus method call.
+  dbus::MessageWriter writer(response.get());
+  writer.AppendProtoAsArrayOfBytes(payload);
+  std::move(response_sender).Run(std::move(response));
+}
+
+void VmDiskManagementServiceProvider::OnReleaseSpace(
+    std::unique_ptr<dbus::Response> response,
+    dbus::ExportedObject::ResponseSender response_sender,
+    borealis::Expected<uint64_t, std::string> response_or_error) {
+  vm_tools::disk_management::ReleaseSpaceResponse payload;
+  if (!response_or_error) {
+    LOG(ERROR) << "ReleaseSpaceRequest failed: " << response_or_error.Error();
+    payload.set_error(1);
+  } else {
+    payload.set_space_released(response_or_error.Value());
+  }
+  // Reply to the original D-Bus method call.
+  dbus::MessageWriter writer(response.get());
+  writer.AppendProtoAsArrayOfBytes(payload);
+  std::move(response_sender).Run(std::move(response));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/dbus/vm/vm_disk_management_service_provider.h b/chrome/browser/chromeos/dbus/vm/vm_disk_management_service_provider.h
new file mode 100644
index 0000000..46d3b43c
--- /dev/null
+++ b/chrome/browser/chromeos/dbus/vm/vm_disk_management_service_provider.h
@@ -0,0 +1,71 @@
+// 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 CHROME_BROWSER_CHROMEOS_DBUS_VM_VM_DISK_MANAGEMENT_SERVICE_PROVIDER_H_
+#define CHROME_BROWSER_CHROMEOS_DBUS_VM_VM_DISK_MANAGEMENT_SERVICE_PROVIDER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ash/borealis/borealis_disk_manager.h"
+#include "chromeos/dbus/services/cros_dbus_service.h"
+#include "dbus/exported_object.h"
+
+namespace dbus {
+class MethodCall;
+}  // namespace dbus
+
+namespace chromeos {
+
+// This class exports D-Bus methods used by Crostini VMs (currently only
+// Borealis) for resizing their disks.
+class VmDiskManagementServiceProvider
+    : public CrosDBusService::ServiceProviderInterface {
+ public:
+  VmDiskManagementServiceProvider();
+  VmDiskManagementServiceProvider(const VmDiskManagementServiceProvider&) =
+      delete;
+  VmDiskManagementServiceProvider& operator=(
+      const VmDiskManagementServiceProvider&) = delete;
+  ~VmDiskManagementServiceProvider() override;
+
+  // CrosDBusService::ServiceProviderInterface overrides:
+  void Start(scoped_refptr<dbus::ExportedObject> exported_object) override;
+
+ private:
+  // Called from ExportedObject when UpdateApplicationList() is exported as a
+  // D-Bus method or failed to be exported.
+  void OnExported(const std::string& interface_name,
+                  const std::string& method_name,
+                  bool success);
+
+  // Called on UI thread in response to a D-Bus request.
+  void GetDiskInfo(dbus::MethodCall* method_call,
+                   dbus::ExportedObject::ResponseSender response_sender);
+  void RequestSpace(dbus::MethodCall* method_call,
+                    dbus::ExportedObject::ResponseSender response_sender);
+  void ReleaseSpace(dbus::MethodCall* method_call,
+                    dbus::ExportedObject::ResponseSender response_sender);
+
+  // Callbacks for responding to certain D-Bus requests.
+  void OnGetDiskInfo(
+      std::unique_ptr<dbus::Response> response,
+      dbus::ExportedObject::ResponseSender response_sender,
+      borealis::Expected<borealis::BorealisDiskManager::GetDiskInfoResponse,
+                         std::string> response_or_error);
+  void OnRequestSpace(
+      std::unique_ptr<dbus::Response> response,
+      dbus::ExportedObject::ResponseSender response_sender,
+      borealis::Expected<uint64_t, std::string> response_or_error);
+  void OnReleaseSpace(
+      std::unique_ptr<dbus::Response> response,
+      dbus::ExportedObject::ResponseSender response_sender,
+      borealis::Expected<uint64_t, std::string> response_or_error);
+
+  base::WeakPtrFactory<VmDiskManagementServiceProvider> weak_ptr_factory_{this};
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_DBUS_VM_VM_DISK_MANAGEMENT_SERVICE_PROVIDER_H_
diff --git a/chrome/browser/chromeos/nearby/nearby_process_manager_impl.cc b/chrome/browser/chromeos/nearby/nearby_process_manager_impl.cc
index 3fba385..6af7b02 100644
--- a/chrome/browser/chromeos/nearby/nearby_process_manager_impl.cc
+++ b/chrome/browser/chromeos/nearby/nearby_process_manager_impl.cc
@@ -166,8 +166,10 @@
       connections_receiver = connections.InitWithNewPipeAndPassReceiver();
   connections_.Bind(std::move(connections), /*bind_task_runner=*/nullptr);
   connections_.set_disconnect_handler(
-      base::BindOnce(&NearbyProcessManagerImpl::OnMojoPipeDisconnect,
-                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(
+          &NearbyProcessManagerImpl::OnMojoPipeDisconnect,
+          weak_ptr_factory_.GetWeakPtr(),
+          NearbyProcessShutdownReason::kConnectionsMojoPipeDisconnection),
       base::SequencedTaskRunnerHandle::Get());
 
   mojo::PendingRemote<sharing::mojom::NearbySharingDecoder> decoder;
@@ -175,8 +177,10 @@
       decoder.InitWithNewPipeAndPassReceiver();
   decoder_.Bind(std::move(decoder), /*bind_task_runner=*/nullptr);
   decoder_.set_disconnect_handler(
-      base::BindOnce(&NearbyProcessManagerImpl::OnMojoPipeDisconnect,
-                     weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(
+          &NearbyProcessManagerImpl::OnMojoPipeDisconnect,
+          weak_ptr_factory_.GetWeakPtr(),
+          NearbyProcessShutdownReason::kDecoderMojoPipeDisconnection),
       base::SequencedTaskRunnerHandle::Get());
 
   // Pass these references to Connect() to start up the process.
@@ -196,11 +200,11 @@
   NotifyProcessStopped(shutdown_reason);
 }
 
-void NearbyProcessManagerImpl::OnMojoPipeDisconnect() {
-  NS_LOG(ERROR) << "A utility process Mojo pipe has disconnected.";
-
-  NearbyProcessShutdownReason shutdown_reason =
-      NearbyProcessShutdownReason::kMojoPipeDisconnection;
+void NearbyProcessManagerImpl::OnMojoPipeDisconnect(
+    NearbyProcessShutdownReason shutdown_reason) {
+  NS_LOG(ERROR) << "The browser process has detected that the utility process "
+                   "disconnected from a mojo pipe. ["
+                << shutdown_reason << "]";
 
   ShutDownProcess(shutdown_reason);
   NotifyProcessStopped(shutdown_reason);
diff --git a/chrome/browser/chromeos/nearby/nearby_process_manager_impl.h b/chrome/browser/chromeos/nearby/nearby_process_manager_impl.h
index 2179661..2461fc7 100644
--- a/chrome/browser/chromeos/nearby/nearby_process_manager_impl.h
+++ b/chrome/browser/chromeos/nearby/nearby_process_manager_impl.h
@@ -96,7 +96,7 @@
   bool AttemptToBindToUtilityProcess();
 
   void OnSharingProcessCrash();
-  void OnMojoPipeDisconnect();
+  void OnMojoPipeDisconnect(NearbyProcessShutdownReason shutdown_reason);
   void OnReferenceDeleted(const base::UnguessableToken& reference_id);
   void ShutDownProcess(NearbyProcessShutdownReason shutdown_reason);
   void NotifyProcessStopped(NearbyProcessShutdownReason shutdown_reason);
diff --git a/chrome/browser/chromeos/secure_channel/nearby_connector_impl.cc b/chrome/browser/chromeos/secure_channel/nearby_connector_impl.cc
index 29562d45..52fdb9b3 100644
--- a/chrome/browser/chromeos/secure_channel/nearby_connector_impl.cc
+++ b/chrome/browser/chromeos/secure_channel/nearby_connector_impl.cc
@@ -155,7 +155,8 @@
           util::NearbyDisconnectionReason::kNearbyProcessCrash;
       break;
 
-    case NearbyProcessShutdownReason::kMojoPipeDisconnection:
+    case NearbyProcessShutdownReason::kConnectionsMojoPipeDisconnection:
+    case NearbyProcessShutdownReason::kDecoderMojoPipeDisconnection:
       disconnection_reason =
           util::NearbyDisconnectionReason::kNearbyProcessMojoDisconnection;
       break;
diff --git a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
index f2719e6..c176431 100644
--- a/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
+++ b/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc
@@ -139,6 +139,8 @@
 
 // Canvas tests rely on the harness producing pixel output in order to read back
 // pixels from a canvas element. So we have to override the setup function.
+// TODO(https://crbug.com/1093066): Investigate to see if these tests can be
+// enabled for Service Worker-based extensions.
 class BrowserActionApiCanvasTest : public BrowserActionApiTest {
  public:
   void SetUp() override {
@@ -149,7 +151,7 @@
 
 using ContextType = ExtensionBrowserTest::ContextType;
 
-class BrowserActionApiLazyTest
+class BrowserActionApiTestWithContextType
     : public BrowserActionApiTest,
       public testing::WithParamInterface<ContextType> {
  protected:
@@ -225,11 +227,17 @@
               action->GetIsVisible(ExtensionAction::kDefaultTabId));
   }
 
+  bool RunTest(const char* name) WARN_UNUSED_RESULT {
+    return RunExtensionTest(
+        name, {},
+        {.load_as_service_worker = GetParam() == ContextType::kServiceWorker});
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, Basic) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, Basic) {
   ExtensionTestMessageListener ready_listener("ready", false);
   ASSERT_TRUE(embedded_test_server()->Start());
   const Extension* extension = LoadExtensionWithParamOptions(
@@ -253,19 +261,19 @@
   EXPECT_TRUE(catcher.GetNextResult());
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, Disable) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, Disable) {
   ASSERT_NO_FATAL_FAILURE(RunEnableTest("browser_action/enable", true));
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, Enable) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, Enable) {
   ASSERT_NO_FATAL_FAILURE(RunEnableTest("browser_action/enable", false));
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, Update) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, Update) {
   ASSERT_NO_FATAL_FAILURE(RunUpdateTest("browser_action/update", false));
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, UpdateSvg) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, UpdateSvg) {
   // TODO(crbug.com/1064671): Service Workers currently don't support loading
   // SVG images.
   const bool expect_failure = GetParam() == ContextType::kServiceWorker;
@@ -273,11 +281,11 @@
       RunUpdateTest("browser_action/update_svg", expect_failure));
 }
 
-INSTANTIATE_TEST_SUITE_P(EventPage,
-                         BrowserActionApiLazyTest,
-                         ::testing::Values(ContextType::kEventPage));
+INSTANTIATE_TEST_SUITE_P(PersistentBackground,
+                         BrowserActionApiTestWithContextType,
+                         ::testing::Values(ContextType::kPersistentBackground));
 INSTANTIATE_TEST_SUITE_P(ServiceWorker,
-                         BrowserActionApiLazyTest,
+                         BrowserActionApiTestWithContextType,
                          ::testing::Values(ContextType::kServiceWorker));
 
 IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, DynamicBrowserAction) {
@@ -523,10 +531,9 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest,
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType,
                        TabSpecificBrowserActionState) {
-  ASSERT_TRUE(RunExtensionTest("browser_action/tab_specific_state")) <<
-      message_;
+  ASSERT_TRUE(RunTest("browser_action/tab_specific_state")) << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension) << message_;
 
@@ -558,8 +565,8 @@
 
 // Test that calling chrome.browserAction.setIcon() can set the icon for
 // extension.
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, BrowserActionSetIcon) {
-  ASSERT_TRUE(RunExtensionTest("browser_action/set_icon")) << message_;
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, SetIcon) {
+  ASSERT_TRUE(RunTest("browser_action/set_icon")) << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension) << message_;
 
@@ -592,8 +599,8 @@
 
 // Test that calling chrome.browserAction.setPopup() can enable and change
 // a popup.
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, BrowserActionAddPopup) {
-  ASSERT_TRUE(RunExtensionTest("browser_action/add_popup")) << message_;
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, AddPopup) {
+  ASSERT_TRUE(RunTest("browser_action/add_popup")) << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension) << message_;
 
@@ -647,9 +654,9 @@
 }
 
 // Test that calling chrome.browserAction.setPopup() can remove a popup.
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, BrowserActionRemovePopup) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, RemovePopup) {
   // Load the extension, which has a browser action with a default popup.
-  ASSERT_TRUE(RunExtensionTest("browser_action/remove_popup")) << message_;
+  ASSERT_TRUE(RunTest("browser_action/remove_popup")) << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension) << message_;
 
@@ -681,7 +688,7 @@
       << "a specific tab id.";
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, IncognitoBasic) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, IncognitoBasic) {
   ExtensionTestMessageListener ready_listener("ready", false);
   ASSERT_TRUE(embedded_test_server()->Start());
   scoped_refptr<const Extension> extension = LoadExtensionWithParamOptions(
@@ -729,7 +736,7 @@
   EXPECT_TRUE(catcher.GetNextResult());
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, IncognitoUpdate) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, IncognitoUpdate) {
   ASSERT_TRUE(embedded_test_server()->Start());
   ExtensionTestMessageListener incognito_not_allowed_listener(
       "incognito not allowed", false);
@@ -792,7 +799,10 @@
 
 // Tests that events are dispatched to the correct profile for split mode
 // extensions.
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, IncognitoSplit) {
+// TODO(https://crbug.com/1212866): Crashes or times out when running as a
+// Service Worker-based extension. When fixed, make this a
+// BrowserActionApiTestWithContextType test.
+IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoSplit) {
   ResultCatcher catcher;
   const Extension* extension =
       LoadExtension(test_data_dir_.AppendASCII("browser_action/split_mode"),
@@ -873,9 +883,10 @@
             action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId));
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, BadgeBackgroundColor) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType,
+                       BadgeBackgroundColor) {
   ASSERT_TRUE(embedded_test_server()->Start());
-  ASSERT_TRUE(RunExtensionTest("browser_action/color")) << message_;
+  ASSERT_TRUE(RunTest("browser_action/color")) << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension) << message_;
 
@@ -922,8 +933,8 @@
             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, Getters) {
-  ASSERT_TRUE(RunExtensionTest("browser_action/getters")) << message_;
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType, Getters) {
+  ASSERT_TRUE(RunTest("browser_action/getters")) << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension) << message_;
 
@@ -943,10 +954,11 @@
 }
 
 // Verify triggering browser action.
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, TestTriggerBrowserAction) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType,
+                       TestTriggerBrowserAction) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
-  ASSERT_TRUE(RunExtensionTest("trigger_actions/browser_action")) << message_;
+  ASSERT_TRUE(RunTest("trigger_actions/browser_action")) << message_;
   const Extension* extension = GetSingleLoadedExtension();
   ASSERT_TRUE(extension) << message_;
 
@@ -979,11 +991,11 @@
   EXPECT_EQ(result, "red");
 }
 
-IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest,
-                       BrowserActionWithRectangularIcon) {
+IN_PROC_BROWSER_TEST_P(BrowserActionApiTestWithContextType,
+                       WithRectangularIcon) {
   ExtensionTestMessageListener ready_listener("ready", true);
 
-  const Extension* extension = LoadExtension(
+  const Extension* extension = LoadExtensionWithParamOptions(
       test_data_dir_.AppendASCII("browser_action").AppendASCII("rect_icon"));
   ASSERT_TRUE(extension);
   EXPECT_TRUE(ready_listener.WaitUntilSatisfied());
diff --git a/chrome/browser/extensions/api/module/module_apitest.cc b/chrome/browser/extensions/api/module/module_apitest.cc
index 9330ebfe1..73bdee84 100644
--- a/chrome/browser/extensions/api/module/module_apitest.cc
+++ b/chrome/browser/extensions/api/module/module_apitest.cc
@@ -5,27 +5,58 @@
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "content/public/test/browser_test.h"
 
-using ExtensionModuleApiTest = extensions::ExtensionApiTest;
+namespace extensions {
 
-IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, CognitoFile) {
-  ASSERT_TRUE(RunExtensionTest("extension_module/cognito_file", {},
-                               {.allow_file_access = true}))
-      << message_;
-}
+namespace {
 
-IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, IncognitoFile) {
+using ContextType = ExtensionBrowserTest::ContextType;
+
+class ExtensionModuleApiTest : public ExtensionApiTest,
+                               public testing::WithParamInterface<ContextType> {
+ public:
+  ExtensionModuleApiTest() = default;
+  ~ExtensionModuleApiTest() override = default;
+  ExtensionModuleApiTest(const ExtensionModuleApiTest&) = delete;
+  ExtensionModuleApiTest& operator=(const ExtensionModuleApiTest&) = delete;
+
+ protected:
+  bool RunTest(const char* name,
+               LoadOptions load_options = {}) WARN_UNUSED_RESULT {
+    load_options.load_as_service_worker =
+        GetParam() == ContextType::kServiceWorker;
+    return RunExtensionTest(name, {}, load_options);
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(PersistentBackground,
+                         ExtensionModuleApiTest,
+                         ::testing::Values(ContextType::kPersistentBackground));
+INSTANTIATE_TEST_SUITE_P(ServiceWorker,
+                         ExtensionModuleApiTest,
+                         ::testing::Values(ContextType::kServiceWorker));
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, CognitoFile) {
   ASSERT_TRUE(
-      RunExtensionTest("extension_module/incognito_file", {},
-                       {.allow_in_incognito = true, .allow_file_access = true}))
+      RunTest("extension_module/cognito_file", {.allow_file_access = true}))
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, CognitoNoFile) {
-  ASSERT_TRUE(RunExtensionTest("extension_module/cognito_nofile")) << message_;
-}
-
-IN_PROC_BROWSER_TEST_F(ExtensionModuleApiTest, IncognitoNoFile) {
-  ASSERT_TRUE(RunExtensionTest("extension_module/incognito_nofile", {},
-                               {.allow_in_incognito = true}))
+IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, IncognitoFile) {
+  ASSERT_TRUE(RunTest("extension_module/incognito_file",
+                      {.allow_in_incognito = true, .allow_file_access = true}))
       << message_;
 }
+
+IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, CognitoNoFile) {
+  ASSERT_TRUE(RunTest("extension_module/cognito_nofile")) << message_;
+}
+
+IN_PROC_BROWSER_TEST_P(ExtensionModuleApiTest, IncognitoNoFile) {
+  ASSERT_TRUE(RunTest("extension_module/incognito_nofile",
+                      {.allow_in_incognito = true}))
+      << message_;
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/back_forward_cache_browsertest.cc b/chrome/browser/extensions/back_forward_cache_browsertest.cc
index e7b7ed2b..4fba49a 100644
--- a/chrome/browser/extensions/back_forward_cache_browsertest.cc
+++ b/chrome/browser/extensions/back_forward_cache_browsertest.cc
@@ -12,6 +12,7 @@
 #include "extensions/common/extension.h"
 #include "net/dns/mock_host_resolver.h"
 #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
+#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-shared.h"
 
 namespace extensions {
 
@@ -339,4 +340,177 @@
           blink::scheduler::WebSchedulerTrackedFeature::kIsolatedWorldScript));
 }
 
+// Tests sending a message to all frames does not send it to back-forward
+// cached frames.
+IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest,
+                       MessageSentToAllFramesDoesNotSendToBackForwardCache) {
+  const Extension* extension = extension =
+      LoadExtension(test_data_dir_.AppendASCII("back_forward_cache")
+                        .AppendASCII("background_page"));
+  ASSERT_TRUE(extension);
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title2.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // 1) Navigate to A.
+  content::RenderFrameHost* rfh_a =
+      ui_test_utils::NavigateToURL(browser(), url_a);
+  content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
+
+  // 2) Navigate to B.
+  content::RenderFrameHost* rfh_b =
+      ui_test_utils::NavigateToURL(browser(), url_b);
+  content::RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
+
+  // Ensure that `rfh_a` is in the cache.
+  EXPECT_FALSE(delete_observer_rfh_a.deleted());
+  EXPECT_NE(rfh_a, rfh_b);
+  EXPECT_EQ(rfh_a->GetLifecycleState(),
+            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
+
+  std::u16string expected_title = u"foo";
+  auto title_watcher = std::make_unique<content::TitleWatcher>(
+      browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
+
+  constexpr char kScript[] =
+      R"HTML(
+      chrome.tabs.executeScript({allFrames: true, code: "document.title='foo'"})
+    )HTML";
+  ASSERT_TRUE(ExecuteScriptInBackgroundPageNoWait(extension->id(), kScript));
+
+  EXPECT_EQ(expected_title, title_watcher->WaitAndGetTitle());
+
+  // `rfh_a` should still be in the cache.
+  EXPECT_FALSE(delete_observer_rfh_a.deleted());
+  EXPECT_NE(rfh_a, rfh_b);
+  EXPECT_EQ(rfh_a->GetLifecycleState(),
+            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
+
+  // Expect the original title when going back to A.
+  expected_title = u"Title Of Awesomeness";
+  title_watcher = std::make_unique<content::TitleWatcher>(
+      browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
+  // Go back to A.
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  web_contents->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(web_contents));
+
+  EXPECT_EQ(expected_title, title_watcher->WaitAndGetTitle());
+
+  // `rfh_b` should still be in the cache.
+  EXPECT_FALSE(delete_observer_rfh_b.deleted());
+  EXPECT_NE(rfh_a, rfh_b);
+  EXPECT_EQ(rfh_b->GetLifecycleState(),
+            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
+
+  // Now go forward to B, and expect that it is what was set before it
+  // went into the back forward cache.
+  expected_title = u"foo";
+  title_watcher = std::make_unique<content::TitleWatcher>(
+      browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
+  web_contents->GetController().GoForward();
+  EXPECT_TRUE(WaitForLoadStop(web_contents));
+
+  EXPECT_EQ(expected_title, title_watcher->WaitAndGetTitle());
+}
+
+// Tests sending a message to specific frame that is in the back forward cache
+// fails.
+IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest,
+                       MessageSentToCachedIdFails) {
+  const Extension* extension = extension =
+      LoadExtension(test_data_dir_.AppendASCII("back_forward_cache")
+                        .AppendASCII("background_page"));
+  ASSERT_TRUE(extension);
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/iframe_blank.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  // 1) Navigate to A.
+  content::RenderFrameHost* rfh_a =
+      ui_test_utils::NavigateToURL(browser(), url_a);
+  content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
+
+  ASSERT_EQ(2u, rfh_a->GetFramesInSubtree().size());
+
+  // Cache the iframe's frame tree node id to send it a message later.
+  int iframe_frame_tree_node_id =
+      rfh_a->GetFramesInSubtree()[1]->GetFrameTreeNodeId();
+
+  // 2) Navigate to B.
+  content::RenderFrameHost* rfh_b =
+      ui_test_utils::NavigateToURL(browser(), url_b);
+
+  // Ensure that `rfh_a` is in the cache.
+  EXPECT_FALSE(delete_observer_rfh_a.deleted());
+  EXPECT_NE(rfh_a, rfh_b);
+  EXPECT_EQ(rfh_a->GetLifecycleState(),
+            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
+
+  std::u16string expected_title = u"foo";
+  auto title_watcher = std::make_unique<content::TitleWatcher>(
+      browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
+
+  constexpr char kScript[] =
+      R"HTML(
+        chrome.tabs.executeScript({frameId: %d,
+                                   code: "document.title='foo'",
+                                   matchAboutBlank: true
+                                  }, (e) => {
+          window.domAutomationController.send(chrome.runtime.lastError ? 'false'
+        : 'true')});
+      )HTML";
+  EXPECT_EQ("false",
+            ExecuteScriptInBackgroundPage(
+                extension->id(),
+                base::StringPrintf(kScript, iframe_frame_tree_node_id)));
+  // Go back to A.
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  web_contents->GetController().GoBack();
+  EXPECT_TRUE(WaitForLoadStop(web_contents));
+
+  // Re-execute the script.
+  EXPECT_EQ("true",
+            ExecuteScriptInBackgroundPage(
+                extension->id(),
+                base::StringPrintf(kScript, iframe_frame_tree_node_id)));
+}
+
+// Test that running extensions message dispatching via a ScriptContext::ForEach
+// for back forward cached pages causes eviction of that RenderFrameHost.
+IN_PROC_BROWSER_TEST_F(ExtensionBackForwardCacheBrowserTest,
+                       StorageCallbackEvicts) {
+  const Extension* extension = extension =
+      LoadExtension(test_data_dir_.AppendASCII("back_forward_cache")
+                        .AppendASCII("content_script_storage"));
+  ASSERT_TRUE(extension);
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
+
+  // 1) Navigate to A.
+  content::RenderFrameHost* rfh_a =
+      ui_test_utils::NavigateToURL(browser(), url_a);
+  content::RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
+
+  // 2) Navigate to B.
+  ui_test_utils::NavigateToURL(browser(), url_b);
+
+  // Expect that `rfh_a` is destroyed as loading page B will causes a storage
+  // event which is sent to all listeners. Since `rfh_a` is a listener but is in
+  // the back forward cache it gets evicted.
+  delete_observer_rfh_a.WaitUntilDeleted();
+
+  // Validate also that the eviction reason is `kJavascriptExecution` due
+  // to the extension processing a callback while in the back forward cache.
+  EXPECT_EQ(1, histogram_tester_.GetBucketCount(
+                   "BackForwardCache.Eviction.Renderer",
+                   blink::mojom::RendererEvictionReason::kJavaScriptExecution));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_security_exploit_browsertest.cc b/chrome/browser/extensions/extension_security_exploit_browsertest.cc
index 0860d8e7..09c08b8 100644
--- a/chrome/browser/extensions/extension_security_exploit_browsertest.cc
+++ b/chrome/browser/extensions/extension_security_exploit_browsertest.cc
@@ -353,10 +353,15 @@
 
   // Inject the malformed/mutated IPC and verify that the renderer is terminated
   // as expected.
-  //
-  // TODO(https://crbug.com/1212918: Once the bug is fixed, re-enable the test
-  // assertion that verifies that `kill_waiter.Wait()` returns
-  // EMF_INVALID_EXTENSION_ID_FOR_CONTENT_SCRIPT.
+  content::RenderProcessHost* main_frame_process =
+      web_contents->GetMainFrame()->GetProcess();
+  RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame_process);
+  IPC::IpcSecurityTestUtil::PwnMessageReceived(
+      main_frame_process->GetChannel(),
+      ExtensionHostMsg_OpenChannelToExtension(source_context, info,
+                                              channel_name, port_id));
+  EXPECT_EQ(bad_message::EMF_INVALID_EXTENSION_ID_FOR_CONTENT_SCRIPT,
+            kill_waiter.Wait());
 }
 
 IN_PROC_BROWSER_TEST_F(OpenChannelToExtensionExploitTest,
diff --git a/chrome/browser/extensions/extension_service_test_with_install.cc b/chrome/browser/extensions/extension_service_test_with_install.cc
index 06f2cb3..622db83 100644
--- a/chrome/browser/extensions/extension_service_test_with_install.cc
+++ b/chrome/browser/extensions/extension_service_test_with_install.cc
@@ -14,9 +14,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_creator.h"
-#include "extensions/browser/extension_util.h"
 #include "extensions/browser/notification_types.h"
 #include "extensions/common/verifier_formats.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -342,13 +340,6 @@
   ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
   EXPECT_TRUE(prefs->GetInstalledExtensionInfo(id));
 
-  // Make sure RegisterClient calls for storage are finished to avoid flaky
-  // crashes in QuotaManagerImpl::RegisterClient due to its getting called
-  // after LazyInitialize.
-  // TODO(crbug.com/1182630) : Remove this when 1182630 is fixed.
-  util::GetStoragePartitionForExtensionId(id, profile());
-  task_environment()->RunUntilIdle();
-
   // We make a copy of the extension's id since the extension can be deleted
   // once it's uninstalled.
   std::string extension_id = id;
@@ -432,11 +423,6 @@
   // did so a bunch of stuff fails. Migrate this over.
   extension_loader.set_ignore_manifest_warnings(true);
   extension_loader.LoadExtension(crx_path);
-
-  // Make sure RegisterClient calls for storage are finished to avoid flaky
-  // crashes in QuotaManagerImpl::RegisterClient on test shutdown.
-  // TODO(crbug.com/1182630) : Remove this when 1182630 is fixed.
-  base::RunLoop().RunUntilIdle();
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 3454267..7c09172 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -959,11 +959,6 @@
     "expiry_milestone": 92
   },
   {
-    "name": "direct-manipulation-stylus",
-    "owners": [ "lanwei", "input-dev" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "disable-accelerated-2d-canvas",
     "owners": [ "fserb" ],
     "expiry_milestone": -1
@@ -1186,9 +1181,9 @@
     "expiry_milestone": 93
   },
   {
-    "name": "elastic-overscroll-win",
-    "owners": [ "arakeri@microsoft.com" ],
-    "expiry_milestone": 90
+    "name": "elastic-overscroll",
+    "owners": [ "arakeri@microsoft.com", "flackr@chromium.org" ],
+    "expiry_milestone": 99
   },
   {
     "name": "enable-accelerated-video-decode",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 3c06517..82d780c6 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3667,11 +3667,6 @@
 const char kD3D11VideoDecoderDescription[] =
     "Enables D3D11VideoDecoder for hardware accelerated video decoding.";
 
-const char kElasticOverscrollWinName[] = "Elastic Overscroll for Windows";
-const char kElasticOverscrollWinDescription[] =
-    "Enables Elastic Overscrolling for Windows on touchscreens and precision "
-    "touchpads.";
-
 const char kEnableIncognitoShortcutOnDesktopName[] =
     "Enable Incognito Desktop Shortcut";
 const char kEnableIncognitoShortcutOnDesktopDescription[] =
@@ -4961,10 +4956,6 @@
     "Enables printing interactions with the operating system to be performed "
     "out-of-process.";
 
-const char kDirectManipulationStylusName[] = "Direct Manipulation Stylus";
-const char kDirectManipulationStylusDescription[] =
-    "If enabled, Chrome will scroll web pages on stylus drag.";
-
 const char kWebuiFeedbackName[] = "WebUI Feedback";
 const char kWebuiFeedbackDescription[] =
     "If enabled, Chrome will show the Feedback WebUI, as opposed to Chrome "
@@ -5130,6 +5121,12 @@
     "will be sent to Google.";
 #endif
 
+#if defined(OS_WIN) || defined(OS_ANDROID)
+const char kElasticOverscrollName[] = "Elastic Overscroll";
+const char kElasticOverscrollDescription[] =
+    "Enables Elastic Overscrolling on touchscreens and precision touchpads.";
+#endif  // defined(OS_WIN) || defined(OS_ANDROID)
+
 #if defined(OS_WIN) || (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) || \
     defined(OS_MAC)
 const char kUIDebugToolsName[] = "Debugging tools for UI";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 38d59e9f..1e30edbd 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2106,9 +2106,6 @@
 extern const char kD3D11VideoDecoderName[];
 extern const char kD3D11VideoDecoderDescription[];
 
-extern const char kElasticOverscrollWinName[];
-extern const char kElasticOverscrollWinDescription[];
-
 extern const char kEnableIncognitoShortcutOnDesktopName[];
 extern const char kEnableIncognitoShortcutOnDesktopDescription[];
 
@@ -2894,9 +2891,6 @@
 extern const char kEnableOopPrintDriversName[];
 extern const char kEnableOopPrintDriversDescription[];
 
-extern const char kDirectManipulationStylusName[];
-extern const char kDirectManipulationStylusDescription[];
-
 extern const char kWebuiFeedbackName[];
 extern const char kWebuiFeedbackDescription[];
 
@@ -3011,6 +3005,11 @@
 extern const char kSendWebUIJavaScriptErrorReportsDescription[];
 #endif
 
+#if defined(OS_WIN) || defined(OS_ANDROID)
+extern const char kElasticOverscrollName[];
+extern const char kElasticOverscrollDescription[];
+#endif  // defined(OS_WIN) || defined(OS_ANDROID)
+
 #if defined(OS_WIN) || (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) || \
     defined(OS_MAC)
 extern const char kUIDebugToolsName[];
diff --git a/chrome/browser/media/router/chrome_media_router_factory.h b/chrome/browser/media/router/chrome_media_router_factory.h
index ee14c57..0bd9664 100644
--- a/chrome/browser/media/router/chrome_media_router_factory.h
+++ b/chrome/browser/media/router/chrome_media_router_factory.h
@@ -15,7 +15,7 @@
 
 namespace media_router {
 
-// A version of MediaRouterFactory for Chrome, which refers incongito contexts
+// A version of MediaRouterFactory for Chrome, which refers incognito contexts
 // to their parent Profile. It also adds support for desktop features.
 class ChromeMediaRouterFactory : public MediaRouterFactory {
  public:
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
index 3fcf3ae..3967a12 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
@@ -1036,7 +1036,8 @@
       return false;
       break;
     case NearbyProcessShutdownReason::kCrash:
-    case NearbyProcessShutdownReason::kMojoPipeDisconnection:
+    case NearbyProcessShutdownReason::kConnectionsMojoPipeDisconnection:
+    case NearbyProcessShutdownReason::kDecoderMojoPipeDisconnection:
       break;
   }
 
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
index 2ea70bc..d80c66d 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
@@ -4303,7 +4303,8 @@
       break;
 
     case NearbyProcessShutdownReason::kCrash:
-    case NearbyProcessShutdownReason::kMojoPipeDisconnection:
+    case NearbyProcessShutdownReason::kConnectionsMojoPipeDisconnection:
+    case NearbyProcessShutdownReason::kDecoderMojoPipeDisconnection:
       expected_to_restart =
           is_enabled && recent_shutdown_count <=
                             NearbySharingServiceImpl::
@@ -4326,9 +4327,11 @@
     NearbySharingServiceRestartTest,
     testing::Combine(
         testing::Bool(),
-        testing::Values(NearbyProcessShutdownReason::kNormal,
-                        NearbyProcessShutdownReason::kCrash,
-                        NearbyProcessShutdownReason::kMojoPipeDisconnection),
+        testing::Values(
+            NearbyProcessShutdownReason::kNormal,
+            NearbyProcessShutdownReason::kCrash,
+            NearbyProcessShutdownReason::kConnectionsMojoPipeDisconnection,
+            NearbyProcessShutdownReason::kDecoderMojoPipeDisconnection),
         testing::Values(0,
                         NearbySharingServiceImpl::
                                 kMaxRecentNearbyProcessUnexpectedShutdownCount -
diff --git a/chrome/browser/policy/android/BUILD.gn b/chrome/browser/policy/android/BUILD.gn
index ffef53d..f49d251 100644
--- a/chrome/browser/policy/android/BUILD.gn
+++ b/chrome/browser/policy/android/BUILD.gn
@@ -4,25 +4,49 @@
 
 import("//build/config/android/rules.gni")
 
-_jni_sources = [
-  "java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferences.java",
-  "java/src/org/chromium/chrome/browser/policy/PolicyServiceFactory.java",
-]
+android_library("util_java") {
+  sources = [ "java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferences.java" ]
 
-android_library("java") {
   deps = [
     "//base:base_java",
     "//chrome/browser/preferences:java",
+    "//components/policy/android:policy_java",
+  ]
+
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+}
+
+android_library("java") {
+  sources = [
+    "java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnection.java",
+    "java/src/org/chromium/chrome/browser/policy/PolicyServiceFactory.java",
+  ]
+
+  deps = [
+    "//base:base_java",
     "//chrome/browser/profiles/android:java",
     "//components/policy/android:policy_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
   ]
+
+  public_deps = [
+    ":delegate_java",
+    ":util_java",
+  ]
+
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
-  sources = _jni_sources
+}
+
+android_library("delegate_java") {
+  sources = [ "java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnectionDelegate.java" ]
 }
 
 generate_jni("jni_headers") {
-  sources = _jni_sources
+  sources = [
+    "java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnection.java",
+    "java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferences.java",
+    "java/src/org/chromium/chrome/browser/policy/PolicyServiceFactory.java",
+  ]
 }
 
 android_library("junit") {
@@ -30,10 +54,15 @@
   bypass_platform_checks = true
   testonly = true
 
-  sources = [ "java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferencesTest.java" ]
+  sources = [
+    "java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnectionTest.java",
+    "java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferencesTest.java",
+  ]
 
   deps = [
+    ":delegate_java",
     ":java",
+    ":util_java",
     "//base:base_junit_test_support",
     "//chrome/browser/preferences:java",
     "//third_party/android_deps:robolectric_all_java",
diff --git a/chrome/browser/policy/android/cloud_management_shared_preferences.h b/chrome/browser/policy/android/cloud_management_shared_preferences.h
index 9a51436..0219941 100644
--- a/chrome/browser/policy/android/cloud_management_shared_preferences.h
+++ b/chrome/browser/policy/android/cloud_management_shared_preferences.h
@@ -12,11 +12,11 @@
 namespace policy {
 namespace android {
 
-// Saves the device management token to Shared Preferences. Ignored if
-// |dm_token| is empty.
+// Saves the device management token to Shared Preferences.
 void SaveDmTokenInSharedPreferences(const std::string& dm_token);
 
-// Returns the DM token available from Shared Preferences, or
+// Returns the DM token available from Shared Preferences or empty if the
+// preference is not set.
 std::string ReadDmTokenFromSharedPreferences();
 
 }  // namespace android
diff --git a/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnection.java b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnection.java
new file mode 100644
index 0000000..0cbe861
--- /dev/null
+++ b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnection.java
@@ -0,0 +1,61 @@
+// 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.policy;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.annotations.CalledByNative;
+
+/**
+ * Allows access to cloud management functionalities implemented downstream.
+ */
+public class CloudManagementAndroidConnection {
+    private static class LazyHolder {
+        private static final CloudManagementAndroidConnection INSTANCE =
+                new CloudManagementAndroidConnection();
+    }
+
+    /** Returns an instance of this class. */
+    @CalledByNative
+    public static CloudManagementAndroidConnection getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    /** Provides access to downstream implementation. */
+    private final CloudManagementAndroidConnectionDelegate mDelegate;
+
+    private CloudManagementAndroidConnection() {
+        mDelegate = new CloudManagementAndroidConnectionDelegate();
+    }
+
+    /* Returns the client ID to be used in the DM token generation. Once generated, the ID is saved
+     * to Shared Preferences so it can be reused. */
+    @CalledByNative
+    public String getClientId() {
+        // Return the ID saved in Shared Preferences, if available.
+        String clientId = CloudManagementSharedPreferences.readClientId();
+        if (!clientId.isEmpty()) {
+            return clientId;
+        }
+
+        // Generate a new ID and save it in Shared Preferences, so it can be reused.
+        String newClientId = getDelegate().generateClientId();
+        CloudManagementSharedPreferences.saveClientId(newClientId);
+
+        return newClientId;
+    }
+
+    /** Overrides {@link mDelegate} if not null. */
+    private static CloudManagementAndroidConnectionDelegate sDelegateForTesting;
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public static void setDelegateForTesting(CloudManagementAndroidConnectionDelegate delegate) {
+        sDelegateForTesting = delegate;
+    }
+
+    private CloudManagementAndroidConnectionDelegate getDelegate() {
+        return sDelegateForTesting != null ? sDelegateForTesting : mDelegate;
+    }
+}
diff --git a/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnectionDelegate.java b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnectionDelegate.java
new file mode 100644
index 0000000..026c87a
--- /dev/null
+++ b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnectionDelegate.java
@@ -0,0 +1,19 @@
+// 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.policy;
+
+import java.util.UUID;
+
+/**
+ * Delegate for cloud management functions implemented downstream for Google Chrome.
+ */
+public class CloudManagementAndroidConnectionDelegate {
+    /* Returns the client ID to be used in the DM token generation. By default a random UUID is
+     * generated for development and testing purposes. Any device that uses randomly generated UUID
+     * as client id for CBCM might be wiped out from the server without notice. */
+    public String generateClientId() {
+        return UUID.randomUUID().toString();
+    }
+}
diff --git a/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnectionTest.java b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnectionTest.java
new file mode 100644
index 0000000..3b8e877b
--- /dev/null
+++ b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementAndroidConnectionTest.java
@@ -0,0 +1,62 @@
+// 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.policy;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/**
+ * Unit tests for CloudManagementAndroidConnection.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public final class CloudManagementAndroidConnectionTest {
+    private static final String CLIENT_ID = "client-id";
+    private static final String SAVED_CLIENT_ID = "saved-client-id";
+
+    /* Simple implementation of {@link CloudManagementAndroidConnection} that overrides {@link
+     * generateClientIdInternal} for easier testing. */
+    private static class FakeCloudManagementAndroidConnectionDelegate
+            extends CloudManagementAndroidConnectionDelegate {
+        @Override
+        public String generateClientId() {
+            return CLIENT_ID;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        CloudManagementAndroidConnection.setDelegateForTesting(
+                new FakeCloudManagementAndroidConnectionDelegate());
+    }
+
+    @Test
+    @SmallTest
+    public void testGetClientId_Generated() {
+        Assert.assertEquals(CloudManagementSharedPreferences.readClientId(), "");
+
+        CloudManagementAndroidConnection connection =
+                CloudManagementAndroidConnection.getInstance();
+        Assert.assertEquals(connection.getClientId(), CLIENT_ID);
+        Assert.assertEquals(CloudManagementSharedPreferences.readClientId(), CLIENT_ID);
+    }
+
+    @Test
+    @SmallTest
+    public void testGetClientId_ReadFromSharedPreferences() {
+        CloudManagementSharedPreferences.saveClientId(SAVED_CLIENT_ID);
+
+        CloudManagementAndroidConnection connection =
+                CloudManagementAndroidConnection.getInstance();
+        Assert.assertEquals(connection.getClientId(), SAVED_CLIENT_ID);
+    }
+}
diff --git a/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferences.java b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferences.java
index fbc9067c..75f2a46 100644
--- a/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferences.java
+++ b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferences.java
@@ -34,4 +34,22 @@
         return SharedPreferencesManager.getInstance().readString(
                 ChromePreferenceKeys.CLOUD_MANAGEMENT_DM_TOKEN, "");
     }
+
+    /**
+     * Sets the "Cloud management client ID" preference.
+     *
+     * @param clientId The ID generated to represent the current browser installation.
+     */
+    public static void saveClientId(String clientId) {
+        SharedPreferencesManager.getInstance().writeString(
+                ChromePreferenceKeys.CLOUD_MANAGEMENT_CLIENT_ID, clientId);
+    }
+
+    /**
+     * Returns the value of the "Cloud management client ID" preference.
+     */
+    public static String readClientId() {
+        return SharedPreferencesManager.getInstance().readString(
+                ChromePreferenceKeys.CLOUD_MANAGEMENT_CLIENT_ID, "");
+    }
 }
diff --git a/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferencesTest.java b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferencesTest.java
index 2495838..571afc0a 100644
--- a/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferencesTest.java
+++ b/chrome/browser/policy/android/java/src/org/chromium/chrome/browser/policy/CloudManagementSharedPreferencesTest.java
@@ -22,6 +22,7 @@
 @Config(manifest = Config.NONE)
 public class CloudManagementSharedPreferencesTest {
     private static final String DM_TOKEN = "fake-dm-token";
+    private static final String CLIENT_ID = "fake-client-id";
 
     @Test
     @SmallTest
@@ -41,4 +42,23 @@
                 ChromePreferenceKeys.CLOUD_MANAGEMENT_DM_TOKEN, DM_TOKEN);
         Assert.assertEquals(CloudManagementSharedPreferences.readDmToken(), DM_TOKEN);
     }
+
+    @Test
+    @SmallTest
+    public void testSaveClientId() {
+        CloudManagementSharedPreferences.saveClientId(CLIENT_ID);
+        Assert.assertEquals(SharedPreferencesManager.getInstance().readString(
+                                    ChromePreferenceKeys.CLOUD_MANAGEMENT_CLIENT_ID, ""),
+                CLIENT_ID);
+    }
+
+    @Test
+    @SmallTest
+    public void testReadClientId() {
+        Assert.assertEquals(CloudManagementSharedPreferences.readClientId(), "");
+
+        SharedPreferencesManager.getInstance().writeString(
+                ChromePreferenceKeys.CLOUD_MANAGEMENT_CLIENT_ID, CLIENT_ID);
+        Assert.assertEquals(CloudManagementSharedPreferences.readClientId(), CLIENT_ID);
+    }
 }
diff --git a/chrome/browser/policy/browser_dm_token_storage_android.cc b/chrome/browser/policy/browser_dm_token_storage_android.cc
index 0c22e91..7261783 100644
--- a/chrome/browser/policy/browser_dm_token_storage_android.cc
+++ b/chrome/browser/policy/browser_dm_token_storage_android.cc
@@ -6,9 +6,12 @@
 
 #include <string>
 
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/policy/android/cloud_management_shared_preferences.h"
+#include "chrome/browser/policy/android/jni_headers/CloudManagementAndroidConnection_jni.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/prefs/pref_service.h"
 
@@ -29,7 +32,10 @@
 BrowserDMTokenStorageAndroid::~BrowserDMTokenStorageAndroid() {}
 
 std::string BrowserDMTokenStorageAndroid::InitClientId() {
-  return std::string();
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return base::android::ConvertJavaStringToUTF8(
+      env, Java_CloudManagementAndroidConnection_getClientId(
+               env, Java_CloudManagementAndroidConnection_getInstance(env)));
 }
 
 std::string BrowserDMTokenStorageAndroid::InitEnrollmentToken() {
diff --git a/chrome/browser/policy/browser_dm_token_storage_android_unittest.cc b/chrome/browser/policy/browser_dm_token_storage_android_unittest.cc
index b19dfc9..40cf3ea9 100644
--- a/chrome/browser/policy/browser_dm_token_storage_android_unittest.cc
+++ b/chrome/browser/policy/browser_dm_token_storage_android_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/location.h"
 #include "base/run_loop.h"
 #include "base/task_runner_util.h"
+#include "chrome/browser/policy/android/cloud_management_shared_preferences.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -25,13 +26,18 @@
 }  // namespace
 
 class BrowserDMTokenStorageAndroidTest : public testing::Test {
+ public:
+  void TearDown() override {
+    android::SaveDmTokenInSharedPreferences(std::string());
+  }
+
  private:
   content::BrowserTaskEnvironment task_environment_;
 };
 
 TEST_F(BrowserDMTokenStorageAndroidTest, InitClientId) {
   BrowserDMTokenStorageAndroid storage;
-  EXPECT_THAT(storage.InitClientId(), IsEmpty());
+  EXPECT_FALSE(storage.InitClientId().empty());
 }
 
 TEST_F(BrowserDMTokenStorageAndroidTest, InitEnrollmentToken) {
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index af570be..5d919e4 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -139,6 +139,12 @@
             "Chrome.Clipboard.SharedUriTimestamp";
 
     /**
+     * The ID generated to represent the current browser installation in the DM Server for Cloud
+     * Management.
+     */
+    public static final String CLOUD_MANAGEMENT_CLIENT_ID = "Chrome.Policy.CloudManagementClientId";
+
+    /**
      * The server-side token generated by the Device Management server on browser enrollment for
      * Cloud Management.
      */
@@ -990,6 +996,7 @@
                 CHROME_SURVEY_PROMPT_DISPLAYED_TIMESTAMP.pattern(),
                 CLIPBOARD_SHARED_URI,
                 CLIPBOARD_SHARED_URI_TIMESTAMP,
+                CLOUD_MANAGEMENT_CLIENT_ID,
                 CLOUD_MANAGEMENT_DM_TOKEN,
                 COMMERCE_SUBSCRIPTIONS_CHROME_MANAGED_TIMESTAMP,
                 CONDITIONAL_TAB_STRIP_CONTINUOUS_DISMISS_COUNTER,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index c66bf99..2c0f20c 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -719,6 +719,13 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   registry->RegisterListPref(kSpellCheckBlacklistedDictionaries);
+
+#if !defined(OS_ANDROID)
+  registry->RegisterListPref(
+      prefs::kManagedProfileSerialAllowAllPortsForUrlsDeprecated);
+  registry->RegisterListPref(
+      prefs::kManagedProfileSerialAllowUsbDevicesForUrlsDeprecated);
+#endif
 }
 
 }  // namespace
@@ -759,6 +766,9 @@
   RegisterScreenshotPrefs(registry);
   safe_browsing::RegisterLocalStatePrefs(registry);
   secure_origin_allowlist::RegisterPrefs(registry);
+#if !defined(OS_ANDROID)
+  SerialPolicyAllowedPorts::RegisterPrefs(registry);
+#endif
   sessions::SessionIdGenerator::RegisterPrefs(registry);
   SSLConfigServiceManager::RegisterPrefs(registry);
   subresource_filter::IndexedRulesetVersion::RegisterPrefs(registry);
@@ -1007,9 +1017,6 @@
       registry);
   security_interstitials::InsecureFormBlockingPage::RegisterProfilePrefs(
       registry);
-#if !defined(OS_ANDROID)
-  SerialPolicyAllowedPorts::RegisterProfilePrefs(registry);
-#endif
   SessionStartupPref::RegisterProfilePrefs(registry);
   SharingSyncPreference::RegisterProfilePrefs(registry);
   site_engagement::SiteEngagementService::RegisterProfilePrefs(registry);
@@ -1446,6 +1453,14 @@
   // Added 05/2021
   profile_prefs->ClearPref(kSpellCheckBlacklistedDictionaries);
 
+#if !defined(OS_ANDROID)
+  // Added 05/2021
+  profile_prefs->ClearPref(
+      prefs::kManagedProfileSerialAllowAllPortsForUrlsDeprecated);
+  profile_prefs->ClearPref(
+      prefs::kManagedProfileSerialAllowUsbDevicesForUrlsDeprecated);
+#endif
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 }
diff --git a/chrome/browser/profiles/profile_keep_alive_types.cc b/chrome/browser/profiles/profile_keep_alive_types.cc
index bc6a765d..e9eddc9 100644
--- a/chrome/browser/profiles/profile_keep_alive_types.cc
+++ b/chrome/browser/profiles/profile_keep_alive_types.cc
@@ -43,6 +43,8 @@
       return out << "kWebAppPermissionDialogWindow";
     case ProfileKeepAliveOrigin::kSessionDataDeleter:
       return out << "kSessionDataDeleter";
+    case ProfileKeepAliveOrigin::kWebAppProtocolHandlerLaunch:
+      return out << "kWebAppProtocolHandlerLaunch";
   }
   NOTREACHED();
   return out << static_cast<int>(origin);
diff --git a/chrome/browser/profiles/profile_keep_alive_types.h b/chrome/browser/profiles/profile_keep_alive_types.h
index 250071d..bb8b5e57 100644
--- a/chrome/browser/profiles/profile_keep_alive_types.h
+++ b/chrome/browser/profiles/profile_keep_alive_types.h
@@ -77,7 +77,10 @@
   // Data for Clear on Exit is being deleted.
   kSessionDataDeleter = 16,
 
-  kMaxValue = kSessionDataDeleter,
+  // Waiting for the provider to be ready in protocol handler web app launch.
+  kWebAppProtocolHandlerLaunch = 17,
+
+  kMaxValue = kWebAppProtocolHandlerLaunch,
 };
 
 std::ostream& operator<<(std::ostream& out,
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
index 9b7df388..bc5934a 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager.js
@@ -117,7 +117,15 @@
       this.exitGroup_();
     }
 
-    this.moveToValidNode();
+    chrome.automation.getFocus(focus => {
+      // First, try to move back to the focused node.
+      if (focus) {
+        this.moveTo_(focus);
+      } else {
+        // Otherwise, move to anything that's valid based on the above history.
+        this.moveToValidNode();
+      }
+    });
   }
 
   /** @override */
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
index 7c68300a..1c1804b6 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/item_scan_manager_test.js
@@ -15,7 +15,8 @@
       await importModule(
           ['BasicNode', 'BasicRootNode'], '/switch_access/nodes/basic_node.js');
       await importModule(
-          'KeyboardNode', '/switch_access/nodes/keyboard_node.js');
+          ['KeyboardNode', 'KeyboardRootNode'],
+          '/switch_access/nodes/keyboard_node.js');
       await importModule('SACache', '/switch_access/cache.js');
       await importModule(
           'SwitchAccessMenuAction',
@@ -357,38 +358,9 @@
     function() {
       const website = `<input type="text" id="input"></input>`;
       this.runWithLoadedTree(website, async (root) => {
-        // Set a hook to watch for node changes.
-        // TODO(anastasi): this should probably be extracted to a helper class
-        // and after some time delay, print out the current state for debugging.
-        function untilFocusIs(expected) {
-          const doesMatch = (expected) => {
-            const newNode = Navigator.byItem.node_;
-            const automationNode = newNode.automationNode || {};
-            return (!expected.instance ||
-                    newNode instanceof expected.instance) &&
-                (!expected.role || expected.role === automationNode.role) &&
-                (!expected.className ||
-                 expected.className === automationNode.className);
-          };
-          return new Promise(resolve => {
-            if (doesMatch(expected)) {
-              resolve(Navigator.byItem.node_);
-              return;
-            }
-            const original = Navigator.byItem.setNode_.bind(Navigator.byItem);
-            Navigator.byItem.setNode_ = (node) => {
-              original(node);
-              if (doesMatch(expected)) {
-                Navigator.byItem.setNode_ = original;
-                resolve(Navigator.byItem.node_);
-                return;
-              }
-            };
-          });
-        }
-
         // SA initially focuses this node; wait for it first.
-        await untilFocusIs({className: 'BrowserNonClientFrameViewChromeOS'});
+        await this.untilFocusIs(
+            {className: 'BrowserNonClientFrameViewChromeOS'});
 
         // Move to the text field.
         Navigator.byItem.moveTo_(this.findNodeById('input'));
@@ -398,11 +370,11 @@
             'Current node is not input');
         input.performAction(SwitchAccessMenuAction.KEYBOARD);
 
-        const keyboard =
-            await untilFocusIs({role: chrome.automation.RoleType.KEYBOARD});
+        const keyboard = await this.untilFocusIs(
+            {role: chrome.automation.RoleType.KEYBOARD});
         keyboard.performAction('select');
 
-        const key = await untilFocusIs({instance: KeyboardNode});
+        const key = await this.untilFocusIs({instance: KeyboardNode});
 
         key.performAction('select');
 
@@ -419,3 +391,50 @@
         }
       });
     });
+
+TEST_F('SwitchAccessItemScanManagerTest', 'DismissVirtualKeyboard', function() {
+  const website = `<input type="text" id="input"></input><button>ok</button>`;
+  this.runWithLoadedTree(website, async (root) => {
+    // SA initially focuses this node; wait for it first.
+    await this.untilFocusIs({className: 'BrowserNonClientFrameViewChromeOS'});
+
+    // Move to the text field.
+    Navigator.byItem.moveTo_(this.findNodeById('input'));
+    const input = Navigator.byItem.node_;
+    assertEquals(
+        'input', input.automationNode.htmlAttributes.id,
+        'Current node is not input');
+    input.performAction(SwitchAccessMenuAction.KEYBOARD);
+
+    const keyboard =
+        await this.untilFocusIs({role: chrome.automation.RoleType.KEYBOARD});
+    keyboard.performAction('select');
+
+    // Grab the key.
+    const key = await this.untilFocusIs({instance: KeyboardNode});
+
+    // Simulate a page focusing the ok button.
+    const okButton = root.find({attributes: {name: 'ok'}});
+    okButton.focus();
+
+    // Wait for the keyboard to become invisible and the ok button to be focused
+    // by automation.
+    await new Promise(resolve => {
+      okButton.addEventListener(chrome.automation.EventType.FOCUS, resolve);
+    });
+    await new Promise(resolve => {
+      keyboard.automationNode.addEventListener(
+          chrome.automation.EventType.STATE_CHANGED, (event) => {
+            if (event.target.role === chrome.automation.RoleType.KEYBOARD &&
+                event.target.state.invisible) {
+              resolve();
+            }
+          });
+    });
+
+    // We should end up back on the focused button in SA.
+    const button =
+        await this.untilFocusIs({role: chrome.automation.RoleType.BUTTON});
+    assertEquals('ok', button.automationNode.name);
+  });
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
index 383c6f3f..66c5f5a 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
@@ -65,4 +65,53 @@
     this.listenUntil(
         predicate, Navigator.byItem.desktopNode, 'childrenChanged', callback);
   }
+
+  /**
+   * @param {!Object} expected
+   * @return {!Promise}
+   */
+  untilFocusIs(expected) {
+    const doesMatch = (expected) => {
+      const newNode = Navigator.byItem.node_;
+      const automationNode = newNode.automationNode || {};
+      return (!expected.instance || newNode instanceof expected.instance) &&
+          (!expected.role || expected.role === automationNode.role) &&
+          (!expected.className ||
+           expected.className === automationNode.className);
+    };
+
+    let didResolve = false;
+    let lastFocusChangeTime = new Date();
+    const id = setInterval(() => {
+      if (didResolve) {
+        clearInterval(id);
+        return;
+      }
+
+      if ((new Date() - lastFocusChangeTime) < 3000) {
+        return;
+      }
+
+      console.error(
+          `\nStill waiting for expectation: ${JSON.stringify(expected)}\n` +
+          `Focus is: ${Navigator.byItem.node_.debugString()}`);
+    }, 1000);
+    return new Promise(resolve => {
+      if (doesMatch(expected)) {
+        didResolve = true;
+        resolve(Navigator.byItem.node_);
+        return;
+      }
+      const original = Navigator.byItem.setNode_.bind(Navigator.byItem);
+      Navigator.byItem.setNode_ = (node) => {
+        original(node);
+        lastFocusChangeTime = new Date();
+        if (doesMatch(expected)) {
+          Navigator.byItem.setNode_ = original;
+          didResolve = true;
+          resolve(Navigator.byItem.node_);
+        }
+      };
+    });
+  }
 };
diff --git a/chrome/browser/resources/new_tab_page/customize_shortcuts.html b/chrome/browser/resources/new_tab_page/customize_shortcuts.html
index 4e3c5131..ff881ea 100644
--- a/chrome/browser/resources/new_tab_page/customize_shortcuts.html
+++ b/chrome/browser/resources/new_tab_page/customize_shortcuts.html
@@ -129,7 +129,8 @@
 
   .option-title {
     font-weight: bold;
-    margin: 8px 0;
+    margin-bottom: 4px;
+    margin-top: 12px;
   }
 
   .option-description {
diff --git a/chrome/browser/resources/read_later/side_panel/app.html b/chrome/browser/resources/read_later/side_panel/app.html
index 266f321..efcd940 100644
--- a/chrome/browser/resources/read_later/side_panel/app.html
+++ b/chrome/browser/resources/read_later/side_panel/app.html
@@ -8,31 +8,56 @@
     width: 100%;
   }
 
-  h1 {
-    font-size: 15px;
-    font-weight: 500;
-    line-height: 22px;
-    margin: 12px 16px 14px 12px;
+  header {
+    border-bottom: solid 1px var(--google-grey-refresh-300);
+    display: flex;
+    padding-block-start: 4px;
+  }
+
+  @media (prefers-color-scheme: dark) {
+    header {
+      border-bottom: solid 1px var(--google-grey-refresh-700);
+    }
   }
 
   cr-tabs {
     --cr-tabs-height: 40px;
     --cr-tabs-font-size: 13px;
+    flex: 1;
+  }
+
+  #closeButtonContainer {
+    align-items: center;
+    display: flex;
+    height: 40px;
+    justify-content: center;
+    width: 48px;
+  }
+
+  #closeButton {
+    --cr-icon-button-size: 28px;
+    --cr-icon-button-icon-size: 16px;
+    margin: 0;
   }
 
   #content {
     flex: 1;
     overflow: auto;
+    padding: 8px 0;
   }
 </style>
 
 <header>
-  <h1>Lists</h1>
   <cr-tabs
       tab-names="[[getTabNames_(tabs_)]]"
       selected="{{selectedTab_}}"
       on-selected-changed="onSelectedTabChanged_">
   </cr-tabs>
+  <div id="closeButtonContainer">
+    <cr-icon-button
+        id="closeButton" iron-icon="cr:close" on-click="onCloseClick_">
+    </cr-icon-button>
+  </div>
 </header>
 
 <div id="content">
diff --git a/chrome/browser/resources/read_later/side_panel/app.js b/chrome/browser/resources/read_later/side_panel/app.js
index 2daff34..7a537f5 100644
--- a/chrome/browser/resources/read_later/side_panel/app.js
+++ b/chrome/browser/resources/read_later/side_panel/app.js
@@ -15,6 +15,7 @@
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {ReadLaterApiProxy, ReadLaterApiProxyImpl} from '../read_later_api_proxy.js';
 
 /**
  * Key for localStorage object that refers to the last active tab's ID.
@@ -50,6 +51,16 @@
     };
   }
 
+  constructor() {
+    super();
+
+    /**
+     * The side panel is currently hosted within Read Later UI.
+     * @const @private {!ReadLaterApiProxy}
+     */
+    this.apiProxy_ = ReadLaterApiProxyImpl.getInstance();
+  }
+
   connectedCallback() {
     super.connectedCallback();
     const lastActiveTab = window.localStorage[LOCAL_STORAGE_TAB_ID_KEY];
@@ -66,6 +77,11 @@
     return Object.values(this.tabs_);
   }
 
+  /** @private */
+  onCloseClick_() {
+    this.apiProxy_.closeUI();
+  }
+
   /**
    * @param {!Event} event
    * @private
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
index 8ad4852..f339920 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
@@ -105,6 +105,7 @@
         hideMoreSettings: false,
         hidePinToShelf: false,
         isPreferredApp: false,
+        windowMode: apps.mojom.WindowMode.kWindow,
       };
 
       if (optConfig) {
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.js b/chrome/browser/resources/settings/site_settings/all_sites.js
index d6aa6c9..98b9cf6 100644
--- a/chrome/browser/resources/settings/site_settings/all_sites.js
+++ b/chrome/browser/resources/settings/site_settings/all_sites.js
@@ -221,15 +221,7 @@
    * @private
    */
   populateList_() {
-    /** @type {!Array<ContentSettingsTypes>} */
-    const contentTypes = this.getCategoryList();
-    // Make sure to include cookies, because All Sites handles data storage +
-    // cookies as well as regular ContentSettingsTypes.
-    if (!contentTypes.includes(ContentSettingsTypes.COOKIES)) {
-      contentTypes.push(ContentSettingsTypes.COOKIES);
-    }
-
-    this.browserProxy.getAllSites(contentTypes).then((response) => {
+    this.browserProxy.getAllSites().then((response) => {
       // Create a new map to make an observable change.
       const newMap = /** @type {!Map<string, !SiteGroup>} */
           (new Map(this.siteGroupMap));
@@ -549,7 +541,7 @@
           this.originRepresentation(
               this.actionMenuModel_.item.origins[0].origin);
       return loadTimeData.substituteString(this.i18n(messageId), displayName);
-      }
+    }
   },
 
   /**
@@ -613,14 +605,13 @@
   },
 
   /**
-   * Resets permission settings for a single origin.
+   * Resets all permission settings for a single origin.
    * @param {string} origin
    * @private
    */
   resetPermissionsForOrigin_: function(origin) {
-    const contentSettingsTypes = this.getCategoryList();
     this.browserProxy.setOriginPermissions(
-        origin, contentSettingsTypes, ContentSetting.DEFAULT);
+        origin, null, ContentSetting.DEFAULT);
   },
 
   /**
diff --git a/chrome/browser/resources/settings/site_settings/site_details.js b/chrome/browser/resources/settings/site_settings/site_details.js
index 7bd17b4..7874fba0 100644
--- a/chrome/browser/resources/settings/site_settings/site_details.js
+++ b/chrome/browser/resources/settings/site_settings/site_details.js
@@ -150,7 +150,9 @@
         this.fetchingForHost_ = this.toUrl(this.origin_).hostname;
         this.storedData_ = '';
         this.websiteUsageProxy_.fetchUsageTotal(this.fetchingForHost_);
-        this.updatePermissions_(this.getCategoryList());
+        this.browserProxy.getCategoryList(this.origin_).then((categoryList) => {
+          this.updatePermissions_(categoryList, /*hideOthers=*/ true);
+        });
       }
     });
   },
@@ -169,13 +171,12 @@
         origin === undefined || origin === '') {
       return;
     }
-    if (!this.getCategoryList().includes(category)) {
-      return;
-    }
 
-    // Site details currently doesn't support embedded origins, so ignore it
-    // and just check whether the origins are the same.
-    this.updatePermissions_([category]);
+    this.browserProxy.getCategoryList(this.origin_).then((categoryList) => {
+      if (categoryList.includes(category)) {
+        this.updatePermissions_([category], /*hideOthers=*/ false);
+      }
+    });
   },
 
   /**
@@ -199,9 +200,11 @@
    * |this.origin_|.
    * @param {!Array<!ContentSettingsTypes>} categoryList The list
    *     of categories to update permissions for.
+   * @param {boolean} hideOthers If true, permissions for categories not in
+   *     |categoryList| will be hidden.
    * @private
    */
-  updatePermissions_(categoryList) {
+  updatePermissions_(categoryList, hideOthers) {
     const permissionsMap =
         /**
          * @type {!Object<!ContentSettingsTypes,
@@ -212,6 +215,9 @@
             (map, element) => {
               if (categoryList.includes(element.category)) {
                 map[element.category] = element;
+              } else if (hideOthers) {
+                // This will hide any permission not in the category list.
+                element.site = null;
               }
               return map;
             },
@@ -266,7 +272,7 @@
    */
   onResetSettings_(e) {
     this.browserProxy.setOriginPermissions(
-        this.origin_, this.getCategoryList(), ContentSetting.DEFAULT);
+        this.origin_, null, ContentSetting.DEFAULT);
 
     this.onCloseDialog_(e);
   },
diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.html b/chrome/browser/resources/settings/site_settings/site_details_permission.html
index 3567e054..26f2632 100644
--- a/chrome/browser/resources/settings/site_settings/site_details_permission.html
+++ b/chrome/browser/resources/settings/site_settings/site_details_permission.html
@@ -1,5 +1,5 @@
     <style include="settings-shared md-select"></style>
-    <div id="details" hidden$="[[shouldHideCategory_(category)]]">
+    <div id="details" hidden$="[[shouldHideCategory_(site)]]">
       <div id="permissionItem"
           class$="list-item [[permissionInfoStringClass_(site.source,
                                                          category,
diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.js b/chrome/browser/resources/settings/site_settings/site_details_permission.js
index c29094a..b81cf93 100644
--- a/chrome/browser/resources/settings/site_settings/site_details_permission.js
+++ b/chrome/browser/resources/settings/site_settings/site_details_permission.js
@@ -41,7 +41,8 @@
     useAutomaticLabel: {type: Boolean, value: false},
 
     /**
-     * The site that this widget is showing details for.
+     * The site that this widget is showing details for, or null if this widget
+     * should be hidden.
      * @type {RawSiteException}
      */
     site: Object,
@@ -67,16 +68,17 @@
         this.onDefaultSettingChanged_.bind(this));
   },
 
-  shouldHideCategory_(category) {
-    return !this.getCategoryList().includes(category);
-  },
-
   /**
-   * Updates the drop-down value after |site| has changed.
-   * @param {!RawSiteException} site The site to display.
+   * Updates the drop-down value after |site| has changed. If |site| is null,
+   * this element will hide.
+   * @param {?RawSiteException} site The site to display.
    * @private
    */
   siteChanged_(site) {
+    if (!site) {
+      return;
+    }
+
     if (site.source === SiteSettingSource.DEFAULT) {
       this.defaultSetting_ = site.setting;
       this.$.permission.value = ContentSetting.DEFAULT;
@@ -123,7 +125,7 @@
    */
   onPermissionSelectionChange_() {
     this.browserProxy.setOriginPermissions(
-        this.site.origin, [this.category], this.$.permission.value);
+        this.site.origin, this.category, this.$.permission.value);
   },
 
   /**
@@ -186,6 +188,14 @@
   },
 
   /**
+   * Returns true if |this| should be hidden.
+   * @private
+   */
+  shouldHideCategory_() {
+    return !this.site;
+  },
+
+  /**
    * Returns true if there's a string to display that provides more information
    * about this permission's setting. Currently, this only gets called when
    * |this.site| is updated.
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_behavior.js b/chrome/browser/resources/settings/site_settings/site_settings_behavior.js
index bfc14bf..6b787bb1 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_behavior.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_behavior.js
@@ -178,59 +178,6 @@
     };
   },
 
-  /**
-   * Returns list of categories for each setting.ContentSettingsTypes that are
-   * currently enabled.
-   * @return {!Array<!ContentSettingsTypes>}
-   */
-  getCategoryList() {
-    if (this.contentTypes_.length === 0) {
-      for (const typeName in ContentSettingsTypes) {
-        const contentType = ContentSettingsTypes[typeName];
-        // <if expr="not chromeos and not is_win">
-        if (contentType === ContentSettingsTypes.PROTECTED_CONTENT) {
-          continue;
-        }
-        // </if>
-        // Some categories store their data in a custom way.
-        if (contentType === ContentSettingsTypes.COOKIES ||
-            contentType === ContentSettingsTypes.PROTOCOL_HANDLERS ||
-            contentType === ContentSettingsTypes.ZOOM_LEVELS) {
-          continue;
-        }
-        this.contentTypes_.push(contentType);
-      }
-    }
-
-    const addOrRemoveSettingWithFlag = (type, flag) => {
-      if (loadTimeData.getBoolean(flag)) {
-        if (!this.contentTypes_.includes(type)) {
-          this.contentTypes_.push(type);
-        }
-      } else {
-        if (this.contentTypes_.includes(type)) {
-          this.contentTypes_.splice(this.contentTypes_.indexOf(type), 1);
-        }
-      }
-    };
-    // These categories are gated behind flags.
-    addOrRemoveSettingWithFlag(
-        ContentSettingsTypes.BLUETOOTH_SCANNING,
-        'enableExperimentalWebPlatformFeatures');
-    addOrRemoveSettingWithFlag(
-        ContentSettingsTypes.ADS, 'enableSafeBrowsingSubresourceFilter');
-    addOrRemoveSettingWithFlag(
-        ContentSettingsTypes.PAYMENT_HANDLER,
-        'enablePaymentHandlerContentSetting');
-    addOrRemoveSettingWithFlag(
-        ContentSettingsTypes.BLUETOOTH_DEVICES,
-        'enableWebBluetoothNewPermissionsBackend');
-    addOrRemoveSettingWithFlag(
-        ContentSettingsTypes.WINDOW_PLACEMENT,
-        'enableExperimentalWebPlatformFeatures');
-    return this.contentTypes_.slice(0);
-  },
-
 };
 
 /** @polymerBehavior */
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
index 0d1354e..afbcf6b9 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -170,13 +170,20 @@
   getDefaultValueForContentType(contentType) {}
 
   /**
-   * Gets a list of sites, grouped by eTLD+1, affected by any of the content
-   * settings specified by |contentTypes|.
-   * @param {!Array<!ContentSettingsTypes>} contentTypes A list of
-   *     the content types to retrieve sites for.
+   * Gets a list of sites, grouped by eTLD+1, affected by any content settings
+   * that should be visible to the user.
    * @return {!Promise<!Array<!SiteGroup>>}
    */
-  getAllSites(contentTypes) {}
+  getAllSites() {}
+
+  /**
+   * Returns a list of content settings types that are controlled via a standard
+   * permissions UI and should be made visible to the user.
+   * @param {!string} origin The associated origin for which categories should
+   *     be shown or hidden.
+   * @return {!Promise<!Array<string>>}
+   */
+  getCategoryList(origin) {}
 
   /**
    * Get the string which describes the current effective cookie setting.
@@ -189,12 +196,10 @@
    * numSources different origin/profile (inconigto/regular) pairings.
    * This includes permissions adjusted by embargo, but excludes any set
    * via policy.
-   * @param {!Array<!ContentSettingsTypes>} contentTypes A list of
-   *     the content types to retrieve sites with recently changed settings.
    * @param {!number} numSources Maximum number of different sources to return
    * @return {!Promise<!Array<!RecentSitePermissions>>}
    */
-  getRecentSitePermissions(contentTypes, numSources) {}
+  getRecentSitePermissions(numSources) {}
 
   /**
    * Gets the chooser exceptions for a particular chooser type.
@@ -235,14 +240,13 @@
    * Resets the permissions for a list of categories for a given origin. This
    * does not support incognito settings or patterns.
    * @param {string} origin The origin to reset permissions for.
-   * @param {!Array<!ContentSettingsTypes>} contentTypes A list of
-   *     categories to set the permission for. Typically this would be a
-   *     single category, but sometimes it is useful to clear any permissions
-   *     set for all categories.
+   * @param {?ContentSettingsTypes} category The category to set the permission
+   *     for. If null, this applies to all categories. (Sometimes it is useful
+   *     to clear any permissions set for all categories.)
    * @param {!ContentSetting} blanketSetting The setting to set all
    *     permissions listed in |contentTypes| to.
    */
-  setOriginPermissions(origin, contentTypes, blanketSetting) {}
+  setOriginPermissions(origin, category, blanketSetting) {}
 
   /**
    * Resets the category permission for a given origin (expressed as primary
@@ -413,8 +417,13 @@
   }
 
   /** @override */
-  getAllSites(contentTypes) {
-    return sendWithPromise('getAllSites', contentTypes);
+  getAllSites() {
+    return sendWithPromise('getAllSites');
+  }
+
+  /** @override */
+  getCategoryList(origin) {
+    return sendWithPromise('getCategoryList', origin);
   }
 
   /** @override */
@@ -423,9 +432,8 @@
   }
 
   /** @override */
-  getRecentSitePermissions(contentTypes, numSources) {
-    return sendWithPromise(
-        'getRecentSitePermissions', contentTypes, numSources);
+  getRecentSitePermissions(numSources) {
+    return sendWithPromise('getRecentSitePermissions', numSources);
   }
 
   /** @override */
diff --git a/chrome/browser/resources/settings/site_settings_page/recent_site_permissions.js b/chrome/browser/resources/settings/site_settings_page/recent_site_permissions.js
index 52db429..dc52ba48 100644
--- a/chrome/browser/resources/settings/site_settings_page/recent_site_permissions.js
+++ b/chrome/browser/resources/settings/site_settings_page/recent_site_permissions.js
@@ -396,8 +396,7 @@
    */
   async populateList_() {
     this.recentSitePermissionsList_ =
-        await this.browserProxy.getRecentSitePermissions(
-            this.getCategoryList(), 3);
+        await this.browserProxy.getRecentSitePermissions(3);
   },
 
   /**
diff --git a/chrome/browser/resources/welcome/BUILD.gn b/chrome/browser/resources/welcome/BUILD.gn
index 7a04bed..6b365d4 100644
--- a/chrome/browser/resources/welcome/BUILD.gn
+++ b/chrome/browser/resources/welcome/BUILD.gn
@@ -176,6 +176,7 @@
   ]
   definitions = [
     "//tools/typescript/definitions/bookmarks.d.ts",
+    "//tools/typescript/definitions/chrome_event.d.ts",
     "//tools/typescript/definitions/chrome_send.d.ts",
     "//tools/typescript/definitions/metrics_private.d.ts",
   ]
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index ddf304b..b6b3584 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -81,6 +81,7 @@
     }
     deps += [
       "//chrome/browser/profiles:profile",
+      "//components/paint_preview/buildflags",
       "//extensions/browser",
     ]
   }
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 57145ff..3523053 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -217,11 +217,10 @@
     most_visited_sites_->SetMostVisitedURLsObserver(
         this, ntp_tiles::kMaxNumMostVisited);
     most_visited_sites_->EnableCustomLinks(IsCustomLinksEnabled());
-  }
 
-  most_visited_info_->use_most_visited = !IsCustomLinksEnabled();
-  most_visited_info_->is_visible =
-      pref_service_->GetBoolean(prefs::kNtpShortcutsVisible);
+    most_visited_info_->use_most_visited = !IsCustomLinksEnabled();
+    most_visited_info_->is_visible = most_visited_sites_->IsShortcutsVisible();
+  }
 
   background_service_ = NtpBackgroundServiceFactory::GetForProfile(profile_);
 
@@ -354,9 +353,7 @@
       !search_provider_observer_->is_google()) {
     return false;
   }
-  bool use_most_visited =
-      !pref_service_->GetBoolean(prefs::kNtpUseMostVisitedTiles);
-  pref_service_->SetBoolean(prefs::kNtpUseMostVisitedTiles, use_most_visited);
+  bool use_most_visited = most_visited_sites_->IsCustomLinksEnabled();
   most_visited_info_->use_most_visited = use_most_visited;
   bool was_initialized = most_visited_sites_->IsCustomLinksInitialized();
 
@@ -393,8 +390,8 @@
       !search_provider_observer_->is_google()) {
     return false;
   }
-  bool is_visible = !pref_service_->GetBoolean(prefs::kNtpShortcutsVisible);
-  pref_service_->SetBoolean(prefs::kNtpShortcutsVisible, is_visible);
+  bool is_visible = !most_visited_sites_->IsShortcutsVisible();
+  most_visited_sites_->SetShortcutsVisible(is_visible);
   most_visited_info_->is_visible = is_visible;
 
   if (do_notify) {
@@ -623,7 +620,7 @@
 
 bool InstantService::IsCustomLinksEnabled() {
   return search_provider_observer_ && search_provider_observer_->is_google() &&
-         !pref_service_->GetBoolean(prefs::kNtpUseMostVisitedTiles);
+         most_visited_sites_->IsCustomLinksEnabled();
 }
 
 void InstantService::BuildNtpTheme() {
@@ -872,17 +869,15 @@
 }
 
 std::pair<bool, bool> InstantService::GetCurrentShortcutSettings() {
-  bool using_most_visited =
-      pref_service_->GetBoolean(prefs::kNtpUseMostVisitedTiles);
-  bool is_visible = pref_service_->GetBoolean(prefs::kNtpShortcutsVisible);
+  bool using_most_visited = !most_visited_sites_->IsCustomLinksEnabled();
+  bool is_visible = most_visited_sites_->IsShortcutsVisible();
   return std::make_pair(using_most_visited, is_visible);
 }
 
 void InstantService::ResetToDefault() {
   ResetCustomLinks();
   ResetCustomBackgroundNtpTheme();
-  pref_service_->SetBoolean(prefs::kNtpUseMostVisitedTiles, false);
-  pref_service_->SetBoolean(prefs::kNtpShortcutsVisible, true);
+  ntp_tiles::MostVisitedSites::ResetProfilePrefs(pref_service_);
 }
 
 void InstantService::UpdateCustomBackgroundColorAsync(
@@ -978,8 +973,6 @@
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterBooleanPref(prefs::kNtpCustomBackgroundLocalToDevice,
                                 false);
-  registry->RegisterBooleanPref(prefs::kNtpUseMostVisitedTiles, false);
-  registry->RegisterBooleanPref(prefs::kNtpShortcutsVisible, true);
 }
 
 // static
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index 452b959..8d2c8b9 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -112,26 +112,27 @@
 }
 
 TEST_F(InstantServiceTest, DoesToggleMostVisitedOrCustomLinks) {
-  sync_preferences::TestingPrefServiceSyncable* pref_service =
-      profile()->GetTestingPrefService();
   SetUserSelectedDefaultSearchProvider("{google:baseURL}");
-  ASSERT_FALSE(pref_service->GetBoolean(prefs::kNtpUseMostVisitedTiles));
+  ASSERT_FALSE(!most_visited_sites()->IsCustomLinksEnabled());
   ASSERT_FALSE(instant_service_->most_visited_info_->use_most_visited);
 
   // Enable most visited tiles.
   EXPECT_TRUE(instant_service_->ToggleMostVisitedOrCustomLinks());
-  EXPECT_TRUE(pref_service->GetBoolean(prefs::kNtpUseMostVisitedTiles));
+  EXPECT_TRUE(!most_visited_sites()->IsCustomLinksEnabled());
   EXPECT_TRUE(instant_service_->most_visited_info_->use_most_visited);
 
   // Disable most visited tiles.
   EXPECT_TRUE(instant_service_->ToggleMostVisitedOrCustomLinks());
-  EXPECT_FALSE(pref_service->GetBoolean(prefs::kNtpUseMostVisitedTiles));
+  EXPECT_FALSE(!most_visited_sites()->IsCustomLinksEnabled());
   EXPECT_FALSE(instant_service_->most_visited_info_->use_most_visited);
 
-  // Should do nothing if this is a non-Google NTP.
   SetUserSelectedDefaultSearchProvider("https://www.search.com");
+  EXPECT_FALSE(most_visited_sites()->IsCustomLinksEnabled());
+  ASSERT_FALSE(instant_service_->most_visited_info_->use_most_visited);
+
+  // Should do nothing if this is a non-Google NTP.
   EXPECT_FALSE(instant_service_->ToggleMostVisitedOrCustomLinks());
-  EXPECT_FALSE(pref_service->GetBoolean(prefs::kNtpUseMostVisitedTiles));
+  EXPECT_FALSE(most_visited_sites()->IsCustomLinksEnabled());
   EXPECT_FALSE(instant_service_->most_visited_info_->use_most_visited);
 }
 
@@ -139,30 +140,28 @@
   testing::StrictMock<MockInstantServiceObserver> mock_observer;
   instant_service_->AddObserver(&mock_observer);
 
-  sync_preferences::TestingPrefServiceSyncable* pref_service =
-      profile()->GetTestingPrefService();
   SetUserSelectedDefaultSearchProvider("{google:baseURL}");
-  ASSERT_TRUE(pref_service->GetBoolean(prefs::kNtpShortcutsVisible));
+  ASSERT_TRUE(most_visited_sites()->IsShortcutsVisible());
   ASSERT_TRUE(instant_service_->most_visited_info_->is_visible);
 
   // Hide shortcuts.
   EXPECT_CALL(mock_observer, MostVisitedInfoChanged(testing::_)).Times(0);
   EXPECT_TRUE(instant_service_->ToggleShortcutsVisibility(false));
-  EXPECT_FALSE(pref_service->GetBoolean(prefs::kNtpShortcutsVisible));
+  EXPECT_FALSE(most_visited_sites()->IsShortcutsVisible());
   EXPECT_FALSE(instant_service_->most_visited_info_->is_visible);
   task_environment()->RunUntilIdle();
 
   // Show shortcuts, and check that a notification was sent.
   EXPECT_CALL(mock_observer, MostVisitedInfoChanged(testing::_)).Times(1);
   EXPECT_TRUE(instant_service_->ToggleShortcutsVisibility(true));
-  EXPECT_TRUE(pref_service->GetBoolean(prefs::kNtpShortcutsVisible));
+  EXPECT_TRUE(most_visited_sites()->IsShortcutsVisible());
   EXPECT_TRUE(instant_service_->most_visited_info_->is_visible);
 
   // Should do nothing if this is a non-Google NTP.
   EXPECT_CALL(mock_observer, MostVisitedInfoChanged(testing::_)).Times(0);
   SetUserSelectedDefaultSearchProvider("https://www.search.com");
   EXPECT_FALSE(instant_service_->ToggleShortcutsVisibility(false));
-  EXPECT_TRUE(pref_service->GetBoolean(prefs::kNtpShortcutsVisible));
+  EXPECT_TRUE(most_visited_sites()->IsShortcutsVisible());
   EXPECT_TRUE(instant_service_->most_visited_info_->is_visible);
 }
 
@@ -184,19 +183,16 @@
 }
 
 TEST_F(InstantServiceTest, IsCustomLinksEnabled) {
-  sync_preferences::TestingPrefServiceSyncable* pref_service =
-      profile()->GetTestingPrefService();
-
   // Test that custom links are only enabled when Most Visited is toggled off
   // and this is a Google NTP.
-  pref_service->SetBoolean(prefs::kNtpUseMostVisitedTiles, false);
+  most_visited_sites()->EnableCustomLinks(true);
   SetUserSelectedDefaultSearchProvider("{google:baseURL}");
   EXPECT_TRUE(instant_service_->IsCustomLinksEnabled());
 
   // All other cases should return false.
   SetUserSelectedDefaultSearchProvider("https://www.search.com");
   EXPECT_FALSE(instant_service_->IsCustomLinksEnabled());
-  pref_service->SetBoolean(prefs::kNtpUseMostVisitedTiles, true);
+  most_visited_sites()->EnableCustomLinks(false);
   EXPECT_FALSE(instant_service_->IsCustomLinksEnabled());
   SetUserSelectedDefaultSearchProvider("{google:baseURL}");
   EXPECT_FALSE(instant_service_->IsCustomLinksEnabled());
diff --git a/chrome/browser/search/instant_unittest_base.cc b/chrome/browser/search/instant_unittest_base.cc
index 18685d2..327ec82 100644
--- a/chrome/browser/search/instant_unittest_base.cc
+++ b/chrome/browser/search/instant_unittest_base.cc
@@ -51,6 +51,10 @@
   BrowserWithTestWindowTest::TearDown();
 }
 
+ntp_tiles::MostVisitedSites* InstantUnitTestBase::most_visited_sites() {
+  return instant_service_->most_visited_sites_.get();
+}
+
 void InstantUnitTestBase::SetUserSelectedDefaultSearchProvider(
     const std::string& base_url) {
   TemplateURLData data;
diff --git a/chrome/browser/search/instant_unittest_base.h b/chrome/browser/search/instant_unittest_base.h
index b1644fe..048da34c 100644
--- a/chrome/browser/search/instant_unittest_base.h
+++ b/chrome/browser/search/instant_unittest_base.h
@@ -11,6 +11,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
+#include "components/ntp_tiles/most_visited_sites.h"
 #include "components/search_engines/template_url_service.h"
 
 // This class provides an extension on top of BrowserWithTestWindowTest, and
@@ -24,6 +25,8 @@
   void SetUp() override;
   void TearDown() override;
 
+  ntp_tiles::MostVisitedSites* most_visited_sites();
+
   // Adds and sets the default search provider using the base_url.
   // The base_url should have the http[s]:// prefix and a trailing / after the
   // TLD.
diff --git a/chrome/browser/serial/serial_chooser_context.cc b/chrome/browser/serial/serial_chooser_context.cc
index 2f7c5f5..a3109c3 100644
--- a/chrome/browser/serial/serial_chooser_context.cc
+++ b/chrome/browser/serial/serial_chooser_context.cc
@@ -13,10 +13,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/serial/serial_blocklist.h"
 #include "chrome/browser/serial/serial_chooser_histograms.h"
+#include "chrome/browser/serial/serial_policy_allowed_ports.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/device_service.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -148,8 +150,7 @@
           ContentSettingsType::SERIAL_GUARD,
           ContentSettingsType::SERIAL_CHOOSER_DATA,
           HostContentSettingsMapFactory::GetForProfile(profile)),
-      is_incognito_(profile->IsOffTheRecord()),
-      policy_(profile->GetPrefs()) {}
+      is_incognito_(profile->IsOffTheRecord()) {}
 
 SerialChooserContext::~SerialChooserContext() = default;
 
@@ -206,7 +207,8 @@
     }
   }
 
-  for (const auto& entry : policy_.usb_device_policy()) {
+  auto* policy = g_browser_process->serial_policy_allowed_ports();
+  for (const auto& entry : policy->usb_device_policy()) {
     if (!base::Contains(entry.second, origin)) {
       continue;
     }
@@ -218,7 +220,7 @@
         is_incognito_));
   }
 
-  for (const auto& entry : policy_.usb_vendor_policy()) {
+  for (const auto& entry : policy->usb_vendor_policy()) {
     if (!base::Contains(entry.second, origin)) {
       continue;
     }
@@ -229,7 +231,7 @@
         is_incognito_));
   }
 
-  if (base::Contains(policy_.all_ports_policy(), origin)) {
+  if (base::Contains(policy->all_ports_policy(), origin)) {
     base::Value object(base::Value::Type::DICTIONARY);
     object.SetStringKey(
         kPortNameKey,
@@ -263,7 +265,8 @@
     }
   }
 
-  for (const auto& entry : policy_.usb_device_policy()) {
+  auto* policy = g_browser_process->serial_policy_allowed_ports();
+  for (const auto& entry : policy->usb_device_policy()) {
     base::Value object =
         VendorAndProductIdsToValue(entry.first.first, entry.first.second);
 
@@ -274,7 +277,7 @@
     }
   }
 
-  for (const auto& entry : policy_.usb_vendor_policy()) {
+  for (const auto& entry : policy->usb_vendor_policy()) {
     base::Value object = VendorIdToValue(entry.first);
 
     for (const auto& origin : entry.second) {
@@ -288,7 +291,7 @@
   object.SetStringKey(
       kPortNameKey,
       l10n_util::GetStringUTF16(IDS_SERIAL_POLICY_DESCRIPTION_FOR_ANY_PORT));
-  for (const auto& origin : policy_.all_ports_policy()) {
+  for (const auto& origin : policy->all_ports_policy()) {
     objects.push_back(std::make_unique<ObjectPermissionContextBase::Object>(
         origin, object.Clone(), content_settings::SETTING_SOURCE_POLICY,
         is_incognito_));
@@ -339,7 +342,8 @@
     return false;
   }
 
-  if (policy_.HasPortPermission(origin, port)) {
+  if (g_browser_process->serial_policy_allowed_ports()->HasPortPermission(
+          origin, port)) {
     return true;
   }
 
diff --git a/chrome/browser/serial/serial_chooser_context.h b/chrome/browser/serial/serial_chooser_context.h
index 4db8839..93d1b50f 100644
--- a/chrome/browser/serial/serial_chooser_context.h
+++ b/chrome/browser/serial/serial_chooser_context.h
@@ -14,7 +14,6 @@
 
 #include "base/memory/weak_ptr.h"
 #include "base/unguessable_token.h"
-#include "chrome/browser/serial/serial_policy_allowed_ports.h"
 #include "components/permissions/object_permission_context_base.h"
 #include "content/public/browser/serial_delegate.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -83,8 +82,6 @@
 
   const bool is_incognito_;
 
-  SerialPolicyAllowedPorts policy_;
-
   // Tracks the set of ports to which an origin has access to.
   std::map<url::Origin, std::set<base::UnguessableToken>> ephemeral_ports_;
 
diff --git a/chrome/browser/serial/serial_chooser_context_unittest.cc b/chrome/browser/serial/serial_chooser_context_unittest.cc
index 18a8bd9..419b24e 100644
--- a/chrome/browser/serial/serial_chooser_context_unittest.cc
+++ b/chrome/browser/serial/serial_chooser_context_unittest.cc
@@ -15,6 +15,8 @@
 #include "chrome/browser/serial/serial_chooser_context_factory.h"
 #include "chrome/browser/serial/serial_chooser_histograms.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/pref_names.h"
@@ -71,7 +73,15 @@
 
 class SerialChooserContextTest : public testing::Test {
  public:
-  SerialChooserContextTest() {
+  SerialChooserContextTest()
+      : testing_local_state_(TestingBrowserProcess::GetGlobal()) {}
+  ~SerialChooserContextTest() override = default;
+
+  // Disallow copy and assignment.
+  SerialChooserContextTest(SerialChooserContextTest&) = delete;
+  SerialChooserContextTest& operator=(SerialChooserContextTest&) = delete;
+
+  void SetUp() override {
     mojo::PendingRemote<device::mojom::SerialPortManager> port_manager;
     port_manager_.AddReceiver(port_manager.InitWithNewPipeAndPassReceiver());
 
@@ -84,12 +94,6 @@
     context_->FlushPortManagerConnectionForTesting();
   }
 
-  ~SerialChooserContextTest() override = default;
-
-  // Disallow copy and assignment.
-  SerialChooserContextTest(SerialChooserContextTest&) = delete;
-  SerialChooserContextTest& operator=(SerialChooserContextTest&) = delete;
-
   void TearDown() override {
     // Because SerialBlocklist is a singleton it must be cleared after tests run
     // to prevent leakage between tests.
@@ -110,6 +114,7 @@
 
   device::FakeSerialPortManager& port_manager() { return port_manager_; }
   TestingProfile* profile() { return &profile_; }
+  TestingPrefServiceSimple* local_state() { return testing_local_state_.Get(); }
   SerialChooserContext* context() { return context_; }
   permissions::MockPermissionObserver& permission_observer() {
     return permission_observer_;
@@ -120,6 +125,7 @@
   content::BrowserTaskEnvironment task_environment_;
   base::test::ScopedFeatureList feature_list_;
   device::FakeSerialPortManager port_manager_;
+  ScopedTestingLocalState testing_local_state_;
   TestingProfile profile_;
   SerialChooserContext* context_;
   permissions::MockPermissionObserver permission_observer_;
@@ -388,9 +394,10 @@
   port->token = base::UnguessableToken::Create();
   context()->GrantPortPermission(origin, *port);
 
-  auto* prefs = profile()->GetTestingPrefService();
-  prefs->SetManagedPref(prefs::kManagedDefaultSerialGuardSetting,
-                        std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
+  auto* profile_prefs = profile()->GetTestingPrefService();
+  profile_prefs->SetManagedPref(
+      prefs::kManagedDefaultSerialGuardSetting,
+      std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
   EXPECT_FALSE(context()->CanRequestObjectPermission(origin));
   EXPECT_FALSE(context()->HasPortPermission(origin, *port));
 
@@ -413,11 +420,12 @@
   context()->GrantPortPermission(kBarOrigin, *port);
 
   // Set the default to "ask" so that the policy being tested overrides it.
-  auto* prefs = profile()->GetTestingPrefService();
-  prefs->SetManagedPref(prefs::kManagedDefaultSerialGuardSetting,
-                        std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
-  prefs->SetManagedPref(prefs::kManagedSerialAskForUrls,
-                        ReadJson(R"([ "https://foo.origin" ])"));
+  auto* profile_prefs = profile()->GetTestingPrefService();
+  profile_prefs->SetManagedPref(
+      prefs::kManagedDefaultSerialGuardSetting,
+      std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
+  profile_prefs->SetManagedPref(prefs::kManagedSerialAskForUrls,
+                                ReadJson(R"([ "https://foo.origin" ])"));
 
   EXPECT_TRUE(context()->CanRequestObjectPermission(kFooOrigin));
   EXPECT_TRUE(context()->HasPortPermission(kFooOrigin, *port));
@@ -444,9 +452,9 @@
   context()->GrantPortPermission(kFooOrigin, *port);
   context()->GrantPortPermission(kBarOrigin, *port);
 
-  auto* prefs = profile()->GetTestingPrefService();
-  prefs->SetManagedPref(prefs::kManagedSerialBlockedForUrls,
-                        ReadJson(R"([ "https://foo.origin" ])"));
+  auto* profile_prefs = profile()->GetTestingPrefService();
+  profile_prefs->SetManagedPref(prefs::kManagedSerialBlockedForUrls,
+                                ReadJson(R"([ "https://foo.origin" ])"));
 
   EXPECT_FALSE(context()->CanRequestObjectPermission(kFooOrigin));
   EXPECT_FALSE(context()->HasPortPermission(kFooOrigin, *port));
@@ -468,11 +476,10 @@
   const auto kFooOrigin = url::Origin::Create(GURL("https://foo.origin"));
   const auto kBarOrigin = url::Origin::Create(GURL("https://bar.origin"));
 
-  auto* prefs = profile()->GetTestingPrefService();
-  prefs->SetManagedPref(prefs::kManagedSerialAllowAllPortsForUrls,
-                        ReadJson(R"([ "https://foo.origin" ])"));
-  prefs->SetManagedPref(prefs::kManagedSerialAllowUsbDevicesForUrls,
-                        ReadJson(R"([
+  local_state()->SetManagedPref(prefs::kManagedSerialAllowAllPortsForUrls,
+                                ReadJson(R"([ "https://foo.origin" ])"));
+  local_state()->SetManagedPref(prefs::kManagedSerialAllowUsbDevicesForUrls,
+                                ReadJson(R"([
                {
                  "devices": [{ "vendor_id": 6353, "product_id": 19985 }],
                  "urls": [ "https://bar.origin" ]
@@ -507,7 +514,7 @@
   EXPECT_FALSE(context()->HasPortPermission(kBarOrigin, *usb_port2));
 
   auto foo_objects = context()->GetGrantedObjects(kFooOrigin);
-  EXPECT_EQ(1u, foo_objects.size());
+  ASSERT_EQ(1u, foo_objects.size());
   const auto& foo_object = foo_objects.front();
   EXPECT_EQ(kFooOrigin.GetURL(), foo_object->origin);
   EXPECT_EQ(u"Any serial port",
@@ -516,7 +523,7 @@
   EXPECT_FALSE(foo_object->incognito);
 
   auto bar_objects = context()->GetGrantedObjects(kBarOrigin);
-  EXPECT_EQ(1u, bar_objects.size());
+  ASSERT_EQ(1u, bar_objects.size());
   const auto& bar_object = bar_objects.front();
   EXPECT_EQ(kBarOrigin.GetURL(), bar_object->origin);
   EXPECT_EQ(u"Nexus One", context()->GetObjectDisplayName(bar_object->value));
@@ -548,9 +555,8 @@
   const auto kFooOrigin = url::Origin::Create(GURL("https://foo.origin"));
   const auto kBarOrigin = url::Origin::Create(GURL("https://bar.origin"));
 
-  auto* prefs = profile()->GetTestingPrefService();
-  prefs->SetManagedPref(prefs::kManagedSerialAllowUsbDevicesForUrls,
-                        ReadJson(R"([
+  local_state()->SetManagedPref(prefs::kManagedSerialAllowUsbDevicesForUrls,
+                                ReadJson(R"([
                {
                  "devices": [{ "vendor_id": 6353 }],
                  "urls": [ "https://google.com" ]
@@ -599,11 +605,12 @@
   const auto kFooOrigin = url::Origin::Create(GURL("https://foo.origin"));
   const auto kBarOrigin = url::Origin::Create(GURL("https://bar.origin"));
 
-  auto* prefs = profile()->GetTestingPrefService();
-  prefs->SetManagedPref(prefs::kManagedDefaultSerialGuardSetting,
-                        std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
-  prefs->SetManagedPref(prefs::kManagedSerialAllowAllPortsForUrls,
-                        ReadJson(R"([ "https://foo.origin" ])"));
+  auto* profile_prefs = profile()->GetTestingPrefService();
+  profile_prefs->SetManagedPref(
+      prefs::kManagedDefaultSerialGuardSetting,
+      std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
+  local_state()->SetManagedPref(prefs::kManagedSerialAllowAllPortsForUrls,
+                                ReadJson(R"([ "https://foo.origin" ])"));
 
   auto port = device::mojom::SerialPortInfo::New();
   port->token = base::UnguessableToken::Create();
@@ -618,12 +625,12 @@
   const auto kFooOrigin = url::Origin::Create(GURL("https://foo.origin"));
   const auto kBarOrigin = url::Origin::Create(GURL("https://bar.origin"));
 
-  auto* prefs = profile()->GetTestingPrefService();
-  prefs->SetManagedPref(
+  auto* profile_prefs = profile()->GetTestingPrefService();
+  profile_prefs->SetManagedPref(
       prefs::kManagedSerialBlockedForUrls,
       ReadJson(R"([ "https://foo.origin", "https://bar.origin" ])"));
-  prefs->SetManagedPref(prefs::kManagedSerialAllowAllPortsForUrls,
-                        ReadJson(R"([ "https://foo.origin" ])"));
+  local_state()->SetManagedPref(prefs::kManagedSerialAllowAllPortsForUrls,
+                                ReadJson(R"([ "https://foo.origin" ])"));
 
   auto port = device::mojom::SerialPortInfo::New();
   port->token = base::UnguessableToken::Create();
@@ -667,9 +674,8 @@
 TEST_F(SerialChooserContextTest, BlocklistOverridesPolicy) {
   const auto origin = url::Origin::Create(GURL("https://google.com"));
 
-  auto* prefs = profile()->GetTestingPrefService();
-  prefs->SetManagedPref(prefs::kManagedSerialAllowUsbDevicesForUrls,
-                        ReadJson(R"([
+  local_state()->SetManagedPref(prefs::kManagedSerialAllowUsbDevicesForUrls,
+                                ReadJson(R"([
                {
                  "devices": [{ "vendor_id": 6353, "product_id": 22768 }],
                  "urls": [ "https://google.com" ]
diff --git a/chrome/browser/serial/serial_policy_allowed_ports.cc b/chrome/browser/serial/serial_policy_allowed_ports.cc
index f2677ac0..f5c1213 100644
--- a/chrome/browser/serial/serial_policy_allowed_ports.cc
+++ b/chrome/browser/serial/serial_policy_allowed_ports.cc
@@ -47,8 +47,7 @@
 SerialPolicyAllowedPorts::~SerialPolicyAllowedPorts() = default;
 
 // static
-void SerialPolicyAllowedPorts::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
+void SerialPolicyAllowedPorts::RegisterPrefs(PrefRegistrySimple* registry) {
   registry->RegisterListPref(prefs::kManagedSerialAllowAllPortsForUrls);
   registry->RegisterListPref(prefs::kManagedSerialAllowUsbDevicesForUrls);
 }
diff --git a/chrome/browser/serial/serial_policy_allowed_ports.h b/chrome/browser/serial/serial_policy_allowed_ports.h
index 46bbe8b..f7caea5 100644
--- a/chrome/browser/serial/serial_policy_allowed_ports.h
+++ b/chrome/browser/serial/serial_policy_allowed_ports.h
@@ -17,10 +17,7 @@
 }  // namespace mojom
 }  // namespace device
 
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
+class PrefRegistrySimple;
 class PrefService;
 
 // This class is used to maintain and interpret the SerialAllowForUrls and
@@ -35,7 +32,7 @@
   SerialPolicyAllowedPorts& operator=(SerialPolicyAllowedPorts& other) = delete;
   ~SerialPolicyAllowedPorts();
 
-  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+  static void RegisterPrefs(PrefRegistrySimple* registry);
 
   // Checks if |origin| is allowed to use the port with |port_info|.
   bool HasPortPermission(const url::Origin& origin,
diff --git a/chrome/browser/serial/serial_policy_allowed_ports_unittest.cc b/chrome/browser/serial/serial_policy_allowed_ports_unittest.cc
index 966bd0c..c47266a 100644
--- a/chrome/browser/serial/serial_policy_allowed_ports_unittest.cc
+++ b/chrome/browser/serial/serial_policy_allowed_ports_unittest.cc
@@ -6,9 +6,9 @@
 
 #include "base/containers/contains.h"
 #include "base/json/json_reader.h"
+#include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "services/device/public/mojom/serial.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -48,21 +48,23 @@
 
 class SerialPolicyAllowedPortsTest : public testing::Test {
  public:
-  SerialPolicyAllowedPortsTest() = default;
+  SerialPolicyAllowedPortsTest() {
+    RegisterLocalState(local_state_.registry());
+  }
+
   ~SerialPolicyAllowedPortsTest() override = default;
 
   void SetAllowAllPortsForUrlsPrefValue(const base::Value& value) {
-    profile_.GetPrefs()->Set(prefs::kManagedSerialAllowAllPortsForUrls, value);
+    local_state_.Set(prefs::kManagedSerialAllowAllPortsForUrls, value);
   }
 
   void SetAllowUsbDevicesForUrlsPrefValue(const base::Value& value) {
-    profile_.GetPrefs()->Set(prefs::kManagedSerialAllowUsbDevicesForUrls,
-                             value);
+    local_state_.Set(prefs::kManagedSerialAllowUsbDevicesForUrls, value);
   }
 
   void InitializePolicy() {
     EXPECT_FALSE(policy_);
-    policy_ = std::make_unique<SerialPolicyAllowedPorts>(profile_.GetPrefs());
+    policy_ = std::make_unique<SerialPolicyAllowedPorts>(&local_state_);
   }
 
  protected:
@@ -70,7 +72,7 @@
 
  private:
   content::BrowserTaskEnvironment task_environment_;
-  TestingProfile profile_;
+  TestingPrefServiceSimple local_state_;
   std::unique_ptr<SerialPolicyAllowedPorts> policy_;
 };
 
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 524417b8..e5fcb05 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -17,6 +17,7 @@
 #include "base/callback.h"
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
+#include "base/containers/flat_map.h"
 #include "base/debug/alias.h"
 #include "base/feature_list.h"
 #include "base/location.h"
@@ -222,10 +223,15 @@
           (*i)->user_title, (*i)->window_id.id());
       browsers.push_back(browser);
 
+      // A foreign session window will not contain tab groups, however an
+      // instance is still required for RestoreTabsToBrowser.
+      base::flat_map<tab_groups::TabGroupId, tab_groups::TabGroupId>
+          new_group_ids;
+
       // Restore and show the browser.
       const int initial_tab_count = 0;
-      RestoreTabsToBrowser(*(*i), browser, initial_tab_count,
-                           &created_contents);
+      RestoreTabsToBrowser(*(*i), browser, initial_tab_count, &created_contents,
+                           &new_group_ids);
       NotifySessionServiceOfRestoredTabs(browser, initial_tab_count);
     }
 
@@ -573,7 +579,10 @@
       // For the cases that users have more than one desk, a window is restored
       // to its parent desk, which can be non-active desk, and left invisible
       // but unminimized.
-      RestoreTabsToBrowser(*(*i), browser, initial_tab_count, created_contents);
+      base::flat_map<tab_groups::TabGroupId, tab_groups::TabGroupId>
+          new_group_ids;
+      RestoreTabsToBrowser(*(*i), browser, initial_tab_count, created_contents,
+                           &new_group_ids);
       (*tab_count) += (static_cast<int>(browser->tab_strip_model()->count()) -
                        initial_tab_count);
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
@@ -586,7 +595,7 @@
       TabGroupModel* group_model = browser->tab_strip_model()->group_model();
       for (auto& session_tab_group : (*i)->tab_groups) {
         TabGroup* model_tab_group =
-            group_model->GetTabGroup(session_tab_group->id);
+            group_model->GetTabGroup(new_group_ids.at(session_tab_group->id));
         DCHECK(model_tab_group);
         model_tab_group->SetVisualData(session_tab_group->visual_data);
       }
@@ -661,10 +670,13 @@
   // tabs but pinned tabs will be pushed in front.
   // If there are no existing tabs, the tab at |window.selected_tab_index| will
   // be selected. Otherwise, the tab selection will remain untouched.
-  void RestoreTabsToBrowser(const sessions::SessionWindow& window,
-                            Browser* browser,
-                            int initial_tab_count,
-                            std::vector<RestoredTab>* created_contents) {
+  void RestoreTabsToBrowser(
+      const sessions::SessionWindow& window,
+      Browser* browser,
+      int initial_tab_count,
+      std::vector<RestoredTab>* created_contents,
+      base::flat_map<tab_groups::TabGroupId, tab_groups::TabGroupId>*
+          new_group_ids) {
     DVLOG(1) << "RestoreTabsToBrowser " << window.tabs.size();
     // TODO(https://crbug.com/1032348): Change to DCHECK once we understand
     // why some browsers don't have an active tab on startup.
@@ -710,8 +722,8 @@
       // the existing ones. E.g. this happens in Win8 Metro where we merge
       // windows or when launching a hosted app from the app launcher.
       int tab_index = i + initial_tab_count;
-      RestoreTab(tab, browser, created_contents, tab_index, is_selected_tab,
-                 last_active_time);
+      RestoreTab(tab, browser, created_contents, new_group_ids, tab_index,
+                 is_selected_tab, last_active_time);
     }
   }
 
@@ -723,6 +735,8 @@
   void RestoreTab(const sessions::SessionTab& tab,
                   Browser* browser,
                   std::vector<RestoredTab>* created_contents,
+                  base::flat_map<tab_groups::TabGroupId,
+                                 tab_groups::TabGroupId>* new_group_ids,
                   const int tab_index,
                   bool is_selected_tab,
                   base::TimeTicks last_active_time) {
@@ -746,17 +760,29 @@
               ->RecreateSessionStorage(tab.session_storage_persistent_id);
     }
 
+    // Relabel group IDs to prevent duplicating groups. See crbug.com/1202102.
+    absl::optional<tab_groups::TabGroupId> new_group;
+    if (tab.group) {
+      auto it = new_group_ids->find(*tab.group);
+      if (it == new_group_ids->end()) {
+        it = new_group_ids
+                 ->emplace(*tab.group, tab_groups::TabGroupId::GenerateNew())
+                 .first;
+      }
+      new_group = it->second;
+    }
+
     // Apply the stored group.
     WebContents* web_contents = chrome::AddRestoredTab(
         browser, tab.navigations, tab_index, selected_index,
-        tab.extension_app_id, tab.group, is_selected_tab, tab.pinned,
+        tab.extension_app_id, new_group, is_selected_tab, tab.pinned,
         last_active_time, session_storage_namespace.get(),
         tab.user_agent_override, true /* from_session_restore */);
     DCHECK(web_contents);
 
     RestoredTab restored_tab(web_contents, is_selected_tab,
                              tab.extension_app_id.empty(), tab.pinned,
-                             tab.group);
+                             new_group);
     created_contents->push_back(restored_tab);
 
     // If this isn't the selected tab, there's nothing else to do.
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index ae500328..ca910ec 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -1039,7 +1039,8 @@
 
   Browser* new_browser = QuitBrowserAndRestore(browser(), kNumTabs);
   ASSERT_EQ(kNumTabs, new_browser->tab_strip_model()->count());
-  EXPECT_EQ(groups, GetTabGroups(new_browser->tab_strip_model()));
+  ASSERT_NO_FATAL_FAILURE(
+      CheckTabGrouping(new_browser->tab_strip_model(), group_spec));
 }
 
 IN_PROC_BROWSER_TEST_P(SessionRestoreTabGroupsTest, GroupMetadataRestored) {
@@ -1070,11 +1071,19 @@
   TabStripModel* const new_tsm = new_browser->tab_strip_model();
   ASSERT_EQ(5, new_tsm->count());
 
+  const absl::optional<tab_groups::TabGroupId> new_group1 =
+      new_tsm->GetTabGroupForTab(0);
+  const absl::optional<tab_groups::TabGroupId> new_group2 =
+      new_tsm->GetTabGroupForTab(2);
+
+  ASSERT_TRUE(new_group1);
+  ASSERT_TRUE(new_group2);
+
   // Check that the restored visual data is the same.
   const tab_groups::TabGroupVisualData* const group1_restored_data =
-      new_tsm->group_model()->GetTabGroup(group1)->visual_data();
+      new_tsm->group_model()->GetTabGroup(*new_group1)->visual_data();
   const tab_groups::TabGroupVisualData* const group2_restored_data =
-      new_tsm->group_model()->GetTabGroup(group2)->visual_data();
+      new_tsm->group_model()->GetTabGroup(*new_group2)->visual_data();
 
   EXPECT_EQ(group1_data.title(), group1_restored_data->title());
   EXPECT_EQ(group1_data.color(), group1_restored_data->color());
@@ -1084,6 +1093,37 @@
   EXPECT_EQ(group2_data.is_collapsed(), group2_restored_data->is_collapsed());
 }
 
+IN_PROC_BROWSER_TEST_P(SessionRestoreTabGroupsTest,
+                       TabGroupIDsRelabeledOnRestore) {
+  constexpr int kNumTabs = 3;
+  const std::array<absl::optional<int>, kNumTabs> group_spec = {0, 0, 1};
+
+  // Open |kNumTabs| tabs.
+  ui_test_utils::NavigateToURL(browser(), GetUrl1());
+  for (int i = 1; i < kNumTabs; ++i) {
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GetUrl1(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+  }
+  ASSERT_EQ(kNumTabs, browser()->tab_strip_model()->count());
+
+  CreateTabGroups(browser()->tab_strip_model(), group_spec);
+  ASSERT_NO_FATAL_FAILURE(
+      CheckTabGrouping(browser()->tab_strip_model(), group_spec));
+  const auto orig_groups = GetTabGroups(browser()->tab_strip_model());
+
+  Browser* const new_browser = QuitBrowserAndRestore(browser(), 5);
+  TabStripModel* const new_tsm = new_browser->tab_strip_model();
+  ASSERT_EQ(kNumTabs, new_tsm->count());
+  ASSERT_NO_FATAL_FAILURE(CheckTabGrouping(new_tsm, group_spec));
+  const auto new_groups = GetTabGroups(new_tsm);
+
+  for (int i = 0; i < kNumTabs; ++i) {
+    SCOPED_TRACE(i);
+    EXPECT_NE(orig_groups[i], new_groups[i]);
+  }
+}
+
 INSTANTIATE_TEST_SUITE_P(WithAndWithoutReset,
                          SessionRestoreTabGroupsTest,
                          testing::Values(false, true));
diff --git a/chrome/browser/sessions/tab_restore_browsertest.cc b/chrome/browser/sessions/tab_restore_browsertest.cc
index afb4fa5a..79612b4c 100644
--- a/chrome/browser/sessions/tab_restore_browsertest.cc
+++ b/chrome/browser/sessions/tab_restore_browsertest.cc
@@ -1418,20 +1418,23 @@
   TabGroupModel* restored_group_model =
       restored_window->tab_strip_model()->group_model();
   ASSERT_EQ(tab_count, restored_window->tab_strip_model()->count());
+  auto restored_group1 =
+      restored_window->tab_strip_model()->GetTabGroupForTab(tab_count - 3);
+  ASSERT_TRUE(restored_group1);
   EXPECT_EQ(
-      absl::make_optional(group1),
-      restored_window->tab_strip_model()->GetTabGroupForTab(tab_count - 3));
-  EXPECT_EQ(
-      absl::make_optional(group1),
+      restored_window->tab_strip_model()->GetTabGroupForTab(tab_count - 3),
       restored_window->tab_strip_model()->GetTabGroupForTab(tab_count - 2));
-  EXPECT_EQ(
-      absl::make_optional(group2),
-      restored_window->tab_strip_model()->GetTabGroupForTab(tab_count - 1));
+  auto restored_group2 =
+      restored_window->tab_strip_model()->GetTabGroupForTab(tab_count - 1);
+  ASSERT_TRUE(restored_group2);
+  EXPECT_NE(restored_group2, restored_group1);
 
-  EXPECT_EQ(group1_data,
-            *restored_group_model->GetTabGroup(group1)->visual_data());
-  EXPECT_EQ(group2_data,
-            *restored_group_model->GetTabGroup(group2)->visual_data());
+  EXPECT_EQ(
+      group1_data,
+      *restored_group_model->GetTabGroup(*restored_group1)->visual_data());
+  EXPECT_EQ(
+      group2_data,
+      *restored_group_model->GetTabGroup(*restored_group2)->visual_data());
 }
 
 // Ensure a tab is not restored between tabs of another group.
@@ -1781,3 +1784,47 @@
   EXPECT_EQ(initial_origin,
             new_popup->GetMainFrame()->GetLastCommittedOrigin());
 }
+
+// Ensures group IDs are regenerated for restored windows so that we don't split
+// the same group between multiple windows. See https://crbug.com/1202102. This
+// test is temporary until a more comprehensive fix is implemented.
+IN_PROC_BROWSER_TEST_F(TabRestoreTest, RestoredWindowHasNewGroupIds) {
+  sessions::TabRestoreService* service =
+      TabRestoreServiceFactory::GetForProfile(browser()->profile());
+
+  AddSomeTabs(browser(), 2);
+  ASSERT_EQ(3, browser()->tab_strip_model()->count());
+
+  // Create a new browser from which to restore the first.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL(chrome::kChromeUINewTabURL),
+      WindowOpenDisposition::NEW_WINDOW,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
+  ASSERT_EQ(2u, active_browser_list_->size());
+  Browser* second_browser = GetBrowser(1);
+  ASSERT_NE(browser(), second_browser);
+
+  auto original_group = browser()->tab_strip_model()->AddToNewGroup({1, 2});
+  CloseBrowserSynchronously(browser());
+  ASSERT_EQ(1u, active_browser_list_->size());
+
+  // We should have a restore entry for the window.
+  const sessions::TabRestoreService::Entries& entries = service->entries();
+  ASSERT_GE(entries.size(), 1u);
+  ASSERT_EQ(entries.front()->type, sessions::TabRestoreService::WINDOW);
+
+  // Restore the window.
+  std::vector<sessions::LiveTab*> restored_window_tabs =
+      service->RestoreEntryById(second_browser->live_tab_context(),
+                                entries.front()->id,
+                                WindowOpenDisposition::NEW_FOREGROUND_TAB);
+  ASSERT_EQ(2u, active_browser_list_->size());
+  ASSERT_EQ(3u, restored_window_tabs.size());
+  Browser* third_browser = GetBrowser(1);
+  ASSERT_NE(second_browser, third_browser);
+  ASSERT_EQ(3, third_browser->tab_strip_model()->count());
+
+  // The group ID should be new.
+  EXPECT_NE(original_group,
+            third_browser->tab_strip_model()->GetTabGroupForTab(1));
+}
diff --git a/chrome/browser/sharing/sharing_ui_controller.cc b/chrome/browser/sharing/sharing_ui_controller.cc
index 95513ee..e9561eb 100644
--- a/chrome/browser/sharing/sharing_ui_controller.cc
+++ b/chrome/browser/sharing/sharing_ui_controller.cc
@@ -23,6 +23,13 @@
 
 namespace {
 
+// In some cases (e.g. SmsRemoteFetcher), we show a success icon after the
+// message is received instead of after sending it out. The icon will be removed
+// be removed after |kShowSuccessIconDuration| seconds.
+// Note that the success icon should be able to persist across navigations.
+static constexpr base::TimeDelta kShowSuccessIconDuration =
+    base::TimeDelta::FromSeconds(8);
+
 BrowserWindow* GetWindowFromWebContents(content::WebContents* web_contents) {
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
   return browser ? browser->window() : nullptr;
@@ -168,16 +175,39 @@
   return data;
 }
 
+bool SharingUiController::ShouldShowLoadingIcon() const {
+  return true;
+}
+
+int SharingUiController::GetIconLabelId() const {
+  return ShouldShowLoadingIcon() ? IDS_BROWSER_SHARING_OMNIBOX_SENDING_LABEL
+                                 : IDS_BROWSER_SHARING_OMNIBOX_SENT_LABEL;
+}
+
+void SharingUiController::ShowSuccessIcon() {
+  last_dialog_id_++;
+  is_loading_ = true;
+  UpdateIcon();
+  // For newly added dialog in |OnResponse|, we remove it with a post task.
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&SharingUiController::HideSuccessIcon,
+                     weak_ptr_factory_.GetWeakPtr(), last_dialog_id_),
+      kShowSuccessIconDuration);
+}
+
 base::OnceClosure SharingUiController::SendMessageToDevice(
     const syncer::DeviceInfo& device,
     absl::optional<base::TimeDelta> response_timeout,
     chrome_browser_sharing::SharingMessage sharing_message,
     absl::optional<SharingMessageSender::ResponseCallback> custom_callback) {
-  last_dialog_id_++;
-  is_loading_ = true;
   send_result_ = SharingSendMessageResult::kSuccessful;
   target_device_name_ = device.client_name();
-  UpdateIcon();
+  if (ShouldShowLoadingIcon()) {
+    last_dialog_id_++;
+    is_loading_ = true;
+    UpdateIcon();
+  }
 
   SharingMessageSender::ResponseCallback response_callback = base::BindOnce(
       &SharingUiController::OnResponse, weak_ptr_factory_.GetWeakPtr(),
@@ -238,8 +268,18 @@
   if (dialog_id != last_dialog_id_)
     return;
 
-  is_loading_ = false;
   send_result_ = result;
+  if (ShouldShowLoadingIcon()) {
+    is_loading_ = false;
+    UpdateIcon();
+  }
+}
+
+void SharingUiController::HideSuccessIcon(int dialog_id) {
+  if (dialog_id != last_dialog_id_)
+    return;
+
+  is_loading_ = false;
   UpdateIcon();
 }
 
diff --git a/chrome/browser/sharing/sharing_ui_controller.h b/chrome/browser/sharing/sharing_ui_controller.h
index eef3a67..4c9eb0d 100644
--- a/chrome/browser/sharing/sharing_ui_controller.h
+++ b/chrome/browser/sharing/sharing_ui_controller.h
@@ -54,6 +54,9 @@
   virtual sync_pb::SharingSpecificFields::EnabledFeatures GetRequiredFeature()
       const = 0;
   virtual const gfx::VectorIcon& GetVectorIcon() const = 0;
+  // If true, shows a loading icon on omnibox when sending out the message.
+  virtual bool ShouldShowLoadingIcon() const;
+  int GetIconLabelId() const;
   virtual std::u16string GetTextForTooltipAndAccessibleName() const = 0;
   // Get the name of the feature to be used as a prefix for the metric name.
   virtual SharingFeatureName GetFeatureMetricsPrefix() const = 0;
@@ -100,6 +103,8 @@
   virtual void DoUpdateApps(UpdateAppsCallback callback) = 0;
   // Prepares a new dialog data.
   virtual SharingDialogData CreateDialogData(SharingDialogType dialog_type);
+  // Shows a successful icon upon |AckReceived|.
+  void ShowSuccessIcon();
 
   // Shows an icon in the omnibox which will be removed when receiving a
   // response or when cancelling the request by calling the returned callback.
@@ -132,6 +137,8 @@
                       const absl::optional<url::Origin>& initiating_origin,
                       std::vector<SharingApp> apps);
 
+  void HideSuccessIcon(int dialog_id);
+
   SharingDialog* dialog_ = nullptr;
   content::WebContents* web_contents_ = nullptr;
   SharingService* sharing_service_ = nullptr;
diff --git a/chrome/browser/sharing/sms/sms_remote_fetcher_ui_controller.cc b/chrome/browser/sharing/sms/sms_remote_fetcher_ui_controller.cc
index ee95456..0ac13e4 100644
--- a/chrome/browser/sharing/sms/sms_remote_fetcher_ui_controller.cc
+++ b/chrome/browser/sharing/sms/sms_remote_fetcher_ui_controller.cc
@@ -65,6 +65,10 @@
   return kSmartphoneIcon;
 }
 
+bool SmsRemoteFetcherUiController::ShouldShowLoadingIcon() const {
+  return false;
+}
+
 std::u16string
 SmsRemoteFetcherUiController::GetTextForTooltipAndAccessibleName() const {
   return l10n_util::GetStringFUTF16(IDS_OMNIBOX_TOOLTIP_SMS_REMOTE_FETCHER,
@@ -86,6 +90,7 @@
     std::move(callback).Run(absl::nullopt, absl::nullopt, absl::nullopt);
     return;
   }
+  ShowSuccessIcon();
 
   DCHECK(response);
   DCHECK(response->has_sms_fetch_response());
diff --git a/chrome/browser/sharing/sms/sms_remote_fetcher_ui_controller.h b/chrome/browser/sharing/sms/sms_remote_fetcher_ui_controller.h
index 4572a0f..974eea2 100644
--- a/chrome/browser/sharing/sms/sms_remote_fetcher_ui_controller.h
+++ b/chrome/browser/sharing/sms/sms_remote_fetcher_ui_controller.h
@@ -52,6 +52,7 @@
   void OnAppChosen(const SharingApp& app) override;
   std::u16string GetContentType() const override;
   const gfx::VectorIcon& GetVectorIcon() const override;
+  bool ShouldShowLoadingIcon() const override;
   std::u16string GetTextForTooltipAndAccessibleName() const override;
   SharingFeatureName GetFeatureMetricsPrefix() const override;
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index e6a92900..8683625 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3510,6 +3510,13 @@
     }
   }
 
+  if (is_win || is_mac || is_linux) {
+    sources += [
+      "startup/web_app_protocol_handling_startup_utils.cc",
+      "startup/web_app_protocol_handling_startup_utils.h",
+    ]
+  }
+
   if (is_win || is_mac || (is_linux && !is_chromeos_lacros)) {
     sources += [
       "startup/web_app_url_handling_startup_utils.cc",
diff --git a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
index b5316b44..95483ad0 100644
--- a/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
+++ b/chrome/browser/ui/android/passwords/manual_filling_view_android.cc
@@ -94,6 +94,14 @@
   }
 }
 
+void ManualFillingViewAndroid::ShowAccessorySheetTab(
+    const autofill::AccessoryTabType& tab_type) {
+  if (auto obj = GetOrCreateJavaObject()) {
+    Java_ManualFillingComponentBridge_showAccessorySheetTab(
+        base::android::AttachCurrentThread(), obj, static_cast<int>(tab_type));
+  }
+}
+
 void ManualFillingViewAndroid::OnAutomaticGenerationStatusChanged(
     bool available) {
   if (!available && java_object_internal_.is_null())
diff --git a/chrome/browser/ui/android/passwords/manual_filling_view_android.h b/chrome/browser/ui/android/passwords/manual_filling_view_android.h
index 2f7ed0cc..f475659 100644
--- a/chrome/browser/ui/android/passwords/manual_filling_view_android.h
+++ b/chrome/browser/ui/android/passwords/manual_filling_view_android.h
@@ -39,6 +39,8 @@
   void SwapSheetWithKeyboard() override;
   void ShowWhenKeyboardIsVisible() override;
   void Hide() override;
+  void ShowAccessorySheetTab(
+      const autofill::AccessoryTabType& tab_type) override;
 
   // Called from Java via JNI:
   void OnFaviconRequested(
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ar.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ar.xtb
index 09d0c2f..af27ec3 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ar.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ar.xtb
@@ -631,7 +631,7 @@
 <translation id="510275257476243843">يتبقى ساعة واحدة</translation>
 <translation id="5118713593561876160">الاهتمامات</translation>
 <translation id="5123685120097942451">علامة تبويب "التصفُّح المتخفي"</translation>
-<translation id="5127805178023152808">المزامنة غير مفعّلة</translation>
+<translation id="5127805178023152808">المزامنة متوقفة.</translation>
 <translation id="5132942445612118989">مزامنة كلمات المرور والسجلّ والمزيد على جميع الأجهزة</translation>
 <translation id="5136035049889637840">تسوُّق منتجات مشابهة <ph name="BEGIN_NEW" />جديد<ph name="END_NEW" /></translation>
 <translation id="5139940364318403933">‏التعرّف على كيفية استخدام Google Drive</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_en-GB.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_en-GB.xtb
index e37c7e8..53973ebe 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_en-GB.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_en-GB.xtb
@@ -138,7 +138,7 @@
 <translation id="1960290143419248813">Chrome updates are no longer supported for this version of Android</translation>
 <translation id="1963976881984600709">Standard protection</translation>
 <translation id="1966710179511230534">Please update your sign-in details.</translation>
-<translation id="1973912524893600642">Keep data</translation>
+<translation id="1973912524893600642">Keep Data</translation>
 <translation id="1974060860693918893">Advanced</translation>
 <translation id="1984417487208496350">No protection (not recommended)</translation>
 <translation id="1984705450038014246">Sync your Chrome data</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_gu.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_gu.xtb
index 87de01a..f09d9f4 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_gu.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_gu.xtb
@@ -684,6 +684,7 @@
 <translation id="545042621069398927">તમારા ડાઉનલોડની ગતિ વધારી રહ્યાં છીએ.</translation>
 <translation id="5454166040603940656"><ph name="PROVIDER" /> સાથે</translation>
 <translation id="5456381639095306749">પૃષ્ઠ ડાઉનલોડ કરો</translation>
+<translation id="5458366071038729214">તમે જે સાઇટ ફૉલો કરશો, તે અહીં દેખાશે</translation>
 <translation id="548278423535722844">નકશા અ‍ૅપ્લિકેશનમાં ખોલો</translation>
 <translation id="5483197086164197190">Chrome પર નૅવિગેટ કરો</translation>
 <translation id="5487521232677179737">ડેટા સાફ કરો</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hy.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hy.xtb
index 023b38f..bb787fb 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_hy.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_hy.xtb
@@ -494,7 +494,7 @@
 <translation id="4242533952199664413">Բացել կարգավորումները</translation>
 <translation id="4248098802131000011">Պաշտպանեք ձեր գաղտնաբառերը տվյալների արտահոսքից և անվտանգության հետ կապված այլ խնդիրներից</translation>
 <translation id="4250229828105606438">Սքրինշոթ</translation>
-<translation id="4256782883801055595">Բաց կոդով ծրագրերի արտոնագրեր</translation>
+<translation id="4256782883801055595">Բաց կոդով ծրագրերի լիցենզիաներ</translation>
 <translation id="4263656433980196874">Օգնականի միջոցով ձայնային որոնման միջերեսը բացվում է ամբողջ բարձրությամբ</translation>
 <translation id="4269820728363426813">Պատճենել հղման հասցեն</translation>
 <translation id="4290281343757112331">Ներբեռնե՞լ ավելի ուշ</translation>
diff --git a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ne.xtb b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ne.xtb
index 06d722b..d3e2571 100644
--- a/chrome/browser/ui/android/strings/translations/android_chrome_strings_ne.xtb
+++ b/chrome/browser/ui/android/strings/translations/android_chrome_strings_ne.xtb
@@ -682,6 +682,7 @@
 <translation id="545042621069398927">तपाईंको डाउनलोडको गति बढाउँदै।</translation>
 <translation id="5454166040603940656"><ph name="PROVIDER" /> सँग</translation>
 <translation id="5456381639095306749">पृष्ठ डाउनलोड गर्नुहोस्</translation>
+<translation id="5458366071038729214">तपाईं आफूले फलो गरेका साइटहरू यहाँ भेट्टाउनु हुने छ</translation>
 <translation id="548278423535722844">नक्सा एपमा खोल्नुहोस्</translation>
 <translation id="5483197086164197190">Chrome नेभिगेट गर्ने तरिका</translation>
 <translation id="5487521232677179737">डेटा मेटाउनुहोस्</translation>
diff --git a/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
index c56cf01..89de627c 100644
--- a/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
@@ -47,13 +47,26 @@
   }
 
   auto task_id = arc::GetTaskIdFromWindowAppId(params.app_id);
-  if (!task_id.has_value())
+  auto session_id = arc::GetSessionIdFromWindowAppId(params.app_id);
+
+  // If neither |task_id| nor |session_id| are valid, this is not an ARC window.
+  if (!task_id.has_value() && !session_id.has_value())
     return;
 
   out_properties_container.SetProperty(aura::client::kAppType,
                                        static_cast<int>(ash::AppType::ARC_APP));
-  out_properties_container.SetProperty(full_restore::kWindowIdKey, *task_id);
-  int32_t restore_window_id = full_restore::GetArcRestoreWindowId(*task_id);
+
+  if (task_id.has_value())
+    out_properties_container.SetProperty(full_restore::kWindowIdKey, *task_id);
+
+  int32_t restore_window_id = 0;
+  if (task_id.has_value()) {
+    restore_window_id = full_restore::GetArcRestoreWindowIdForTaskId(*task_id);
+  } else {
+    restore_window_id =
+        full_restore::GetArcRestoreWindowIdForSessionId(*session_id);
+  }
+
   out_properties_container.SetProperty(full_restore::kRestoreWindowIdKey,
                                        restore_window_id);
 
diff --git a/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.cc b/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.cc
index 031ff27..07c7c97 100644
--- a/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.cc
@@ -6,6 +6,8 @@
 #include <string>
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "chrome/browser/autofill/manual_filling_controller.h"
+#include "chrome/browser/autofill/manual_filling_controller_impl.h"
 #include "chrome/browser/ui/android/autofill/snackbar/autofill_snackbar_view_android.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -40,7 +42,9 @@
 }
 
 void AutofillSnackbarControllerImpl::OnActionClicked() {
-  // TODO(crbug.com/1196021): Trigger manual fallback and log the action.
+  ManualFillingControllerImpl::GetOrCreate(web_contents_)
+      ->ShowAccessorySheetTab(autofill::AccessoryTabType::CREDIT_CARDS);
+  // TODO(crbug.com/1196021):  Log the action.
 }
 
 void AutofillSnackbarControllerImpl::OnDismissed() {
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_service.cc b/chrome/browser/ui/cookie_controls/cookie_controls_service.cc
index 58d97240..35e4b2c 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_service.cc
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_service.cc
@@ -32,8 +32,8 @@
 CookieControlsService::~CookieControlsService() = default;
 
 void CookieControlsService::Init() {
-  incongito_cookie_settings_ = CookieSettingsFactory::GetForProfile(profile_);
-  cookie_observations_.AddObservation(incongito_cookie_settings_.get());
+  incognito_cookie_settings_ = CookieSettingsFactory::GetForProfile(profile_);
+  cookie_observations_.AddObservation(incognito_cookie_settings_.get());
   regular_cookie_settings_ =
       CookieSettingsFactory::GetForProfile(profile_->GetOriginalProfile());
   cookie_observations_.AddObservation(regular_cookie_settings_.get());
@@ -84,7 +84,7 @@
 }
 
 bool CookieControlsService::GetToggleCheckedValue() {
-  return incongito_cookie_settings_->ShouldBlockThirdPartyCookies();
+  return incognito_cookie_settings_->ShouldBlockThirdPartyCookies();
 }
 
 void CookieControlsService::OnThirdPartyCookieBlockingChanged(
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_service.h b/chrome/browser/ui/cookie_controls/cookie_controls_service.h
index e4d1b4c..119b6c7a 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_service.h
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_service.h
@@ -64,7 +64,7 @@
 
   Profile* profile_;
   std::unique_ptr<policy::PolicyChangeRegistrar> policy_registrar_;
-  scoped_refptr<content_settings::CookieSettings> incongito_cookie_settings_;
+  scoped_refptr<content_settings::CookieSettings> incognito_cookie_settings_;
   scoped_refptr<content_settings::CookieSettings> regular_cookie_settings_;
   base::ScopedMultiSourceObservation<content_settings::CookieSettings,
                                      content_settings::CookieSettings::Observer>
diff --git a/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc b/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc
index d3bea20a..38d97ee 100644
--- a/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc
+++ b/chrome/browser/ui/global_media_controls/media_notification_service_unittest.cc
@@ -288,9 +288,7 @@
 
     // Now, close the tab. The session may have been destroyed with
     // |SimulateFocusLost()| above.
-    auto item_itr = sessions().find(id.ToString());
-    if (item_itr != sessions().end())
-      item_itr->second.WebContentsDestroyed();
+    service_->media_session_notification_producer_->OnRequestIdReleased(id);
   }
 
   void SimulatePlaybackStateChanged(const base::UnguessableToken& id,
diff --git a/chrome/browser/ui/global_media_controls/media_session_notification_producer.cc b/chrome/browser/ui/global_media_controls/media_session_notification_producer.cc
index b85e3ce..8a797a2 100644
--- a/chrome/browser/ui/global_media_controls/media_session_notification_producer.cc
+++ b/chrome/browser/ui/global_media_controls/media_session_notification_producer.cc
@@ -91,10 +91,10 @@
     std::unique_ptr<media_message_center::MediaSessionNotificationItem> item,
     content::WebContents* web_contents,
     mojo::Remote<media_session::mojom::MediaController> controller)
-    : content::WebContentsObserver(web_contents),
-      owner_(owner),
+    : owner_(owner),
       id_(id),
       item_(std::move(item)),
+      web_contents_(web_contents),
       presentation_manager_(GetPresentationManager(web_contents)) {
   DCHECK(owner_);
   DCHECK(item_);
@@ -124,13 +124,6 @@
       GlobalMediaControlsDismissReason::kMediaSessionStopped));
 }
 
-void MediaSessionNotificationProducer::Session::WebContentsDestroyed() {
-  // If the WebContents is destroyed, then we should just remove the item
-  // instead of freezing it.
-  set_dismiss_reason(GlobalMediaControlsDismissReason::kTabClosed);
-  owner_->RemoveItem(id_);
-}
-
 void MediaSessionNotificationProducer::Session::MediaSessionInfoChanged(
     media_session::mojom::MediaSessionInfoPtr session_info) {
   is_playing_ =
@@ -191,6 +184,11 @@
   }
 }
 
+void MediaSessionNotificationProducer::Session::OnRequestIdReleased() {
+  // The request ID is released when the tab is closed.
+  set_dismiss_reason(GlobalMediaControlsDismissReason::kTabClosed);
+}
+
 void MediaSessionNotificationProducer::Session::SetController(
     mojo::Remote<media_session::mojom::MediaController> controller) {
   if (controller.is_bound()) {
@@ -429,6 +427,18 @@
   service_->OnNotificationChanged();
 }
 
+void MediaSessionNotificationProducer::OnRequestIdReleased(
+    const base::UnguessableToken& request_id) {
+  const std::string id = request_id.ToString();
+  auto it = sessions_.find(id);
+  if (it == sessions_.end())
+    return;
+
+  // When the tab is closed, just remove the item instead of freezing it.
+  it->second.OnRequestIdReleased();
+  RemoveItem(id);
+}
+
 void MediaSessionNotificationProducer::OnContainerClicked(
     const std::string& id) {
   auto it = sessions_.find(id);
diff --git a/chrome/browser/ui/global_media_controls/media_session_notification_producer.h b/chrome/browser/ui/global_media_controls/media_session_notification_producer.h
index 99d374a3..12185ca2 100644
--- a/chrome/browser/ui/global_media_controls/media_session_notification_producer.h
+++ b/chrome/browser/ui/global_media_controls/media_session_notification_producer.h
@@ -54,6 +54,7 @@
       media_session::mojom::AudioFocusRequestStatePtr session) override;
   void OnFocusLost(
       media_session::mojom::AudioFocusRequestStatePtr session) override;
+  void OnRequestIdReleased(const base::UnguessableToken& request_id) override;
 
   // MediaNotificationContainerObserver implementation.
   void OnContainerClicked(const std::string& id) override;
@@ -106,8 +107,7 @@
   friend class MediaToolbarButtonControllerTest;
 
   class Session
-      : public content::WebContentsObserver,
-        public media_session::mojom::MediaControllerObserver,
+      : public media_session::mojom::MediaControllerObserver,
         public media_router::WebContentsPresentationManager::Observer {
    public:
     Session(MediaSessionNotificationProducer* owner,
@@ -120,9 +120,6 @@
     Session& operator=(const Session&) = delete;
     ~Session() override;
 
-    // content::WebContentsObserver:
-    void WebContentsDestroyed() override;
-
     // media_session::mojom::MediaControllerObserver:
     void MediaSessionInfoChanged(
         media_session::mojom::MediaSessionInfoPtr session_info) override;
@@ -141,6 +138,10 @@
     void OnMediaRoutesChanged(
         const std::vector<media_router::MediaRoute>& routes) override;
 
+    // Called when the request ID associated with this session is released (i.e.
+    // when the tab is closed).
+    void OnRequestIdReleased();
+
     media_message_center::MediaSessionNotificationItem* item() {
       return item_.get();
     }
@@ -169,6 +170,8 @@
     RegisterIsAudioDeviceSwitchingSupportedCallback(
         base::RepeatingCallback<void(bool)> callback);
 
+    content::WebContents* web_contents() const { return web_contents_; }
+
     void SetPresentationManagerForTesting(
         base::WeakPtr<media_router::WebContentsPresentationManager>
             presentation_manager);
@@ -219,6 +222,8 @@
     // Used to request audio output be routed to a different device.
     mojo::Remote<media_session::mojom::MediaController> controller_;
 
+    content::WebContents* const web_contents_;
+
     base::WeakPtr<media_router::WebContentsPresentationManager>
         presentation_manager_;
   };
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index f3fba4c..bd57a15 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -28,6 +28,8 @@
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
@@ -124,7 +126,9 @@
 
 class PageInfoTest : public ChromeRenderViewHostTestHarness {
  public:
-  PageInfoTest() { SetURL("http://www.example.com"); }
+  PageInfoTest() : testing_local_state_(TestingBrowserProcess::GetGlobal()) {
+    SetURL("http://www.example.com");
+  }
 
   ~PageInfoTest() override {}
 
@@ -265,6 +269,7 @@
   security_state::VisibleSecurityState visible_security_state_;
 
  private:
+  ScopedTestingLocalState testing_local_state_;
   std::unique_ptr<PageInfo> page_info_;
   std::unique_ptr<MockPageInfoUI> mock_ui_;
 
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 1b49179..0d4fc22 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -63,8 +63,6 @@
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/web_applications/web_app_ui_manager_impl.h"
-#include "chrome/browser/web_applications/components/os_integration_manager.h"
-#include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_features.h"
@@ -129,9 +127,7 @@
 #endif
 
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
-#include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/web_applications/components/web_app_id.h"
-#include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h"
+#include "chrome/browser/ui/startup/web_app_protocol_handling_startup_utils.h"
 #endif
 
 #if defined(OS_WIN) || defined(OS_MAC) || \
@@ -401,86 +397,6 @@
   StartupBrowserCreatorImpl::MaybeToggleFullscreen(browser);
 }
 
-#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
-// Tries to launch the web app from a protocol handler url. Checks if the passed
-// in url is a potential protocol url, if it is, check the protocol handler
-// registry for an entry. If there is an entry, then check if we need approval
-// from the user to launch this web app. If we didn't have approval from a
-// previous session, launch the permission dialog to ask for approval. If we did
-// get permission in the past, directly launch the web app with the translated
-// url. Returns true if the command line references a valid app and also
-// contains a protocol url that is specifically registered by the app.
-// `launch_mode_recorder` is used if and only if this function returns true.
-bool MaybeLaunchProtocolHandlerWebApp(
-    const base::CommandLine& command_line,
-    const base::FilePath& cur_dir,
-    Profile* profile,
-    std::unique_ptr<LaunchModeRecorder> launch_mode_recorder) {
-  std::string app_id = command_line.GetSwitchValueASCII(switches::kAppId);
-  // We must have a kAppId switch arg in the command line to launch.
-  if (app_id.empty())
-    return false;
-
-  GURL protocol_url;
-  base::CommandLine::StringVector args = command_line.GetArgs();
-  for (const auto& arg : args) {
-#if defined(OS_WIN)
-    GURL potential_protocol(base::AsStringPiece16(arg));
-#else
-    GURL potential_protocol(arg);
-#endif  // defined(OS_WIN)
-    // protocol_url is checked for validity later on with GetHandlersFor() where
-    // we consult the ProtocolHandlerRegistry and find entries that match this
-    // protocol.
-    if (potential_protocol.is_valid()) {
-      protocol_url = std::move(potential_protocol);
-      break;
-    }
-  }
-  if (protocol_url.is_empty())
-    return false;
-
-  web_app::WebAppProviderBase* provider =
-      web_app::WebAppProviderBase::GetProviderBase(profile);
-  web_app::OsIntegrationManager& os_integration_manager =
-      provider->os_integration_manager();
-  const std::vector<ProtocolHandler> handlers =
-      os_integration_manager.GetHandlersForProtocol(protocol_url.scheme());
-
-  // Nothing to do if there are no handlers with a web_app_id.
-  if (!base::Contains(handlers, true, [](const auto& handler) {
-        return handler.web_app_id().has_value();
-      })) {
-    return false;
-  }
-
-  auto launch_callback = base::BindOnce(
-      [](const base::CommandLine& command_line, const base::FilePath& cur_dir,
-         Profile* profile, const GURL& protocol_url,
-         const web_app::AppId& app_id,
-         std::unique_ptr<LaunchModeRecorder> launch_mode_recorder,
-         bool accepted) {
-        if (accepted) {
-          apps::AppServiceProxyFactory::GetForProfile(profile)
-              ->BrowserAppLauncher()
-              ->LaunchAppWithCallback(
-                  app_id, command_line, cur_dir,
-                  /*url_handler_launch_url=*/absl::nullopt, protocol_url,
-                  base::BindOnce(&FinalizeWebAppLaunch,
-                                 std::move(launch_mode_recorder)));
-        }  // else allow the process to exit without opening a browser.
-      },
-      command_line, cur_dir, profile, protocol_url, app_id,
-      std::move(launch_mode_recorder));
-  // ShowWebAppProtocolHandlerIntentPicker keeps the `profile` alive through
-  // running of `launch_callback`.
-  chrome::ShowWebAppProtocolHandlerIntentPicker(std::move(protocol_url),
-                                                profile, std::move(app_id),
-                                                std::move(launch_callback));
-  return true;
-}
-#endif  // defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
-
 // If the process was launched with the web application command line flags,
 // e.g. --app=http://www.google.com/ or --app_id=... return true.
 // In this case |app_url| or |app_id| are populated if they're non-null.
@@ -554,7 +470,6 @@
   }
   return false;
 }
-
 }  // namespace
 
 StartupBrowserCreator::StartupBrowserCreator() = default;
@@ -1058,13 +973,44 @@
 
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
   // Web app Protocol handling.
-  if (MaybeLaunchProtocolHandlerWebApp(
-          command_line, cur_dir, privacy_safe_profile,
-          std::make_unique<LaunchModeRecorder>())) {
+  auto startup_callback = base::BindOnce(
+      [](bool process_startup, const base::CommandLine& command_line,
+         const base::FilePath& cur_dir, Profile* profile,
+         Profile* last_used_profile,
+         const std::vector<Profile*>& last_opened_profiles) {
+        // TODO(crbug.com/1208199): Refactor StartupBrowserCreator and use the
+        // state struct here.
+        StartupBrowserCreator browser_creator;
+        browser_creator.StartupLaunchAfterProtocolHandler(
+            command_line, cur_dir, profile, process_startup, last_used_profile,
+            last_opened_profiles);
+      },
+      process_startup);
+
+  // web_app::startup::MaybeLaunchProtocolHandlerWebApp keeps the `profile`
+  // alive through running of `startup_callback`.
+  if (web_app::startup::MaybeLaunchProtocolHandlerWebApp(
+          command_line, cur_dir, privacy_safe_profile, last_used_profile,
+          last_opened_profiles,
+          base::BindOnce(&FinalizeWebAppLaunch,
+                         std::make_unique<LaunchModeRecorder>()),
+          std::move(startup_callback))) {
     return true;
   }
 #endif
 
+  return StartupLaunchAfterProtocolHandler(
+      command_line, cur_dir, privacy_safe_profile, process_startup,
+      last_used_profile, last_opened_profiles);
+}
+
+bool StartupBrowserCreator::StartupLaunchAfterProtocolHandler(
+    const base::CommandLine& command_line,
+    const base::FilePath& cur_dir,
+    Profile* privacy_safe_profile,
+    bool process_startup,
+    Profile* last_used_profile,
+    const std::vector<Profile*>& last_opened_profiles) {
   // If we're being run as an application window or application tab, don't
   // restore tabs or open initial URLs as the user has directly launched an app
   // shortcut. In the first case, the user should see a standlone app window. In
diff --git a/chrome/browser/ui/startup/startup_browser_creator.h b/chrome/browser/ui/startup/startup_browser_creator.h
index 60f651e..a288b33 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.h
+++ b/chrome/browser/ui/startup/startup_browser_creator.h
@@ -191,6 +191,16 @@
       Profile* profile,
       Profile::CreateStatus status);
 
+  // TODO(crbug/1213171): Move web-app functionality to its own file.
+  // The startup launch logic that is shared between ProcessCmdLineImpl()
+  // and web_app::MaybeLaunchProtocolHandlerWebApp().
+  bool StartupLaunchAfterProtocolHandler(const base::CommandLine& command_line,
+                                         const base::FilePath& cur_dir,
+                                         Profile* privacy_safe_profile,
+                                         bool process_startup,
+                                         Profile* last_used_profile,
+                                         const Profiles& last_opened_profiles);
+
   // Returns true once a profile was activated. Used by the
   // StartupBrowserCreatorTest.LastUsedProfileActivated test.
   static bool ActivatedProfile();
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index d813c2a..eb05e1b 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -637,13 +637,21 @@
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
     PrefService* local_state = g_browser_process->local_state();
-    if (local_state &&
-        !local_state->GetBoolean(lacros_prefs::kShowedExperimentalBannerPref)) {
-      LacrosStartupInfoBarDelegate::Create(infobar_manager);
+    if (local_state) {
+      // We show the banner if it's never shown before.
+      bool should_show_banner =
+          !local_state->GetBoolean(lacros_prefs::kShowedExperimentalBannerPref);
+      // If Lacros is not the primary browser, we always show the banner.
+      should_show_banner |= !chromeos::LacrosService::Get()
+                                 ->init_params()
+                                 ->standalone_browser_is_primary;
 
-      // Mark the pref as shown, so that we don't show the banner again.
-      local_state->SetBoolean(lacros_prefs::kShowedExperimentalBannerPref,
-                              true);
+      if (should_show_banner) {
+        LacrosStartupInfoBarDelegate::Create(infobar_manager);
+
+        local_state->SetBoolean(lacros_prefs::kShowedExperimentalBannerPref,
+                                true);
+      }
     }
 #endif
 
diff --git a/chrome/browser/ui/startup/web_app_protocol_handling_startup_utils.cc b/chrome/browser/ui/startup/web_app_protocol_handling_startup_utils.cc
new file mode 100644
index 0000000..e7c541e
--- /dev/null
+++ b/chrome/browser/ui/startup/web_app_protocol_handling_startup_utils.cc
@@ -0,0 +1,173 @@
+// 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 "chrome/browser/ui/startup/web_app_protocol_handling_startup_utils.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/check.h"
+#include "base/command_line.h"
+#include "base/containers/contains.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/strings/string_util.h"
+#include "build/build_config.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_keep_alive_types.h"
+#include "chrome/browser/profiles/scoped_profile_keep_alive.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/startup/startup_browser_creator.h"
+#include "chrome/browser/web_applications/components/os_integration_manager.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/common/chrome_switches.h"
+#include "components/keep_alive_registry/keep_alive_types.h"
+#include "components/keep_alive_registry/scoped_keep_alive.h"
+#include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Tries to launch the web app when the `provider` is ready. `startup_callback`
+// will run if there is no web app registered for `profile` that can handle
+// `protocol_url`. If os_integration_manager finds a web app, then check if the
+// web app has approval from the user to handle `protocol_url`. If the web app
+// didn't have approval from a previous launch, show the permission dialog to
+// ask for approval. The permission dialog will then launch the web app if the
+// user accepts the dialog or close the dialog window if the user cancels it.
+// If the web app did get permission in the past, the browser will directly
+// launch the web app with the translated url. The passed in `keep_alive` and
+// `profile_keep_alives` ensure the profiles and the browser are alive while
+// `provider` is waiting for the signal for "on_registry_ready()".
+void OnWebAppSystemReadyMaybeLaunchProtocolHandler(
+    web_app::WebAppProvider* provider,
+    const GURL& protocol_url,
+    const web_app::AppId& app_id,
+    const base::CommandLine& command_line,
+    const base::FilePath& cur_dir,
+    Profile* profile,
+    Profile* last_used_profile,
+    const std::vector<Profile*>& last_opened_profiles,
+    std::unique_ptr<ScopedKeepAlive> keep_alive,
+    const std::vector<std::unique_ptr<ScopedProfileKeepAlive>>
+        profile_keep_alives,
+    web_app::startup::FinalizeWebAppLaunchCallback finalize_callback,
+    web_app::startup::StartupLaunchAfterProtocolCallback startup_callback) {
+  web_app::OsIntegrationManager& os_integration_manager =
+      provider->os_integration_manager();
+  const std::vector<ProtocolHandler> handlers =
+      os_integration_manager.GetHandlersForProtocol(protocol_url.scheme());
+
+  if (!base::Contains(handlers, true, [](const auto& handler) {
+        return handler.web_app_id().has_value();
+      })) {
+    std::move(startup_callback)
+        .Run(command_line, cur_dir, profile, last_used_profile,
+             last_opened_profiles);
+    return;
+  }
+
+  auto launch_callback = base::BindOnce(
+      [](const base::CommandLine& command_line, const base::FilePath& cur_dir,
+         Profile* profile, const GURL& protocol_url,
+         const web_app::AppId& app_id,
+         web_app::startup::FinalizeWebAppLaunchCallback callback,
+         bool accepted) {
+        if (accepted) {
+          apps::AppServiceProxyFactory::GetForProfile(profile)
+              ->BrowserAppLauncher()
+              ->LaunchAppWithCallback(app_id, command_line, cur_dir,
+                                      /*url_handler_launch_url=*/absl::nullopt,
+                                      protocol_url, std::move(callback));
+        }  // else allow the process to exit without opening a browser.
+      },
+      command_line, cur_dir, profile, protocol_url, app_id,
+      std::move(finalize_callback));
+
+  // ShowWebAppProtocolHandlerIntentPicker keeps the `profile` alive through
+  // running of `launch_callback`.
+  chrome::ShowWebAppProtocolHandlerIntentPicker(protocol_url, profile, app_id,
+                                                std::move(launch_callback));
+}
+
+}  // namespace
+
+namespace web_app {
+namespace startup {
+
+bool MaybeLaunchProtocolHandlerWebApp(
+    const base::CommandLine& command_line,
+    const base::FilePath& cur_dir,
+    Profile* profile,
+    Profile* last_used_profile,
+    const std::vector<Profile*>& last_opened_profiles,
+    FinalizeWebAppLaunchCallback finalize_callback,
+    StartupLaunchAfterProtocolCallback startup_callback) {
+  std::string app_id = command_line.GetSwitchValueASCII(switches::kAppId);
+  // There must be a kAppId switch arg in the command line to launch.
+  if (app_id.empty())
+    return false;
+
+  GURL protocol_url;
+  base::CommandLine::StringVector args = command_line.GetArgs();
+  for (const auto& arg : args) {
+#if defined(OS_WIN)
+    GURL potential_protocol(base::AsStringPiece16(arg));
+#else
+    GURL potential_protocol(arg);
+#endif  // defined(OS_WIN)
+    // protocol_url is checked for validity later with getting the provider and
+    // consulting the os_integration_manager. However because that process has a
+    // wait for "on_registry_ready()", `potential_protocol` checks for
+    // blink::IsValidCustomHandlerScheme() here to avoid loading the
+    // web_app::WebAppProvider with a false positive.
+    bool unused_has_custom_scheme_prefix = false;
+    if (potential_protocol.is_valid() &&
+        blink::IsValidCustomHandlerScheme(potential_protocol.scheme(),
+                                          /*allow_ext_prefix=*/false,
+                                          unused_has_custom_scheme_prefix)) {
+      protocol_url = std::move(potential_protocol);
+      break;
+    }
+  }
+  if (protocol_url.is_empty())
+    return false;
+
+  auto* provider = web_app::WebAppProvider::Get(profile);
+  DCHECK(provider);
+  // Create the keep_alives so the profiles and the browser stays alive as we
+  // wait for the provider() to be ready.
+  auto keep_alive = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::WEB_APP_PROTOCOL_HANDLER_LAUNCH,
+      KeepAliveRestartOption::DISABLED);
+  std::vector<std::unique_ptr<ScopedProfileKeepAlive>> profile_keep_alives;
+  profile_keep_alives.push_back(std::make_unique<ScopedProfileKeepAlive>(
+      profile, ProfileKeepAliveOrigin::kWebAppProtocolHandlerLaunch));
+  profile_keep_alives.push_back(std::make_unique<ScopedProfileKeepAlive>(
+      last_used_profile, ProfileKeepAliveOrigin::kWebAppProtocolHandlerLaunch));
+  for (Profile* last_opened_profile : last_opened_profiles) {
+    profile_keep_alives.push_back(std::make_unique<ScopedProfileKeepAlive>(
+        last_opened_profile,
+        ProfileKeepAliveOrigin::kWebAppProtocolHandlerLaunch));
+  }
+
+  provider->on_registry_ready().Post(
+      FROM_HERE,
+      base::BindOnce(OnWebAppSystemReadyMaybeLaunchProtocolHandler, provider,
+                     std::move(protocol_url), std::move(app_id), command_line,
+                     cur_dir, profile, last_used_profile, last_opened_profiles,
+                     std::move(keep_alive), std::move(profile_keep_alives),
+                     std::move(finalize_callback),
+                     std::move(startup_callback)));
+  return true;
+}
+
+}  // namespace startup
+}  // namespace web_app
diff --git a/chrome/browser/ui/startup/web_app_protocol_handling_startup_utils.h b/chrome/browser/ui/startup/web_app_protocol_handling_startup_utils.h
new file mode 100644
index 0000000..b2d3ebcf
--- /dev/null
+++ b/chrome/browser/ui/startup/web_app_protocol_handling_startup_utils.h
@@ -0,0 +1,59 @@
+// 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 CHROME_BROWSER_UI_STARTUP_WEB_APP_PROTOCOL_HANDLING_STARTUP_UTILS_H_
+#define CHROME_BROWSER_UI_STARTUP_WEB_APP_PROTOCOL_HANDLING_STARTUP_UTILS_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+
+class Browser;
+class Profile;
+namespace base {
+class CommandLine;
+class FilePath;
+}  // namespace base
+
+namespace web_app {
+namespace startup {
+
+using FinalizeWebAppLaunchCallback =
+    base::OnceCallback<void(Browser* browser,
+                            apps::mojom::LaunchContainer container)>;
+using StartupLaunchAfterProtocolCallback =
+    base::OnceCallback<void(const base::CommandLine& command_line,
+                            const base::FilePath& cur_dir,
+                            Profile* profile,
+                            Profile* last_used_profile,
+                            const std::vector<Profile*>& last_opened_profiles)>;
+
+// Launches a web app to handle a URL if `command_line` contains
+// --app-id=<app> and at least one URL for which the referenced app is
+// is registered in `profile` to handle the URL's protocol. Returns true
+// if the command_line contains --app-id=<app> and at least one valid URL
+// by checking for blink::IsValidCustomHandlerScheme(). Only one valid URL
+// is expected in `command_line` and all other URLs are ignored.
+// The URL will be further validated by consulting the os_integration_manager
+// and checked for a installed app that is registered in `profile` to handle
+// the URL's protocol. `startup_callback` will run when the check fails and
+// there is no installed web app registered for `profile` that can handle the
+// protocol.`finalize_callback` is used if and only if this function
+// returns true.
+bool MaybeLaunchProtocolHandlerWebApp(
+    const base::CommandLine& command_line,
+    const base::FilePath& cur_dir,
+    Profile* profile,
+    Profile* last_used_profile,
+    const std::vector<Profile*>& last_opened_profiles,
+    FinalizeWebAppLaunchCallback finalize_callback,
+    StartupLaunchAfterProtocolCallback startup_callback);
+
+}  // namespace startup
+
+}  // namespace web_app
+
+#endif  //  CHROME_BROWSER_UI_STARTUP_WEB_APP_PROTOCOL_HANDLING_STARTUP_UTILS_H_
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index cbfa041..ddebb0b 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -490,9 +490,8 @@
   if (browser_view)
     SetBackground(std::make_unique<TopContainerBackground>(browser_view));
 
-  views::FocusRing::SetColorContextForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR,
-      ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
+  views::FocusRing::SetBackgroundColorIdForSubtree(
+      this, ThemeProperties::COLOR_TOOLBAR);
 }
 
 BookmarkBarView::~BookmarkBarView() {
diff --git a/chrome/browser/ui/views/chrome_web_dialog_view.cc b/chrome/browser/ui/views/chrome_web_dialog_view.cc
index 61323fa..9a5ffca 100644
--- a/chrome/browser/ui/views/chrome_web_dialog_view.cc
+++ b/chrome/browser/ui/views/chrome_web_dialog_view.cc
@@ -63,6 +63,10 @@
     bool show) {
   views::WebDialogView* view = new views::WebDialogView(
       context, delegate, std::make_unique<ChromeWebContentsHandler>());
+  // If the corner radius is specified, set it to |views::DialogDelegate|.
+  if (extra_params && extra_params->corner_radius)
+    view->set_corner_radius(*(extra_params->corner_radius));
+
   views::Widget::InitParams params;
   if (extra_params)
     params = std::move(*extra_params);
diff --git a/chrome/browser/ui/views/download/download_shelf_view.cc b/chrome/browser/ui/views/download/download_shelf_view.cc
index 638cef9..d840bb6 100644
--- a/chrome/browser/ui/views/download/download_shelf_view.cc
+++ b/chrome/browser/ui/views/download/download_shelf_view.cc
@@ -109,9 +109,8 @@
   // and return to chrome with the download shelf still open.
   mouse_watcher_.set_notify_on_exit_time(base::TimeDelta::FromSeconds(5));
   SetID(VIEW_ID_DOWNLOAD_SHELF);
-  views::FocusRing::SetColorContextForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR,
-      ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
+  views::FocusRing::SetBackgroundColorIdForSubtree(
+      this, ThemeProperties::COLOR_TOOLBAR);
 }
 
 DownloadShelfView::~DownloadShelfView() = default;
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
index 3d5a552f..8772a0ad 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -155,10 +155,8 @@
 }  // namespace
 
 TabStripRegionView::TabStripRegionView(std::unique_ptr<TabStrip> tab_strip) {
-  // TOOD(pbos): Find a better update. See TabStrip::GetTabForegroundColor.
-  views::FocusRing::SetColorContextForSubtree(
-      this, ThemeProperties::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE,
-      ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
+  views::FocusRing::SetBackgroundColorIdForSubtree(
+      this, ThemeProperties::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE);
 
   layout_manager_ = SetLayoutManager(std::make_unique<views::FlexLayout>());
   layout_manager_->SetOrientation(views::LayoutOrientation::kHorizontal);
diff --git a/chrome/browser/ui/views/infobars/infobar_container_view.cc b/chrome/browser/ui/views/infobars/infobar_container_view.cc
index 1123c61..a9c5cd1 100644
--- a/chrome/browser/ui/views/infobars/infobar_container_view.cc
+++ b/chrome/browser/ui/views/infobars/infobar_container_view.cc
@@ -66,9 +66,8 @@
       content_shadow_(new ContentShadow()) {
   SetID(VIEW_ID_INFO_BAR_CONTAINER);
   AddChildView(content_shadow_);
-  views::FocusRing::SetColorContextForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR,
-      ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
+  views::FocusRing::SetBackgroundColorIdForSubtree(
+      this, ThemeProperties::COLOR_TOOLBAR);
 }
 
 InfoBarContainerView::~InfoBarContainerView() {
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
index ec50f0fc..97029c2a 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
@@ -92,6 +92,8 @@
 
 }  // namespace
 
+// Tests various cases that effect the visibility of the install icon in the
+// omnibox.
 class PwaInstallViewBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   PwaInstallViewBrowserTest()
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index b049562..f519face3b 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -20,6 +20,8 @@
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_test_views_delegate.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -201,7 +203,8 @@
 
 class PageInfoBubbleViewTest : public testing::Test {
  public:
-  PageInfoBubbleViewTest() {}
+  PageInfoBubbleViewTest()
+      : testing_local_state_(TestingBrowserProcess::GetGlobal()) {}
 
   // testing::Test:
   void SetUp() override {
@@ -225,6 +228,7 @@
   }
 
  protected:
+  ScopedTestingLocalState testing_local_state_;
   ScopedWebContentsTestHelper web_contents_helper_;
   views::ScopedViewsTestHelper views_helper_{
       std::make_unique<ChromeTestViewsDelegate<>>()};
@@ -569,9 +573,8 @@
   EXPECT_EQ(kExpectedChildren, api_->permissions_view()->children().size());
 
   // Add the policy setting to prefs.
-  Profile* profile = web_contents_helper_.profile();
-  profile->GetPrefs()->Set(prefs::kManagedSerialAllowUsbDevicesForUrls,
-                           ReadJson(R"([
+  testing_local_state_.Get()->Set(prefs::kManagedSerialAllowUsbDevicesForUrls,
+                                  ReadJson(R"([
                {
                  "devices": [{ "vendor_id": 6353, "product_id": 5678 }],
                  "urls": [ "http://www.example.com" ]
diff --git a/chrome/browser/ui/views/sharing/sharing_icon_view.cc b/chrome/browser/ui/views/sharing/sharing_icon_view.cc
index 5336660..7d710c08 100644
--- a/chrome/browser/ui/views/sharing/sharing_icon_view.cc
+++ b/chrome/browser/ui/views/sharing/sharing_icon_view.cc
@@ -42,12 +42,12 @@
   return web_contents ? get_controller_callback_.Run(web_contents) : nullptr;
 }
 
-void SharingIconView::StartLoadingAnimation() {
+void SharingIconView::StartLoadingAnimation(int icon_label_id) {
   if (loading_animation_)
     return;
 
   loading_animation_ = true;
-  AnimateIn(IDS_BROWSER_SHARING_OMNIBOX_SENDING_LABEL);
+  AnimateIn(icon_label_id);
   SchedulePaint();
 }
 
@@ -75,7 +75,7 @@
   }
 
   if (controller->is_loading())
-    StartLoadingAnimation();
+    StartLoadingAnimation(controller->GetIconLabelId());
   else
     StopLoadingAnimation();
 
diff --git a/chrome/browser/ui/views/sharing/sharing_icon_view.h b/chrome/browser/ui/views/sharing/sharing_icon_view.h
index c3261ff..5a7c5d7 100644
--- a/chrome/browser/ui/views/sharing/sharing_icon_view.h
+++ b/chrome/browser/ui/views/sharing/sharing_icon_view.h
@@ -34,7 +34,7 @@
   SharingIconView& operator=(const SharingIconView&) = delete;
   ~SharingIconView() override;
 
-  void StartLoadingAnimation();
+  void StartLoadingAnimation(int icon_label_id);
   void StopLoadingAnimation();
 
  protected:
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 6f01d8c..b0fb911 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1114,9 +1114,11 @@
           base::BindRepeating(&TabStrip::tabs_view_model,
                               base::Unretained(this)))),
       drag_context_(std::make_unique<TabDragContextImpl>(this)) {
-  views::FocusRing::SetColorContextForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR,
-      ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
+  // TODO(pbos): This is probably incorrect, the background of individual tabs
+  // depend on their selected state. This should probably be pushed down into
+  // tabs.
+  views::FocusRing::SetBackgroundColorIdForSubtree(
+      this, ThemeProperties::COLOR_TOOLBAR);
   Init();
   SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index b80a9dc170..eab5868 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -176,9 +176,8 @@
     for (const auto& view_and_command : GetViewCommandMap())
       chrome::AddCommandObserver(browser_, view_and_command.second, this);
   }
-  views::FocusRing::SetColorContextForSubtree(
-      this, ThemeProperties::COLOR_TOOLBAR,
-      ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
+  views::FocusRing::SetBackgroundColorIdForSubtree(
+      this, ThemeProperties::COLOR_TOOLBAR);
 }
 
 ToolbarView::~ToolbarView() {
diff --git a/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc b/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
index b8692634..700f05b 100644
--- a/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
+++ b/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
@@ -74,6 +74,8 @@
                                   "/ssl/page_displays_insecure_content.html");
   }
 
+  // This URL is on app.com, and the page contains a secure iframe that points
+  // to foo.com/simple.html.
   GURL GetSecureIFrameAppURL() {
     net::HostPortPair host_port_pair = net::HostPortPair::FromURL(
         https_server()->GetURL("foo.com", "/simple.html"));
@@ -140,7 +142,7 @@
                                          ->GetActiveWebContents()
                                          ->GetLastCommittedURL());
 
-  // The WebContents is just reparented, so mixed content is still not loaded.
+  // The WebContents is just reparented, so mixed content is still loaded.
   CheckMixedContentLoaded(browser());
   EXPECT_EQ(GetAppMenuCommandState(IDC_OPEN_IN_PWA_WINDOW, browser()),
             kEnabled);
@@ -223,6 +225,7 @@
       browser()->tab_strip_model()->GetActiveWebContents(), app_id);
   CheckMixedContentFailedToLoad(app_browser);
 
+  // Change the mixed content to be acceptable.
   content::RenderFrameHost* main_frame =
       app_browser->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
   content::RenderFrameHost* iframe = content::ChildFrameAt(main_frame, 0);
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index e85f0f5..aa7fda5 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -431,7 +431,7 @@
                                    /*open_as_window=*/false));
 }
 
-// Tests that desktop PWAs open links in the browser.
+// Tests that desktop PWAs open out-of-scope links with a custom toolbar.
 IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, DesktopPWAsOpenLinksInApp) {
   const GURL app_url = GetSecureAppURL();
   const AppId app_id = InstallPWA(app_url);
diff --git a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
index ed790af1..76ce7af 100644
--- a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
+#include "chrome/browser/ui/webui/settings/site_settings_helper.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/file_handler_manager.h"
 #include "chrome/browser/web_applications/components/os_integration_manager.h"
@@ -129,10 +130,6 @@
     return https_server()->GetURL("app.com", "/ssl/page_with_frame.html");
   }
 
-  GURL GetSecondAppUrl() {
-    return https_server()->GetURL("app.com", "/pwa/app2.html");
-  }
-
   void InstallFileHandlingPWA() {
     GURL url = GetSecureAppURL();
 
@@ -168,9 +165,7 @@
         WebAppControllerBrowserTest::InstallWebApp(std::move(web_app_info));
   }
 
-  void InstallSecondFileHandlingPWASameOrigin() {
-    GURL url = GetSecondAppUrl();
-
+  void InstallAnotherFileHandlingPwa(const GURL& url) {
     auto web_app_info = std::make_unique<WebApplicationInfo>();
     web_app_info->start_url = url;
     web_app_info->scope = url.GetWithoutFilename();
@@ -214,6 +209,9 @@
   return new_file_path;
 }
 
+// // Launches the |app_id| web app with |files| handles, awaits for
+// |expected_launch_url| to load and stashes any launch params on
+// "window.launchParams" for further inspection.
 content::WebContents* LaunchApplication(
     Profile* profile,
     const std::string& app_id,
@@ -409,6 +407,95 @@
   }
 }
 
+// Tests that when two apps are installed and share an origin (but not scope),
+// `GetFileHandlersForAllWebAppsWithOrigin` will report all the file handlers
+// across both apps.
+IN_PROC_BROWSER_TEST_F(WebAppFileHandlingBrowserTest,
+                       FileHandlerAggregationForUi) {
+  InstallFileHandlingPWA();
+  EXPECT_EQ(3U,
+            GetFileHandlersForAllWebAppsWithOrigin(profile(), GetSecureAppURL())
+                .size());
+
+  GURL second_app_url = https_server()->GetURL("app.com", "/pwa/app2.html");
+  InstallAnotherFileHandlingPwa(second_app_url);
+  EXPECT_EQ(2U, registrar().GetAppIds().size());
+  EXPECT_EQ(4U,
+            GetFileHandlersForAllWebAppsWithOrigin(profile(), GetSecureAppURL())
+                .size());
+  EXPECT_EQ(
+      4U,
+      GetFileHandlersForAllWebAppsWithOrigin(profile(), second_app_url).size());
+
+  std::u16string display_string_app1 =
+      GetFileTypeAssociationsHandledByWebAppsForDisplay(profile(),
+                                                        GetSecureAppURL());
+  std::u16string display_string_app2 =
+      GetFileTypeAssociationsHandledByWebAppsForDisplay(profile(),
+                                                        second_app_url);
+  EXPECT_EQ(display_string_app1, display_string_app2);
+#if defined(OS_LINUX)
+  const std::u16string kHtmlDisplayString = u"text/html";
+  const std::u16string kJpegDisplayString = u"image/jpeg";
+#else
+  const std::u16string kHtmlDisplayString = u"HTML";
+  const std::u16string kJpegDisplayString = u"JPEG";
+#endif
+  EXPECT_NE(std::u16string::npos, display_string_app1.find(kHtmlDisplayString));
+  EXPECT_NE(std::u16string::npos, display_string_app1.find(kJpegDisplayString));
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppFileHandlingBrowserTest,
+                       SometimesResetPermission) {
+  // Install the first app and simulate the user granting it the file handling
+  // permission.
+  InstallFileHandlingPWA();
+  auto* map = HostContentSettingsMapFactory::GetForProfile(profile());
+  const GURL origin = GetSecureAppURL().GetOrigin();
+  EXPECT_EQ(CONTENT_SETTING_ASK,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
+  map->SetContentSettingDefaultScope(origin, origin,
+                                     ContentSettingsType::FILE_HANDLING,
+                                     CONTENT_SETTING_ALLOW);
+  EXPECT_EQ(CONTENT_SETTING_ALLOW,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
+
+  // Install a second app, which is on the same origin and asks to handle more
+  // file types. The permission should have been set back to ASK.
+  GURL second_app_url = https_server()->GetURL("app.com", "/pwa/app2.html");
+  InstallAnotherFileHandlingPwa(second_app_url);
+  EXPECT_EQ(CONTENT_SETTING_ASK,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
+
+  // Set to ALLOW again.
+  map->SetContentSettingDefaultScope(origin, origin,
+                                     ContentSettingsType::FILE_HANDLING,
+                                     CONTENT_SETTING_ALLOW);
+
+  // Install a third app, which is on a different origin; this should have no
+  // effect on the permission.
+  GURL third_app_url = https_server()->GetURL("otherapp.com", "/pwa/app2.html");
+  InstallAnotherFileHandlingPwa(third_app_url);
+  EXPECT_EQ(CONTENT_SETTING_ALLOW,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
+  GURL third_app_origin = third_app_url.GetOrigin();
+  EXPECT_EQ(CONTENT_SETTING_ASK,
+            map->GetContentSetting(third_app_origin, third_app_origin,
+                                   ContentSettingsType::FILE_HANDLING));
+  // Install a fourth app, which is on the same origin but asks for a subset of
+  // the file types of the first two. This should have no effect on the
+  // permission.
+  GURL fourth_app_url = https_server()->GetURL("app.com", "/pwa2/app2.html");
+  InstallAnotherFileHandlingPwa(fourth_app_url);
+  EXPECT_EQ(CONTENT_SETTING_ALLOW,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
+}
+
 class WebAppFileHandlingOriginTrialBrowserTest
     : public WebAppFileHandlingTestBase {
  public:
@@ -761,41 +848,33 @@
                   ->file_handler_permission_blocked());
 }
 
-// Tests that when two apps are installed and share an origin (but not scope),
-// `GetFileHandlersForAllWebAppsWithOrigin` will report all the file handlers
-// across both apps.
-IN_PROC_BROWSER_TEST_F(WebAppFileHandlingPolicyBrowserTest,
-                       FileHandlerAggregationForUi) {
+IN_PROC_BROWSER_TEST_F(WebAppFileHandlingBrowserTest,
+                       SettingsCategoryVisibility) {
+  // The file handling permission is visible in a general context.
+  const std::vector<ContentSettingsType>& all_categories =
+      site_settings::GetVisiblePermissionCategories();
+  EXPECT_FALSE(std::find(all_categories.begin(), all_categories.end(),
+                         ContentSettingsType::FILE_HANDLING) ==
+               all_categories.end());
+
+  // The file handling permission is not visible in the context of an origin
+  // that doesn't correspond to a PWA.
+  std::vector<ContentSettingsType> categories_for_arbitrary_website =
+      site_settings::GetVisiblePermissionCategoriesForOrigin(
+          profile(), GURL("https://example.com"));
+  EXPECT_TRUE(std::find(categories_for_arbitrary_website.begin(),
+                        categories_for_arbitrary_website.end(),
+                        ContentSettingsType::FILE_HANDLING) ==
+              categories_for_arbitrary_website.end());
+
+  // The file handling permission *is* visible for a PWA origin.
   InstallFileHandlingPWA();
-  EXPECT_EQ(3U,
-            GetFileHandlersForAllWebAppsWithOrigin(profile(), GetSecureAppURL())
-                .size());
-
-  InstallSecondFileHandlingPWASameOrigin();
-  EXPECT_EQ(2U, registrar().GetAppIds().size());
-  EXPECT_EQ(4U,
-            GetFileHandlersForAllWebAppsWithOrigin(profile(), GetSecureAppURL())
-                .size());
-  EXPECT_EQ(4U,
-            GetFileHandlersForAllWebAppsWithOrigin(profile(), GetSecondAppUrl())
-                .size());
-
-  std::u16string display_string_app1 =
-      GetFileTypeAssociationsHandledByWebAppsForDisplay(profile(),
-                                                        GetSecureAppURL());
-  std::u16string display_string_app2 =
-      GetFileTypeAssociationsHandledByWebAppsForDisplay(profile(),
-                                                        GetSecondAppUrl());
-  EXPECT_EQ(display_string_app1, display_string_app2);
-#if defined(OS_LINUX)
-  std::u16string html = u"text/html";
-  std::u16string jpeg = u"image/jpeg";
-#else
-  std::u16string html = u"HTML";
-  std::u16string jpeg = u"JPEG";
-#endif
-  EXPECT_NE(std::u16string::npos, display_string_app1.find(html));
-  EXPECT_NE(std::u16string::npos, display_string_app1.find(jpeg));
+  std::vector<ContentSettingsType> categories_for_pwa =
+      site_settings::GetVisiblePermissionCategoriesForOrigin(
+          profile(), GetSecureAppURL().GetOrigin());
+  EXPECT_FALSE(std::find(categories_for_pwa.begin(), categories_for_pwa.end(),
+                         ContentSettingsType::FILE_HANDLING) ==
+               categories_for_pwa.end());
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
index dd6fe10..2543ce6d 100644
--- a/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_link_capturing_browsertest.cc
@@ -68,6 +68,8 @@
 
 namespace web_app {
 
+// Tests that links are captured correctly into an installed WebApp using the
+// 'tabbed' display mode, which allows the webapp window to have multiple tabs.
 class WebAppLinkCapturingBrowserTest : public WebAppNavigationBrowserTest {
  public:
   WebAppLinkCapturingBrowserTest() {
@@ -206,6 +208,8 @@
   base::test::ScopedFeatureList features_;
 };
 
+// First in scope navigation from about:blank gets captured and reparented into
+// the app window.
 IN_PROC_BROWSER_TEST_F(WebAppTabStripLinkCapturingBrowserTest,
                        InScopeNavigationsCaptured) {
   InstallTestApp();
diff --git a/chrome/browser/ui/webui/app_management/app_management.mojom b/chrome/browser/ui/webui/app_management/app_management.mojom
index a39fcf3..d9612846 100644
--- a/chrome/browser/ui/webui/app_management/app_management.mojom
+++ b/chrome/browser/ui/webui/app_management/app_management.mojom
@@ -25,6 +25,7 @@
   bool hide_more_settings;
   bool hide_pin_to_shelf;
   bool is_preferred_app;
+  apps.mojom.WindowMode window_mode;
 };
 
 // Extension-based apps primarily use install-time permissions that cannot be
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index 9e1cc5a..3a72668 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -224,6 +224,7 @@
   app->hide_pin_to_shelf =
       update.ShowInShelf() == apps::mojom::OptionalBool::kFalse ||
       ShouldHidePinToShelf(app->id);
+  app->window_mode = update.WindowMode();
 
   return app;
 }
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
index 8a072a2..f79625d 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
@@ -62,9 +62,10 @@
   }
 
   current_instance_ = new MultiDeviceSetupDialog();
-  containing_window_ = chrome::ShowWebDialog(
-      nullptr /* parent */, ProfileManager::GetActiveUserProfile(),
-      current_instance_);
+  current_instance_->ShowSystemDialogForBrowserContext(
+      ProfileManager::GetActiveUserProfile(), nullptr);
+
+  containing_window_ = current_instance_->dialog_window();
 
   // Remove the black backdrop behind the dialog window which appears in tablet
   // and full-screen mode.
diff --git a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc
index f20c80f..391fc72 100644
--- a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc
+++ b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.cc
@@ -29,12 +29,25 @@
 
 namespace {
 
+constexpr int kSystemDialogCornerRadiusDp = 12;
+
 // Track all open system web dialog instances. This should be a small list.
 std::list<SystemWebDialogDelegate*>* GetInstances() {
   static base::NoDestructor<std::list<SystemWebDialogDelegate*>> instances;
   return instances.get();
 }
 
+// Creates default initial parameters. The system web dialog has 12 dip corner
+// radius by default. If the window has a non-client frame view, we don't need
+// to set shadow, since the bubble frame view will help draw the shadow.
+views::Widget::InitParams CreateWidgetParams() {
+  views::Widget::InitParams params;
+  params.corner_radius = kSystemDialogCornerRadiusDp;
+  // Dialog frame view has its own shadow.
+  params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
+  return params;
+}
+
 }  // namespace
 
 // static
@@ -166,6 +179,11 @@
   size->SetSize(kDialogWidth, kDialogHeight);
 }
 
+SystemWebDialogDelegate::FrameKind
+SystemWebDialogDelegate::GetWebDialogFrameKind() const {
+  return FrameKind::kDialog;
+}
+
 std::string SystemWebDialogDelegate::GetDialogArgs() const {
   return std::string();
 }
@@ -200,7 +218,8 @@
 void SystemWebDialogDelegate::ShowSystemDialogForBrowserContext(
     content::BrowserContext* browser_context,
     gfx::NativeWindow parent) {
-  views::Widget::InitParams extra_params;
+  views::Widget::InitParams extra_params = CreateWidgetParams();
+
   // If unparented and not modal, keep it on top (see header comment).
   if (!parent && GetDialogModalType() == ui::MODAL_TYPE_NONE)
     extra_params.z_order = ui::ZOrderLevel::kFloatingWindow;
diff --git a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h
index b0e28ac..719356c8 100644
--- a/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h
+++ b/chrome/browser/ui/webui/chromeos/system_web_dialog_delegate.h
@@ -72,6 +72,7 @@
   void GetWebUIMessageHandlers(
       std::vector<content::WebUIMessageHandler*>* handlers) const override;
   void GetDialogSize(gfx::Size* size) const override;
+  FrameKind GetWebDialogFrameKind() const override;
   std::string GetDialogArgs() const override;
   void OnDialogShown(content::WebUI* webui) override;
   // Note: deletes |this|.
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 93ef644..c3c17be 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -70,22 +70,6 @@
 
 constexpr char kPrevNavigationTimePrefName[] = "NewTabPage.PrevNavigationTime";
 
-bool IsDriveModuleEnabled(Profile* profile) {
-  if (!base::FeatureList::IsEnabled(ntp_features::kNtpDriveModule)) {
-    return false;
-  }
-  if (base::GetFieldTrialParamValueByFeature(
-          ntp_features::kNtpDriveModule,
-          ntp_features::kNtpDriveModuleManagedUsersOnlyParam) != "true") {
-    return true;
-  }
-  auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
-  return identity_manager
-      ->FindExtendedAccountInfoByAccountId(
-          identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync))
-      .IsManaged();
-}
-
 content::WebUIDataSource* CreateNewTabPageUiHtmlSource(
     Profile* profile,
     const base::Time& navigation_start_time) {
@@ -276,7 +260,8 @@
   source->AddBoolean(
       "chromeCartModuleEnabled",
       base::FeatureList::IsEnabled(ntp_features::kNtpChromeCartModule));
-  source->AddBoolean("driveModuleEnabled", IsDriveModuleEnabled(profile));
+  source->AddBoolean("driveModuleEnabled",
+                     NewTabPageUI::IsDriveModuleEnabled(profile));
   source->AddBoolean(
       "ruleBasedDiscountEnabled",
       base::GetFieldTrialParamValueByFeature(
@@ -366,6 +351,24 @@
   registry->RegisterTimePref(kPrevNavigationTimePrefName, base::Time());
 }
 
+// static
+bool NewTabPageUI::IsDriveModuleEnabled(Profile* profile) {
+  if (!base::FeatureList::IsEnabled(ntp_features::kNtpDriveModule)) {
+    return false;
+  }
+  if (base::GetFieldTrialParamValueByFeature(
+          ntp_features::kNtpDriveModule,
+          ntp_features::kNtpDriveModuleManagedUsersOnlyParam) != "true") {
+    return true;
+  }
+  // TODO(https://crbug.com/1213351): Stop calling the private method
+  // FindExtendedPrimaryAccountInfo().
+  auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
+  return identity_manager
+      ->FindExtendedPrimaryAccountInfo(signin::ConsentLevel::kSync)
+      .IsManaged();
+}
+
 void NewTabPageUI::BindInterface(
     mojo::PendingReceiver<new_tab_page::mojom::PageHandlerFactory>
         pending_receiver) {
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
index 8d71068..04c66f5 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -64,6 +64,7 @@
 
   static bool IsNewTabPageOrigin(const GURL& url);
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+  static bool IsDriveModuleEnabled(Profile* profile);
 
   // Instantiates the implementor of the mojom::PageHandlerFactory mojo
   // interface passing the pending receiver that will be internally bound.
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chrome/browser/ui/webui/settings/site_settings_handler.cc
index 918e662f..88ad648 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.cc
@@ -422,6 +422,10 @@
       base::BindRepeating(&SiteSettingsHandler::HandleGetAllSites,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
+      "getCategoryList",
+      base::BindRepeating(&SiteSettingsHandler::HandleGetCategoryList,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       "getCookieSettingDescription",
       base::BindRepeating(
           &SiteSettingsHandler::HandleGetCookieSettingDescription,
@@ -730,15 +734,12 @@
 void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) {
   AllowJavascript();
 
-  CHECK_EQ(2U, args->GetList().size());
+  CHECK_EQ(1U, args->GetList().size());
   std::string callback_id = args->GetList()[0].GetString();
-  auto types = args->GetList()[1].GetList();
 
   all_sites_map_.clear();
   origin_permission_set_.clear();
 
-  auto content_types = site_settings::ContentSettingsTypesFromGroupNames(types);
-
   // Incognito contains incognito content settings plus non-incognito content
   // settings. Thus if it exists, just get exceptions for the incognito profile.
   Profile* profile = profile_;
@@ -750,6 +751,12 @@
   HostContentSettingsMap* map =
       HostContentSettingsMapFactory::GetForProfile(profile);
 
+  std::vector<ContentSettingsType> content_types =
+      site_settings::GetVisiblePermissionCategories();
+  // Make sure to include cookies, because All Sites handles data storage
+  // cookies as well as regular ContentSettingsTypes.
+  content_types.push_back(ContentSettingsType::COOKIES);
+
   // Retrieve a list of embargoed settings to check separately. This ensures
   // that only settings included in |content_types| will be listed in all sites.
   auto* autoblocker =
@@ -792,6 +799,24 @@
   ResolveJavascriptCallback(base::Value(callback_id), result);
 }
 
+void SiteSettingsHandler::HandleGetCategoryList(const base::ListValue* args) {
+  AllowJavascript();
+
+  CHECK_EQ(2U, args->GetList().size());
+  std::string callback_id = args->GetList()[0].GetString();
+  GURL origin(args->GetList()[1].GetString());
+
+  std::vector<ContentSettingsType> content_types =
+      site_settings::GetVisiblePermissionCategoriesForOrigin(profile_, origin);
+
+  base::Value result(base::Value::Type::LIST);
+  for (ContentSettingsType content_type : content_types) {
+    result.Append(site_settings::ContentSettingsTypeToGroupName(content_type));
+  }
+
+  ResolveJavascriptCallback(base::Value(callback_id), result);
+}
+
 void SiteSettingsHandler::HandleGetCookieSettingDescription(
     const base::ListValue* args) {
   AllowJavascript();
@@ -805,12 +830,12 @@
     const base::ListValue* args) {
   AllowJavascript();
 
-  CHECK_EQ(3U, args->GetList().size());
+  CHECK_EQ(2U, args->GetList().size());
   std::string callback_id = args->GetList()[0].GetString();
-  auto types = args->GetList()[1].GetList();
-  size_t max_sources = base::checked_cast<size_t>(args->GetList()[2].GetInt());
+  size_t max_sources = base::checked_cast<size_t>(args->GetList()[1].GetInt());
 
-  auto content_types = site_settings::ContentSettingsTypesFromGroupNames(types);
+  const std::vector<ContentSettingsType>& content_types =
+      site_settings::GetVisiblePermissionCategories();
   auto recent_site_permissions = site_settings::GetRecentSitePermissions(
       profile_, content_types, max_sources);
 
@@ -1019,28 +1044,28 @@
 void SiteSettingsHandler::HandleSetOriginPermissions(
     const base::ListValue* args) {
   CHECK_EQ(3U, args->GetSize());
-  std::string origin_string;
-  CHECK(args->GetString(0, &origin_string));
-  const base::ListValue* types;
-  CHECK(args->GetList(1, &types));
-  std::string value;
-  CHECK(args->GetString(2, &value));
+  std::string origin_string = args->GetList()[0].GetString();
+  const std::string* type_string = args->GetList()[1].GetIfString();
+  std::string value = args->GetList()[2].GetString();
 
   const GURL origin(origin_string);
   if (!origin.is_valid())
     return;
 
+  std::vector<ContentSettingsType> types;
+  if (type_string) {
+    types.push_back(
+        site_settings::ContentSettingsTypeFromGroupName(*type_string));
+  } else {
+    types = site_settings::GetVisiblePermissionCategoriesForOrigin(profile_,
+                                                                   origin);
+  }
+
   ContentSetting setting;
   CHECK(content_settings::ContentSettingFromString(value, &setting));
-  for (size_t i = 0; i < types->GetSize(); ++i) {
-    std::string type;
-    types->GetString(i, &type);
-
-    ContentSettingsType content_type =
-        site_settings::ContentSettingsTypeFromGroupName(type);
-    HostContentSettingsMap* map =
-        HostContentSettingsMapFactory::GetForProfile(profile_);
-
+  HostContentSettingsMap* map =
+      HostContentSettingsMapFactory::GetForProfile(profile_);
+  for (ContentSettingsType content_type : types) {
     permissions::PermissionUmaUtil::ScopedRevocationReporter
         scoped_revocation_reporter(
             profile_, origin, origin, content_type,
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler.h b/chrome/browser/ui/webui/settings/site_settings_handler.h
index 881025a..9dd5158 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_handler.h
@@ -169,14 +169,20 @@
   // the front end when fetching finished.
   void HandleGetAllSites(const base::ListValue* args);
 
+  // Returns a list of content settings types that are controlled via a standard
+  // permissions UI and should be made visible to the user. There is a single
+  // nullable string argument, which represents an associated origin. See
+  // `SiteSettingsPrefsBrowserProxy#getCategoryList`.
+  void HandleGetCategoryList(const base::ListValue* args);
+
   // Returns a string for display describing the current cookie settings.
   void HandleGetCookieSettingDescription(const base::ListValue* args);
 
   // Returns a list containing the most recent permission changes for the
-  // provided content types grouped by origin/profile (incognito, regular)
-  // combinations, limited to N origin/profile pairings. This includes
-  // permission changes made by embargo, but does not include permissions
-  // enforced via policy.
+  // content types that are visiblein settings, grouped by origin/profile
+  // (incognito, regular) combinations, limited to N origin/profile pairings.
+  // This includes permission changes made by embargo, but does not include
+  // permissions enforced via policy.
   void HandleGetRecentSitePermissions(const base::ListValue* args);
 
   // Called when the list of origins using storage has been fetched, and sends
diff --git a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
index 98c96b29..c6775725 100644
--- a/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc
@@ -543,9 +543,6 @@
 TEST_F(SiteSettingsHandlerTest, GetAllSites) {
   base::ListValue get_all_sites_args;
   get_all_sites_args.AppendString(kCallbackId);
-  base::Value category_list(base::Value::Type::LIST);
-  category_list.Append(kNotifications);
-  get_all_sites_args.Append(std::move(category_list));
 
   // Test all sites is empty when there are no preferences.
   handler()->HandleGetAllSites(&get_all_sites_args);
@@ -744,9 +741,6 @@
 
   base::ListValue get_recent_permissions_args;
   get_recent_permissions_args.AppendString(kCallbackId);
-  base::Value category_list(base::Value::Type::LIST);
-  category_list.Append(kNotifications);
-  get_recent_permissions_args.Append(std::move(category_list));
   get_recent_permissions_args.Append(3);
 
   // Configure prefs and auto blocker with a controllable clock.
@@ -783,15 +777,11 @@
         url1, ContentSettingsType::NOTIFICATIONS, false);
 
   clock.Advance(base::TimeDelta::FromHours(2));
-  map->SetContentSettingDefaultScope(url2, url2, ContentSettingsType::IMAGES,
-                                     CONTENT_SETTING_ALLOW);
   clock.Advance(base::TimeDelta::FromHours(1));
   CreateIncognitoProfile();
   HostContentSettingsMap* incognito_map =
       HostContentSettingsMapFactory::GetForProfile(incognito_profile());
   incognito_map->SetClockForTesting(&clock);
-  incognito_map->SetContentSettingDefaultScope(
-      url1, url1, ContentSettingsType::IMAGES, CONTENT_SETTING_ALLOW);
 
   clock.Advance(base::TimeDelta::FromHours(1));
   permissions::PermissionDecisionAutoBlocker* incognito_auto_blocker =
@@ -1340,11 +1330,7 @@
   // Block notifications.
   base::ListValue set_args;
   set_args.AppendString(origin_with_port);
-  {
-    auto category_list = std::make_unique<base::ListValue>();
-    category_list->AppendString(kNotifications);
-    set_args.Append(std::move(category_list));
-  }
+  set_args.AppendString(kNotifications);
   set_args.AppendString(
       content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
   handler()->HandleSetOriginPermissions(&set_args);
@@ -1353,9 +1339,7 @@
   // Reset things back to how they were.
   base::ListValue reset_args;
   reset_args.AppendString(origin_with_port);
-  auto category_list = std::make_unique<base::ListValue>();
-  category_list->AppendString(kNotifications);
-  reset_args.Append(std::move(category_list));
+  reset_args.AppendString(std::move(kNotifications));
   reset_args.AppendString(
       content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT));
 
@@ -1677,11 +1661,7 @@
   // Block notifications.
   base::ListValue set_args;
   set_args.AppendString(origin_anchor_string);
-  {
-    auto category_list = std::make_unique<base::ListValue>();
-    category_list->AppendString(kNotifications);
-    set_args.Append(std::move(category_list));
-  }
+  set_args.AppendString(kNotifications);
   set_args.AppendString(
       content_settings::ContentSettingToString(CONTENT_SETTING_BLOCK));
   handler()->HandleSetOriginPermissions(&set_args);
@@ -1873,9 +1853,6 @@
   {
     base::ListValue get_all_sites_args;
     get_all_sites_args.AppendString(kCallbackId);
-    base::Value category_list(base::Value::Type::LIST);
-    category_list.Append(kNotifications);
-    get_all_sites_args.Append(std::move(category_list));
 
     handler()->HandleGetAllSites(&get_all_sites_args);
 
@@ -1911,9 +1888,6 @@
   {
     base::ListValue get_recent_permissions_args;
     get_recent_permissions_args.AppendString(kCallbackId);
-    base::Value category_list(base::Value::Type::LIST);
-    category_list.Append(kNotifications);
-    get_recent_permissions_args.Append(std::move(category_list));
     get_recent_permissions_args.Append(3);
 
     handler()->HandleGetRecentSitePermissions(&get_recent_permissions_args);
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chrome/browser/ui/webui/settings/site_settings_helper.cc
index b4dcf0a..7831684 100644
--- a/chrome/browser/ui/webui/settings/site_settings_helper.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_helper.cc
@@ -9,10 +9,13 @@
 #include <set>
 #include <string>
 
+#include "base/command_line.h"
 #include "base/containers/contains.h"
 #include "base/feature_list.h"
+#include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/browser/bluetooth/bluetooth_chooser_context_factory.h"
 #include "chrome/browser/content_settings/chrome_content_settings_utils.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -25,6 +28,7 @@
 #include "chrome/browser/subresource_filter/subresource_filter_profile_context_factory.h"
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
+#include "chrome/browser/web_applications/components/web_app_utils.h"
 #include "chrome/common/pref_names.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
@@ -43,6 +47,7 @@
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/common/url_utils.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/constants.h"
@@ -401,16 +406,80 @@
   return base::StringPiece();
 }
 
-std::vector<ContentSettingsType> ContentSettingsTypesFromGroupNames(
-    const base::Value::ConstListView types) {
-  std::vector<ContentSettingsType> content_types;
-  content_types.reserve(types.size());
-  for (const auto& value : types) {
-    const auto& type = value.GetString();
-    content_types.push_back(
-        site_settings::ContentSettingsTypeFromGroupName(type));
+const std::vector<ContentSettingsType>& GetVisiblePermissionCategories() {
+  // First build the list of permissions that will be shown regardless of
+  // `origin`. Some categories such as COOKIES store their data in a custom way,
+  // so are not included here.
+  static base::NoDestructor<std::vector<ContentSettingsType>> base_types({
+    ContentSettingsType::AR, ContentSettingsType::AUTOMATIC_DOWNLOADS,
+        ContentSettingsType::BACKGROUND_SYNC,
+        ContentSettingsType::CLIPBOARD_READ_WRITE,
+        ContentSettingsType::FILE_HANDLING,
+        ContentSettingsType::FILE_SYSTEM_WRITE_GUARD,
+        ContentSettingsType::FONT_ACCESS, ContentSettingsType::GEOLOCATION,
+        ContentSettingsType::HID_GUARD, ContentSettingsType::IDLE_DETECTION,
+        ContentSettingsType::IMAGES, ContentSettingsType::JAVASCRIPT,
+        ContentSettingsType::MEDIASTREAM_CAMERA,
+        ContentSettingsType::MEDIASTREAM_MIC, ContentSettingsType::MIDI_SYSEX,
+        ContentSettingsType::MIXEDSCRIPT, ContentSettingsType::NOTIFICATIONS,
+        ContentSettingsType::POPUPS,
+#if defined(IS_CHROMEOS_ASH) || defined(OS_WIN)
+        ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER,
+#endif
+        ContentSettingsType::SENSORS, ContentSettingsType::SERIAL_GUARD,
+        ContentSettingsType::SOUND, ContentSettingsType::USB_GUARD,
+        ContentSettingsType::VR,
+  });
+  static bool initialized = false;
+  if (!initialized) {
+    // The permission categories in this block are only shown when running with
+    // certain flags/switches.
+    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+            ::switches::kEnableExperimentalWebPlatformFeatures)) {
+      base_types->push_back(ContentSettingsType::BLUETOOTH_SCANNING);
+      base_types->push_back(ContentSettingsType::WINDOW_PLACEMENT);
+    }
+
+    if (base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps))
+      base_types->push_back(ContentSettingsType::PAYMENT_HANDLER);
+
+    if (base::FeatureList::IsEnabled(
+            features::kWebBluetoothNewPermissionsBackend)) {
+      base_types->push_back(ContentSettingsType::BLUETOOTH_GUARD);
+    }
+
+    if (base::FeatureList::IsEnabled(
+            subresource_filter::kSafeBrowsingSubresourceFilter)) {
+      base_types->push_back(ContentSettingsType::ADS);
+    }
+
+    initialized = true;
   }
-  return content_types;
+
+  return *base_types;
+}
+
+std::vector<ContentSettingsType> GetVisiblePermissionCategoriesForOrigin(
+    Profile* profile,
+    const GURL& origin) {
+  const std::vector<ContentSettingsType>& base_types =
+      GetVisiblePermissionCategories();
+  std::vector<ContentSettingsType> types_for_origin;
+  std::copy_if(
+      base_types.begin(), base_types.end(),
+      std::back_inserter(types_for_origin),
+      [&profile, &origin](ContentSettingsType type) {
+        // File Handling is only relevant for installed PWAs that ask for
+        // certain file types; if this is not the case, the control will do
+        // nothing and thus is hidden.
+        if (type == ContentSettingsType::FILE_HANDLING &&
+            web_app::GetFileHandlersForAllWebAppsWithOrigin(profile, origin)
+                .empty()) {
+          return false;
+        }
+        return true;
+      });
+  return types_for_origin;
 }
 
 std::string SiteSettingSourceToString(const SiteSettingSource source) {
diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.h b/chrome/browser/ui/webui/settings/site_settings_helper.h
index 817b2b0..685203f 100644
--- a/chrome/browser/ui/webui/settings/site_settings_helper.h
+++ b/chrome/browser/ui/webui/settings/site_settings_helper.h
@@ -112,9 +112,17 @@
 ContentSettingsType ContentSettingsTypeFromGroupName(base::StringPiece name);
 base::StringPiece ContentSettingsTypeToGroupName(ContentSettingsType type);
 
-// Converts a ListValue of group names to a list of ContentSettingsTypes
-std::vector<ContentSettingsType> ContentSettingsTypesFromGroupNames(
-    const base::Value::ConstListView types);
+// Returns a list of all content settings types that correspond to permissions
+// and which should be displayed in chrome://settings, for any situation not
+// tied to particular a origin.
+const std::vector<ContentSettingsType>& GetVisiblePermissionCategories();
+
+// Returns a list of all content settings types that correspond to permissions
+// and which should be displayed in chrome://settings for the given |origin|.
+// This will not include categories that are not relevant for the given origin.
+std::vector<ContentSettingsType> GetVisiblePermissionCategoriesForOrigin(
+    Profile* profile,
+    const GURL& origin);
 
 // Converts a SiteSettingSource to its string identifier.
 std::string SiteSettingSourceToString(const SiteSettingSource source);
diff --git a/chrome/browser/web_applications/README.md b/chrome/browser/web_applications/README.md
index b0ba8f57b..1a865bd 100644
--- a/chrome/browser/web_applications/README.md
+++ b/chrome/browser/web_applications/README.md
@@ -125,9 +125,20 @@
 
 ## What makes up Chromium's implementation?
 
-The task of turning web sites into "apps" in the user's OS environment has
-surprisingly many parts to it. Here are some (but not all) of the key ones.
+The task of turning web sites into "apps" in the user's OS environment has many parts to it. Before going into the parts, here is where they live:
 
+[![](docs/webappprovider_component_ownership.jpg)](https://docs.google.com/drawings/d/1TqUF2Pqh2S5qPGyA6njQWxOgSgKQBPePKPIH_srGeRk/edit?usp=sharing)
+
+* The `WebAppProvider` core system lives on the `Profile` object.
+* The `WebAppUiManagerImpl` also lives on the `Profile` object (to avoid deps issues).
+* The `AppBrowserController` (typically `WebAppBrowserController` for our interests) lives on the `Browser` object.
+* The `WebAppTabHelperBase` lives on the `WebContents` object.
+
+While most on-disk storage is done in the [`WebAppSyncBridge`](#webappsyncbridge), the system also sometimes uses the `PrefService`. Most of these prefs live on the `Profile` (`profile->GetPrefs()`), but some prefs are in the global browser prefs (`g_browser_process->local_state()`). See the [storage](#storage) section below for more info.
+
+There is a presentation that also goes over the class structure and dependency diagram [here](https://docs.google.com/presentation/d/1bJfUFPMh7J_Avw3J4HBvAN2RWnV4T9LOXr0JSAgQAvg/pub), but it may be out of date.
+
+Here is more info for some (but not all) of the key parts:
 
 ### [`WebAppProvider`](web_app_provider.h)
 
@@ -154,6 +165,20 @@
 `Extension`s and in that mode there were no `WebApp`s; instead everything was
 stored on an `Extension`.
 
+### [`WebAppSyncBridge`](web_app_sync_bridge.h)
+
+This is "bridge" between the WebAppProvider system's in-memory representation of web apps and the sync system's database representation (along with sync system functionality like add/remove/modify operations). This integration is a little complex and deserves it's own document, but it basically:
+* Stores all WebApps into a database and updates the database if any fields change.
+* Updates the system when there are changes from the sync system.
+  * Installs new apps, uninstalls apps the user uninstalled elsewhere, updates metadata like user display mode preference, etc.
+* Tells the sync system if there are local changes (installs, uninstalls, etc).
+
+There is also a slide in a presentation [here](https://docs.google.com/presentation/d/e/2PACX-1vQxYZoCyhZ4xHS4pVuBC9YoE0O-QpW2Wj3scl6jtr3TEYheeod5Ch4b7OVEQEj_Hc6PM1RBGzovug3C/pub?start=false&loop=false&delayms=3000&slide=id.g59d9cb05b6_6_5) which illustrates how this system works, but it may be out of date.
+
+Note: This only stores per-web-app data, and that data will be deleted if the web app is uninstalled. To store data that persists after uninstall, or applies to a more general scope than a single web app, then the `PrefService` can be used, either on the `Profile` object (per-profile data, `profile->GetPrefs()`) or on the browser process (`g_browser_process->local_state()`). Example of needing prefs:
+* Storing if an app was previously installed as a preinstalled app in the past.
+* Information is needed during chrome startup before profiles are loaded.
+* A feature needs to store global data - e.g. "When was the last time we showed the in-product-help banner for any webapp?"
 
 ### [`WebAppInstallManager`](web_app_install_manager.h)
 
@@ -197,6 +222,10 @@
 [`WebAppUiManager::Create()`](https://source.chromium.org/search?q=WebAppUiManager::Create)'s
 declaration and definition locations).
 
+## Storage
+
+TODO
+
 ## Deep Dives
 
 * [Installation Sources & Pipeline](docs/installation_pipeline.md)
diff --git a/chrome/browser/web_applications/components/url_handler_prefs.cc b/chrome/browser/web_applications/components/url_handler_prefs.cc
index 7312551..eabbe8f 100644
--- a/chrome/browser/web_applications/components/url_handler_prefs.cc
+++ b/chrome/browser/web_applications/components/url_handler_prefs.cc
@@ -8,6 +8,8 @@
 
 #include "base/check.h"
 #include "base/files/file_path.h"
+#include "base/ranges/algorithm.h"
+#include "base/ranges/functional.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
@@ -210,45 +212,35 @@
     if (exclude_paths_exist && ExcludePathMatches(url_path, *exclude_paths))
       continue;
 
-    // Do not include a match if it should open in a normal browser tab.
-    if (best_choice == UrlHandlerSavedChoice::kInBrowser)
-      continue;
-
     matches.emplace_back(*profile_path, *app_id, url, best_choice,
                          latest_timestamp);
   }
 }
 
-// If one or more match should open in app, keep the most recent one and remove
-// the others. Also remove matches with no saved choice.
-// Otherwise, any remaining matches should all have no saved choice.
-// Any match that should open in browser have already been removed and should
-// not be found in |matches|.
+// Find the most recent match. If it is saved as kInBrowser, preferred choice
+// is the browser so no matches should be returned; If saved as kNone, all the
+// matches should be returned so the user can make a new saved choice; If
+// kInApp, only returned the app match as it is the saved choice.
 void FilterBySavedChoice(std::vector<UrlHandlerLaunchParams>& matches) {
-  for (const UrlHandlerLaunchParams& params : matches)
-    DCHECK(params.saved_choice != UrlHandlerSavedChoice::kInBrowser);
+  if (matches.empty())
+    return;
 
-  // Are there matches that open in app? Which is the most recently saved?
-  bool has_in_app = false;
-  base::Time most_recent_time = base::Time::Min();
-  size_t most_recent_pos = 0;
-  for (size_t i = 0; i < matches.size(); i++) {
-    const UrlHandlerLaunchParams& params = matches[i];
-    if (params.saved_choice == UrlHandlerSavedChoice::kInApp) {
-      has_in_app = true;
-      if (params.saved_choice_timestamp > most_recent_time) {
-        most_recent_time = params.saved_choice_timestamp;
-        most_recent_pos = i;
-      }
-    }
+  // Record the most recent match.
+  auto most_recent_match_iterator = base::ranges::max_element(
+      matches, base::ranges::less(),
+      &UrlHandlerLaunchParams::saved_choice_timestamp);
+
+  switch (most_recent_match_iterator->saved_choice) {
+    case UrlHandlerSavedChoice::kInApp:
+      matches = {std::move(*most_recent_match_iterator)};
+      break;
+    case UrlHandlerSavedChoice::kInBrowser:
+      matches = {};
+      break;
+    case UrlHandlerSavedChoice::kNone:
+      // `matches` already contain all matches. Do not modify.
+      break;
   }
-
-  // Only keep the most recently saved match that opens in app.
-  if (has_in_app)
-    matches = {std::move(matches[most_recent_pos])};
-
-  // Since no matches opened in browser to begin with and now no matches open in
-  // app, any remaining matches must have no saved choice.
 }
 
 void FindMatchesImpl(const base::Value& pref_value,
@@ -315,8 +307,8 @@
   return matches;
 }
 
-base::Value GetIncludePathsValue(
-    const std::vector<std::string>& include_paths) {
+base::Value GetIncludePathsValue(const std::vector<std::string>& include_paths,
+                                 const base::Time& time) {
   base::Value value(base::Value::Type::LIST);
   // When no "paths" are specified in web-app-origin-association, all include
   // paths are allowed.
@@ -327,7 +319,7 @@
     path_dict.SetStringKey(kPath, include_path);
     path_dict.SetIntKey(kChoice,
                         static_cast<int>(UrlHandlerSavedChoice::kNone));
-    path_dict.SetKey(kTimestamp, util::TimeToValue(base::Time::Min()));
+    path_dict.SetKey(kTimestamp, util::TimeToValue(time));
     value.Append(std::move(path_dict));
   }
   return value;
@@ -344,13 +336,14 @@
 
 base::Value NewHandler(const AppId& app_id,
                        const base::FilePath& profile_path,
-                       const apps::UrlHandlerInfo& info) {
+                       const apps::UrlHandlerInfo& info,
+                       const base::Time& time) {
   base::Value value(base::Value::Type::DICTIONARY);
   value.SetStringKey(kAppId, app_id);
   value.SetKey(kProfilePath, util::FilePathToValue(profile_path));
   value.SetBoolKey(kHasOriginWildcard, info.has_origin_wildcard);
   // Set include_paths and exclude paths from associated app.
-  value.SetKey(kIncludePaths, GetIncludePathsValue(info.paths));
+  value.SetKey(kIncludePaths, GetIncludePathsValue(info.paths, time));
   value.SetKey(kExcludePaths, GetExcludePathsValue(info.exclude_paths));
   return value;
 }
@@ -520,7 +513,8 @@
 void AddWebApp(PrefService* local_state,
                const AppId& app_id,
                const base::FilePath& profile_path,
-               const apps::UrlHandlers& url_handlers) {
+               const apps::UrlHandlers& url_handlers,
+               const base::Time& time) {
   if (profile_path.empty() || url_handlers.empty())
     return;
 
@@ -534,7 +528,8 @@
     if (origin.opaque())
       continue;
 
-    base::Value new_handler(NewHandler(app_id, profile_path, handler_info));
+    base::Value new_handler(
+        NewHandler(app_id, profile_path, handler_info, time));
     base::Value* const handlers_mutable =
         pref_value->FindListKey(origin.Serialize());
     // One or more apps are already associated with this origin.
diff --git a/chrome/browser/web_applications/components/url_handler_prefs.h b/chrome/browser/web_applications/components/url_handler_prefs.h
index 156c4606..1512dc5 100644
--- a/chrome/browser/web_applications/components/url_handler_prefs.h
+++ b/chrome/browser/web_applications/components/url_handler_prefs.h
@@ -78,7 +78,8 @@
 void AddWebApp(PrefService* local_state,
                const AppId& app_id,
                const base::FilePath& profile_path,
-               const apps::UrlHandlers& url_handlers);
+               const apps::UrlHandlers& url_handlers,
+               const base::Time& time = base::Time::Now());
 
 void UpdateWebApp(PrefService* local_state,
                   const AppId& app_id,
@@ -95,12 +96,12 @@
 void Clear(PrefService* local_state);
 
 // Search for all (app, profile) combinations that have active URL handlers
-// which matches |url|.
-// If there are results with saved_choice == kInApp, only the one with the most
-// recent timestamp is returned. Otherwise, if there results with saved_choice
-// == kNone, all of those will be returned. No results with saved_choiced ==
-// kInBrowser will be returned in any case.
-// |url| is a fully specified URL, eg. "https://contoso.com/abc/def".
+// which matches `url`.
+// `url` is a fully specified URL, eg. "https://contoso.com/abc/def".
+// If the most recent match is saved as kInApp, only it will be returned;
+// If saved as kNone, all the matches regardless of saved_choice value are
+// returned; If saved as kInBrowser, the return value is empty as the preferred
+// choice is the browser.
 std::vector<UrlHandlerLaunchParams> FindMatchingUrlHandlers(
     PrefService* local_state,
     const GURL& url);
diff --git a/chrome/browser/web_applications/components/url_handler_prefs_unittest.cc b/chrome/browser/web_applications/components/url_handler_prefs_unittest.cc
index e7d87ef1..46d6cfbe 100644
--- a/chrome/browser/web_applications/components/url_handler_prefs_unittest.cc
+++ b/chrome/browser/web_applications/components/url_handler_prefs_unittest.cc
@@ -705,14 +705,14 @@
   const auto web_app = WebAppWithUrlHandlers(
       app_url_1_, {apps::UrlHandlerInfo(origin_1_, false, {}, {})});
   url_handler_prefs::AddWebApp(LocalState(), web_app->app_id(), profile_1_,
-                               web_app->url_handlers());
+                               web_app->url_handlers(), time_1_);
   {
     // Check default choice and timestamp.
     auto matches =
         url_handler_prefs::FindMatchingUrlHandlers(LocalState(), origin_url_1_);
     ASSERT_EQ(1u, matches.size());
     EXPECT_EQ(matches[0].saved_choice, UrlHandlerSavedChoice::kNone);
-    EXPECT_EQ(matches[0].saved_choice_timestamp, base::Time::Min());
+    EXPECT_EQ(matches[0].saved_choice_timestamp, time_1_);
   }
   // Save choice as UrlHandlerSavedChoice::kInApp.
   url_handler_prefs::SaveOpenInApp(LocalState(), web_app->app_id(), profile_1_,
@@ -733,17 +733,17 @@
       app_url_1_,
       {apps::UrlHandlerInfo(origin_1_, false, {"/abc", "/def"}, {})});
   url_handler_prefs::AddWebApp(LocalState(), web_app->app_id(), profile_1_,
-                               web_app->url_handlers());
+                               web_app->url_handlers(), time_1_);
   // Save choice as UrlHandlerSavedChoice::kInApp to "/abc" path.
   url_handler_prefs::SaveOpenInApp(LocalState(), web_app->app_id(), profile_1_,
-                                   origin_1_.GetURL().Resolve("abc"), time_1_);
+                                   origin_1_.GetURL().Resolve("abc"), time_2_);
   {
     // Check saved choice and timestamp.
     auto matches = url_handler_prefs::FindMatchingUrlHandlers(
         LocalState(), origin_1_.GetURL().Resolve("abc"));
     ASSERT_EQ(1u, matches.size());
     EXPECT_EQ(matches[0].saved_choice, UrlHandlerSavedChoice::kInApp);
-    EXPECT_EQ(matches[0].saved_choice_timestamp, time_1_);
+    EXPECT_EQ(matches[0].saved_choice_timestamp, time_2_);
   }
   {
     // Check unaffected path.
@@ -751,7 +751,7 @@
         LocalState(), origin_1_.GetURL().Resolve("def"));
     ASSERT_EQ(1u, matches.size());
     EXPECT_EQ(matches[0].saved_choice, UrlHandlerSavedChoice::kNone);
-    EXPECT_EQ(matches[0].saved_choice_timestamp, base::Time::Min());
+    EXPECT_EQ(matches[0].saved_choice_timestamp, time_1_);
   }
 }
 
@@ -783,21 +783,23 @@
       app_url_1_,
       {apps::UrlHandlerInfo(origin_1_, false, {"/a", "/b"}, {"/x"})});
   url_handler_prefs::AddWebApp(LocalState(), web_app->app_id(), profile_1_,
-                               web_app->url_handlers());
+                               web_app->url_handlers(), time_1_);
   // Save choice as UrlHandlerSavedChoice::kInApp for "/a" path.
   url_handler_prefs::SaveOpenInApp(LocalState(), web_app->app_id(), profile_1_,
-                                   origin_1_.GetURL().Resolve("a"), time_1_);
+                                   origin_1_.GetURL().Resolve("a"), time_2_);
   {
     auto matches = url_handler_prefs::FindMatchingUrlHandlers(
         LocalState(), origin_1_.GetURL().Resolve("a"));
     ASSERT_EQ(1u, matches.size());
     EXPECT_EQ(matches[0].saved_choice, UrlHandlerSavedChoice::kInApp);
+    EXPECT_EQ(matches[0].saved_choice_timestamp, time_2_);
   }
   {
     auto matches = url_handler_prefs::FindMatchingUrlHandlers(
         LocalState(), origin_1_.GetURL().Resolve("b"));
     ASSERT_EQ(1u, matches.size());
     EXPECT_EQ(matches[0].saved_choice, UrlHandlerSavedChoice::kNone);
+    EXPECT_EQ(matches[0].saved_choice_timestamp, time_1_);
   }
   {
     auto matches = url_handler_prefs::FindMatchingUrlHandlers(
@@ -842,9 +844,9 @@
       app_url_2_, {apps::UrlHandlerInfo(origin_1_, false, {"/*"}, {})});
 
   url_handler_prefs::AddWebApp(LocalState(), web_app_1->app_id(), profile_1_,
-                               web_app_1->url_handlers());
+                               web_app_1->url_handlers(), time_1_);
   url_handler_prefs::AddWebApp(LocalState(), web_app_2->app_id(), profile_1_,
-                               web_app_2->url_handlers());
+                               web_app_2->url_handlers(), time_1_);
   {
     // Both apps should match the input URL.
     auto matches =
@@ -852,13 +854,13 @@
     ASSERT_EQ(2u, matches.size());
     EXPECT_EQ(matches[0].app_id, web_app_1->app_id());
     EXPECT_EQ(matches[0].saved_choice, UrlHandlerSavedChoice::kNone);
-    EXPECT_EQ(matches[0].saved_choice_timestamp, base::Time::Min());
+    EXPECT_EQ(matches[0].saved_choice_timestamp, time_1_);
     EXPECT_EQ(matches[1].app_id, web_app_2->app_id());
     EXPECT_EQ(matches[1].saved_choice, UrlHandlerSavedChoice::kNone);
-    EXPECT_EQ(matches[1].saved_choice_timestamp, base::Time::Min());
+    EXPECT_EQ(matches[1].saved_choice_timestamp, time_1_);
   }
   url_handler_prefs::SaveOpenInApp(LocalState(), web_app_1->app_id(),
-                                   profile_1_, origin_url_1_, time_1_);
+                                   profile_1_, origin_url_1_, time_2_);
   {
     // Only the app with a path saved as UrlHandlerSavedChoice::kInApp is
     // returned from matching.
@@ -867,7 +869,7 @@
     ASSERT_EQ(1u, matches.size());
     EXPECT_EQ(matches[0].app_id, web_app_1->app_id());
     EXPECT_EQ(matches[0].saved_choice, UrlHandlerSavedChoice::kInApp);
-    EXPECT_EQ(matches[0].saved_choice_timestamp, time_1_);
+    EXPECT_EQ(matches[0].saved_choice_timestamp, time_2_);
   }
 }
 
@@ -878,11 +880,11 @@
       app_url_2_, {apps::UrlHandlerInfo(origin_1_, false, {"/*"}, {})});
 
   url_handler_prefs::AddWebApp(LocalState(), web_app_1->app_id(), profile_1_,
-                               web_app_1->url_handlers());
+                               web_app_1->url_handlers(), time_1_);
   url_handler_prefs::AddWebApp(LocalState(), web_app_2->app_id(), profile_1_,
-                               web_app_2->url_handlers());
+                               web_app_2->url_handlers(), time_1_);
 
-  url_handler_prefs::SaveOpenInBrowser(LocalState(), origin_url_1_, time_1_);
+  url_handler_prefs::SaveOpenInBrowser(LocalState(), origin_url_1_, time_2_);
   {
     auto matches =
         url_handler_prefs::FindMatchingUrlHandlers(LocalState(), origin_url_1_);
@@ -924,4 +926,78 @@
   }
 }
 
+TEST_F(UrlHandlerPrefsTest, SaveUserChoiceInAppAndInstallNewApp) {
+  const auto web_app_1 = WebAppWithUrlHandlers(
+      app_url_1_, {apps::UrlHandlerInfo(origin_1_, false, {"/abc"}, {})});
+  url_handler_prefs::AddWebApp(LocalState(), web_app_1->app_id(), profile_1_,
+                               web_app_1->url_handlers(), time_1_);
+  // Save choice as UrlHandlerSavedChoice::kInApp to "/abc" path.
+  url_handler_prefs::SaveOpenInApp(LocalState(), web_app_1->app_id(),
+                                   profile_1_,
+                                   origin_1_.GetURL().Resolve("abc"), time_1_);
+  {
+    // Check saved choice.
+    auto matches = url_handler_prefs::FindMatchingUrlHandlers(
+        LocalState(), origin_1_.GetURL().Resolve("abc"));
+    ASSERT_EQ(1u, matches.size());
+    EXPECT_EQ(matches.front().saved_choice, UrlHandlerSavedChoice::kInApp);
+    EXPECT_EQ(matches.front().app_id, web_app_1->app_id());
+    EXPECT_EQ(matches.front().saved_choice_timestamp, time_1_);
+  }
+
+  // Install another app.
+  const auto web_app_2 = WebAppWithUrlHandlers(
+      app_url_2_, {apps::UrlHandlerInfo(origin_1_, false, {"/abc"}, {})});
+  url_handler_prefs::AddWebApp(LocalState(), web_app_2->app_id(), profile_1_,
+                               web_app_2->url_handlers(), time_2_);
+  {
+    // Check now there should be two matches.
+    auto matches = url_handler_prefs::FindMatchingUrlHandlers(
+        LocalState(), origin_1_.GetURL().Resolve("abc"));
+    ASSERT_EQ(2u, matches.size());
+  }
+
+  // Save the new app as the default choice.
+  url_handler_prefs::SaveOpenInApp(LocalState(), web_app_2->app_id(),
+                                   profile_1_,
+                                   origin_1_.GetURL().Resolve("abc"), time_2_);
+  {
+    // Verify the new saved choice.
+    auto matches = url_handler_prefs::FindMatchingUrlHandlers(
+        LocalState(), origin_1_.GetURL().Resolve("abc"));
+    ASSERT_EQ(1u, matches.size());
+    EXPECT_EQ(matches.front().saved_choice, UrlHandlerSavedChoice::kInApp);
+    EXPECT_EQ(matches.front().app_id, web_app_2->app_id());
+    EXPECT_EQ(matches.front().saved_choice_timestamp, time_2_);
+  }
+}
+
+TEST_F(UrlHandlerPrefsTest, SaveUserChoiceInBrowserAndInstallNewApp) {
+  const auto web_app_1 = WebAppWithUrlHandlers(
+      app_url_1_, {apps::UrlHandlerInfo(origin_1_, false, {"/abc"}, {})});
+  url_handler_prefs::AddWebApp(LocalState(), web_app_1->app_id(), profile_1_,
+                               web_app_1->url_handlers(), time_1_);
+  // Save choice to open in browser.
+  url_handler_prefs::SaveOpenInBrowser(
+      LocalState(), origin_1_.GetURL().Resolve("abc"), time_1_);
+  {
+    // Check saved choice.
+    auto matches = url_handler_prefs::FindMatchingUrlHandlers(
+        LocalState(), origin_1_.GetURL().Resolve("abc"));
+    ASSERT_EQ(0u, matches.size());
+  }
+
+  // Install another app.
+  const auto web_app_2 = WebAppWithUrlHandlers(
+      app_url_2_, {apps::UrlHandlerInfo(origin_1_, false, {"/abc"}, {})});
+  url_handler_prefs::AddWebApp(LocalState(), web_app_2->app_id(), profile_1_,
+                               web_app_2->url_handlers(), time_2_);
+  {
+    // Check there are now two matches.
+    auto matches = url_handler_prefs::FindMatchingUrlHandlers(
+        LocalState(), origin_1_.GetURL().Resolve("abc"));
+    ASSERT_EQ(2u, matches.size());
+  }
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_install_utils.cc b/chrome/browser/web_applications/components/web_app_install_utils.cc
index 3b2c26f..2f97b699 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils.cc
+++ b/chrome/browser/web_applications/components/web_app_install_utils.cc
@@ -307,6 +307,8 @@
 
   if (manifest_url.is_valid())
     web_app_info->manifest_url = manifest_url;
+
+  web_app_info->is_storage_isolated = manifest.isolated_storage;
 }
 
 std::vector<GURL> GetValidIconUrlsToDownload(
diff --git a/chrome/browser/web_applications/components/web_app_utils.cc b/chrome/browser/web_applications/components/web_app_utils.cc
index 900b16a5..2d8b2ef 100644
--- a/chrome/browser/web_applications/components/web_app_utils.cc
+++ b/chrome/browser/web_applications/components/web_app_utils.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/web_applications/components/web_app_utils.h"
 
+#include "base/containers/contains.h"
 #include "base/files/file_path.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -152,6 +153,37 @@
 #endif
 }
 
+bool AreFileHandlersAlreadyRegistered(
+    Profile* profile,
+    const GURL& url,
+    const std::vector<blink::Manifest::FileHandler>& new_handlers) {
+  if (new_handlers.empty())
+    return true;
+
+  const apps::FileHandlers old_handlers =
+      GetFileHandlersForAllWebAppsWithOrigin(profile, url);
+  const std::set<std::string> mime_types_set =
+      apps::GetMimeTypesFromFileHandlers(old_handlers);
+  const std::set<std::string> extensions_set =
+      apps::GetFileExtensionsFromFileHandlers(old_handlers);
+
+  for (const blink::Manifest::FileHandler& new_handler : new_handlers) {
+    for (const auto& new_handler_accept : new_handler.accept) {
+      if (!base::Contains(mime_types_set,
+                          base::UTF16ToUTF8(new_handler_accept.first))) {
+        return false;
+      }
+
+      for (const auto& new_extension : new_handler_accept.second) {
+        if (!base::Contains(extensions_set, base::UTF16ToUTF8(new_extension)))
+          return false;
+      }
+    }
+  }
+
+  return true;
+}
+
 apps::FileHandlers GetFileHandlersForAllWebAppsWithOrigin(Profile* profile,
                                                           const GURL& url) {
   auto* provider = WebAppProviderBase::GetProviderBase(profile);
diff --git a/chrome/browser/web_applications/components/web_app_utils.h b/chrome/browser/web_applications/components/web_app_utils.h
index 3c59b0d..7d63b10 100644
--- a/chrome/browser/web_applications/components/web_app_utils.h
+++ b/chrome/browser/web_applications/components/web_app_utils.h
@@ -10,6 +10,7 @@
 
 #include "chrome/browser/web_applications/components/web_app_id.h"
 #include "components/services/app_service/public/cpp/file_handler.h"
+#include "third_party/blink/public/common/manifest/manifest.h"
 
 class GURL;
 class Profile;
@@ -75,6 +76,16 @@
 // Returns true if sync should install web apps locally by default.
 bool AreAppsLocallyInstalledBySync();
 
+// Returns true if `new_handlers` are effectively the same or less broad than
+// the file handlers for PWAs installed under the same origin as `url` in
+// `profile`. In other words, if `new_handlers` would not change the text
+// returned by `GetFileHandlersForAllWebAppsWithOrigin()`, then this will return
+// true, otherwise false.
+bool AreFileHandlersAlreadyRegistered(
+    Profile* profile,
+    const GURL& url,
+    const std::vector<blink::Manifest::FileHandler>& new_handlers);
+
 // Returns all file handlers associated with any apps at the origin of `url`, in
 // the `profile`. This is not limited to a particular app's scope because it's
 // used for display in permissions contexts, and permissions are origin-bound.
diff --git a/chrome/browser/web_applications/components/web_application_info.h b/chrome/browser/web_applications/components/web_application_info.h
index f32210f..0ac5b9f1 100644
--- a/chrome/browser/web_applications/components/web_application_info.h
+++ b/chrome/browser/web_applications/components/web_application_info.h
@@ -277,6 +277,9 @@
   // scope.
   blink::mojom::CaptureLinks capture_links =
       blink::mojom::CaptureLinks::kUndefined;
+
+  // Whether the app should be loaded in a dedicated storage partition.
+  bool is_storage_isolated = false;
 };
 
 std::ostream& operator<<(std::ostream& out,
diff --git a/chrome/browser/web_applications/docs/webappprovider_component_ownership.jpg b/chrome/browser/web_applications/docs/webappprovider_component_ownership.jpg
new file mode 100644
index 0000000..9fbf633e
--- /dev/null
+++ b/chrome/browser/web_applications/docs/webappprovider_component_ownership.jpg
Binary files differ
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_impl_browsertest.cc b/chrome/browser/web_applications/externally_managed_app_manager_impl_browsertest.cc
index dace874..d85b5ad 100644
--- a/chrome/browser/web_applications/externally_managed_app_manager_impl_browsertest.cc
+++ b/chrome/browser/web_applications/externally_managed_app_manager_impl_browsertest.cc
@@ -218,6 +218,7 @@
 IN_PROC_BROWSER_TEST_F(ExternallyManagedAppManagerImplBrowserTest,
                        ForceReinstall) {
   ASSERT_TRUE(embedded_test_server()->Start());
+  absl::optional<AppId> app_id;
   {
     GURL url(embedded_test_server()->GetURL(
         "/banners/"
@@ -226,7 +227,7 @@
     install_options.force_reinstall = true;
     InstallApp(std::move(install_options));
 
-    absl::optional<AppId> app_id = registrar().FindAppWithUrlInScope(url);
+    app_id = registrar().FindAppWithUrlInScope(url);
     EXPECT_TRUE(app_id.has_value());
     EXPECT_EQ("Manifest", registrar().GetAppShortName(app_id.value()));
   }
@@ -237,9 +238,11 @@
     install_options.force_reinstall = true;
     InstallApp(std::move(install_options));
 
-    absl::optional<AppId> app_id = registrar().FindAppWithUrlInScope(url);
-    EXPECT_TRUE(app_id.has_value());
-    EXPECT_EQ("Manifest test app", registrar().GetAppShortName(app_id.value()));
+    absl::optional<AppId> new_app_id = registrar().FindAppWithUrlInScope(url);
+    EXPECT_TRUE(new_app_id.has_value());
+    EXPECT_EQ(new_app_id, app_id);
+    EXPECT_EQ("Manifest test app",
+              registrar().GetAppShortName(new_app_id.value()));
   }
 }
 
diff --git a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
index 07349d7..c125a4a 100644
--- a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
@@ -1577,7 +1577,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
-                       AllowedPermissionResetToAskOnUpdate) {
+                       FileHandlingPermissionResetsOnUpdate) {
   constexpr char kFileHandlerManifestTemplate[] = R"(
     {
       "name": "Test app name",
@@ -1593,12 +1593,18 @@
           }
         }
       ],
-      "icons": $2
+      "icons": [
+        {
+          "src": "launcher-icon-4x.png",
+          "sizes": "192x192",
+          "type": "image/png"
+        }
+      ],
+      "theme_color": "$2"
     }
   )";
 
-  OverrideManifest(kFileHandlerManifestTemplate,
-                   {".txt", kInstallableIconList});
+  OverrideManifest(kFileHandlerManifestTemplate, {".txt", "red"});
   AppId app_id = InstallWebApp();
   const WebApp* web_app =
       GetProvider().registrar().AsWebAppRegistrar()->GetAppById(app_id);
@@ -1619,18 +1625,66 @@
   EXPECT_EQ(CONTENT_SETTING_ALLOW,
             map->GetContentSetting(origin, origin,
                                    ContentSettingsType::FILE_HANDLING));
-  // Update manifest.
-  OverrideManifest(kFileHandlerManifestTemplate, {".md", kInstallableIconList});
+  // Update manifest, adding an extension to the file handler. Permission should
+  // be downgraded to ASK. The time override is necessary to make sure the
+  // manifest update isn't skipped due to throttling.
+  base::Time time_override = base::Time::Now();
+  SetTimeOverride(time_override);
+  OverrideManifest(kFileHandlerManifestTemplate, {".md\", \".txt", "red"});
   EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
             GetResultAfterPageLoad(url, &app_id));
-  const auto& new_file_handler = web_app->file_handlers()[0];
-  auto new_extensions = new_file_handler.accept[0].file_extensions;
+  auto new_extensions = web_app->file_handlers()[0].accept[0].file_extensions;
   EXPECT_TRUE(base::Contains(new_extensions, ".md"));
-
-  // Confirm permission is downgraded to ASK after manifest update.
+  EXPECT_TRUE(base::Contains(new_extensions, ".txt"));
   EXPECT_EQ(CONTENT_SETTING_ASK,
             map->GetContentSetting(origin, origin,
                                    ContentSettingsType::FILE_HANDLING));
+
+  // Set permission to ALLOW.
+  map->SetContentSettingDefaultScope(origin, origin,
+                                     ContentSettingsType::FILE_HANDLING,
+                                     CONTENT_SETTING_ALLOW);
+
+  // Update manifest, but keep same file handlers. Permission should be left on
+  // ALLOW.
+  time_override += base::TimeDelta::FromDays(10);
+  SetTimeOverride(time_override);
+  OverrideManifest(kFileHandlerManifestTemplate, {".md\", \".txt", "blue"});
+  EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
+            GetResultAfterPageLoad(url, &app_id));
+  new_extensions = web_app->file_handlers()[0].accept[0].file_extensions;
+  EXPECT_TRUE(base::Contains(new_extensions, ".md"));
+  EXPECT_TRUE(base::Contains(new_extensions, ".txt"));
+  EXPECT_EQ(CONTENT_SETTING_ALLOW,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
+
+  // Update manifest, asking for /fewer/ file types. Permission should be left
+  // on ALLOW.
+  time_override += base::TimeDelta::FromDays(10);
+  SetTimeOverride(time_override);
+  OverrideManifest(kFileHandlerManifestTemplate, {".txt", "blue"});
+  EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
+            GetResultAfterPageLoad(url, &app_id));
+  new_extensions = web_app->file_handlers()[0].accept[0].file_extensions;
+  EXPECT_FALSE(base::Contains(new_extensions, ".md"));
+  EXPECT_TRUE(base::Contains(new_extensions, ".txt"));
+  EXPECT_EQ(CONTENT_SETTING_ALLOW,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
+
+  // Block the permission, update manifest, permission should still be block.
+  map->SetContentSettingDefaultScope(origin, origin,
+                                     ContentSettingsType::FILE_HANDLING,
+                                     CONTENT_SETTING_BLOCK);
+  OverrideManifest(kFileHandlerManifestTemplate, {".txt", "red"});
+  time_override += base::TimeDelta::FromDays(10);
+  SetTimeOverride(time_override);
+  EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
+            GetResultAfterPageLoad(url, &app_id));
+  EXPECT_EQ(CONTENT_SETTING_BLOCK,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
diff --git a/chrome/browser/web_applications/manifest_update_task.cc b/chrome/browser/web_applications/manifest_update_task.cc
index 632c477..078240d 100644
--- a/chrome/browser/web_applications/manifest_update_task.cc
+++ b/chrome/browser/web_applications/manifest_update_task.cc
@@ -358,6 +358,8 @@
     return true;
   }
 
+  // TODO(crbug.com/1212849): Handle changes to is_storage_isolated.
+
   // TODO(crbug.com/926083): Check more manifest fields.
   return false;
 }
diff --git a/chrome/browser/web_applications/proto/web_app.proto b/chrome/browser/web_applications/proto/web_app.proto
index 63d51363..828c2207 100644
--- a/chrome/browser/web_applications/proto/web_app.proto
+++ b/chrome/browser/web_applications/proto/web_app.proto
@@ -211,4 +211,7 @@
 
   // If present, the URL to use to create a new note in the app.
   optional string note_taking_new_note_url = 35;
+
+  // Whether the app should be loaded in a dedicated storage partition.
+  optional bool is_storage_isolated = 36;
 }
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.cc b/chrome/browser/web_applications/test/web_app_test_utils.cc
index 6d7d566b..040f2a2 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_test_utils.cc
@@ -369,6 +369,8 @@
   sync_fallback_data.icon_infos = app->icon_infos();
   app->SetSyncFallbackData(std::move(sync_fallback_data));
 
+  app->SetStorageIsolated(random.next_bool());
+
   return app;
 }
 
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index dbcb70f..dfc857e2 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -305,6 +305,10 @@
   window_controls_overlay_enabled_ = enabled;
 }
 
+void WebApp::SetStorageIsolated(bool is_storage_isolated) {
+  is_storage_isolated_ = is_storage_isolated;
+}
+
 WebApp::ClientData::ClientData() = default;
 
 WebApp::ClientData::~ClientData() = default;
@@ -497,6 +501,8 @@
   out << "window_controls_overlay_enabled:" << std::endl
       << Indent(app.window_controls_overlay_enabled_) << std::endl;
 
+  out << "is_storage_isolated: " << app.is_storage_isolated_ << std::endl;
+
   return out;
 }
 
@@ -559,7 +565,8 @@
         app.manifest_id_,
         app.client_data_.system_web_app_data,
         app.file_handler_permission_blocked_,
-        app.window_controls_overlay_enabled_
+        app.window_controls_overlay_enabled_,
+        app.is_storage_isolated_
         // clang-format on
     );
   };
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index 98b8efdc..c6de1b6 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -198,6 +198,8 @@
     return manifest_id_;
   }
 
+  bool IsStorageIsolated() const { return is_storage_isolated_; }
+
   // A Web App can be installed from multiple sources simultaneously. Installs
   // add a source to the app. Uninstalls remove a source from the app.
   void AddSource(Source::Type source);
@@ -257,6 +259,7 @@
   void SetManifestId(const absl::optional<std::string>& manifest_id);
   void SetFileHandlerPermissionBlocked(bool permission_blocked);
   void SetWindowControlsOverlayEnabled(bool enabled);
+  void SetStorageIsolated(bool is_storage_isolated);
 
   // For logging and debug purposes.
   bool operator==(const WebApp&) const;
@@ -319,6 +322,7 @@
   absl::optional<std::string> manifest_id_;
   bool file_handler_permission_blocked_ = false;
   bool window_controls_overlay_enabled_ = false;
+  bool is_storage_isolated_ = false;
   // New fields must be added to:
   //  - |operator==|
   //  - |operator<<|
diff --git a/chrome/browser/web_applications/web_app_database.cc b/chrome/browser/web_applications/web_app_database.cc
index 6572f68..56a5de4 100644
--- a/chrome/browser/web_applications/web_app_database.cc
+++ b/chrome/browser/web_applications/web_app_database.cc
@@ -394,6 +394,8 @@
 
   local_data->set_window_controls_overlay_enabled(
       web_app.window_controls_overlay_enabled());
+
+  local_data->set_is_storage_isolated(web_app.IsStorageIsolated());
   return local_data;
 }
 
@@ -806,6 +808,7 @@
     web_app->SetWindowControlsOverlayEnabled(
         local_data.window_controls_overlay_enabled());
   }
+  web_app->SetStorageIsolated(local_data.is_storage_isolated());
   return web_app;
 }
 
diff --git a/chrome/browser/web_applications/web_app_database_unittest.cc b/chrome/browser/web_applications/web_app_database_unittest.cc
index 61814020..081801d 100644
--- a/chrome/browser/web_applications/web_app_database_unittest.cc
+++ b/chrome/browser/web_applications/web_app_database_unittest.cc
@@ -323,6 +323,7 @@
   EXPECT_EQ(app->run_on_os_login_mode(), RunOnOsLoginMode::kNotRun);
   EXPECT_TRUE(app->manifest_url().is_empty());
   EXPECT_FALSE(app->manifest_id().has_value());
+  EXPECT_FALSE(app->IsStorageIsolated());
   controller().RegisterApp(std::move(app));
 
   Registry registry = database_factory().ReadRegistry();
@@ -383,6 +384,7 @@
   EXPECT_EQ(app_copy->run_on_os_login_mode(), RunOnOsLoginMode::kNotRun);
   EXPECT_TRUE(app_copy->manifest_url().is_empty());
   EXPECT_FALSE(app_copy->manifest_id().has_value());
+  EXPECT_FALSE(app_copy->IsStorageIsolated());
 }
 
 TEST_F(WebAppDatabaseTest, WebAppWithManyIcons) {
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index da9efa8..0832123f 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/browser/web_applications/components/web_app_shortcuts_menu.h"
 #include "chrome/browser/web_applications/components/web_app_system_web_app_data.h"
+#include "chrome/browser/web_applications/components/web_app_utils.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
 #include "chrome/browser/web_applications/manifest_update_task.h"
 #include "chrome/browser/web_applications/web_app.h"
@@ -221,6 +222,7 @@
   web_app->SetAdditionalSearchTerms(web_app_info.additional_search_terms);
   web_app->AddSource(source);
   web_app->SetIsInSyncInstall(false);
+  web_app->SetStorageIsolated(web_app_info.is_storage_isolated);
 
   UpdateIntWebAppPref(profile_->GetPrefs(), app_id, kLatestWebAppInstallSource,
                       static_cast<int>(options.install_source));
@@ -231,6 +233,11 @@
       app_id, web_app_info.enable_experimental_tabbed_window,
       /*is_user_action=*/false);
 
+  // This step is necessary in case this app shares an origin with another PWA
+  // which already asked for file handling permissions, and the new app asks to
+  // handle more file types.
+  MaybeResetFileHandlingPermission(web_app_info);
+
   CommitCallback commit_callback = base::BindOnce(
       &WebAppInstallFinalizer::OnDatabaseCommitCompletedForInstall,
       weak_ptr_factory_.GetWeakPtr(), std::move(callback), app_id);
@@ -644,14 +651,6 @@
   if (!os_integration_manager().IsFileHandlingAPIAvailable(app_id))
     return FileHandlerUpdateAction::kNoUpdate;
 
-  // TODO(https://crbug.com/1197013): Consider trying to re-use
-  // HaveFileHandlersChanged() results from the ManifestUpdateTask.
-  if (!HaveFileHandlersChanged(
-          /*old_handlers=*/registrar().GetAppFileHandlers(app_id),
-          /*new_handlers=*/web_app_info.file_handlers)) {
-    return FileHandlerUpdateAction::kNoUpdate;
-  }
-
   const GURL& url = web_app_info.scope;
 
   // Keep in sync with chromeos::kChromeUIMediaAppURL.
@@ -675,35 +674,55 @@
   // It's possible we'll downgrade the permission and then fail to update OS
   // integrations (ex. if the disk or icon downloads fail), but this is ok
   // because these failures should rarely occur.
-  permissions::PermissionManager* permission_manager =
-      PermissionManagerFactory::GetForProfile(
-          Profile::FromBrowserContext(web_contents->GetBrowserContext()));
-  DCHECK(permission_manager);
-  // Note: Using GetPermissionStatus instead of GetPermissionStatusForFrame()
-  // because the relevant frame isn't available here.
-  permissions::PermissionResult status =
-      permission_manager->GetPermissionStatus(
-          ContentSettingsType::FILE_HANDLING, url, url);
+  ContentSetting content_setting =
+      MaybeResetFileHandlingPermission(web_app_info);
 
-  // If file handling permission is "ALLOW" during manifest update, downgrade
-  // to "ASK" via reset, as the user may not want to allow newly added file
-  // handlers, which may include more dangerous extensions.
-  // If the permission is "ASK" or "BLOCK", leave it as is. When permission is
+  // If the permission is "BLOCK", leave it as is. When permission is
   // "BLOCK", the `OnContentSettingChanged()` and
   // `DetectAndCorrectFileHandlingPermissionBlocks()` should capture the
   // permission change and make sure the OS and db state are in sync with the
   // PermissionManager permission setting. Therefore, manifest update task
   // should not update file handlers due to blocked permission state.
-  if (status.content_setting == CONTENT_SETTING_ALLOW) {
-    permission_manager->ResetPermission(content::PermissionType::FILE_HANDLING,
-                                        url, url);
-  } else if (status.content_setting == CONTENT_SETTING_BLOCK) {
-    DCHECK(registrar().IsAppFileHandlerPermissionBlocked(app_id));
+  if (content_setting == CONTENT_SETTING_BLOCK)
+    return FileHandlerUpdateAction::kNoUpdate;
+
+  // TODO(https://crbug.com/1197013): Consider trying to re-use
+  // HaveFileHandlersChanged() results from the ManifestUpdateTask.
+  if (!HaveFileHandlersChanged(
+          /*old_handlers=*/registrar().GetAppFileHandlers(app_id),
+          /*new_handlers=*/web_app_info.file_handlers)) {
     return FileHandlerUpdateAction::kNoUpdate;
   }
+
   return FileHandlerUpdateAction::kUpdate;
 }
 
+ContentSetting WebAppInstallFinalizer::MaybeResetFileHandlingPermission(
+    const WebApplicationInfo& web_app_info) {
+  permissions::PermissionManager* permission_manager =
+      PermissionManagerFactory::GetForProfile(profile_);
+  DCHECK(permission_manager);
+  const GURL& url = web_app_info.scope;
+  // Note: Since a frame is not available, using GetPermissionStatus() instead
+  // of GetPermissionStatusForFrame().
+  permissions::PermissionResult status =
+      permission_manager->GetPermissionStatus(
+          ContentSettingsType::FILE_HANDLING, url, url);
+
+  // If file handling permission is "ALLOW", downgrade to "ASK" via reset, as
+  // the user may not want to allow newly added file handlers, which may include
+  // more dangerous extensions.
+  if (status.content_setting == CONTENT_SETTING_ALLOW &&
+      !AreFileHandlersAlreadyRegistered(profile_, url,
+                                        web_app_info.file_handlers)) {
+    permission_manager->ResetPermission(content::PermissionType::FILE_HANDLING,
+                                        url, url);
+    return CONTENT_SETTING_ASK;
+  }
+
+  return status.content_setting;
+}
+
 void WebAppInstallFinalizer::OnDatabaseCommitCompletedForUpdate(
     InstallFinalizedCallback callback,
     AppId app_id,
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index 5638596..88abcd7 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -105,7 +105,9 @@
       const AppId app_id,
       const WebApplicationInfo& web_app_info,
       std::unique_ptr<ShortcutInfo> old_shortcut);
+
   bool ShouldUpdateOsHooks(const AppId& app_id);
+
   // Checks whether OS registered file handlers need to update, taking into
   // account permission settings, as file handlers should be unregistered
   // when the permission has been denied. Also, downgrades granted file handling
@@ -114,6 +116,15 @@
       const AppId app_id,
       const WebApplicationInfo& web_app_info,
       content::WebContents* web_contents);
+
+  // Resets the FILE_HANDLING content setting permission if `web_app_info` is
+  // asking for more file handling types than were previously granted to the
+  // app's origin. Returns the new content setting, which will be either
+  // unchanged, or will have switched from ALLOW to ASK. If the previous setting
+  // was BLOCK, no change is made.
+  ContentSetting MaybeResetFileHandlingPermission(
+      const WebApplicationInfo& web_app_info);
+
   void OnDatabaseCommitCompletedForUpdate(
       InstallFinalizedCallback callback,
       AppId app_id,
@@ -123,6 +134,7 @@
       FileHandlerUpdateAction file_handlers_need_os_update,
       const WebApplicationInfo& web_app_info,
       bool success);
+
   void OnUninstallOsHooks(const AppId& app_id,
                           webapps::WebappUninstallSource uninstall_source,
                           UninstallWebAppCallback callback,
diff --git a/chrome/browser/web_applications/web_app_install_task_unittest.cc b/chrome/browser/web_applications/web_app_install_task_unittest.cc
index 2afb1e51..0f2b5cd5 100644
--- a/chrome/browser/web_applications/web_app_install_task_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_task_unittest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/browser/web_applications/components/web_app_icon_generator.h"
+#include "chrome/browser/web_applications/components/web_app_install_utils.h"
 #include "chrome/browser/web_applications/components/web_app_utils.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
 #include "chrome/browser/web_applications/test/test_data_retriever.h"
@@ -1275,6 +1276,46 @@
   }
 }
 
+TEST_F(WebAppInstallTaskTest, StorageIsolationFlagSaved) {
+  const GURL manifest_start_url = GURL("https://example.com/start");
+  const AppId app_id = GenerateAppIdFromURL(manifest_start_url);
+
+  auto manifest = std::make_unique<blink::Manifest>();
+  manifest->short_name = u"Short Name from Manifest";
+  manifest->name = u"Name from Manifest";
+  manifest->start_url = GURL("https://example.com/start");
+  manifest->isolated_storage = true;
+
+  auto web_app_info = std::make_unique<WebApplicationInfo>();
+  UpdateWebAppInfoFromManifest(*manifest, manifest_start_url,
+                               web_app_info.get());
+
+  data_retriever_->SetManifest(std::move(manifest), /*is_installable=*/true);
+  data_retriever_->SetRendererWebApplicationInfo(std::move(web_app_info));
+
+  base::RunLoop run_loop;
+  bool callback_called = false;
+
+  install_task_->InstallWebAppFromManifestWithFallback(
+      web_contents(), /*force_shortcut_app=*/false,
+      webapps::WebappInstallSource::MENU_BROWSER_TAB,
+      base::BindOnce(test::TestAcceptDialogCallback),
+      base::BindLambdaForTesting(
+          [&](const AppId& installed_app_id, InstallResultCode code) {
+            EXPECT_EQ(InstallResultCode::kSuccessNewInstall, code);
+            EXPECT_EQ(app_id, installed_app_id);
+            callback_called = true;
+            run_loop.Quit();
+          }));
+  run_loop.Run();
+
+  EXPECT_TRUE(callback_called);
+
+  const WebApp* web_app = registrar().GetAppById(app_id);
+  EXPECT_NE(nullptr, web_app);
+  EXPECT_TRUE(web_app->IsStorageIsolated());
+}
+
 TEST_F(WebAppInstallTaskWithRunOnOsLoginTest,
        InstallFromWebContentsRunOnOsLogin) {
   EXPECT_TRUE(AreWebAppsUserInstallable(profile()));
diff --git a/chrome/browser/web_applications/web_app_unittest.cc b/chrome/browser/web_applications/web_app_unittest.cc
index 92c85e8..b259d1c 100644
--- a/chrome/browser/web_applications/web_app_unittest.cc
+++ b/chrome/browser/web_applications/web_app_unittest.cc
@@ -205,6 +205,7 @@
   nullopt
 window_controls_overlay_enabled:
   0
+is_storage_isolated: 0
 )") << "Copypastable expectation: \n"
     << debug_string;
 }
@@ -374,6 +375,7 @@
   nullopt
 window_controls_overlay_enabled:
   0
+is_storage_isolated: 0
 )") << "Copypastable expectation: \n"
     << debug_string;
 }
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 3bd9bbf..8c72c14 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1622635148-895a7a9b984ca2b99a4b00b2a04fc8e55c529fad.profdata
+chrome-win32-master-1622667452-ac768622a806a59c6280aa80b067d944195b9b61.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 2da6b3a..f7088e7 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1622635148-ba23959f0a77f895fe0cf2da1fc8384a42cf7236.profdata
+chrome-win64-master-1622656662-3c6a534cfebd08404535bcdc0b3c72622ff4c893.profdata
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 03f729f4..e9da05f 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -112,12 +112,11 @@
 const char kUserFeedbackAllowed[] = "feedback_allowed";
 
 #if !defined(OS_ANDROID)
-// Used to store the value of the SerialAllowAllPortsForUrls policy.
-const char kManagedSerialAllowAllPortsForUrls[] =
+// Replaced by kManagedSerialAllowAllPortsForUrls in M-93.
+const char kManagedProfileSerialAllowAllPortsForUrlsDeprecated[] =
     "profile.managed.serial_allow_all_ports_for_urls";
-
-// Used to store the value of the SerialAllowUsbDevicesForUrls policy.
-const char kManagedSerialAllowUsbDevicesForUrls[] =
+// Replaced by kManagedSerialAllowUsbDevicesForUrls in M-93.
+const char kManagedProfileSerialAllowUsbDevicesForUrlsDeprecated[] =
     "profile.managed.serial_allow_usb_devices_for_urls";
 #endif  // !defined(OS_ANDROID)
 
@@ -1610,6 +1609,16 @@
 // *************** LOCAL STATE ***************
 // These are attached to the machine/installation
 
+#if !defined(OS_ANDROID)
+// Used to store the value of the SerialAllowAllPortsForUrls policy.
+const char kManagedSerialAllowAllPortsForUrls[] =
+    "managed.serial_allow_all_ports_for_urls";
+
+// Used to store the value of the SerialAllowUsbDevicesForUrls policy.
+const char kManagedSerialAllowUsbDevicesForUrls[] =
+    "managed.serial_allow_usb_devices_for_urls";
+#endif  // !defined(OS_ANDROID)
+
 // Directory of the last profile used.
 const char kProfileLastUsed[] = "profile.last_used";
 
@@ -1853,11 +1862,6 @@
 const char kNtpSearchSuggestionsImpressions[] =
     "ntp.search_suggestions_impressions";
 const char kNtpSearchSuggestionsOptOut[] = "ntp.search_suggestions_opt_out";
-// Tracks whether the user has chosen to hide the shortcuts tiles on the NTP.
-const char kNtpShortcutsVisible[] = "ntp.shortcust_visible";
-// Tracks whether the user has chosen to use custom links or most visited sites
-// for the shortcut tiles on the NTP.
-const char kNtpUseMostVisitedTiles[] = "ntp.use_most_visited_tiles";
 #endif  // defined(OS_ANDROID)
 
 // Which page should be visible on the new tab page v4
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 99828b1..f57cd98e 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -43,8 +43,8 @@
 extern const char kRestoreOnStartup[];
 extern const char kSessionExitType[];
 #if !defined(OS_ANDROID)
-extern const char kManagedSerialAllowAllPortsForUrls[];
-extern const char kManagedSerialAllowUsbDevicesForUrls[];
+extern const char kManagedProfileSerialAllowAllPortsForUrlsDeprecated[];
+extern const char kManagedProfileSerialAllowUsbDevicesForUrlsDeprecated[];
 #endif  // !defined(OS_ANDROID)
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS) && BUILDFLAG(ENABLE_EXTENSIONS)
 extern const char kSupervisedUserApprovedExtensions[];
@@ -528,6 +528,11 @@
 extern const char kPermissionActions[];
 extern const char kHadThreeConsecutiveNotificationPermissionDenies[];
 
+#if !defined(OS_ANDROID)
+extern const char kManagedSerialAllowAllPortsForUrls[];
+extern const char kManagedSerialAllowUsbDevicesForUrls[];
+#endif  // !defined(OS_ANDROID)
+
 extern const char kProfileLastUsed[];
 extern const char kProfilesLastActive[];
 extern const char kProfilesNumCreated[];
@@ -626,8 +631,6 @@
 extern const char kNtpSearchSuggestionsBlocklist[];
 extern const char kNtpSearchSuggestionsImpressions[];
 extern const char kNtpSearchSuggestionsOptOut[];
-extern const char kNtpShortcutsVisible[];
-extern const char kNtpUseMostVisitedTiles[];
 #endif  // defined(OS_ANDROID)
 extern const char kNtpShownPage[];
 
diff --git a/chrome/services/sharing/nearby/nearby_connections.cc b/chrome/services/sharing/nearby/nearby_connections.cc
index a0a1144..ae2e44d 100644
--- a/chrome/services/sharing/nearby/nearby_connections.cc
+++ b/chrome/services/sharing/nearby/nearby_connections.cc
@@ -335,7 +335,8 @@
     return;
   }
 
-  LOG(WARNING) << "Nearby dependency mojo disconnected: ["
+  LOG(WARNING) << "The utility process has detected that the browser process "
+                  "has disconnected from a mojo pipe: ["
                << GetMojoDependencyName(dependency_name) << "]";
   base::UmaHistogramEnumeration(
       "Nearby.Connections.UtilityProcessShutdownReason."
diff --git a/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc b/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc
index 6596663..b8c2a57 100644
--- a/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc
+++ b/chrome/services/speech/cros_speech_recognition_recognizer_impl.cc
@@ -92,8 +92,12 @@
     config->api_key = google_apis::GetSodaAPIKey();
     config->language_dlc_path = languagepack_path_.value();
     config->library_dlc_path = binary_path_.value();
-    // TODO(crbug.com/1173135): Set options_->recognition_mode and
-    // options_->enable_formatting in the SodaConfig when available.
+    // TODO(crbug.com/1173135): Set options_->recognition_mode in the SodaConfig
+    // when available.
+    config->enable_formatting =
+        options_->enable_formatting
+            ? chromeos::machine_learning::mojom::OptionalBool::kTrue
+            : chromeos::machine_learning::mojom::OptionalBool::kFalse;
     cros_soda_client_->Reset(std::move(config), recognition_event_callback_);
   }
   cros_soda_client_->AddAudio(reinterpret_cast<char*>(buffer->data.data()),
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.cc b/chrome/services/speech/speech_recognition_recognizer_impl.cc
index 3937722..6e5d7a7 100644
--- a/chrome/services/speech/speech_recognition_recognizer_impl.cc
+++ b/chrome/services/speech/speech_recognition_recognizer_impl.cc
@@ -162,9 +162,9 @@
     const base::FilePath& binary_path,
     const base::FilePath& config_path)
     : enable_soda_(base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption)),
+      options_(std::move(options)),
       client_remote_(std::move(remote)),
-      config_path_(config_path),
-      options_(std::move(options)) {
+      config_path_(config_path) {
   recognition_event_callback_ = media::BindToCurrentLoop(
       base::BindRepeating(&SpeechRecognitionRecognizerImpl::OnRecognitionEvent,
                           weak_factory_.GetWeakPtr()));
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.h b/chrome/services/speech/speech_recognition_recognizer_impl.h
index 7e7c170..1c8c5d5 100644
--- a/chrome/services/speech/speech_recognition_recognizer_impl.h
+++ b/chrome/services/speech/speech_recognition_recognizer_impl.h
@@ -84,6 +84,7 @@
       const media::mojom::ConfidenceLevel confidence_level);
 
   const bool enable_soda_;
+  media::mojom::SpeechRecognitionOptionsPtr options_;
 
  private:
   void OnLanguageChanged(const std::string& language) final;
@@ -120,7 +121,6 @@
   int sample_rate_ = 0;
   int channel_count_ = 0;
   LanguageCode language_ = LanguageCode::kNone;
-  media::mojom::SpeechRecognitionOptionsPtr options_;
 
   base::TimeDelta caption_bubble_visible_duration_;
   base::TimeDelta caption_bubble_hidden_duration_;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 321dfac..cc5d0b7 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3794,8 +3794,6 @@
     sources = [
       "../browser/lacros/browser_test_util.cc",
       "../browser/lacros/browser_test_util.h",
-      "../browser/lacros/clipboard_lacros_browsertest.cc",
-      "../browser/lacros/crosapi_pref_observer_lacros_browsertest.cc",
       "../browser/lacros/device_attributes_lacros_browsertest.cc",
       "../browser/lacros/file_manager_lacros_browsertest.cc",
       "../browser/lacros/holding_space_service_lacros_browsertest.cc",
@@ -3805,10 +3803,8 @@
       "../browser/lacros/media_session_lacros_browsertest.cc",
       "../browser/lacros/message_center_lacros_browsertest.cc",
       "../browser/lacros/metrics_reporting_lacros_browsertest.cc",
-      "../browser/lacros/overview_lacros_browsertest.cc",
       "../browser/lacros/popup_lacros_browsertest.cc",
       "../browser/lacros/screen_manager_lacros_browsertest.cc",
-      "../browser/lacros/tablet_mode_lacros_browsertest.cc",
       "../browser/lacros/web_contents_can_go_back_observer_browsertest.cc",
     ]
 
@@ -3827,6 +3823,41 @@
 
     data_deps = [ "//chrome:packed_resources" ]
   }
+
+  # This test target is intended for lacros chrome specific tests that depend
+  # on crosapi and must run in series, for those that can run in parallel,
+  # please use lacros_chrome_browsertests target instead.
+  test("lacros_chrome_browsertests_run_in_series") {
+    use_xvfb = use_xvfb_in_this_config
+
+    configs += [ "//build/config:precompiled_headers" ]
+    defines = [
+      "HAS_OUT_OF_PROC_TEST_RUNNER",
+      "CHROME_VERSION_MAJOR=" + chrome_version_major,
+    ]
+
+    sources = [
+      "../browser/lacros/browser_test_util.cc",
+      "../browser/lacros/browser_test_util.h",
+      "../browser/lacros/clipboard_lacros_browsertest.cc",
+      "../browser/lacros/crosapi_pref_observer_lacros_browsertest.cc",
+      "../browser/lacros/overview_lacros_browsertest.cc",
+      "../browser/lacros/tablet_mode_lacros_browsertest.cc",
+    ]
+
+    deps = [
+      ":browser_tests_runner",
+      ":test_support",
+      "//chromeos/crosapi/mojom",
+      "//chromeos/lacros",
+      "//chromeos/services/machine_learning/public/cpp",
+      "//chromeos/services/machine_learning/public/cpp:stub",
+      "//chromeos/services/machine_learning/public/mojom",
+      "//ui/platform_window",
+    ]
+
+    data_deps = [ "//chrome:packed_resources" ]
+  }
 }
 
 if (is_linux || is_chromeos || is_mac || is_win || is_android) {
@@ -4591,13 +4622,6 @@
     ]
   }
 
-  if (is_android) {
-    sources += [
-      "../browser/enterprise/util/android_enterprise_info_unittest.cc",
-      "../browser/policy/browser_dm_token_storage_android_unittest.cc",
-    ]
-  }
-
   if (!is_android) {
     sources += [
       "../browser/browsing_data/chrome_browsing_data_lifetime_manager_unittest.cc",
@@ -4957,6 +4981,7 @@
       "../browser/autofill/autofill_save_card_infobar_delegate_mobile_unittest.cc",
       "../browser/autofill/credit_card_accessory_controller_impl_unittest.cc",
       "../browser/autofill/manual_filling_controller_impl_unittest.cc",
+      "../browser/enterprise/util/android_enterprise_info_unittest.cc",
       "../browser/long_screenshots/long_screenshots_tab_service_unittest.cc",
       "../browser/media/android/cdm/media_drm_origin_id_manager_unittest.cc",
       "../browser/metrics/chrome_android_metrics_provider_unittest.cc",
@@ -4975,6 +5000,7 @@
       "../browser/password_manager/android/save_password_message_delegate_unittest.cc",
       "../browser/password_manager/android/update_password_infobar_delegate_android_unittest.cc",
       "../browser/permissions/permission_prompt_android_unittest.cc",
+      "../browser/policy/browser_dm_token_storage_android_unittest.cc",
       "../browser/profiles/android/profile_resolver_unittest.cc",
       "../browser/reputation/safety_tip_message_delegate_unittest.cc",
       "../browser/sharing/click_to_call/click_to_call_message_handler_android_unittest.cc",
@@ -8566,7 +8592,7 @@
       "//third_party/blink/web_tests/resources/testharness.js",
     ]
 
-    if (is_win) {
+    if (is_win && enable_vr) {
       deps += [
         "//device/vr",
         "//device/vr:directx_helpers",
diff --git a/chrome/test/base/testing_browser_process.cc b/chrome/test/base/testing_browser_process.cc
index a3f87044..f236495c 100644
--- a/chrome/test/base/testing_browser_process.cc
+++ b/chrome/test/base/testing_browser_process.cc
@@ -64,6 +64,7 @@
 #endif
 
 #if !defined(OS_ANDROID)
+#include "chrome/browser/serial/serial_policy_allowed_ports.h"
 #include "components/keep_alive_registry/keep_alive_registry.h"
 #endif
 
@@ -431,6 +432,16 @@
   return resource_coordinator_parts_.get();
 }
 
+#if !defined(OS_ANDROID)
+SerialPolicyAllowedPorts* TestingBrowserProcess::serial_policy_allowed_ports() {
+  if (!serial_policy_allowed_ports_) {
+    serial_policy_allowed_ports_ =
+        std::make_unique<SerialPolicyAllowedPorts>(local_state());
+  }
+  return serial_policy_allowed_ports_.get();
+}
+#endif
+
 BuildState* TestingBrowserProcess::GetBuildState() {
 #if !defined(OS_ANDROID)
   return &build_state_;
@@ -476,6 +487,9 @@
     // are also freed.
     network_time_tracker_.reset();
     notification_ui_manager_.reset();
+#if !defined(OS_ANDROID)
+    serial_policy_allowed_ports_.reset();
+#endif
     ShutdownBrowserPolicyConnector();
     created_browser_policy_connector_ = false;
   }
diff --git a/chrome/test/base/testing_browser_process.h b/chrome/test/base/testing_browser_process.h
index a60f9e6..eb859f99 100644
--- a/chrome/test/base/testing_browser_process.h
+++ b/chrome/test/base/testing_browser_process.h
@@ -144,6 +144,9 @@
   resource_coordinator::TabManager* GetTabManager() override;
   resource_coordinator::ResourceCoordinatorParts* resource_coordinator_parts()
       override;
+#if !defined(OS_ANDROID)
+  SerialPolicyAllowedPorts* serial_policy_allowed_ports() override;
+#endif
   BuildState* GetBuildState() override;
 
   // Set the local state for tests. Consumer is responsible for cleaning it up
@@ -228,6 +231,7 @@
       resource_coordinator_parts_;
 
 #if !defined(OS_ANDROID)
+  std::unique_ptr<SerialPolicyAllowedPorts> serial_policy_allowed_ports_;
   BuildState build_state_;
 #endif
 };
diff --git a/chrome/test/data/extensions/api_test/browser_action/add_popup/manifest.json b/chrome/test/data/extensions/api_test/browser_action/add_popup/manifest.json
index 2e1b42d..2bd211b 100644
--- a/chrome/test/data/extensions/api_test/browser_action/add_popup/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/add_popup/manifest.json
@@ -3,7 +3,8 @@
   "version": "1.0",
   "manifest_version": 2,
   "background": {
-    "scripts": ["background.js"]
+    "scripts": ["background.js"],
+    "persistent": true
   },
   "permissions": [
     "tabs", "http://*/*"
diff --git a/chrome/test/data/extensions/api_test/browser_action/basics/manifest.json b/chrome/test/data/extensions/api_test/browser_action/basics/manifest.json
index 27b1d54..321ff0dc 100644
--- a/chrome/test/data/extensions/api_test/browser_action/basics/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/basics/manifest.json
@@ -3,7 +3,7 @@
   "version": "1.0",
   "manifest_version": 2,
   "background": {
-    "persistent": false,
+    "persistent": true,
     "scripts": ["background.js"]
   },
   "permissions": [
diff --git a/chrome/test/data/extensions/api_test/browser_action/color/manifest.json b/chrome/test/data/extensions/api_test/browser_action/color/manifest.json
index 65b7668..3d084cc 100644
--- a/chrome/test/data/extensions/api_test/browser_action/color/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/color/manifest.json
@@ -3,7 +3,8 @@
   "version": "1.0",
   "manifest_version": 2,
   "background": {
-    "scripts": ["background.js"]
+    "scripts": ["background.js"],
+    "persistent": true
   },
   "permissions": [
     "tabs", "http://*/*"
diff --git a/chrome/test/data/extensions/api_test/browser_action/getters/manifest.json b/chrome/test/data/extensions/api_test/browser_action/getters/manifest.json
index 08f9845..8aa1852 100644
--- a/chrome/test/data/extensions/api_test/browser_action/getters/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/getters/manifest.json
@@ -3,7 +3,8 @@
   "version": "1.0",
   "manifest_version": 2,
   "background": {
-    "scripts": ["background.js"]
+    "scripts": ["background.js"],
+    "persistent": true
   },
   "permissions": [
     "tabs", "http://*/*"
diff --git a/chrome/test/data/extensions/api_test/browser_action/rect_icon/background.js b/chrome/test/data/extensions/api_test/browser_action/rect_icon/background.js
index 6a9b1ef..4c534d8 100644
--- a/chrome/test/data/extensions/api_test/browser_action/rect_icon/background.js
+++ b/chrome/test/data/extensions/api_test/browser_action/rect_icon/background.js
@@ -5,6 +5,6 @@
 chrome.test.sendMessage('ready', function() {
   chrome.browserAction.setIcon({path: 'rectangle.png'}, function() {
     chrome.test.assertNoLastError();
-    chrome.test.succeed();
+    chrome.test.notifyPass();
   });
 });
diff --git a/chrome/test/data/extensions/api_test/browser_action/rect_icon/manifest.json b/chrome/test/data/extensions/api_test/browser_action/rect_icon/manifest.json
index fe353c5..d36e517 100644
--- a/chrome/test/data/extensions/api_test/browser_action/rect_icon/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/rect_icon/manifest.json
@@ -2,7 +2,10 @@
   "name": "Rectangle icons",
   "description": "A browser action with rectangular icons",
   "manifest_version": 2,
-  "background": {"scripts": ["background.js"]},
+  "background": {
+    "scripts": ["background.js"],
+    "persistent": true
+  },
   "browser_action": {
     "default_icon": "square.png"
   },
diff --git a/chrome/test/data/extensions/api_test/browser_action/remove_popup/manifest.json b/chrome/test/data/extensions/api_test/browser_action/remove_popup/manifest.json
index 79e796b..0a4ef04 100644
--- a/chrome/test/data/extensions/api_test/browser_action/remove_popup/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/remove_popup/manifest.json
@@ -3,7 +3,8 @@
   "version": "1.0",
   "manifest_version": 2,
   "background": {
-    "scripts": ["background.js"]
+    "scripts": ["background.js"],
+    "persistent": true
   },
   "permissions": [
     "tabs", "http://*/*"
diff --git a/chrome/test/data/extensions/api_test/browser_action/set_icon/background.js b/chrome/test/data/extensions/api_test/browser_action/set_icon/background.js
index b6e3953..3e01f2a 100644
--- a/chrome/test/data/extensions/api_test/browser_action/set_icon/background.js
+++ b/chrome/test/data/extensions/api_test/browser_action/set_icon/background.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 function getImageData() {
-  var canvas = document.createElement("canvas");
+  var canvas = new OffscreenCanvas(10, 100);
   var ctx = canvas.getContext("2d");
 
   ctx.fillStyle = "green";
diff --git a/chrome/test/data/extensions/api_test/browser_action/set_icon/manifest.json b/chrome/test/data/extensions/api_test/browser_action/set_icon/manifest.json
index aa9f00c..9dcb00d 100644
--- a/chrome/test/data/extensions/api_test/browser_action/set_icon/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/set_icon/manifest.json
@@ -3,7 +3,8 @@
   "version": "1.0",
   "manifest_version": 2,
   "background": {
-    "scripts": ["background.js"]
+    "scripts": ["background.js"],
+    "persistent": true
   },
   "permissions": [
     "tabs", "http://*/*"
diff --git a/chrome/test/data/extensions/api_test/browser_action/tab_specific_state/manifest.json b/chrome/test/data/extensions/api_test/browser_action/tab_specific_state/manifest.json
index 99612eb..db99512 100644
--- a/chrome/test/data/extensions/api_test/browser_action/tab_specific_state/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/tab_specific_state/manifest.json
@@ -3,7 +3,8 @@
   "version": "1.0",
   "manifest_version": 2,
   "background": {
-    "scripts": ["background.js"]
+    "scripts": ["background.js"],
+    "persistent": true
   },
   "permissions": [
     "tabs", "http://*/*"
@@ -12,4 +13,4 @@
     "default_icon": "icon1.png",
     "default_title": "hi!"
   }
-}
\ No newline at end of file
+}
diff --git a/chrome/test/data/extensions/api_test/browser_action/update/manifest.json b/chrome/test/data/extensions/api_test/browser_action/update/manifest.json
index ca88418..d7b382d 100644
--- a/chrome/test/data/extensions/api_test/browser_action/update/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/update/manifest.json
@@ -3,7 +3,7 @@
   "version": "1.0",
   "manifest_version": 2,
   "background": {
-    "persistent": false,
+    "persistent": true,
     "scripts": ["background.js"]
   },
   "browser_action": {
diff --git a/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json b/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json
index 6d2143f..176335f 100644
--- a/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json
+++ b/chrome/test/data/extensions/api_test/extension_module/cognito_file/manifest.json
@@ -4,6 +4,7 @@
   "manifest_version": 2,
   "description": "end-to-end browser test for some chrome.extension API: cognito mode.",
   "background": {
-    "page": "test.html"
+    "scripts": ["test.js"],
+    "persistent": true
   }
 }
diff --git a/chrome/test/data/extensions/api_test/extension_module/cognito_file/test.html b/chrome/test/data/extensions/api_test/extension_module/cognito_file/test.html
deleted file mode 100644
index 3efb342..0000000
--- a/chrome/test/data/extensions/api_test/extension_module/cognito_file/test.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!--
- * Copyright (c) 2011 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.
--->
-<script src="test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json b/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json
index 6d2143f..176335f 100644
--- a/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json
+++ b/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/manifest.json
@@ -4,6 +4,7 @@
   "manifest_version": 2,
   "description": "end-to-end browser test for some chrome.extension API: cognito mode.",
   "background": {
-    "page": "test.html"
+    "scripts": ["test.js"],
+    "persistent": true
   }
 }
diff --git a/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/test.html b/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/test.html
deleted file mode 100644
index 3efb342..0000000
--- a/chrome/test/data/extensions/api_test/extension_module/cognito_nofile/test.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!--
- * Copyright (c) 2011 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.
--->
-<script src="test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json b/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json
index 7844475..0d304812 100644
--- a/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json
+++ b/chrome/test/data/extensions/api_test/extension_module/incognito_file/manifest.json
@@ -4,6 +4,7 @@
   "manifest_version": 2,
   "description": "end-to-end browser test for some chrome.extension API: incognito mode",
   "background": {
-    "page": "test.html"
+    "scripts": ["test.js"],
+    "persistent": true
   }
 }
diff --git a/chrome/test/data/extensions/api_test/extension_module/incognito_file/test.html b/chrome/test/data/extensions/api_test/extension_module/incognito_file/test.html
deleted file mode 100644
index 3efb342..0000000
--- a/chrome/test/data/extensions/api_test/extension_module/incognito_file/test.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!--
- * Copyright (c) 2011 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.
--->
-<script src="test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json b/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json
index 7844475..0d304812 100644
--- a/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json
+++ b/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/manifest.json
@@ -4,6 +4,7 @@
   "manifest_version": 2,
   "description": "end-to-end browser test for some chrome.extension API: incognito mode",
   "background": {
-    "page": "test.html"
+    "scripts": ["test.js"],
+    "persistent": true
   }
 }
diff --git a/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/test.html b/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/test.html
deleted file mode 100644
index 3efb342..0000000
--- a/chrome/test/data/extensions/api_test/extension_module/incognito_nofile/test.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!--
- * Copyright (c) 2011 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.
--->
-<script src="test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/trigger_actions/browser_action/manifest.json b/chrome/test/data/extensions/api_test/trigger_actions/browser_action/manifest.json
index e4850aa..7e8ab8d 100644
--- a/chrome/test/data/extensions/api_test/trigger_actions/browser_action/manifest.json
+++ b/chrome/test/data/extensions/api_test/trigger_actions/browser_action/manifest.json
@@ -3,7 +3,8 @@
   "version": "1.0",
   "manifest_version": 2,
   "background": {
-    "scripts": ["background.js"]
+    "scripts": ["background.js"],
+    "persistent": true
   },
   "permissions": [
     "tabs", "http://*/*"
diff --git a/chrome/test/data/extensions/api_test/virtual_keyboard_private/test.js b/chrome/test/data/extensions/api_test/virtual_keyboard_private/test.js
index 55488dd..350ebdf 100644
--- a/chrome/test/data/extensions/api_test/virtual_keyboard_private/test.js
+++ b/chrome/test/data/extensions/api_test/virtual_keyboard_private/test.js
@@ -6,19 +6,42 @@
 
 function callbackResult(result) {
   var result = result.map((item) => {
-    return {displayFormat: item.displayFormat,
-            textData: !!item.textData,
-            imageData: !!item.imageData};
+    return {
+      displayFormat: item.displayFormat,
+      textData: !!item.textData,
+      imageData: !!item.imageData,
+      timeCopied: !!item.timeCopied
+    };
   });
 
   // Test that clipboard items are in the correct order with the correct data
   // types.
-  chrome.test.assertEq(result,
-    [{"displayFormat":"file","textData":true, "imageData":true},
-     {"displayFormat":"png", "textData":false,"imageData":true},
-     {"displayFormat":"text","textData":true, "imageData":false},
-     {"displayFormat":"html","textData":false,"imageData":true}]
-  );
+  chrome.test.assertEq(result, [
+    {
+      'displayFormat': 'file',
+      'textData': true,
+      'imageData': true,
+      'timeCopied': true
+    },
+    {
+      'displayFormat': 'png',
+      'textData': false,
+      'imageData': true,
+      'timeCopied': true
+    },
+    {
+      'displayFormat': 'text',
+      'textData': true,
+      'imageData': false,
+      'timeCopied': true
+    },
+    {
+      'displayFormat': 'html',
+      'textData': false,
+      'imageData': true,
+      'timeCopied': true
+    }
+  ]);
 }
 
 chrome.test.runTests([
diff --git a/chrome/test/data/extensions/back_forward_cache/background_page/background.js b/chrome/test/data/extensions/back_forward_cache/background_page/background.js
new file mode 100644
index 0000000..96d4b44
--- /dev/null
+++ b/chrome/test/data/extensions/back_forward_cache/background_page/background.js
@@ -0,0 +1,3 @@
+// 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.
diff --git a/chrome/test/data/extensions/back_forward_cache/background_page/manifest.json b/chrome/test/data/extensions/back_forward_cache/background_page/manifest.json
new file mode 100644
index 0000000..a3d6f99
--- /dev/null
+++ b/chrome/test/data/extensions/back_forward_cache/background_page/manifest.json
@@ -0,0 +1,10 @@
+{
+  "name": "Background Page Extension",
+  "version": "1.0",
+  "manifest_version": 2,
+  "description": "A minimal extension for testing purposes.",
+  "background": {
+    "scripts": ["background.js"]
+  },
+  "permissions": ["*://a.com/*", "*://b.com/*"]
+}
diff --git a/chrome/test/data/extensions/back_forward_cache/content_script_storage/manifest.json b/chrome/test/data/extensions/back_forward_cache/content_script_storage/manifest.json
new file mode 100644
index 0000000..8ae32a8
--- /dev/null
+++ b/chrome/test/data/extensions/back_forward_cache/content_script_storage/manifest.json
@@ -0,0 +1,14 @@
+{
+  "name": "no caching",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Checks that content scripts receiving events evict from back forward cache.",
+  "permissions": ["storage"],
+  "content_scripts": [
+    {
+      "matches": ["*://a.com/*", "*://b.com/*"],
+      "js": ["storage.js"],
+      "run_at": "document_end"
+    }
+  ]
+}
diff --git a/chrome/test/data/extensions/back_forward_cache/content_script_storage/storage.js b/chrome/test/data/extensions/back_forward_cache/content_script_storage/storage.js
new file mode 100644
index 0000000..9f134f7
--- /dev/null
+++ b/chrome/test/data/extensions/back_forward_cache/content_script_storage/storage.js
@@ -0,0 +1,12 @@
+// 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.
+
+chrome.storage.onChanged.addListener((changes, area) => {
+  // do nothing
+});
+
+if (window.location.host.indexOf('b.com') != -1) {
+  var options = {test: true};
+  chrome.storage.sync.set({options});
+}
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 151693a..2087cd8 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2609,7 +2609,7 @@
     "policy_pref_mapping_tests": [
       {
         "policies": {"SerialAllowAllPortsForUrls": ["https://www.google.com"]},
-        "prefs": { "profile.managed.serial_allow_all_ports_for_urls": {} }
+        "prefs": { "managed.serial_allow_all_ports_for_urls": { "location": "local_state" } }
       }
     ]
   },
@@ -2628,7 +2628,7 @@
             "urls": ["https://www.google.com"]
           }
         ]},
-        "prefs": { "profile.managed.serial_allow_usb_devices_for_urls": {} }
+        "prefs": { "managed.serial_allow_usb_devices_for_urls": { "location": "local_state" } }
       }
     ]
   },
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
index 792a360..0d6b6ea 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/fake_shimless_rma_service_test.js
@@ -22,7 +22,7 @@
 
   test('GetCurrentStateDefaultRmaNotRequired', () => {
     return service.getCurrentState().then((state) => {
-      assertEquals(state.currentState, RmaState.kUnknown);
+      assertEquals(state.state, RmaState.kUnknown);
       assertEquals(state.error, RmadErrorCode.kRmaNotRequired);
     });
   });
@@ -34,7 +34,7 @@
     service.setStates(states);
 
     return service.getCurrentState().then((state) => {
-      assertEquals(state.currentState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -46,14 +46,14 @@
     service.setStates(states);
 
     return service.getCurrentState().then((state) => {
-      assertEquals(state.currentState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kMissingComponent);
     });
   });
 
   test('GetNextStateDefaultRmaNotRequired', () => {
     return service.getNextState().then((state) => {
-      assertEquals(state.nextState, RmaState.kUnknown);
+      assertEquals(state.state, RmaState.kUnknown);
       assertEquals(state.error, RmadErrorCode.kRmaNotRequired);
     });
   });
@@ -66,7 +66,7 @@
     service.setStates(states);
 
     return service.getNextState().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -78,14 +78,14 @@
     service.setStates(states);
 
     return service.getNextState().then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kTransitionFailed);
     });
   });
 
   test('GetPrevStateDefaultRmaNotRequired', () => {
     return service.getPrevState().then((state) => {
-      assertEquals(state.prevState, RmaState.kUnknown);
+      assertEquals(state.state, RmaState.kUnknown);
       assertEquals(state.error, RmadErrorCode.kRmaNotRequired);
     });
   });
@@ -98,11 +98,11 @@
     service.setStates(states);
 
     service.getNextState().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
     return service.getPrevState().then((state) => {
-      assertEquals(state.prevState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -114,7 +114,7 @@
     service.setStates(states);
 
     return service.getPrevState().then((state) => {
-      assertEquals(state.prevState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kTransitionFailed);
     });
   });
@@ -166,14 +166,14 @@
     service.setStates(states);
 
     return service.setSameOwner().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
 
   test('SetSameOwnerWhenRmaNotRequired', () => {
     return service.setSameOwner().then((state) => {
-      assertEquals(state.nextState, RmaState.kUnknown);
+      assertEquals(state.state, RmaState.kUnknown);
       assertEquals(state.error, RmadErrorCode.kRmaNotRequired);
     });
   });
@@ -186,7 +186,7 @@
     service.setStates(states);
 
     return service.setSameOwner().then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kRequestInvalid);
     });
   });
@@ -199,7 +199,7 @@
     service.setStates(states);
 
     return service.setDifferentOwner().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -212,7 +212,7 @@
     service.setStates(states);
 
     return service.setDifferentOwner().then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kRequestInvalid);
     });
   });
@@ -228,7 +228,7 @@
     service.setStates(states);
 
     return service.chooseManuallyDisableWriteProtect().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -241,7 +241,7 @@
     service.setStates(states);
 
     return service.chooseManuallyDisableWriteProtect().then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kRequestInvalid);
     });
   });
@@ -257,7 +257,7 @@
     service.setStates(states);
 
     return service.chooseRsuDisableWriteProtect().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -270,7 +270,33 @@
     service.setStates(states);
 
     return service.chooseRsuDisableWriteProtect().then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
+      assertEquals(state.error, RmadErrorCode.kRequestInvalid);
+    });
+  });
+
+  test('SetRsuDisableWriteProtectCodeOk', () => {
+    let states = [
+      {state: RmaState.kEnterRSUWPDisableCode, error: RmadErrorCode.kOk},
+      {state: RmaState.kUpdateChrome, error: RmadErrorCode.kOk},
+    ];
+    service.setStates(states);
+
+    return service.setRsuDisableWriteProtectCode('ignored').then((state) => {
+      assertEquals(state.state, RmaState.kUpdateChrome);
+      assertEquals(state.error, RmadErrorCode.kOk);
+    });
+  });
+
+  test('SetRsuDisableWriteProtectCodeWrongStateFails', () => {
+    let states = [
+      {state: RmaState.kWelcomeScreen, error: RmadErrorCode.kOk},
+      {state: RmaState.kUpdateChrome, error: RmadErrorCode.kOk},
+    ];
+    service.setStates(states);
+
+    return service.setRsuDisableWriteProtectCode('ignored').then((state) => {
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kRequestInvalid);
     });
   });
@@ -316,7 +342,7 @@
     service.setStates(states);
 
     return service.setComponentsRepairState(components).then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -339,7 +365,7 @@
     service.setStates(states);
 
     return service.setComponentsRepairState(components).then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kRequestInvalid);
     });
   });
@@ -352,7 +378,7 @@
     service.setStates(states);
 
     return service.reworkMainboard().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -365,7 +391,7 @@
     service.setStates(states);
 
     return service.reworkMainboard().then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kRequestInvalid);
     });
   });
@@ -378,7 +404,7 @@
     service.setStates(states);
 
     return service.reimageSkipped().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -391,7 +417,7 @@
     service.setStates(states);
 
     return service.reimageSkipped().then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kRequestInvalid);
     });
   });
@@ -404,7 +430,7 @@
     service.setStates(states);
 
     return service.reimageFromDownload().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -417,7 +443,7 @@
     service.setStates(states);
 
     return service.reimageFromDownload().then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kRequestInvalid);
     });
   });
@@ -430,7 +456,7 @@
     service.setStates(states);
 
     return service.reimageFromUsb().then((state) => {
-      assertEquals(state.nextState, RmaState.kUpdateChrome);
+      assertEquals(state.state, RmaState.kUpdateChrome);
       assertEquals(state.error, RmadErrorCode.kOk);
     });
   });
@@ -443,7 +469,7 @@
     service.setStates(states);
 
     return service.reimageFromUsb().then((state) => {
-      assertEquals(state.nextState, RmaState.kWelcomeScreen);
+      assertEquals(state.state, RmaState.kWelcomeScreen);
       assertEquals(state.error, RmadErrorCode.kRequestInvalid);
     });
   });
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
index 8ee6018..96b0a02 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/shimless_rma_app_test.js
@@ -7,7 +7,7 @@
 import {FakeShimlessRmaService} from 'chrome://shimless-rma/fake_shimless_rma_service.js';
 import {setShimlessRmaServiceForTesting} from 'chrome://shimless-rma/mojo_interface_provider.js';
 import {ButtonState, ShimlessRmaElement} from 'chrome://shimless-rma/shimless_rma.js';
-import {RmaState, State} from 'chrome://shimless-rma/shimless_rma_types.js';
+import {RmadErrorCode, RmaState, StateResult} from 'chrome://shimless-rma/shimless_rma_types.js';
 
 import {assertFalse, assertTrue} from '../../chai_assert.js';
 import {flushTasks, isVisible} from '../../test_util.m.js';
@@ -36,7 +36,7 @@
   });
 
   /**
-   * @param {!Array<!State>} states
+   * @param {!Array<!StateResult>} states
    * @param {string} chromeVersion
    */
   function initializeShimlessRMAApp(states, chromeVersion) {
@@ -138,7 +138,7 @@
     await clickNext();
     assertFalse(initialPage.hidden);
 
-    resolver.resolve(RmaState.kUpdateChrome);
+    resolver.resolve({state: RmaState.kUpdateChrome, error: RmadErrorCode.kOk});
     await flushTasks();
 
     const updatePage =
diff --git a/chrome/test/data/webui/js/cr/ui/BUILD.gn b/chrome/test/data/webui/js/cr/ui/BUILD.gn
index c0b4a17..75e7570 100644
--- a/chrome/test/data/webui/js/cr/ui/BUILD.gn
+++ b/chrome/test/data/webui/js/cr/ui/BUILD.gn
@@ -19,7 +19,6 @@
     "menu_button_test.js",
     "menu_test.js",
     "position_util_test.js",
-    "splitter_test.js",
   ]
 
   namespace_rewrites = cr_namespace_rewrites
@@ -144,16 +143,13 @@
   extra_deps = [ ":modulize" ]
 }
 
-js_library("splitter_test.m") {
-  sources =
-      [ "$root_gen_dir/chrome/test/data/webui/js/cr/ui/splitter_test.m.js" ]
+js_library("splitter_test") {
   deps = [
     "../../..:chai_assert",
     "//ui/webui/resources/js:util.m",
     "//ui/webui/resources/js/cr:ui.m",
-    "//ui/webui/resources/js/cr/ui:splitter.m",
+    "//ui/webui/resources/js/cr/ui:splitter",
   ]
-  extra_deps = [ ":modulize" ]
 }
 
 js_type_check("closure_compile") {
@@ -169,6 +165,6 @@
     ":menu_button_test.m",
     ":menu_test.m",
     ":position_util_test.m",
-    ":splitter_test.m",
+    ":splitter_test",
   ]
 }
diff --git a/chrome/test/data/webui/js/cr/ui/splitter_test.html b/chrome/test/data/webui/js/cr/ui/splitter_test.html
deleted file mode 100644
index fa7eff4..0000000
--- a/chrome/test/data/webui/js/cr/ui/splitter_test.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!doctype html>
-<html>
-<body>
-<script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://resources/js/cr/ui.js"></script>
-<script src="chrome://resources/js/cr/ui/splitter.js"></script>
-<script src="chrome://resources/js/assert.js"></script>
-<script src="chrome://resources/js/util.js"></script>
-<script src="splitter_test.js"></script>
-</body>
-</html>
diff --git a/chrome/test/data/webui/js/cr/ui/splitter_test.js b/chrome/test/data/webui/js/cr/ui/splitter_test.js
index 11a3bd2..b75c4e40 100644
--- a/chrome/test/data/webui/js/cr/ui/splitter_test.js
+++ b/chrome/test/data/webui/js/cr/ui/splitter_test.js
@@ -3,10 +3,12 @@
 // found in the LICENSE file.
 
 // clang-format off
-// #import {assertEquals, assertTrue, assertFalse} from '../../../chai_assert.js';
-// #import {getRequiredElement} from 'chrome://resources/js/util.m.js';
-// #import {decorate} from 'chrome://resources/js/cr/ui.m.js';
-// #import {Splitter} from 'chrome://resources/js/cr/ui/splitter.m.js';
+import {decorate} from 'chrome://resources/js/cr/ui.m.js';
+import {Splitter} from 'chrome://resources/js/cr/ui/splitter.js';
+import {getRequiredElement} from 'chrome://resources/js/util.m.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../../../chai_assert.js';
+
 // clang-format on
 
 function setUp() {
@@ -20,7 +22,7 @@
 
 function testSplitter_IgnoresRightMouse() {
   const splitter = getRequiredElement('splitter');
-  cr.ui.decorate(splitter, cr.ui.Splitter);
+  decorate(splitter, Splitter);
 
   const downRight = new MouseEvent('mousedown', {button: 1, cancelable: true});
   assertTrue(splitter.dispatchEvent(downRight));
@@ -33,7 +35,7 @@
 
 function testSplitter_ResizePreviousElement() {
   const splitter = getRequiredElement('splitter');
-  cr.ui.decorate(splitter, cr.ui.Splitter);
+  decorate(splitter, Splitter);
   splitter.resizeNextElement = false;
 
   const previousElement = document.getElementById('previous');
@@ -62,7 +64,7 @@
 
 function testSplitter_ResizeNextElement() {
   const splitter = getRequiredElement('splitter');
-  cr.ui.decorate(splitter, cr.ui.Splitter);
+  decorate(splitter, Splitter);
   splitter.resizeNextElement = true;
   const nextElement = document.getElementById('next');
   nextElement.style.width = '0px';
diff --git a/chrome/test/data/webui/settings/site_details_permission_tests.js b/chrome/test/data/webui/settings/site_details_permission_tests.js
index 1934c20..cd18a0c5 100644
--- a/chrome/test/data/webui/settings/site_details_permission_tests.js
+++ b/chrome/test/data/webui/settings/site_details_permission_tests.js
@@ -56,7 +56,7 @@
 
     return browserProxy.whenCalled('setOriginPermissions').then((args) => {
       assertEquals(origin, args[0]);
-      assertDeepEquals([testElement.category], args[1]);
+      assertDeepEquals(testElement.category, args[1]);
       assertEquals(expectedContentSetting, args[2]);
     });
   }
diff --git a/chrome/test/data/webui/settings/site_details_tests.js b/chrome/test/data/webui/settings/site_details_tests.js
index b5008e4..1f2f06c8 100644
--- a/chrome/test/data/webui/settings/site_details_tests.js
+++ b/chrome/test/data/webui/settings/site_details_tests.js
@@ -197,83 +197,6 @@
     return siteDetailsElement;
   }
 
-  test('all site settings are shown', function() {
-    // Add ContentsSettingsTypes which are not supposed to be shown on the Site
-    // Details page here.
-    const nonSiteDetailsContentSettingsTypes = [
-      ContentSettingsTypes.COOKIES,
-      ContentSettingsTypes.PROTOCOL_HANDLERS,
-      ContentSettingsTypes.ZOOM_LEVELS,
-    ];
-    if (!isChromeOS && !isWindows) {
-      nonSiteDetailsContentSettingsTypes.push(
-          ContentSettingsTypes.PROTECTED_CONTENT);
-    }
-
-    // A list of optionally shown content settings mapped to their loadTimeData
-    // flag string.
-    const optionalSiteDetailsContentSettingsTypes =
-        /** @type {!ContentSettingsType : string} */ ({});
-    optionalSiteDetailsContentSettingsTypes[ContentSettingsTypes
-                                                .BLUETOOTH_SCANNING] =
-        'enableExperimentalWebPlatformFeatures';
-    optionalSiteDetailsContentSettingsTypes[ContentSettingsTypes
-                                                .WINDOW_PLACEMENT] =
-        'enableExperimentalWebPlatformFeatures';
-    optionalSiteDetailsContentSettingsTypes[ContentSettingsTypes
-                                                .PAYMENT_HANDLER] =
-        'enablePaymentHandlerContentSetting';
-    optionalSiteDetailsContentSettingsTypes[ContentSettingsTypes.ADS] =
-        'enableSafeBrowsingSubresourceFilter';
-    optionalSiteDetailsContentSettingsTypes[ContentSettingsTypes
-                                                .BLUETOOTH_DEVICES] =
-        'enableWebBluetoothNewPermissionsBackend';
-
-    const controlledSettingsCount = /** @type{string : int } */ ({});
-
-    controlledSettingsCount['enableExperimentalWebPlatformFeatures'] = 2;
-    controlledSettingsCount['enablePaymentHandlerContentSetting'] = 1;
-    controlledSettingsCount['enableSafeBrowsingSubresourceFilter'] = 1;
-    controlledSettingsCount['enableWebBluetoothNewPermissionsBackend'] = 1;
-
-    browserProxy.setPrefs(prefs);
-
-    // First, explicitly set all the optional settings to false.
-    for (const contentSetting in optionalSiteDetailsContentSettingsTypes) {
-      const loadTimeDataOverride = {};
-      loadTimeDataOverride[optionalSiteDetailsContentSettingsTypes
-                               [contentSetting]] = false;
-      loadTimeData.overrideValues(loadTimeDataOverride);
-    }
-
-    // Iterate over each flag in on / off state, assuming that the on state
-    // means the content setting will show, and off hides it.
-    for (const contentSetting in optionalSiteDetailsContentSettingsTypes) {
-      const numContentSettings = Object.keys(ContentSettingsTypes).length -
-          nonSiteDetailsContentSettingsTypes.length -
-          Object.keys(optionalSiteDetailsContentSettingsTypes).length;
-
-      const loadTimeDataOverride = {};
-      loadTimeDataOverride[optionalSiteDetailsContentSettingsTypes
-                               [contentSetting]] = true;
-      loadTimeData.overrideValues(loadTimeDataOverride);
-      testElement = createSiteDetails('https://foo.com:443');
-      assertEquals(
-          numContentSettings +
-              controlledSettingsCount[optionalSiteDetailsContentSettingsTypes[
-                  [contentSetting]]],
-          testElement.getCategoryList().length);
-
-      // Check for setting = off at the end to ensure that the setting does
-      // not carry over for the next iteration.
-      loadTimeDataOverride[optionalSiteDetailsContentSettingsTypes
-                               [contentSetting]] = false;
-      loadTimeData.overrideValues(loadTimeDataOverride);
-      testElement = createSiteDetails('https://foo.com:443');
-      assertEquals(numContentSettings, testElement.getCategoryList().length);
-    }
-  });
-
   test('usage heading shows properly', function() {
     browserProxy.setPrefs(prefs);
     testElement = createSiteDetails('https://foo.com:443');
@@ -364,14 +287,6 @@
 
   test('correct pref settings are shown', function() {
     browserProxy.setPrefs(prefs);
-    // Make sure all the possible content settings are shown for this test.
-    loadTimeData.overrideValues({
-      enableExperimentalWebPlatformFeatures: true,
-      enableFileSystemWriteContentSetting: true,
-      enablePaymentHandlerContentSetting: true,
-      enableSafeBrowsingSubresourceFilter: true,
-      enableWebBluetoothNewPermissionsBackend: true,
-    });
     testElement = createSiteDetails('https://foo.com:443');
 
     return browserProxy.whenCalled('isOriginValid')
@@ -425,6 +340,31 @@
         });
   });
 
+  test('categories can be hidden', function() {
+    browserProxy.setPrefs(prefs);
+    // Only the categories in this list should be visible to the user.
+    browserProxy.setCategoryList(
+        [ContentSettingsTypes.NOTIFICATIONS, ContentSettingsTypes.GEOLOCATION]);
+    testElement = createSiteDetails('https://foo.com:443');
+
+    return browserProxy.whenCalled('isOriginValid')
+        .then(() => {
+          return browserProxy.whenCalled('getOriginPermissions');
+        })
+        .then(() => {
+          testElement.root.querySelectorAll('site-details-permission')
+              .forEach((siteDetailsPermission) => {
+                const shouldBeVisible = siteDetailsPermission.category ===
+                        ContentSettingsTypes.NOTIFICATIONS ||
+                    siteDetailsPermission.category ===
+                        ContentSettingsTypes.GEOLOCATION;
+                assertEquals(
+                    !shouldBeVisible, siteDetailsPermission.$.details.hidden);
+              });
+        });
+  });
+
+
   test('show confirmation dialog on reset settings', function() {
     browserProxy.setPrefs(prefs);
     testElement = createSiteDetails('https://foo.com:443');
@@ -444,7 +384,7 @@
     // Accepting the dialog will make a call to setOriginPermissions.
     return browserProxy.whenCalled('setOriginPermissions').then((args) => {
       assertEquals(testElement.origin, args[0]);
-      assertDeepEquals(testElement.getCategoryList(), args[1]);
+      assertDeepEquals(null, args[1]);
       assertEquals(ContentSetting.DEFAULT, args[2]);
     });
   });
diff --git a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
index 2b3ee99e..ebf2d9532 100644
--- a/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
+++ b/chrome/test/data/webui/settings/test_site_settings_prefs_browser_proxy.js
@@ -26,6 +26,7 @@
       'fetchBlockAutoplayStatus',
       'fetchZoomLevels',
       'getAllSites',
+      'getCategoryList',
       'getChooserExceptionList',
       'getDefaultValueForContentType',
       'getFormattedBytes',
@@ -56,6 +57,38 @@
     /** @private {boolean} */
     this.hasIncognito_ = false;
 
+    this.categoryList_ = [
+      ContentSettingsTypes.ADS,
+      ContentSettingsTypes.AR,
+      ContentSettingsTypes.AUTOMATIC_DOWNLOADS,
+      ContentSettingsTypes.BACKGROUND_SYNC,
+      ContentSettingsTypes.BLUETOOTH_DEVICES,
+      ContentSettingsTypes.BLUETOOTH_SCANNING,
+      ContentSettingsTypes.CAMERA,
+      ContentSettingsTypes.CLIPBOARD,
+      ContentSettingsTypes.FILE_HANDLING,
+      ContentSettingsTypes.FILE_SYSTEM_WRITE,
+      ContentSettingsTypes.FONT_ACCESS,
+      ContentSettingsTypes.GEOLOCATION,
+      ContentSettingsTypes.HID_DEVICES,
+      ContentSettingsTypes.IDLE_DETECTION,
+      ContentSettingsTypes.IMAGES,
+      ContentSettingsTypes.JAVASCRIPT,
+      ContentSettingsTypes.MIC,
+      ContentSettingsTypes.MIDI_DEVICES,
+      ContentSettingsTypes.MIXEDSCRIPT,
+      ContentSettingsTypes.NOTIFICATIONS,
+      ContentSettingsTypes.PAYMENT_HANDLER,
+      ContentSettingsTypes.POPUPS,
+      ContentSettingsTypes.PROTECTED_CONTENT,
+      ContentSettingsTypes.SENSORS,
+      ContentSettingsTypes.SERIAL_PORTS,
+      ContentSettingsTypes.SOUND,
+      ContentSettingsTypes.USB_DEVICES,
+      ContentSettingsTypes.VR,
+      ContentSettingsTypes.WINDOW_PLACEMENT,
+    ];
+
     /** @private {!SiteSettingsPref} */
     this.prefs_ = createSiteSettingsPrefs([], [], []);
 
@@ -82,6 +115,18 @@
   }
 
   /**
+   * Test/fake implementation for {@link getCategoryList}.
+   * @param {!string} origin the origin for the list of visible permissions.
+   */
+  getCategoryListForTest(origin) {
+    return this.categoryList_;
+  }
+
+  setCategoryList(list) {
+    this.categoryList_ = list;
+  }
+
+  /**
    * Pretends an incognito session started or ended.
    * @param {boolean} hasIncognito True for session started.
    */
@@ -172,7 +217,9 @@
   }
 
   /** @override */
-  setOriginPermissions(origin, contentTypes, blanketSetting) {
+  setOriginPermissions(origin, category, blanketSetting) {
+    const contentTypes =
+        category ? [category] : this.getCategoryListForTest(origin);
     for (let i = 0; i < contentTypes.length; ++i) {
       const type = contentTypes[i];
       const exceptionList = this.prefs_.exceptions[type];
@@ -188,12 +235,13 @@
 
     this.setPrefs(this.prefs_);
     this.methodCalled(
-        'setOriginPermissions', [origin, contentTypes, blanketSetting]);
+        'setOriginPermissions', [origin, category, blanketSetting]);
   }
 
   /** @override */
-  getAllSites(contentTypes) {
-    this.methodCalled('getAllSites', contentTypes);
+  getAllSites() {
+    this.methodCalled('getAllSites');
+    const contentTypes = this.getCategoryListForTest('https://example.com');
     const origins_set = new Set();
 
     contentTypes.forEach((contentType) => {
@@ -236,6 +284,12 @@
   }
 
   /** @override */
+  getCategoryList(origin) {
+    this.methodCalled('getCategoryList', origin);
+    return Promise.resolve(this.getCategoryListForTest(origin));
+  }
+
+  /** @override */
   getFormattedBytes(numBytes) {
     this.methodCalled('getFormattedBytes', numBytes);
     return Promise.resolve(`${numBytes} B`);
diff --git a/chrome/test/data/webui/webui_resource_browsertest.cc b/chrome/test/data/webui/webui_resource_browsertest.cc
index e3b770b..84cd4b6e 100644
--- a/chrome/test/data/webui/webui_resource_browsertest.cc
+++ b/chrome/test/data/webui/webui_resource_browsertest.cc
@@ -175,14 +175,8 @@
   LoadTestUrl("?module=js/cr/ui/menu_button_test.m.js");
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-IN_PROC_BROWSER_TEST_F(WebUIResourceBrowserTest, SplitterTest) {
-  LoadTestUrl("js/cr/ui/splitter_test.html");
-}
-#endif
-
 IN_PROC_BROWSER_TEST_F(WebUIResourceBrowserTest, SplitterModuleTest) {
-  LoadTestUrl("?module=js/cr/ui/splitter_test.m.js");
+  LoadTestUrl("?module=js/cr/ui/splitter_test.js");
 }
 
 IN_PROC_BROWSER_TEST_F(WebUIResourceBrowserTest, UtilTest) {
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 0c50854..398b20e 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -435,6 +435,7 @@
 
     if (is_win) {
       sources += [
+        "activity_impl_win_unittest.cc",
         "policy/win/group_policy_manager_unittest.cc",
         "test/integration_tests_win.cc",
         "win/installer_api_unittest.cc",
diff --git a/chrome/updater/activity_impl_win.cc b/chrome/updater/activity_impl_win.cc
index b8de570c..3ecc1780 100644
--- a/chrome/updater/activity_impl_win.cc
+++ b/chrome/updater/activity_impl_win.cc
@@ -6,16 +6,23 @@
 
 #include <string>
 
+#include "base/bind.h"
+#include "base/callback.h"
 #include "base/logging.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/registry.h"
 #include "base/win/windows_types.h"
 #include "chrome/updater/updater_scope.h"
+#include "chrome/updater/win/user_info.h"
 #include "chrome/updater/win/win_constants.h"
 
 namespace updater {
 namespace {
+
+using ProcessActiveBitUnderKeyCallback =
+    base::RepeatingCallback<bool(HKEY, const std::wstring&)>;
+
 constexpr wchar_t kDidRun[] = L"dr";
 
 std::wstring GetAppClientStateKey(const std::string& id) {
@@ -24,49 +31,107 @@
 
 bool GetActiveBitUnderKey(HKEY rootkey, const std::wstring& key_name) {
   base::win::RegKey key;
-  if (key.Open(rootkey, key_name.c_str(), KEY_READ | KEY_WOW64_32KEY) ==
+  if (key.Open(rootkey, key_name.c_str(), KEY_QUERY_VALUE | KEY_WOW64_32KEY) ==
       ERROR_SUCCESS) {
+    // We support both string and DWORD formats for backward compatibility.
     std::wstring value;
-    if (key.ReadValue(kDidRun, &value) == ERROR_SUCCESS && value == L"1")
+    if ((key.ReadValue(kDidRun, &value) == ERROR_SUCCESS) && (value == L"1"))
       return true;
+
+    DWORD value_dw = 0;
+    if ((key.ReadValueDW(kDidRun, &value_dw) == ERROR_SUCCESS) &&
+        (value_dw == 1)) {
+      return true;
+    }
   }
   return false;
 }
 
-bool GetMachineActiveBit(const std::string& id) {
-  // Read the active bit under each user in HKU\<sid>.
-  for (base::win::RegistryKeyIterator it(HKEY_USERS, L"", KEY_WOW64_32KEY);
-       it.Valid(); ++it) {
-    std::wstring user_state_key_name =
-        std::wstring(it.Name()).append(L"\\").append(GetAppClientStateKey(id));
-    if (GetActiveBitUnderKey(HKEY_USERS, user_state_key_name))
-      return true;
-  }
-
-  return false;
-}
-
-void ClearActiveBitUnderKey(HKEY rootkey, const std::wstring& key_name) {
+bool ClearActiveBitUnderKey(HKEY rootkey, const std::wstring& key_name) {
   base::win::RegKey key;
-  if (key.Open(rootkey, key_name.c_str(), KEY_WRITE | KEY_WOW64_32KEY) !=
+  if (key.Open(rootkey, key_name.c_str(),
+               KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_32KEY) !=
       ERROR_SUCCESS) {
     VLOG(2) << "Failed to open activity key with write for " << key_name;
-    return;
+    return false;
   }
 
+  if (!key.HasValue(kDidRun))
+    return true;
+
+  // We always clear the value as a string "0".
   const LONG result = key.WriteValue(kDidRun, L"0");
   VLOG_IF(2, result) << "Failed to clear activity key for " << key_name << ": "
                      << result;
+  return !result;
 }
 
-void ClearMachineActiveBit(const std::string& id) {
+bool ProcessActiveBit(ProcessActiveBitUnderKeyCallback callback,
+                      HKEY rootkey,
+                      const std::wstring& sid,
+                      const std::string& id) {
+  const std::wstring rootkey_suffix =
+      (rootkey == HKEY_USERS) ? base::StrCat({sid, L"\\"}) : L"";
+  const bool process_success = callback.Run(
+      rootkey, base::StrCat({rootkey_suffix, GetAppClientStateKey(id)}));
+
+  // For Google Toolbar and similar apps that run at low integrity, we need to
+  // also look at the low integrity IE key. Note that we cannot use the
+  // IEGetWriteableHKCU function since this function assumes that we are
+  // running with the user's credentials. The path is as follows:
+  // USER_REG_VISTA_LOW_INTEGRITY_HKCU\\SID
+  // \\GOOPDATE_REG_RELATIVE_CLIENT_STATE\\app_guid
+  const std::wstring low_integrity_key_name =
+      base::StrCat({rootkey_suffix, USER_REG_VISTA_LOW_INTEGRITY_HKCU, L"\\",
+                    sid, L"\\", GetAppClientStateKey(id)});
+
+  return callback.Run(rootkey, low_integrity_key_name) || process_success;
+}
+
+bool ProcessUserActiveBit(ProcessActiveBitUnderKeyCallback callback,
+                          const std::string& id) {
+  // Clear the active bit under HKCU.
+  std::wstring sid;
+  const HRESULT hr = GetProcessUser(nullptr, nullptr, &sid);
+  if (FAILED(hr)) {
+    VLOG(2) << "Failed to GetProcessUser " << hr;
+    return false;
+  }
+
+  return ProcessActiveBit(callback, HKEY_CURRENT_USER, sid, id);
+}
+
+bool ProcessSystemActiveBit(ProcessActiveBitUnderKeyCallback callback,
+                            const std::string& id) {
   // Clear the active bit under each user in HKU\<sid>.
   for (base::win::RegistryKeyIterator it(HKEY_USERS, L"", KEY_WOW64_32KEY);
        it.Valid(); ++it) {
-    std::wstring user_state_key_name =
-        std::wstring(it.Name()).append(L"\\").append(GetAppClientStateKey(id));
-    ClearActiveBitUnderKey(HKEY_USERS, user_state_key_name);
+    const std::wstring sid = it.Name();
+    if (ProcessActiveBit(callback, HKEY_USERS, sid, id))
+      return true;
   }
+
+  return false;
+}
+
+bool GetUserActiveBit(const std::string& id) {
+  // Read the active bit under HKCU.
+  return ProcessUserActiveBit(base::BindRepeating(&GetActiveBitUnderKey), id);
+}
+
+void ClearUserActiveBit(const std::string& id) {
+  // Clear the active bit under HKCU.
+  ProcessUserActiveBit(base::BindRepeating(&ClearActiveBitUnderKey), id);
+}
+
+bool GetSystemActiveBit(const std::string& id) {
+  // Read the active bit under each user in HKU\<sid>.
+  return ProcessSystemActiveBit(base::BindRepeating(&GetActiveBitUnderKey), id);
+}
+
+void ClearSystemActiveBit(const std::string& id) {
+  // Clear the active bit under each user in HKU\<sid>.
+  ProcessSystemActiveBit(base::BindRepeating(&ClearActiveBitUnderKey), id);
 }
 
 }  // namespace
@@ -75,11 +140,10 @@
   switch (scope) {
     case UpdaterScope::kUser:
       // TODO(crbug/1159498): Standardize registry access.
-      return GetActiveBitUnderKey(HKEY_CURRENT_USER,
-                                  GetAppClientStateKey(id).c_str());
+      return GetUserActiveBit(id);
 
     case UpdaterScope::kSystem:
-      return GetMachineActiveBit(id);
+      return GetSystemActiveBit(id);
   }
 }
 
@@ -87,12 +151,11 @@
   switch (scope) {
     case UpdaterScope::kUser:
       // TODO(crbug/1159498): Standardize registry access.
-      ClearActiveBitUnderKey(HKEY_CURRENT_USER,
-                             GetAppClientStateKey(id).c_str());
+      ClearUserActiveBit(id);
       break;
 
     case UpdaterScope::kSystem:
-      ClearMachineActiveBit(id);
+      ClearSystemActiveBit(id);
       break;
   }
 }
diff --git a/chrome/updater/activity_impl_win_unittest.cc b/chrome/updater/activity_impl_win_unittest.cc
new file mode 100644
index 0000000..7559cd1
--- /dev/null
+++ b/chrome/updater/activity_impl_win_unittest.cc
@@ -0,0 +1,202 @@
+// 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 "chrome/updater/activity_impl.h"
+
+#include <tchar.h>
+
+#include <string>
+#include <tuple>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/strings/strcat.h"
+#include "base/win/registry.h"
+#include "base/win/windows_types.h"
+#include "chrome/updater/updater_scope.h"
+#include "chrome/updater/win/user_info.h"
+#include "chrome/updater/win/win_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace updater {
+namespace {
+
+struct ReadActiveBitRetval {
+  ReadActiveBitRetval() = default;
+  ReadActiveBitRetval(DWORD result, bool active_bit)
+      : read_result(result), active_bit_set(active_bit) {}
+  ReadActiveBitRetval(const ReadActiveBitRetval&) = default;
+  ReadActiveBitRetval& operator=(const ReadActiveBitRetval&) = default;
+
+  DWORD read_result = ERROR_FILE_NOT_FOUND;
+  bool active_bit_set = false;
+};
+
+using ReadActiveBitCallback =
+    base::RepeatingCallback<ReadActiveBitRetval(base::win::RegKey&)>;
+using WriteActiveBitCallback =
+    base::RepeatingCallback<DWORD(base::win::RegKey&, bool)>;
+
+struct ReadWriteCallbacks {
+  ReadWriteCallbacks() = default;
+  ReadWriteCallbacks(ReadActiveBitCallback read, WriteActiveBitCallback write)
+      : read_callback(read), write_callback(write) {}
+  ReadWriteCallbacks(const ReadWriteCallbacks&) = default;
+  ReadWriteCallbacks& operator=(const ReadWriteCallbacks&) = default;
+
+  ReadActiveBitCallback read_callback;
+  WriteActiveBitCallback write_callback;
+};
+
+constexpr wchar_t kDidRun[] = L"dr";
+constexpr char kAppId[] = "{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}";
+constexpr wchar_t kClientStateKeyPath[] =
+    _T(CLIENT_STATE_KEY) L"{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}";
+
+DWORD WriteActiveBitAsString(base::win::RegKey& key, bool value) {
+  return key.WriteValue(kDidRun, value ? L"1" : L"0");
+}
+
+DWORD WriteActiveBitAsDword(base::win::RegKey& key, bool value) {
+  return key.WriteValue(kDidRun, value);
+}
+
+ReadActiveBitRetval ReadActiveBitAsString(base::win::RegKey& key) {
+  std::wstring did_run_str(L"0");
+  const DWORD result = key.ReadValue(kDidRun, &did_run_str);
+  return ReadActiveBitRetval(result, did_run_str == L"1");
+}
+
+ReadActiveBitRetval ReadActiveBitAsDword(base::win::RegKey& key) {
+  DWORD did_run = 0;
+  const DWORD result = key.ReadValueDW(kDidRun, &did_run);
+  return ReadActiveBitRetval(result, !!did_run);
+}
+
+}  // namespace
+
+class ActivityWinTest
+    : public ::testing::TestWithParam<
+          std::tuple<UpdaterScope, bool, bool, ReadWriteCallbacks>> {
+ protected:
+  void SetUp() override {
+    std::wstring sid;
+    ASSERT_HRESULT_SUCCEEDED(GetProcessUser(nullptr, nullptr, &sid));
+    low_integrity_key_path_ =
+        base::StrCat({USER_REG_VISTA_LOW_INTEGRITY_HKCU, L"\\", sid, L"\\",
+                      kClientStateKeyPath});
+    TearDown();
+
+    CreateUserActiveBit(SetUserValue(), WriteActiveBitFn());
+    CreateLowIntegrityUserActiveBit(SetLowUserValue(), WriteActiveBitFn());
+  }
+
+  void TearDown() override {
+    base::win::RegKey(HKEY_CURRENT_USER, L"", DELETE)
+        .DeleteKey(kClientStateKeyPath);
+    base::win::RegKey(HKEY_CURRENT_USER, L"", DELETE)
+        .DeleteKey(low_integrity_key_path_.c_str());
+  }
+
+  UpdaterScope GetScope() const { return std::get<0>(GetParam()); }
+
+  bool SetUserValue() const { return std::get<1>(GetParam()); }
+
+  bool SetLowUserValue() const { return std::get<2>(GetParam()); }
+
+  WriteActiveBitCallback WriteActiveBitFn() const {
+    return std::get<3>(GetParam()).write_callback;
+  }
+
+  ReadActiveBitCallback ReadActiveBitFn() const {
+    return std::get<3>(GetParam()).read_callback;
+  }
+
+  void CreateActiveBit(const std::wstring& key_name,
+                       bool value,
+                       WriteActiveBitCallback callback) const {
+    base::win::RegKey key;
+    ASSERT_EQ(ERROR_SUCCESS,
+              key.Create(HKEY_CURRENT_USER, key_name.c_str(), KEY_SET_VALUE));
+    ASSERT_EQ(DWORD{ERROR_SUCCESS}, callback.Run(key, value));
+  }
+
+  void DeleteActiveBit(const std::wstring& key_name) const {
+    base::win::RegKey key;
+    ASSERT_EQ(ERROR_SUCCESS,
+              key.Open(HKEY_CURRENT_USER, key_name.c_str(), KEY_SET_VALUE));
+    ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kDidRun));
+  }
+
+  void CheckActiveBit(const std::wstring& key_name,
+                      bool expected,
+                      ReadActiveBitCallback callback) const {
+    base::win::RegKey key;
+    ASSERT_EQ(ERROR_SUCCESS,
+              key.Open(HKEY_CURRENT_USER, key_name.c_str(), KEY_QUERY_VALUE));
+
+    const ReadActiveBitRetval retval = callback.Run(key);
+    ASSERT_EQ(DWORD{ERROR_SUCCESS}, retval.read_result);
+    ASSERT_EQ(expected, retval.active_bit_set);
+  }
+
+  void CreateUserActiveBit(bool value, WriteActiveBitCallback callback) const {
+    CreateActiveBit(kClientStateKeyPath, value, callback);
+  }
+
+  void DeleteUserActiveBit() const { DeleteActiveBit(kClientStateKeyPath); }
+
+  void CheckUserActiveBit(bool expected, ReadActiveBitCallback callback) const {
+    CheckActiveBit(kClientStateKeyPath, expected, callback);
+  }
+
+  void CreateLowIntegrityUserActiveBit(bool value,
+                                       WriteActiveBitCallback callback) const {
+    CreateActiveBit(low_integrity_key_path_, value, callback);
+  }
+
+  void DeleteLowIntegrityUserActiveBit() const {
+    DeleteActiveBit(low_integrity_key_path_);
+  }
+
+  void CheckLowIntegrityUserActiveBit(bool expected,
+                                      ReadActiveBitCallback callback) const {
+    CheckActiveBit(low_integrity_key_path_, expected, callback);
+  }
+
+ private:
+  std::wstring low_integrity_key_path_;
+};
+
+TEST_P(ActivityWinTest, GetActiveBit) {
+  ASSERT_EQ(SetUserValue() || SetLowUserValue(),
+            GetActiveBit(GetScope(), kAppId));
+
+  CheckUserActiveBit(SetUserValue(), ReadActiveBitFn());
+  CheckLowIntegrityUserActiveBit(SetLowUserValue(), ReadActiveBitFn());
+}
+
+TEST_P(ActivityWinTest, ClearActiveBit) {
+  ClearActiveBit(GetScope(), kAppId);
+
+  CheckUserActiveBit(false, base::BindRepeating(&ReadActiveBitAsString));
+  CheckLowIntegrityUserActiveBit(false,
+                                 base::BindRepeating(&ReadActiveBitAsString));
+  ASSERT_FALSE(GetActiveBit(GetScope(), kAppId));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    UpdaterScopeSetUserValueSetLowUserValueSetAsDword,
+    ActivityWinTest,
+    ::testing::Combine(
+        ::testing::Values(UpdaterScope::kUser, UpdaterScope::kSystem),
+        ::testing::Bool(),
+        ::testing::Bool(),
+        ::testing::Values(
+            ReadWriteCallbacks(base::BindRepeating(&ReadActiveBitAsString),
+                               base::BindRepeating(&WriteActiveBitAsString)),
+            ReadWriteCallbacks(base::BindRepeating(&ReadActiveBitAsDword),
+                               base::BindRepeating(&WriteActiveBitAsDword)))));
+
+}  // namespace updater
diff --git a/chrome/updater/win/win_constants.h b/chrome/updater/win/win_constants.h
index fbfad84..624cb515 100644
--- a/chrome/updater/win/win_constants.h
+++ b/chrome/updater/win/win_constants.h
@@ -31,6 +31,10 @@
   L"Software\\Policies\\" COMPANY_SHORTNAME_STRING L"\\"
 #define UPDATER_POLICIES_KEY COMPANY_POLICIES_KEY UPDATER_KEY L"\\"
 
+#define USER_REG_VISTA_LOW_INTEGRITY_HKCU     \
+  L"Software\\Microsoft\\Internet Explorer\\" \
+  L"InternetRegistry\\REGISTRY\\USER"
+
 extern const wchar_t kRegValuePV[];
 extern const wchar_t kRegValueName[];
 
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 744b0366..eb2472d 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -770,7 +770,6 @@
 #else
 
 #if defined(USE_AURA)
-  rounded_window_corners_manager_.reset();
   // Reset display change observer here to ensure it is deleted before
   // display_configurator since display_configurator is deleted when
   // `cast_browser_process_` is reset below.
@@ -781,6 +780,10 @@
   cast_browser_process_->cast_browser_metrics()->Finalize();
   cast_browser_process_.reset();
 
+#if defined(USE_AURA)
+  rounded_window_corners_manager_.reset();
+#endif
+
   window_manager_.reset();
 #if defined(USE_AURA)
   display::Screen::SetScreenInstance(nullptr);
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 77d8bdf2..5f9243f 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-14008.0.0
\ No newline at end of file
+14009.0.0
\ No newline at end of file
diff --git a/chromeos/dbus/BUILD.gn b/chromeos/dbus/BUILD.gn
index 8fc6eff..585ea332 100644
--- a/chromeos/dbus/BUILD.gn
+++ b/chromeos/dbus/BUILD.gn
@@ -34,6 +34,7 @@
     ":smbprovider_proto",
     ":update_engine_proto",
     ":vm_applications_apps_proto",
+    ":vm_disk_management_proto",
     ":vm_permission_service_proto",
     ":vm_plugin_dispatcher_proto",
     ":vm_sk_forwarding_proto",
@@ -315,6 +316,12 @@
   proto_out_dir = "chromeos/dbus/vm_applications"
 }
 
+proto_library("vm_disk_management_proto") {
+  sources = [ "//third_party/cros_system_api/dbus/vm_disk_management/disk_management.proto" ]
+
+  proto_out_dir = "chromeos/dbus/vm_disk_management"
+}
+
 proto_library("vm_sk_forwarding_proto") {
   sources = [
     "//third_party/cros_system_api/dbus/vm_sk_forwarding/sk_forwarding.proto",
diff --git a/chromeos/dbus/shill/fake_shill_service_client.cc b/chromeos/dbus/shill/fake_shill_service_client.cc
index e497e1c4..b1308c6 100644
--- a/chromeos/dbus/shill/fake_shill_service_client.cc
+++ b/chromeos/dbus/shill/fake_shill_service_client.cc
@@ -361,6 +361,28 @@
   std::move(callback).Run(passphrase ? *passphrase : std::string());
 }
 
+void FakeShillServiceClient::RequestTrafficCounters(
+    const dbus::ObjectPath& service_path,
+    ListValueCallback callback,
+    ErrorCallback error_callback) {
+  base::Value traffic_counters(base::Value::Type::LIST);
+
+  base::Value chrome_dict(base::Value::Type::DICTIONARY);
+  chrome_dict.SetKey("source", base::Value(shill::kTrafficCounterSourceChrome));
+  chrome_dict.SetKey("rx_bytes", base::Value(12));
+  chrome_dict.SetKey("tx_bytes", base::Value(32));
+  traffic_counters.Append(std::move(chrome_dict));
+
+  base::Value user_dict(base::Value::Type::DICTIONARY);
+  user_dict.SetKey("source", base::Value(shill::kTrafficCounterSourceUser));
+  user_dict.SetKey("rx_bytes", base::Value(90));
+  user_dict.SetKey("tx_bytes", base::Value(87));
+  traffic_counters.Append(std::move(user_dict));
+
+  std::move(callback).Run(
+      base::Value::AsListValue(std::move(traffic_counters)));
+}
+
 ShillServiceClient::TestInterface* FakeShillServiceClient::GetTestInterface() {
   return this;
 }
diff --git a/chromeos/dbus/shill/fake_shill_service_client.h b/chromeos/dbus/shill/fake_shill_service_client.h
index afc3729..b6fa731 100644
--- a/chromeos/dbus/shill/fake_shill_service_client.h
+++ b/chromeos/dbus/shill/fake_shill_service_client.h
@@ -73,6 +73,9 @@
   void GetWiFiPassphrase(const dbus::ObjectPath& service_path,
                          StringCallback callback,
                          ErrorCallback error_callback) override;
+  void RequestTrafficCounters(const dbus::ObjectPath& service_path,
+                              ListValueCallback callback,
+                              ErrorCallback error_callback) override;
   ShillServiceClient::TestInterface* GetTestInterface() override;
 
   // ShillServiceClient::TestInterface overrides.
diff --git a/chromeos/dbus/shill/shill_service_client.cc b/chromeos/dbus/shill/shill_service_client.cc
index d1e70f7..b343ccc0 100644
--- a/chromeos/dbus/shill/shill_service_client.cc
+++ b/chromeos/dbus/shill/shill_service_client.cc
@@ -213,6 +213,17 @@
                                             std::move(error_callback));
   }
 
+  void RequestTrafficCounters(const dbus::ObjectPath& service_path,
+                              ListValueCallback callback,
+                              ErrorCallback error_callback) override {
+    dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
+                                 shill::kRequestTrafficCountersFunction);
+
+    GetHelper(service_path)
+        ->CallListValueMethodWithErrorCallback(
+            &method_call, std::move(callback), std::move(error_callback));
+  }
+
   ShillServiceClient::TestInterface* GetTestInterface() override {
     return nullptr;
   }
diff --git a/chromeos/dbus/shill/shill_service_client.h b/chromeos/dbus/shill/shill_service_client.h
index b5b1189..77caac1 100644
--- a/chromeos/dbus/shill/shill_service_client.h
+++ b/chromeos/dbus/shill/shill_service_client.h
@@ -213,6 +213,12 @@
                                  StringCallback callback,
                                  ErrorCallback error_callback) = 0;
 
+  // Calls the RequestTrafficCounters method.
+  // |callback| is called after the method call succeeds.
+  virtual void RequestTrafficCounters(const dbus::ObjectPath& service_path,
+                                      ListValueCallback callback,
+                                      ErrorCallback error_callback) = 0;
+
   // Returns an interface for testing (stub only), or returns null.
   virtual TestInterface* GetTestInterface() = 0;
 
diff --git a/chromeos/dbus/shill/shill_service_client_unittest.cc b/chromeos/dbus/shill/shill_service_client_unittest.cc
index 4e7196b..eb4211b 100644
--- a/chromeos/dbus/shill/shill_service_client_unittest.cc
+++ b/chromeos/dbus/shill/shill_service_client_unittest.cc
@@ -291,4 +291,43 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(ShillServiceClientTest, RequestTrafficCounters) {
+  // Set up value of response.
+  base::Value traffic_counters(base::Value::Type::LIST);
+
+  base::Value chrome_dict(base::Value::Type::DICTIONARY);
+  chrome_dict.SetKey("source", base::Value(shill::kTrafficCounterSourceChrome));
+  chrome_dict.SetKey("rx_bytes", base::Value(12));
+  chrome_dict.SetKey("tx_bytes", base::Value(34));
+  traffic_counters.Append(std::move(chrome_dict));
+
+  base::Value user_dict(base::Value::Type::DICTIONARY);
+  user_dict.SetKey("source", base::Value(shill::kTrafficCounterSourceUser));
+  user_dict.SetKey("rx_bytes", base::Value(90));
+  user_dict.SetKey("tx_bytes", base::Value(87));
+  traffic_counters.Append(std::move(user_dict));
+
+  // Create response.
+  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
+  dbus::MessageWriter writer(response.get());
+  AppendValueDataAsVariant(&writer, traffic_counters);
+
+  // Set expectations.
+  PrepareForMethodCall(shill::kRequestTrafficCountersFunction,
+                       base::BindRepeating(&ExpectNoArgument), response.get());
+
+  // Call method.
+  base::MockCallback<ShillServiceClient::ListValueCallback>
+      mock_list_value_callback;
+  base::MockCallback<ShillServiceClient::ErrorCallback> mock_error_callback;
+  client_->RequestTrafficCounters(dbus::ObjectPath(kExampleServicePath),
+                                  mock_list_value_callback.Get(),
+                                  mock_error_callback.Get());
+  EXPECT_CALL(mock_list_value_callback, Run(_)).Times(1);
+  EXPECT_CALL(mock_error_callback, Run(_, _)).Times(0);
+
+  // Run the message loop.
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace chromeos
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index b7365c9d..7024e9b 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-93-4515.9-1622454091-benchmark-93.0.4529.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-93-4515.9-1622454091-benchmark-93.0.4530.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 0b3ce2ce..38d663c 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-93-4515.9-1622460530-benchmark-93.0.4529.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-93-4515.9-1622460530-benchmark-93.0.4530.0-r1-redacted.afdo.xz
diff --git a/chromeos/services/libassistant/device_settings_controller.cc b/chromeos/services/libassistant/device_settings_controller.cc
index d72bfb1..a6757c7 100644
--- a/chromeos/services/libassistant/device_settings_controller.cc
+++ b/chromeos/services/libassistant/device_settings_controller.cc
@@ -13,6 +13,7 @@
 #include "chromeos/assistant/internal/internal_util.h"
 #include "chromeos/assistant/internal/proto/google3/assistant/api/client_op/device_args.pb.h"
 #include "chromeos/services/libassistant/public/mojom/device_settings_delegate.mojom.h"
+#include "chromeos/services/libassistant/util.h"
 #include "libassistant/shared/internal_api/assistant_manager_internal.h"
 
 namespace client_op = ::assistant::api::client_op;
@@ -317,7 +318,8 @@
   voiceless_options.is_user_initiated = true;
 
   assistant_manager_internal_->SendVoicelessInteraction(
-      CreateGetDeviceSettingInteraction(interaction_id, result),
+      chromeos::libassistant::CreateGetDeviceSettingInteraction(interaction_id,
+                                                                result),
       /*description=*/"get_settings_result", voiceless_options, [](auto) {});
 }
 
diff --git a/chromeos/services/libassistant/display_controller.cc b/chromeos/services/libassistant/display_controller.cc
index 453e791..6f8e9599 100644
--- a/chromeos/services/libassistant/display_controller.cc
+++ b/chromeos/services/libassistant/display_controller.cc
@@ -11,6 +11,7 @@
 #include "chromeos/services/assistant/public/cpp/features.h"
 #include "chromeos/services/libassistant/display_connection_impl.h"
 #include "chromeos/services/libassistant/public/mojom/speech_recognition_observer.mojom.h"
+#include "chromeos/services/libassistant/util.h"
 #include "libassistant/shared/internal_api/assistant_manager_internal.h"
 
 namespace chromeos {
@@ -121,8 +122,9 @@
     result_apps_info.emplace_back(result_app_info);
   }
 
-  std::string interaction_proto = CreateVerifyProviderResponseInteraction(
-      interaction.interaction_id, result_apps_info);
+  std::string interaction_proto =
+      chromeos::libassistant::CreateVerifyProviderResponseInteraction(
+          interaction.interaction_id, result_apps_info);
 
   assistant_client::VoicelessOptions options;
   options.obfuscated_gaia_id = interaction.user_id;
diff --git a/chromeos/services/libassistant/util.cc b/chromeos/services/libassistant/util.cc
index a92c3ee..766cac1 100644
--- a/chromeos/services/libassistant/util.cc
+++ b/chromeos/services/libassistant/util.cc
@@ -14,10 +14,12 @@
 #include "build/util/webkit_version.h"
 #include "chromeos/assistant/buildflags.h"
 #include "chromeos/assistant/internal/internal_constants.h"
+#include "chromeos/assistant/internal/internal_util.h"
 #include "chromeos/assistant/internal/util_headers.h"
 #include "chromeos/dbus/util/version_loader.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
 #include "chromeos/services/libassistant/constants.h"
+#include "chromeos/services/libassistant/public/cpp/android_app_info.h"
 
 using chromeos::assistant::shared::ClientInteraction;
 using chromeos::assistant::shared::ClientOpResult;
@@ -291,22 +293,22 @@
 
 std::string CreateVerifyProviderResponseInteraction(
     const int interaction_id,
-    const std::vector<libassistant::mojom::AndroidAppInfoPtr>& apps_info) {
+    const std::vector<chromeos::assistant::AndroidAppInfo>& apps_info) {
   // Construct verify provider result proto.
   VerifyProviderClientOpResult result_proto;
   bool any_provider_available = false;
   for (const auto& android_app_info : apps_info) {
     auto* provider_status = result_proto.add_provider_status();
     provider_status->set_status(
-        GetProviderVerificationStatus(android_app_info->status));
+        GetProviderVerificationStatus(android_app_info.status));
     auto* app_info =
         provider_status->mutable_provider_info()->mutable_android_app_info();
-    app_info->set_package_name(android_app_info->package_name);
-    app_info->set_app_version(android_app_info->version);
-    app_info->set_localized_app_name(android_app_info->localized_app_name);
-    app_info->set_android_intent(android_app_info->intent);
+    app_info->set_package_name(android_app_info.package_name);
+    app_info->set_app_version(android_app_info.version);
+    app_info->set_localized_app_name(android_app_info.localized_app_name);
+    app_info->set_android_intent(android_app_info.intent);
 
-    if (android_app_info->status == AppStatus::kAvailable)
+    if (android_app_info.status == AppStatus::kAvailable)
       any_provider_available = true;
   }
 
@@ -320,11 +322,11 @@
 
 std::string CreateGetDeviceSettingInteraction(
     int interaction_id,
-    const std::vector<libassistant::mojom::DeviceSettingPtr>& device_settings) {
+    const std::vector<chromeos::assistant::DeviceSetting>& device_settings) {
   GetDeviceSettingsResult result_proto;
   for (const auto& setting : device_settings) {
-    (*result_proto.mutable_settings_info())[setting->setting_id] =
-        ToSettingInfo(setting->is_supported);
+    (*result_proto.mutable_settings_info())[setting.setting_id] =
+        ToSettingInfo(setting.is_supported);
   }
 
   // Construct response interaction.
diff --git a/chromeos/services/libassistant/util.h b/chromeos/services/libassistant/util.h
index fa809f2..5b78f46 100644
--- a/chromeos/services/libassistant/util.h
+++ b/chromeos/services/libassistant/util.h
@@ -7,8 +7,6 @@
 
 #include <string>
 
-#include "chromeos/services/libassistant/public/mojom/android_app_info.mojom.h"
-#include "chromeos/services/libassistant/public/mojom/conversation_controller.mojom.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
@@ -16,6 +14,13 @@
 }  // namespace base
 
 namespace chromeos {
+namespace assistant {
+struct AndroidAppInfo;
+struct DeviceSetting;
+}  // namespace assistant
+}  // namespace chromeos
+
+namespace chromeos {
 namespace libassistant {
 
 // Creates the configuration for libassistant.
@@ -28,11 +33,11 @@
 
 std::string CreateVerifyProviderResponseInteraction(
     const int interaction_id,
-    const std::vector<libassistant::mojom::AndroidAppInfoPtr>& apps_info);
+    const std::vector<chromeos::assistant::AndroidAppInfo>& apps_info);
 
 std::string CreateGetDeviceSettingInteraction(
     int interaction_id,
-    const std::vector<libassistant::mojom::DeviceSettingPtr>& device_settings);
+    const std::vector<chromeos::assistant::DeviceSetting>& device_settings);
 
 }  // namespace libassistant
 }  // namespace chromeos
diff --git a/chromeos/services/nearby/public/cpp/nearby_process_manager.cc b/chromeos/services/nearby/public/cpp/nearby_process_manager.cc
index 6a9d631..79f40fc 100644
--- a/chromeos/services/nearby/public/cpp/nearby_process_manager.cc
+++ b/chromeos/services/nearby/public/cpp/nearby_process_manager.cc
@@ -16,8 +16,11 @@
     case NearbyProcessManager::NearbyProcessShutdownReason::kCrash:
       return os << "Crash";
     case NearbyProcessManager::NearbyProcessShutdownReason::
-        kMojoPipeDisconnection:
-      return os << "Mojo Pipe Disconnection";
+        kConnectionsMojoPipeDisconnection:
+      return os << "Connections Mojo Pipe Disconnection";
+    case NearbyProcessManager::NearbyProcessShutdownReason::
+        kDecoderMojoPipeDisconnection:
+      return os << "Decoder Mojo Pipe Disconnection";
   }
 }
 
diff --git a/chromeos/services/nearby/public/cpp/nearby_process_manager.h b/chromeos/services/nearby/public/cpp/nearby_process_manager.h
index 684c09a..76c9b41 100644
--- a/chromeos/services/nearby/public/cpp/nearby_process_manager.h
+++ b/chromeos/services/nearby/public/cpp/nearby_process_manager.h
@@ -34,8 +34,9 @@
   enum class NearbyProcessShutdownReason {
     kNormal = 0,
     kCrash = 1,
-    kMojoPipeDisconnection = 2,
-    kMaxValue = kMojoPipeDisconnection
+    kDecoderMojoPipeDisconnection = 3,
+    kConnectionsMojoPipeDisconnection = 4,
+    kMaxValue = kConnectionsMojoPipeDisconnection
   };
 
   using NearbyProcessStoppedCallback =
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillSuggestion.java b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillSuggestion.java
index 921032a..ed80ce14 100644
--- a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillSuggestion.java
+++ b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillSuggestion.java
@@ -115,4 +115,76 @@
     public String getFeatureForIPH() {
         return mFeatureForIPH;
     }
+
+    /**
+     * Builder for the {@link AutofillSuggestion}.
+     */
+    public static final class Builder {
+        private int mIconId;
+        private boolean mIsBoldLabel;
+        private boolean mIsIconAtStart;
+        private boolean mIsDeletable;
+        private boolean mIsMultiLineLabel;
+        private String mFeatureForIPH;
+        private String mItemTag;
+        private String mLabel;
+        private String mSubLabel;
+        private int mSuggestionId;
+
+        public Builder setIconId(int iconId) {
+            this.mIconId = iconId;
+            return this;
+        }
+
+        public Builder setIsBoldLabel(boolean isBoldLabel) {
+            this.mIsBoldLabel = isBoldLabel;
+            return this;
+        }
+
+        public Builder setIsIconAtStart(boolean isIconAtStart) {
+            this.mIsIconAtStart = isIconAtStart;
+            return this;
+        }
+
+        public Builder setIsDeletable(boolean isDeletable) {
+            this.mIsDeletable = isDeletable;
+            return this;
+        }
+
+        public Builder setIsMultiLineLabel(boolean isMultiLineLabel) {
+            this.mIsMultiLineLabel = isMultiLineLabel;
+            return this;
+        }
+
+        public Builder setFeatureForIPH(String featureForIPH) {
+            this.mFeatureForIPH = featureForIPH;
+            return this;
+        }
+
+        public Builder setItemTag(String itemTag) {
+            this.mItemTag = itemTag;
+            return this;
+        }
+
+        public Builder setLabel(String label) {
+            this.mLabel = label;
+            return this;
+        }
+
+        public Builder setSubLabel(String subLabel) {
+            this.mSubLabel = subLabel;
+            return this;
+        }
+
+        public Builder setSuggestionId(int suggestionId) {
+            this.mSuggestionId = suggestionId;
+            return this;
+        }
+
+        public AutofillSuggestion build() {
+            assert !mLabel.isEmpty() : "AutofillSuggestion requires the label to be set.";
+            return new AutofillSuggestion(mLabel, mSubLabel, mItemTag, mIconId, mIsIconAtStart,
+                    mSuggestionId, mIsDeletable, mIsMultiLineLabel, mIsBoldLabel, mFeatureForIPH);
+        }
+    }
 }
diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h
index 39f9944..7276726 100644
--- a/components/autofill/content/renderer/autofill_agent.h
+++ b/components/autofill/content/renderer/autofill_agent.h
@@ -307,9 +307,6 @@
   // The elements that currently are being previewed.
   std::vector<blink::WebFormControlElement> previewed_elements_;
 
-  // The form element currently requesting an interactive autocomplete.
-  blink::WebFormElement in_flight_request_form_;
-
   // Last form which was interacted with by the user.
   blink::WebFormElement last_interacted_form_;
 
diff --git a/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc
index 915fa45..c43af8e 100644
--- a/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc
+++ b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.cc
@@ -34,9 +34,12 @@
   // If the user has navigated away without saving the modal, then the
   // |address_profile_save_prompt_callback_| is run here.
   if (!address_profile_save_prompt_callback_.is_null()) {
-    // TODO(crbug.com/1167062): Record last action on the infobar and send
-    // callback accordingly.
-    InfoBarDismissed();
+    DCHECK(
+        user_decision_ !=
+            AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted &&
+        user_decision_ !=
+            AutofillClient::SaveAddressProfileOfferUserDecision::kEditAccepted);
+    RunSaveAddressProfilePromptCallback();
   }
 }
 
@@ -117,10 +120,25 @@
                                    locale_);
 }
 
-bool AutofillSaveUpdateAddressProfileDelegateIOS::EditAccepted() {
-  RunSaveAddressProfilePromptCallback(
-      AutofillClient::SaveAddressProfileOfferUserDecision::kEditAccepted);
-  return true;
+void AutofillSaveUpdateAddressProfileDelegateIOS::EditAccepted() {
+  user_decision_ =
+      AutofillClient::SaveAddressProfileOfferUserDecision::kEditAccepted;
+  RunSaveAddressProfilePromptCallback();
+}
+
+void AutofillSaveUpdateAddressProfileDelegateIOS::EditDeclined() {
+  SetUserDecision(
+      AutofillClient::SaveAddressProfileOfferUserDecision::kEditDeclined);
+}
+
+void AutofillSaveUpdateAddressProfileDelegateIOS::MessageTimeout() {
+  SetUserDecision(
+      AutofillClient::SaveAddressProfileOfferUserDecision::kMessageTimeout);
+}
+
+void AutofillSaveUpdateAddressProfileDelegateIOS::MessageDeclined() {
+  SetUserDecision(
+      AutofillClient::SaveAddressProfileOfferUserDecision::kMessageDeclined);
 }
 
 void AutofillSaveUpdateAddressProfileDelegateIOS::SetProfileInfo(
@@ -141,18 +159,14 @@
 }
 
 bool AutofillSaveUpdateAddressProfileDelegateIOS::Accept() {
-  RunSaveAddressProfilePromptCallback(
-      AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted);
+  user_decision_ =
+      AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted;
+  RunSaveAddressProfilePromptCallback();
   return true;
 }
 
-void AutofillSaveUpdateAddressProfileDelegateIOS::InfoBarDismissed() {
-  RunSaveAddressProfilePromptCallback(
-      AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined);
-}
-
 bool AutofillSaveUpdateAddressProfileDelegateIOS::Cancel() {
-  RunSaveAddressProfilePromptCallback(
+  SetUserDecision(
       AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined);
   return true;
 }
@@ -163,7 +177,7 @@
 }
 
 int AutofillSaveUpdateAddressProfileDelegateIOS::GetIconId() const {
-  // TODO(crbug.com/1167062): Replace with proper icon.
+  NOTREACHED();
   return IDR_INFOBAR_AUTOFILL_CC;
 }
 
@@ -188,21 +202,32 @@
          ConfirmInfoBarDelegate::ShouldExpire(details);
 }
 
-int AutofillSaveUpdateAddressProfileDelegateIOS::GetButtons() const {
-  return BUTTON_OK | BUTTON_CANCEL;
-}
-
-std::u16string AutofillSaveUpdateAddressProfileDelegateIOS::GetButtonLabel(
-    InfoBarButton button) const {
-
-  NOTREACHED() << "Unsupported button label requested.";
-  return std::u16string();
-}
-
 void AutofillSaveUpdateAddressProfileDelegateIOS::
-    RunSaveAddressProfilePromptCallback(
-        AutofillClient::SaveAddressProfileOfferUserDecision decision) {
-  std::move(address_profile_save_prompt_callback_).Run(decision, profile_);
+    RunSaveAddressProfilePromptCallback() {
+  std::move(address_profile_save_prompt_callback_)
+      .Run(user_decision_, profile_);
+}
+
+void AutofillSaveUpdateAddressProfileDelegateIOS::SetUserDecision(
+    AutofillClient::SaveAddressProfileOfferUserDecision user_decision) {
+  if (user_decision == AutofillClient::SaveAddressProfileOfferUserDecision::
+                           kMessageTimeout &&
+      user_decision_ == AutofillClient::SaveAddressProfileOfferUserDecision::
+                            kMessageDeclined) {
+    // |SaveAddressProfileInfobarBannerInteractionHandler::InfobarVisibilityChanged|
+    // would be called even when the banner is explicitly dismissed by the
+    // user. In that case, do not change the |user_decision_|.
+    return;
+  }
+  if (user_decision_ ==
+          AutofillClient::SaveAddressProfileOfferUserDecision::kEditAccepted ||
+      user_decision_ ==
+          AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted) {
+    // The infobar has already been saved. So, cancel should not change the
+    // |user_decision_| now.
+    return;
+  }
+  user_decision_ = user_decision;
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h
index 0f83f94..710b07a 100644
--- a/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h
+++ b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h
@@ -61,9 +61,10 @@
   // |original_profile_|.
   std::vector<ProfileValueDifference> GetProfileDiff() const;
 
-  // Calls |RunSaveAddressProfilePromptCallback| with the kEditAccepted|
-  // decision.
-  virtual bool EditAccepted();
+  virtual void EditAccepted();
+  void EditDeclined();
+  void MessageTimeout();
+  void MessageDeclined();
 
   // Updates |profile_| |type| value to |value|.
   void SetProfileInfo(const ServerFieldType& type, const std::u16string& value);
@@ -76,17 +77,25 @@
   std::u16string GetMessageText() const override;
   infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
   bool ShouldExpire(const NavigationDetails& details) const override;
-  void InfoBarDismissed() override;
-  int GetButtons() const override;
-  std::u16string GetButtonLabel(InfoBarButton button) const override;
   bool Accept() override;
   bool Cancel() override;
   bool EqualsDelegate(infobars::InfoBarDelegate* delegate) const override;
 
+#if defined(UNIT_TEST)
+  // Getter for |user_decision_|. Used for the testing purposes.
+  AutofillClient::SaveAddressProfileOfferUserDecision user_decision() const {
+    return user_decision_;
+  }
+#endif
+
  private:
-  // Fires the |address_profile_save_prompt_callback_| callback.
-  void RunSaveAddressProfilePromptCallback(
-      AutofillClient::SaveAddressProfileOfferUserDecision decision);
+  // Fires the |address_profile_save_prompt_callback_| callback with
+  // |user_decision_|.
+  void RunSaveAddressProfilePromptCallback();
+
+  // Sets |user_decision_| based on |user_decision|.
+  void SetUserDecision(
+      AutofillClient::SaveAddressProfileOfferUserDecision user_decision);
 
   // The application locale.
   std::string locale_;
@@ -101,6 +110,11 @@
   // The callback to run once the user makes a decision.
   AutofillClient::AddressProfileSavePromptCallback
       address_profile_save_prompt_callback_;
+
+  // Records the last user decision based on the interactions with the
+  // banner/modal to be sent with |address_profile_save_prompt_callback_|.
+  AutofillClient::SaveAddressProfileOfferUserDecision user_decision_ =
+      AutofillClient::SaveAddressProfileOfferUserDecision::kIgnored;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios_unittest.cc b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios_unittest.cc
index 780549c..3921741 100644
--- a/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios_unittest.cc
+++ b/components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios_unittest.cc
@@ -70,4 +70,49 @@
             std::u16string(u"John Doe, 666 Erebus St."));
 }
 
+// Tests that the callback is run with kDeclined on destruction.
+TEST(AutofillSaveUpdateAddressProfileDelegateIOSTest,
+     TestCallbackOnDestruction) {
+  AutofillProfile profile = test::GetFullProfile();
+  base::MockCallback<AutofillClient::AddressProfileSavePromptCallback> callback;
+  auto delegate = std::make_unique<AutofillSaveUpdateAddressProfileDelegateIOS>(
+      profile, /*original_profile=*/nullptr, /*locale=*/"en-US",
+      callback.Get());
+
+  delegate->Cancel();
+  EXPECT_CALL(
+      callback,
+      Run(AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined,
+          testing::_));
+  // The callback should run in the destructor.
+  delegate.reset();
+}
+
+// Tests that the callback is run with kAccepted on Accept.
+TEST(AutofillSaveUpdateAddressProfileDelegateIOSTest, TestCallbackOnSave) {
+  AutofillProfile profile = test::GetFullProfile();
+  base::MockCallback<AutofillClient::AddressProfileSavePromptCallback> callback;
+  EXPECT_CALL(
+      callback,
+      Run(AutofillClient::SaveAddressProfileOfferUserDecision::kAccepted,
+          testing::_));
+  AutofillSaveUpdateAddressProfileDelegateIOS(
+      profile, /*original_profile=*/nullptr, /*locale=*/"en-US", callback.Get())
+      .Accept();
+}
+
+// Tests that the callback is run with kEditAccepted on EditAccepted.
+TEST(AutofillSaveUpdateAddressProfileDelegateIOSTest,
+     TestCallbackOnEditAccepted) {
+  AutofillProfile profile = test::GetFullProfile();
+  base::MockCallback<AutofillClient::AddressProfileSavePromptCallback> callback;
+  EXPECT_CALL(
+      callback,
+      Run(AutofillClient::SaveAddressProfileOfferUserDecision::kEditAccepted,
+          testing::_));
+  AutofillSaveUpdateAddressProfileDelegateIOS(
+      profile, /*original_profile=*/nullptr, /*locale=*/"en-US", callback.Get())
+      .EditAccepted();
+}
+
 }  // namespace autofill
diff --git a/components/full_restore/arc_read_handler.cc b/components/full_restore/arc_read_handler.cc
index 698f507..847b72c 100644
--- a/components/full_restore/arc_read_handler.cc
+++ b/components/full_restore/arc_read_handler.cc
@@ -23,7 +23,12 @@
 }
 
 void ArcReadHandler::AddArcWindowCandidate(aura::Window* window) {
-  arc_window_candidates_.insert(window);
+  // If the ARC task is not created yet, add |window| to
+  // |arc_window_candidates_| to wait for the task to be created.
+  if (!base::Contains(task_id_to_window_id_,
+                      window->GetProperty(::full_restore::kWindowIdKey))) {
+    arc_window_candidates_.insert(window);
+  }
 }
 
 void ArcReadHandler::OnWindowDestroyed(aura::Window* window) {
@@ -116,7 +121,7 @@
   return window_info;
 }
 
-int32_t ArcReadHandler::GetArcRestoreWindowId(int32_t task_id) {
+int32_t ArcReadHandler::GetArcRestoreWindowIdForTaskId(int32_t task_id) {
   auto it = task_id_to_window_id_.find(task_id);
   if (it != task_id_to_window_id_.end())
     return it->second;
@@ -132,6 +137,12 @@
   return kParentToHiddenContainer;
 }
 
+int32_t ArcReadHandler::GetArcRestoreWindowIdForSessionId(int32_t session_id) {
+  // If `session_id` doesn't exist, that means there is no ARC app restored.
+  auto it = session_id_to_window_id_.find(session_id);
+  return it == session_id_to_window_id_.end() ? 0 : it->second;
+}
+
 int32_t ArcReadHandler::GetArcSessionId() {
   if (session_id_ < kArcSessionIdOffsetForRestoredLaunching) {
     LOG(WARNING) << "ARC session id is overflow: " << session_id_;
diff --git a/components/full_restore/arc_read_handler.h b/components/full_restore/arc_read_handler.h
index 2761ac0..fed6664 100644
--- a/components/full_restore/arc_read_handler.h
+++ b/components/full_restore/arc_read_handler.h
@@ -56,7 +56,10 @@
   std::unique_ptr<WindowInfo> GetWindowInfo(int32_t restore_window_id);
 
   // Returns the restore window id for the ARC app's |task_id|.
-  int32_t GetArcRestoreWindowId(int32_t task_id);
+  int32_t GetArcRestoreWindowIdForTaskId(int32_t task_id);
+
+  // Returns the restore window id for the ARC app's `session_id`.
+  int32_t GetArcRestoreWindowIdForSessionId(int32_t session_id);
 
   // Generates the ARC session id (1,000,000,001 - INT_MAX) for restored ARC
   // apps.
diff --git a/components/full_restore/full_restore_read_and_save_unittest.cc b/components/full_restore/full_restore_read_and_save_unittest.cc
index 63b01589..9eab7de1 100644
--- a/components/full_restore/full_restore_read_and_save_unittest.cc
+++ b/components/full_restore/full_restore_read_and_save_unittest.cc
@@ -491,10 +491,15 @@
   read_handler->SetArcSessionIdForWindowId(kArcSessionId2, kArcTaskId1);
   EXPECT_EQ(1u, read_test_api.GetArcSessionIdMap().size());
 
+  // Before OnTaskCreated is called, return |kArcTaskId1| for |kArcSessionId2|
+  // to simulate the ghost window property setting.
+  EXPECT_EQ(kArcTaskId1,
+            full_restore::GetArcRestoreWindowIdForSessionId(kArcSessionId2));
+
   // Before OnTaskCreated is called, return -1 to add the ARC app window to the
   // hidden container.
   EXPECT_EQ(kParentToHiddenContainer,
-            full_restore::GetArcRestoreWindowId(kArcTaskId2));
+            full_restore::GetArcRestoreWindowIdForTaskId(kArcTaskId2));
 
   // Call OnTaskCreated to simulate that the ARC app with |kAppId| has been
   // launched, and the new task id |kArcTaskId2| has been created with
@@ -506,7 +511,8 @@
   // map can be cleared. And verify that we can get the restore window id
   // |kArcTaskId1| with the new |kArcTaskId2|.
   EXPECT_TRUE(read_test_api.GetArcSessionIdMap().empty());
-  EXPECT_EQ(kArcTaskId1, full_restore::GetArcRestoreWindowId(kArcTaskId2));
+  EXPECT_EQ(kArcTaskId1,
+            full_restore::GetArcRestoreWindowIdForTaskId(kArcTaskId2));
 
   // Verify |window_info| for |kArcTaskId1|.
   auto window_info = GetArcWindowInfo(kArcTaskId1);
@@ -517,7 +523,7 @@
   // for |kArcTaskId2|, and verify the task id map is now empty and a invalid
   // value is returned when trying to get the restore window id.
   read_handler->OnTaskDestroyed(kArcTaskId2);
-  EXPECT_EQ(0, full_restore::GetArcRestoreWindowId(kArcTaskId2));
+  EXPECT_EQ(0, full_restore::GetArcRestoreWindowIdForTaskId(kArcTaskId2));
   EXPECT_TRUE(read_test_api.GetArcTaskIdMap().empty());
   EXPECT_TRUE(read_test_api.GetArcWindowIdMap().empty());
 }
diff --git a/components/full_restore/full_restore_read_handler.cc b/components/full_restore/full_restore_read_handler.cc
index 67dcfb2..2dd67e4 100644
--- a/components/full_restore/full_restore_read_handler.cc
+++ b/components/full_restore/full_restore_read_handler.cc
@@ -47,13 +47,7 @@
     if (window_id == kParentToHiddenContainer ||
         arc_read_handler_->HasRestoreData(window_id)) {
       observed_windows_.AddObservation(window);
-
-      // If |window| is added to a hidden container, that means the ARC task is
-      // not created yet, so add |window| to |arc_window_candidates_| to wait
-      // the task to be created.
-      if (window_id == kParentToHiddenContainer)
-        arc_read_handler_->AddArcWindowCandidate(window);
-
+      arc_read_handler_->AddArcWindowCandidate(window);
       FullRestoreInfo::GetInstance()->OnWindowInitialized(window);
     }
     return;
@@ -182,11 +176,20 @@
   return it->second->FetchRestoreWindowId(app_id);
 }
 
-int32_t FullRestoreReadHandler::GetArcRestoreWindowId(int32_t task_id) {
+int32_t FullRestoreReadHandler::GetArcRestoreWindowIdForTaskId(
+    int32_t task_id) {
   if (!arc_read_handler_)
     return 0;
 
-  return arc_read_handler_->GetArcRestoreWindowId(task_id);
+  return arc_read_handler_->GetArcRestoreWindowIdForTaskId(task_id);
+}
+
+int32_t FullRestoreReadHandler::GetArcRestoreWindowIdForSessionId(
+    int32_t session_id) {
+  if (!arc_read_handler_)
+    return 0;
+
+  return arc_read_handler_->GetArcRestoreWindowIdForSessionId(session_id);
 }
 
 void FullRestoreReadHandler::ModifyWidgetParams(
diff --git a/components/full_restore/full_restore_read_handler.h b/components/full_restore/full_restore_read_handler.h
index 2a529329..dd06e52 100644
--- a/components/full_restore/full_restore_read_handler.h
+++ b/components/full_restore/full_restore_read_handler.h
@@ -102,10 +102,10 @@
   int32_t FetchRestoreWindowId(const std::string& app_id);
 
   // Returns the restore window id for the ARC app's |task_id|.
-  //
-  // TODO(crbug.com/1146900): Handle the scenario that the window is created
-  // first, and OnTaskCreated is called later..
-  int32_t GetArcRestoreWindowId(int32_t task_id);
+  int32_t GetArcRestoreWindowIdForTaskId(int32_t task_id);
+
+  // Returns the restore window id for the ARC app's |session_id|.
+  int32_t GetArcRestoreWindowIdForSessionId(int32_t session_id);
 
   // Modifies `out_params` based on the window info associated with
   // `restore_window_id`.
diff --git a/components/full_restore/full_restore_utils.cc b/components/full_restore/full_restore_utils.cc
index d776e2c..3ddde78 100644
--- a/components/full_restore/full_restore_utils.cc
+++ b/components/full_restore/full_restore_utils.cc
@@ -54,11 +54,20 @@
   return FullRestoreReadHandler::GetInstance()->FetchRestoreWindowId(app_id);
 }
 
-int32_t GetArcRestoreWindowId(int32_t task_id) {
+int32_t GetArcRestoreWindowIdForTaskId(int32_t task_id) {
   if (!ash::features::IsFullRestoreEnabled())
     return 0;
 
-  return FullRestoreReadHandler::GetInstance()->GetArcRestoreWindowId(task_id);
+  return FullRestoreReadHandler::GetInstance()->GetArcRestoreWindowIdForTaskId(
+      task_id);
+}
+
+int32_t GetArcRestoreWindowIdForSessionId(int32_t session_id) {
+  if (!ash::features::IsFullRestoreEnabled())
+    return 0;
+
+  return FullRestoreReadHandler::GetInstance()
+      ->GetArcRestoreWindowIdForSessionId(session_id);
 }
 
 bool ShouldRestore(const AccountId& account_id) {
diff --git a/components/full_restore/full_restore_utils.h b/components/full_restore/full_restore_utils.h
index 747a46b2..14d2e376 100644
--- a/components/full_restore/full_restore_utils.h
+++ b/components/full_restore/full_restore_utils.h
@@ -95,7 +95,11 @@
 
 // Returns the restore window id for the ARC app's |task_id|.
 COMPONENT_EXPORT(FULL_RESTORE)
-int32_t GetArcRestoreWindowId(int32_t task_id);
+int32_t GetArcRestoreWindowIdForTaskId(int32_t task_id);
+
+// Returns the restore window id for the ARC app's |session_id|.
+COMPONENT_EXPORT(FULL_RESTORE)
+int32_t GetArcRestoreWindowIdForSessionId(int32_t session_id);
 
 // Returns true if we should restore apps and pages based on the restore setting
 // and the user's choice from the notification. Otherwise, returns false.
diff --git a/components/history/core/browser/visit_database.cc b/components/history/core/browser/visit_database.cc
index e7c04dc0..ea6ef6b 100644
--- a/components/history/core/browser/visit_database.cc
+++ b/components/history/core/browser/visit_database.cc
@@ -790,17 +790,13 @@
       "  visit_time >= ? AND "
       // Restrict to visits that are older than the specified end time.
       "  visit_time < ? "));
-  statement.BindInt64(0,
-                      begin_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
-  statement.BindInt64(1, end_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+  statement.BindTime(0, begin_time);
+  statement.BindTime(1, end_time);
   std::vector<DomainVisit> domain_visits;
   while (statement.Step()) {
     const GURL url(statement.ColumnString(1));
     if (google_util::IsGoogleSearchUrl(url)) {
-      domain_visits.emplace_back(
-          url.host(),
-          base::Time::FromDeltaSinceWindowsEpoch(
-              base::TimeDelta::FromMicroseconds(statement.ColumnInt64(0))));
+      domain_visits.emplace_back(url.host(), statement.ColumnTime(0));
     }
   }
   return domain_visits;
diff --git a/components/history_clusters/core/memories_features.cc b/components/history_clusters/core/memories_features.cc
index 8824f1a3..2cacde3a 100644
--- a/components/history_clusters/core/memories_features.cc
+++ b/components/history_clusters/core/memories_features.cc
@@ -26,7 +26,7 @@
     &kMemories, "MemoriesPersistContextAnnotationsInHistoryDb", false};
 
 const base::FeatureParam<int> kMaxVisitsToCluster{
-    &kMemories, "MemoriesMaxVisitsToCluster", 10};
+    &kMemories, "MemoriesMaxVisitsToCluster", 1000};
 
 const base::FeatureParam<int> kMaxDaysToCluster{&kMemories,
                                                 "MemoriesMaxDaysToCluster", 9};
diff --git a/components/history_clusters/core/memories_remote_model_helper.cc b/components/history_clusters/core/memories_remote_model_helper.cc
index 9232a9b..19fac31 100644
--- a/components/history_clusters/core/memories_remote_model_helper.cc
+++ b/components/history_clusters/core/memories_remote_model_helper.cc
@@ -164,6 +164,10 @@
   std::string request_body;
   base::JSONWriter::Write(container_value, &request_body);
 
+  // Also dump the encoded request, as it allows us to repro server-side errors.
+  if (debug_logger_)
+    debug_logger_->Run(request_body);
+
   auto url_loader = CreateLoader(CreateRequest(endpoint), request_body);
   network::SimpleURLLoader* unowned_url_loader = url_loader.get();
   unowned_url_loader->DownloadToString(
diff --git a/components/keep_alive_registry/keep_alive_types.cc b/components/keep_alive_registry/keep_alive_types.cc
index bdb31eb..2138828 100644
--- a/components/keep_alive_registry/keep_alive_types.cc
+++ b/components/keep_alive_registry/keep_alive_types.cc
@@ -68,6 +68,8 @@
       return out << "NATIVE_MESSAGING_HOST_ERROR_REPORT";
     case KeepAliveOrigin::WEB_APP_INTENT_PICKER:
       return out << "WEB_APP_INTENT_PICKER";
+    case KeepAliveOrigin::WEB_APP_PROTOCOL_HANDLER_LAUNCH:
+      return out << "WEB_APP_PROTOCOL_HANDLER_LAUNCH";
     case KeepAliveOrigin::SESSION_DATA_DELETER:
       return out << "SESSION_DATA_DELETER";
   }
diff --git a/components/keep_alive_registry/keep_alive_types.h b/components/keep_alive_registry/keep_alive_types.h
index 6a2ec7d..ab3fb23 100644
--- a/components/keep_alive_registry/keep_alive_types.h
+++ b/components/keep_alive_registry/keep_alive_types.h
@@ -62,6 +62,7 @@
   USER_MANAGER_VIEW,
   CREDENTIAL_PROVIDER_SIGNIN_DIALOG,
   WEB_APP_INTENT_PICKER,
+  WEB_APP_PROTOCOL_HANDLER_LAUNCH,
 
   // c/b/web_applications
   APP_START_URL_MIGRATION,
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc
index ba0539d..8b1a9a42 100644
--- a/components/ntp_tiles/most_visited_sites.cc
+++ b/components/ntp_tiles/most_visited_sites.cc
@@ -250,7 +250,7 @@
 }
 
 void MostVisitedSites::InitializeCustomLinks() {
-  if (!custom_links_ || !current_tiles_.has_value() || !custom_links_enabled_)
+  if (!custom_links_ || !current_tiles_.has_value() || !IsCustomLinksEnabled())
     return;
 
   if (custom_links_->Initialize(current_tiles_.value()))
@@ -258,7 +258,7 @@
 }
 
 void MostVisitedSites::UninitializeCustomLinks() {
-  if (!custom_links_ || !custom_links_enabled_)
+  if (!custom_links_ || !IsCustomLinksEnabled())
     return;
 
   custom_links_action_count_ = -1;
@@ -267,22 +267,46 @@
 }
 
 bool MostVisitedSites::IsCustomLinksInitialized() {
-  if (!custom_links_ || !custom_links_enabled_)
+  if (!custom_links_ || !IsCustomLinksEnabled())
     return false;
 
   return custom_links_->IsInitialized();
 }
 
 void MostVisitedSites::EnableCustomLinks(bool enable) {
-  if (custom_links_enabled_ != enable) {
-    custom_links_enabled_ = enable;
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  if (IsCustomLinksEnabled() != enable) {
+    prefs_->SetBoolean(prefs::kNtpUseMostVisitedTiles, !enable);
     BuildCurrentTiles();
   }
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
+}
+
+bool MostVisitedSites::IsCustomLinksEnabled() const {
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  return !prefs_->GetBoolean(prefs::kNtpUseMostVisitedTiles);
+#else
+  return false;
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
+}
+
+void MostVisitedSites::SetShortcutsVisible(bool visible) {
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  prefs_->SetBoolean(prefs::kNtpShortcutsVisible, visible);
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
+}
+
+bool MostVisitedSites::IsShortcutsVisible() const {
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  return prefs_->GetBoolean(prefs::kNtpShortcutsVisible);
+#else
+  return true;
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
 }
 
 bool MostVisitedSites::AddCustomLink(const GURL& url,
                                      const std::u16string& title) {
-  if (!custom_links_ || !custom_links_enabled_)
+  if (!custom_links_ || !IsCustomLinksEnabled())
     return false;
 
   bool is_first_action = !custom_links_->IsInitialized();
@@ -305,7 +329,7 @@
 bool MostVisitedSites::UpdateCustomLink(const GURL& url,
                                         const GURL& new_url,
                                         const std::u16string& new_title) {
-  if (!custom_links_ || !custom_links_enabled_)
+  if (!custom_links_ || !IsCustomLinksEnabled())
     return false;
 
   bool is_first_action = !custom_links_->IsInitialized();
@@ -326,7 +350,7 @@
 }
 
 bool MostVisitedSites::ReorderCustomLink(const GURL& url, size_t new_pos) {
-  if (!custom_links_ || !custom_links_enabled_)
+  if (!custom_links_ || !IsCustomLinksEnabled())
     return false;
 
   bool is_first_action = !custom_links_->IsInitialized();
@@ -347,7 +371,7 @@
 }
 
 bool MostVisitedSites::DeleteCustomLink(const GURL& url) {
-  if (!custom_links_ || !custom_links_enabled_)
+  if (!custom_links_ || !IsCustomLinksEnabled())
     return false;
 
   bool is_first_action = !custom_links_->IsInitialized();
@@ -368,7 +392,7 @@
 }
 
 void MostVisitedSites::UndoCustomLinkAction() {
-  if (!custom_links_ || !custom_links_enabled_)
+  if (!custom_links_ || !IsCustomLinksEnabled())
     return;
 
   // If this is undoing the first action after initialization, uninitialize
@@ -424,10 +448,23 @@
 void MostVisitedSites::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterIntegerPref(prefs::kNumPersonalTiles, 0);
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  registry->RegisterBooleanPref(prefs::kNtpUseMostVisitedTiles, false);
+  registry->RegisterBooleanPref(prefs::kNtpShortcutsVisible, true);
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
+}
+
+// static
+void MostVisitedSites::ResetProfilePrefs(PrefService* prefs) {
+  prefs->SetInteger(prefs::kNumPersonalTiles, 0);
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+  prefs->SetBoolean(prefs::kNtpUseMostVisitedTiles, false);
+  prefs->SetBoolean(prefs::kNtpShortcutsVisible, true);
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
 }
 
 size_t MostVisitedSites::GetMaxNumSites() const {
-  return max_num_sites_ + (custom_links_ && custom_links_enabled_ ? 1 : 0);
+  return max_num_sites_ + (custom_links_ && IsCustomLinksEnabled() ? 1 : 0);
 }
 
 void MostVisitedSites::InitiateTopSitesQuery() {
@@ -736,7 +773,7 @@
 
 void MostVisitedSites::OnCustomLinksChanged() {
   DCHECK(custom_links_);
-  if (!custom_links_enabled_)
+  if (!IsCustomLinksEnabled())
     return;
 
   if (custom_links_->IsInitialized()) {
diff --git a/components/ntp_tiles/most_visited_sites.h b/components/ntp_tiles/most_visited_sites.h
index 6720ee68..fc7d7a69 100644
--- a/components/ntp_tiles/most_visited_sites.h
+++ b/components/ntp_tiles/most_visited_sites.h
@@ -182,6 +182,13 @@
   // when a third-party NTP is being used, or when the user switches between
   // custom links and Most Visited sites.
   void EnableCustomLinks(bool enable);
+  // Returns true if custom links have been enabled and false if custom links
+  // are disabled and Most Visited sites should be returned instead.
+  bool IsCustomLinksEnabled() const;
+  // Sets the visibility of the NTP tiles.
+  void SetShortcutsVisible(bool visible);
+  // Returns whether NTP tiles should be shown.
+  bool IsShortcutsVisible() const;
   // Adds a custom link. If the number of current links is maxed, returns false
   // and does nothing. Will initialize custom links if they have not been
   // initialized yet, unless the action fails. Custom links must be enabled.
@@ -219,6 +226,7 @@
   void OnBlockedSitesChanged() override;
 
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+  static void ResetProfilePrefs(PrefService* prefs);
 
   // Workhorse for SaveNewTilesAndNotify. Implemented as a separate static and
   // public method for ease of testing.
@@ -359,9 +367,6 @@
   // Do not use directly. Use GetMaxNumSites() instead.
   size_t max_num_sites_;
 
-  // False if custom links is disabled and Most Visited sites should be returned
-  // instead.
-  bool custom_links_enabled_ = true;
   // Number of actions after custom link initialization. Set to -1 and not
   // incremented if custom links was not initialized during this session.
   int custom_links_action_count_ = -1;
diff --git a/components/ntp_tiles/pref_names.cc b/components/ntp_tiles/pref_names.cc
index 8044493..39ef802 100644
--- a/components/ntp_tiles/pref_names.cc
+++ b/components/ntp_tiles/pref_names.cc
@@ -35,5 +35,13 @@
 const char kCustomLinksList[] = "custom_links.list";
 const char kCustomLinksInitialized[] = "custom_links.initialized";
 
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+// Tracks whether the user has chosen to hide the shortcuts tiles on the NTP.
+const char kNtpShortcutsVisible[] = "ntp.shortcust_visible";
+// Tracks whether the user has chosen to use custom links or most visited sites
+// for the shortcut tiles on the NTP.
+const char kNtpUseMostVisitedTiles[] = "ntp.use_most_visited_tiles";
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
+
 }  // namespace prefs
 }  // namespace ntp_tiles
diff --git a/components/ntp_tiles/pref_names.h b/components/ntp_tiles/pref_names.h
index 1b0959b..c412f27 100644
--- a/components/ntp_tiles/pref_names.h
+++ b/components/ntp_tiles/pref_names.h
@@ -23,6 +23,11 @@
 extern const char kCustomLinksList[];
 extern const char kCustomLinksInitialized[];
 
+#if !defined(OS_IOS) && !defined(OS_ANDROID)
+extern const char kNtpShortcutsVisible[];
+extern const char kNtpUseMostVisitedTiles[];
+#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
+
 }  // namespace prefs
 }  // namespace ntp_tiles
 
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteResult.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteResult.java
index e5cdd9f0..79029c2 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteResult.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteResult.java
@@ -9,6 +9,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.core.util.ObjectsCompat;
 
 import org.chromium.base.annotations.CalledByNative;
@@ -70,7 +71,8 @@
      * @param suggestions List of AutocompleteMatch objects.
      * @param groupsDetails Additional information about the AutocompleteMatch groups.
      */
-    private AutocompleteResult(long nativeResult, @Nullable List<AutocompleteMatch> suggestions,
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    AutocompleteResult(long nativeResult, @Nullable List<AutocompleteMatch> suggestions,
             @Nullable SparseArray<GroupDetails> groupsDetails) {
         // Consider all locally constructed AutocompleteResult objects as coming from Cache.
         // These results do not have a native counterpart, meaning there's no corresponding C++
@@ -99,8 +101,25 @@
         return new AutocompleteResult(0, suggestions, groupsDetails);
     }
 
+    /**
+     * Create AutocompleteResult object from native object.
+     *
+     * Newly created AutocompleteResult object is associated with its Native counterpart.
+     *
+     * @param nativeAutocompleteResult Corresponding Native object.
+     * @param suggestions Array of encompassed, associated AutocompleteMatch objects.
+     *         These suggestions must be exact same and in same order as the ones held by
+     *         Native AutocompleteResult content.
+     * @param groupIds An array of known group identifiers (used for matching group headers).
+     * @param groupNames An array of group names for each of the identifiers. The length and
+     *         the content of this array must match the length and IDs of the |groupIds|.
+     * @param groupCollapsedStates An array of group default collapsed states. The length and
+     *         the content of this array must match the length and IDs of the |groupIds|.
+     * @return AutocompleteResult object encompassing supplied information.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     @CalledByNative
-    private static AutocompleteResult build(long nativeAutocompleteResult,
+    static AutocompleteResult fromNative(long nativeAutocompleteResult,
             @NonNull AutocompleteMatch[] suggestions, @NonNull int[] groupIds,
             @NonNull String[] groupNames, @NonNull boolean[] groupCollapsedStates) {
         assert groupIds.length == groupNames.length;
@@ -124,8 +143,9 @@
         Collections.addAll(mSuggestions, suggestions);
     }
 
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     @CalledByNative
-    private void destroy() {
+    void notifyNativeDestroyed() {
         mNativeAutocompleteResult = 0;
     }
 
diff --git a/components/omnibox/browser/android/javatests/src/org/chromium/components/omnibox/AutocompleteResultUnitTest.java b/components/omnibox/browser/android/javatests/src/org/chromium/components/omnibox/AutocompleteResultUnitTest.java
index 162f969d..81543bb 100644
--- a/components/omnibox/browser/android/javatests/src/org/chromium/components/omnibox/AutocompleteResultUnitTest.java
+++ b/components/omnibox/browser/android/javatests/src/org/chromium/components/omnibox/AutocompleteResultUnitTest.java
@@ -279,4 +279,33 @@
         Assert.assertNotEquals(res1, res2);
         Assert.assertNotEquals(res1.hashCode(), res2.hashCode());
     }
+
+    @Test
+    @SmallTest
+    public void resultCreatedFromCacheIsIdentifiedAsCached() {
+        AutocompleteResult res = new AutocompleteResult(0, null, null);
+        Assert.assertTrue(res.isFromCachedResult());
+        res.notifyNativeDestroyed();
+        Assert.assertTrue(res.isFromCachedResult());
+
+        res = AutocompleteResult.fromCache(new ArrayList<>(), new SparseArray<>());
+        Assert.assertTrue(res.isFromCachedResult());
+        res.notifyNativeDestroyed();
+        Assert.assertTrue(res.isFromCachedResult());
+    }
+
+    @Test
+    @SmallTest
+    public void resultCreatedFromNativeAreNotIdentifiedAsCached() {
+        AutocompleteResult res = new AutocompleteResult(0x12345678, null, null);
+        Assert.assertFalse(res.isFromCachedResult());
+        res.notifyNativeDestroyed();
+        Assert.assertFalse(res.isFromCachedResult());
+
+        res = AutocompleteResult.fromNative(
+                0xfedcba98, new AutocompleteMatch[0], new int[0], new String[0], new boolean[0]);
+        Assert.assertFalse(res.isFromCachedResult());
+        res.notifyNativeDestroyed();
+        Assert.assertFalse(res.isFromCachedResult());
+    }
 }
diff --git a/components/omnibox/browser/autocomplete_provider_client.cc b/components/omnibox/browser/autocomplete_provider_client.cc
index 418d3d8..d0805f83 100644
--- a/components/omnibox/browser/autocomplete_provider_client.cc
+++ b/components/omnibox/browser/autocomplete_provider_client.cc
@@ -9,6 +9,10 @@
   return nullptr;
 }
 
+bool AutocompleteProviderClient::AllowDeletingBrowserHistory() const {
+  return true;
+}
+
 std::string AutocompleteProviderClient::ProfileUserName() const {
   return "";
 }
diff --git a/components/omnibox/browser/autocomplete_provider_client.h b/components/omnibox/browser/autocomplete_provider_client.h
index 05141fc6..0f30bd17 100644
--- a/components/omnibox/browser/autocomplete_provider_client.h
+++ b/components/omnibox/browser/autocomplete_provider_client.h
@@ -125,6 +125,9 @@
   virtual bool IsOffTheRecord() const = 0;
   virtual bool SearchSuggestEnabled() const = 0;
 
+  // True for almost all users except ones with a specific enterprise policy.
+  virtual bool AllowDeletingBrowserHistory() const;
+
   // Returns whether personalized URL data collection is enabled.  I.e.,
   // the user has consented to have URLs recorded keyed by their Google account.
   // In this case, the user has agreed to share browsing data with Google and so
diff --git a/components/omnibox/browser/autocomplete_result_android.cc b/components/omnibox/browser/autocomplete_result_android.cc
index 72687ca..4cc7bda 100644
--- a/components/omnibox/browser/autocomplete_result_android.cc
+++ b/components/omnibox/browser/autocomplete_result_android.cc
@@ -62,7 +62,7 @@
   ScopedJavaLocalRef<jobjectArray> j_group_names =
       ToJavaArrayOfStrings(env, group_names);
 
-  java_result_ = Java_AutocompleteResult_build(
+  java_result_ = Java_AutocompleteResult_fromNative(
       env, reinterpret_cast<intptr_t>(this), BuildJavaMatches(env), j_group_ids,
       j_group_names, j_group_collapsed_states);
 
@@ -74,7 +74,7 @@
     return;
 
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_AutocompleteResult_destroy(env, java_result_);
+  Java_AutocompleteResult_notifyNativeDestroyed(env, java_result_);
   java_result_.Reset();
 }
 
diff --git a/components/omnibox/browser/history_provider.h b/components/omnibox/browser/history_provider.h
index 17534ec4..6179f4d1c 100644
--- a/components/omnibox/browser/history_provider.h
+++ b/components/omnibox/browser/history_provider.h
@@ -41,7 +41,7 @@
   // backing data.
   void DeleteMatchFromMatches(const AutocompleteMatch& match);
 
-  AutocompleteProviderClient* client() { return client_; }
+  AutocompleteProviderClient* client() const { return client_; }
 
  private:
   AutocompleteProviderClient* client_;
diff --git a/components/omnibox/browser/history_quick_provider.cc b/components/omnibox/browser/history_quick_provider.cc
index 6cf2e63..d968eb3 100644
--- a/components/omnibox/browser/history_quick_provider.cc
+++ b/components/omnibox/browser/history_quick_provider.cc
@@ -205,11 +205,12 @@
     const ScoredHistoryMatch& history_match,
     int score) {
   const history::URLRow& info = history_match.url_info;
-  AutocompleteMatch match(
-      this, score, !!info.visit_count(),
-      history_match.url_matches.empty() ?
-          AutocompleteMatchType::HISTORY_TITLE :
-          AutocompleteMatchType::HISTORY_URL);
+  bool deletable =
+      !!info.visit_count() && client()->AllowDeletingBrowserHistory();
+  AutocompleteMatch match(this, score, deletable,
+                          history_match.url_matches.empty()
+                              ? AutocompleteMatchType::HISTORY_TITLE
+                              : AutocompleteMatchType::HISTORY_URL);
   match.typed_count = info.typed_count();
   match.destination_url = info.url();
   DCHECK(match.destination_url.is_valid());
diff --git a/components/omnibox/browser/history_url_provider.cc b/components/omnibox/browser/history_url_provider.cc
index 00ace4b7..1654568e 100644
--- a/components/omnibox/browser/history_url_provider.cc
+++ b/components/omnibox/browser/history_url_provider.cc
@@ -452,7 +452,8 @@
     bool trim_http,
     const AutocompleteMatch& what_you_typed_match,
     const TemplateURL* default_search_provider,
-    const SearchTermsData* search_terms_data)
+    const SearchTermsData* search_terms_data,
+    bool allow_deleting_browser_history)
     : origin_task_runner(base::SequencedTaskRunnerHandle::Get()),
       input(input),
       input_before_fixup(input_before_fixup),
@@ -465,7 +466,8 @@
           default_search_provider
               ? new TemplateURL(default_search_provider->data())
               : nullptr),
-      search_terms_data(new SearchTermsDataSnapshot(search_terms_data)) {}
+      search_terms_data(new SearchTermsDataSnapshot(search_terms_data)),
+      allow_deleting_browser_history(allow_deleting_browser_history) {}
 
 HistoryURLProviderParams::~HistoryURLProviderParams() {
 }
@@ -574,7 +576,8 @@
   // 2.
   std::unique_ptr<HistoryURLProviderParams> params(new HistoryURLProviderParams(
       fixed_up_input, input, trim_http, what_you_typed_match,
-      default_search_provider, search_terms_data));
+      default_search_provider, search_terms_data,
+      client()->AllowDeletingBrowserHistory()));
 
   // Pass 1: Get the in-memory URL database, and use it to find and promote
   // the inline autocomplete match, if any.
@@ -917,8 +920,9 @@
       break;
     default:
       DCHECK_EQ(VisitClassifier::VISITED, classifier.type());
+      params->what_you_typed_match.deletable =
+          params->allow_deleting_browser_history;
       // We have data for this match, use it.
-      params->what_you_typed_match.deletable = true;
       auto title = classifier.url_row().title();
       params->what_you_typed_match.description = title;
       params->what_you_typed_match.destination_url = classifier.url_row().url();
@@ -1179,8 +1183,10 @@
 
   const history::HistoryMatch& history_match = params.matches[match_number];
   const history::URLRow& info = history_match.url_info;
-  AutocompleteMatch match(this, relevance,
-      !!info.visit_count(), AutocompleteMatchType::HISTORY_URL);
+  bool deletable =
+      !!info.visit_count() && client()->AllowDeletingBrowserHistory();
+  AutocompleteMatch match(this, relevance, deletable,
+                          AutocompleteMatchType::HISTORY_URL);
   match.typed_count = info.typed_count();
   match.destination_url = info.url();
   DCHECK(match.destination_url.is_valid());
diff --git a/components/omnibox/browser/history_url_provider.h b/components/omnibox/browser/history_url_provider.h
index 980dda5..08fe9ea 100644
--- a/components/omnibox/browser/history_url_provider.h
+++ b/components/omnibox/browser/history_url_provider.h
@@ -106,7 +106,8 @@
                            bool trim_http,
                            const AutocompleteMatch& what_you_typed_match,
                            const TemplateURL* default_search_provider,
-                           const SearchTermsData* search_terms_data);
+                           const SearchTermsData* search_terms_data,
+                           bool allow_deleting_browser_history);
   ~HistoryURLProviderParams();
   HistoryURLProviderParams(const HistoryURLProviderParams&) = delete;
   HistoryURLProviderParams& operator=(const HistoryURLProviderParams&) = delete;
@@ -181,6 +182,10 @@
   // Similarly, we use a std::unique_ptr<SearchTermsData> so that we can store a
   // snapshot of the SearchTermsData accessible from the history thread.
   std::unique_ptr<SearchTermsData> search_terms_data;
+
+  // True if the user is allowed to delete browser history. Stored here because
+  // we aren't allowed to read user preferences from the History sequence.
+  const bool allow_deleting_browser_history;
 };
 
 // This class is an autocomplete provider and is also a pseudo-internal
diff --git a/components/omnibox/browser/history_url_provider_unittest.cc b/components/omnibox/browser/history_url_provider_unittest.cc
index 7e0c5bd..c055cb6 100644
--- a/components/omnibox/browser/history_url_provider_unittest.cc
+++ b/components/omnibox/browser/history_url_provider_unittest.cc
@@ -1351,7 +1351,7 @@
   history_match.url_info.set_url(GURL(url_text));
   history_match.match_in_scheme = match_in_scheme;
   auto params = std::make_unique<HistoryURLProviderParams>(
-      input, input, true, AutocompleteMatch(), nullptr, nullptr);
+      input, input, true, AutocompleteMatch(), nullptr, nullptr, true);
   params->matches.push_back(history_match);
 
   return params;
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider.cc b/components/omnibox/browser/local_history_zero_suggest_provider.cc
index a6769df..35c8b1da 100644
--- a/components/omnibox/browser/local_history_zero_suggest_provider.cc
+++ b/components/omnibox/browser/local_history_zero_suggest_provider.cc
@@ -227,7 +227,7 @@
         template_url_service->search_terms_data(),
         TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
         /*append_extra_query_params_from_command_line*/ true);
-    match.deletable = true;
+    match.deletable = client_->AllowDeletingBrowserHistory();
 
     matches_.push_back(match);
     if (matches_.size() >= max_matches_)
diff --git a/components/permissions/permission_auditing_database.cc b/components/permissions/permission_auditing_database.cc
index 7f70722..4f2e432 100644
--- a/components/permissions/permission_auditing_database.cc
+++ b/components/permissions/permission_auditing_database.cc
@@ -24,15 +24,6 @@
 
 namespace {
 
-int64_t TimeToInt64(base::Time time) {
-  return time.ToDeltaSinceWindowsEpoch().InMicroseconds();
-}
-
-base::Time Int64ToTime(const int64_t& time) {
-  return base::Time::FromDeltaSinceWindowsEpoch(
-      base::TimeDelta::FromMicroseconds(time));
-}
-
 // For this database, schema migration is supported for at least 1 year.
 // This means we can deprecate old versions that landed more than a year ago.
 //
@@ -124,8 +115,8 @@
                              "VALUES (?, ?, ?, ?, ?, ?, ?)"));
   statement.BindString(0, session.origin.Serialize());
   statement.BindInt(1, static_cast<int32_t>(session.type));
-  statement.BindInt64(2, TimeToInt64(session.usage_start));
-  statement.BindInt64(3, TimeToInt64(session.usage_end));
+  statement.BindTime(2, session.usage_start);
+  statement.BindTime(3, session.usage_end);
   statement.BindBool(4, session.had_user_activation);
   statement.BindBool(5, session.was_foreground);
   statement.BindBool(6, session.had_focus);
@@ -155,15 +146,13 @@
       "AND usage_end_time >= ?"));
   statement.BindString(0, origin.Serialize());
   statement.BindInt(1, static_cast<int32_t>(type));
-  statement.BindInt64(2, start_time.is_null()
-                             ? std::numeric_limits<int64_t>::min()
-                             : TimeToInt64(start_time));
+  statement.BindTime(2, start_time.is_null() ? base::Time::Min() : start_time);
 
   while (statement.Step()) {
     sessions.push_back({.origin = origin,
                         .type = type,
-                        .usage_start = Int64ToTime(statement.ColumnInt64(0)),
-                        .usage_end = Int64ToTime(statement.ColumnInt64(1)),
+                        .usage_start = statement.ColumnTime(0),
+                        .usage_end = statement.ColumnTime(1),
                         .had_user_activation = statement.ColumnBool(2),
                         .was_foreground = statement.ColumnBool(3),
                         .had_focus = statement.ColumnBool(4)});
@@ -187,7 +176,7 @@
   statement.BindInt(1, static_cast<int32_t>(type));
   absl::optional<base::Time> last_usage;
   if (statement.Step()) {
-    last_usage = Int64ToTime(statement.ColumnInt64(0));
+    last_usage = statement.ColumnTime(0);
   }
   return last_usage;
 }
@@ -206,10 +195,10 @@
                              "SET usage_end_time = ? "
                              "WHERE origin = ? AND content_setting_type = ? "
                              "AND usage_start_time = ?"));
-  statement.BindInt64(0, TimeToInt64(new_end_time));
+  statement.BindTime(0, new_end_time);
   statement.BindString(1, origin.Serialize());
   statement.BindInt(2, static_cast<int32_t>(type));
-  statement.BindInt64(3, TimeToInt64(start_time));
+  statement.BindTime(3, start_time);
 
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
@@ -229,14 +218,11 @@
                              "DELETE FROM uses "
                              "WHERE usage_start_time BETWEEN ? AND ? "
                              "OR usage_end_time BETWEEN ? AND ?"));
-  auto start = start_time.is_null() ? std::numeric_limits<int64_t>::min()
-                                    : TimeToInt64(start_time);
-  auto end = end_time.is_null() ? std::numeric_limits<int64_t>::max()
-                                : TimeToInt64(end_time);
-  statement.BindInt64(0, start);
-  statement.BindInt64(1, end);
-  statement.BindInt64(2, start);
-  statement.BindInt64(3, end);
+  statement.BindTime(0, start_time.is_null() ? base::Time::Min() : start_time);
+  statement.BindTime(1, end_time.is_null() ? base::Time::Max() : end_time);
+  statement.BindTime(2, start_time.is_null() ? base::Time::Min() : start_time);
+  statement.BindTime(3, end_time.is_null() ? base::Time::Max() : end_time);
+
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
     return false;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 8d68cc4..4a8b08a 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -6392,7 +6392,7 @@
       'future_on': ['chrome_os', 'chrome.*'],
       'features': {
         'dynamic_refresh': True,
-        'per_profile': True,
+        'per_profile': False,
       },
       'example_value': ['https://www.example.com'],
       'id': 837,
@@ -6435,7 +6435,7 @@
       'future_on': ['chrome_os', 'chrome.*'],
       'features': {
         'dynamic_refresh': True,
-        'per_profile': True,
+        'per_profile': False,
       },
       'example_value': [
         {
diff --git a/components/policy/resources/policy_templates_th.xtb b/components/policy/resources/policy_templates_th.xtb
index c9199838..6712ca8 100644
--- a/components/policy/resources/policy_templates_th.xtb
+++ b/components/policy/resources/policy_templates_th.xtb
@@ -79,7 +79,7 @@
 <translation id="1076751984131277498">รายการที่อนุญาตของอุปกรณ์ USB ที่ถอดได้</translation>
 <translation id="1079801999187584280">ไม่อนุญาตการใช้เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์</translation>
 <translation id="1082802595100075771">ให้ผู้ใช้เลือกที่จะใช้บริการของ Google แบบไม่ระบุตัวตนเพื่อให้คำอธิบายอัตโนมัติสำหรับรูปภาพที่ไม่มีป้ายกำกับ</translation>
-<translation id="1087437665304381368">นโยบายนี้ควบคุมโหมดนักพัฒนาซอฟต์แวร์ของ <ph name="PRODUCT_OS_NAME" /> เท่านั้น หากคุณต้องการป้องกันการเข้าถึงตัวเลือกสำหรับนักพัฒนาซอฟต์แวร์ Android ก็จะต้องตั้งค่านโยบาย <ph name="DEVELOPER_TOOLS_DISABLED_POLICY_NAME" /></translation>
+<translation id="1087437665304381368">นโยบายนี้ควบคุมโหมดนักพัฒนาซอฟต์แวร์ของ <ph name="PRODUCT_OS_NAME" /> เท่านั้น หากคุณต้องการป้องกันการเข้าถึงตัวเลือกสำหรับนักพัฒนาแอป Android ก็จะต้องตั้งค่านโยบาย <ph name="DEVELOPER_TOOLS_DISABLED_POLICY_NAME" /></translation>
 <translation id="1087707496788636333">เรากำลังย้ายรายการนโยบายของ Chrome Enterprise โปรดอัปเดตบุ๊กมาร์กเป็น <ph name="POLICY_DOCUMENTATION_URL" /></translation>
 <translation id="1095209545735032039">บล็อก Serial API ในเว็บไซต์เหล่านี้</translation>
 <translation id="1096105751829466145">ผู้ให้บริการการค้นหาเริ่มต้น</translation>
diff --git a/components/sessions/core/tab_restore_service_helper.cc b/components/sessions/core/tab_restore_service_helper.cc
index 10f1d8d..40ace9274 100644
--- a/components/sessions/core/tab_restore_service_helper.cc
+++ b/components/sessions/core/tab_restore_service_helper.cc
@@ -433,11 +433,36 @@
         context = client_->CreateLiveTabContext(
             window.app_name, window.bounds, window.show_state, window.workspace,
             window.user_title);
+
+        base::flat_map<tab_groups::TabGroupId, tab_groups::TabGroupId>
+            new_group_ids;
+
         for (size_t tab_i = 0; tab_i < window.tabs.size(); ++tab_i) {
           const Tab& tab = *window.tabs[tab_i];
+
+          // Relabel group IDs to prevent duplicating groups, e.g. if the same
+          // window is restored twice or a tab of the same ID is restored
+          // elsewhere. See crbug.com/1202102.
+          absl::optional<tab_groups::TabGroupId> new_group;
+          if (tab.group) {
+            auto it = new_group_ids.find(*tab.group);
+            if (it == new_group_ids.end()) {
+              auto new_id = tab_groups::TabGroupId::GenerateNew();
+              // Ensure the new ID does not collide with an existing group,
+              // failing silently if it does. This is extremely unlikely,
+              // given group IDs are 128 bit randomly generated numbers.
+              if (client_->FindLiveTabContextWithGroup(new_id)) {
+                return std::vector<LiveTab*>();
+              }
+              it = new_group_ids.emplace(*tab.group, new_id).first;
+            }
+
+            new_group = it->second;
+          }
+
           LiveTab* restored_tab = context->AddRestoredTab(
               tab.navigations, context->GetTabCount(),
-              tab.current_navigation_index, tab.extension_app_id, tab.group,
+              tab.current_navigation_index, tab.extension_app_id, new_group,
               tab.group_visual_data.value_or(tab_groups::TabGroupVisualData()),
               static_cast<int>(tab_i) == window.selected_tab_index, tab.pinned,
               tab.platform_data.get(), tab.user_agent_override, nullptr);
@@ -449,7 +474,8 @@
         }
 
         for (const auto& tab_group : window.tab_groups) {
-          context->SetVisualDataForGroup(tab_group.first, tab_group.second);
+          context->SetVisualDataForGroup(new_group_ids.at(tab_group.first),
+                                         tab_group.second);
         }
 
         // All the window's tabs had the same former browser_id.
diff --git a/components/signin/public/identity_manager/identity_manager.cc b/components/signin/public/identity_manager/identity_manager.cc
index b4ed826..be965f0 100644
--- a/components/signin/public/identity_manager/identity_manager.cc
+++ b/components/signin/public/identity_manager/identity_manager.cc
@@ -305,17 +305,34 @@
 
 AccountInfo IdentityManager::FindExtendedAccountInfoByAccountId(
     const CoreAccountId& account_id) const {
+  if (!HasAccountWithRefreshToken(account_id))
+    return AccountInfo();
+
+  // AccountTrackerService returns an empty AccountInfo if the account is not
+  // found.
   return account_tracker_service_->GetAccountInfo(account_id);
 }
 
 AccountInfo IdentityManager::FindExtendedAccountInfoByEmailAddress(
     const std::string& email_address) const {
-  return account_tracker_service_->FindAccountInfoByEmail(email_address);
+  AccountInfo account_info =
+      account_tracker_service_->FindAccountInfoByEmail(email_address);
+  // AccountTrackerService always returns an AccountInfo, even on failure. In
+  // case of failure, the AccountInfo will be unpopulated, thus we should not
+  // be able to find a valid refresh token.
+  return HasAccountWithRefreshToken(account_info.account_id) ? account_info
+                                                             : AccountInfo();
 }
 
 AccountInfo IdentityManager::FindExtendedAccountInfoByGaiaId(
     const std::string& gaia_id) const {
-  return account_tracker_service_->FindAccountInfoByGaiaId(gaia_id);
+  AccountInfo account_info =
+      account_tracker_service_->FindAccountInfoByGaiaId(gaia_id);
+  // AccountTrackerService always returns an AccountInfo, even on failure. In
+  // case of failure, the AccountInfo will be unpopulated, thus we should not
+  // be able to find a valid refresh token.
+  return HasAccountWithRefreshToken(account_info.account_id) ? account_info
+                                                             : AccountInfo();
 }
 
 absl::optional<AccountInfo>
@@ -538,6 +555,12 @@
 }
 #endif
 
+AccountInfo IdentityManager::FindExtendedPrimaryAccountInfo(
+    ConsentLevel consent_level) {
+  CoreAccountId account_id = GetPrimaryAccountId(consent_level);
+  return account_tracker_service_->GetAccountInfo(account_id);
+}
+
 PrimaryAccountManager* IdentityManager::GetPrimaryAccountManager() const {
   return primary_account_manager_.get();
 }
diff --git a/components/signin/public/identity_manager/identity_manager.h b/components/signin/public/identity_manager/identity_manager.h
index 09d9962..8e3a14a3 100644
--- a/components/signin/public/identity_manager/identity_manager.h
+++ b/components/signin/public/identity_manager/identity_manager.h
@@ -53,6 +53,7 @@
 class AccountFetcherService;
 class AccountTrackerService;
 class GaiaCookieManagerService;
+class NewTabPageUI;
 
 namespace signin {
 
@@ -253,9 +254,8 @@
 
   // Returns extended information for account identified by |account_info|, or
   // an empty AccountInfo if the account is not found.
-  // Note: these functions may return a non-empty Accountinfo even if no refresh
-  // token is available for the account (in particular before tokens are
-  // loaded).
+  // Note: these functions return an empty AccountInfo if no refresh token is
+  // available for the account (in particular before tokens are loaded).
   AccountInfo FindExtendedAccountInfo(
       const CoreAccountInfo& account_info) const;
   // The same as `FindExtendedAccountInfo()` but finds an account by account ID.
@@ -627,6 +627,19 @@
   FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, OnNetworkInitialized);
   FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest,
                            ForceRefreshOfExtendedAccountInfo);
+  FRIEND_TEST_ALL_PREFIXES(IdentityManagerTest, FindExtendedPrimaryAccountInfo);
+
+  // Only caller to FindExtendedPrimaryAccountInfo().
+  // TODO(https://crbug.com/1213351): Delete once the private call has been
+  // removed.
+  friend class ::NewTabPageUI;
+
+  // Returns the extended account info for the primary account. This function
+  // does not require tokens to be loaded.
+  // Do not add more external callers, as account info is generally not
+  // available until tokens are loaded.
+  // TODO(https://crbug.com/1213351): Remove existing external callers.
+  AccountInfo FindExtendedPrimaryAccountInfo(ConsentLevel consent_level);
 
   // Private getters used for testing only (i.e. see identity_test_utils.h).
   PrimaryAccountManager* GetPrimaryAccountManager() const;
diff --git a/components/signin/public/identity_manager/identity_manager_unittest.cc b/components/signin/public/identity_manager/identity_manager_unittest.cc
index 0204898..0d4bbb1 100644
--- a/components/signin/public/identity_manager/identity_manager_unittest.cc
+++ b/components/signin/public/identity_manager/identity_manager_unittest.cc
@@ -2177,11 +2177,16 @@
       account_tracker()->SeedAccountInfo(account_info.gaia, account_info.email);
   ASSERT_EQ(account_info.account_id, account_id);
 
-  // FindExtendedAccountInfo() returns extended account information if
-  // the account is known.
+  // The refresh token is not available.
+  EXPECT_TRUE(
+      identity_manager()->FindExtendedAccountInfo(account_info).IsEmpty());
+
+  // FindExtendedAccountInfo() returns extended account information if the
+  // account is known and the token is available.
+  SetRefreshTokenForAccount(identity_manager(), account_info.account_id,
+                            "token");
   const AccountInfo extended_account_info =
       identity_manager()->FindExtendedAccountInfo(account_info);
-
   EXPECT_TRUE(!extended_account_info.IsEmpty());
   EXPECT_EQ(account_info.gaia, extended_account_info.gaia);
   EXPECT_EQ(account_info.email, extended_account_info.email);
@@ -2191,63 +2196,130 @@
 // Checks that FindExtendedAccountInfoByAccountId() returns information about
 // the account if the account is found, or an empty account info.
 TEST_F(IdentityManagerTest, FindExtendedAccountInfoByAccountId) {
-  // Add an account (note: cannot use kTestEmail as it is already inserted
-  // by the fixture common code, so use a different address).
-  const AccountInfo foo_account_info =
-      MakeAccountAvailable(identity_manager(), "foo@bar.com");
+  CoreAccountInfo account_info;
+  account_info.email = kTestEmail2;
+  account_info.gaia = kTestGaiaId2;
+  account_info.account_id = identity_manager()->PickAccountIdForAccount(
+      account_info.gaia, account_info.email);
 
+  // Account is unknown.
   AccountInfo maybe_account_info =
       identity_manager()->FindExtendedAccountInfoByAccountId(
-          CoreAccountId("dummy_value"));
+          account_info.account_id);
   EXPECT_TRUE(maybe_account_info.IsEmpty());
 
+  // Refresh token is not available.
+  const CoreAccountId account_id =
+      account_tracker()->SeedAccountInfo(account_info.gaia, account_info.email);
   maybe_account_info = identity_manager()->FindExtendedAccountInfoByAccountId(
-      foo_account_info.account_id);
+      account_info.account_id);
+  EXPECT_TRUE(maybe_account_info.IsEmpty());
+
+  // Account with refresh token.
+  SetRefreshTokenForAccount(identity_manager(), account_info.account_id,
+                            "token");
+  maybe_account_info = identity_manager()->FindExtendedAccountInfoByAccountId(
+      account_info.account_id);
   EXPECT_FALSE(maybe_account_info.IsEmpty());
-  EXPECT_EQ(foo_account_info.account_id, maybe_account_info.account_id);
-  EXPECT_EQ(foo_account_info.email, maybe_account_info.email);
-  EXPECT_EQ(foo_account_info.gaia, maybe_account_info.gaia);
+  EXPECT_EQ(account_info.account_id, maybe_account_info.account_id);
+  EXPECT_EQ(account_info.email, maybe_account_info.email);
+  EXPECT_EQ(account_info.gaia, maybe_account_info.gaia);
 }
 
 // Checks that FindExtendedAccountInfoByEmailAddress() returns information about
 // the account if the account is found, or an empty account info.
 TEST_F(IdentityManagerTest, FindExtendedAccountInfoByEmailAddress) {
-  // Add an account (note: cannot use kTestEmail as it is already inserted
-  // by the fixture common code, so use a different address).
-  const AccountInfo foo_account_info =
-      MakeAccountAvailable(identity_manager(), "foo@bar.com");
+  CoreAccountInfo account_info;
+  account_info.email = kTestEmail2;
+  account_info.gaia = kTestGaiaId2;
+  account_info.account_id = identity_manager()->PickAccountIdForAccount(
+      account_info.gaia, account_info.email);
 
+  // Account is unknown.
   AccountInfo maybe_account_info =
-      identity_manager()->FindExtendedAccountInfoByEmailAddress("dummy_value");
+      identity_manager()->FindExtendedAccountInfoByEmailAddress(
+          account_info.email);
   EXPECT_TRUE(maybe_account_info.IsEmpty());
 
+  // Refresh token is not available.
+  const CoreAccountId account_id =
+      account_tracker()->SeedAccountInfo(account_info.gaia, account_info.email);
   maybe_account_info =
       identity_manager()->FindExtendedAccountInfoByEmailAddress(
-          foo_account_info.email);
+          account_info.email);
+  EXPECT_TRUE(maybe_account_info.IsEmpty());
+
+  // Account with refresh token.
+  SetRefreshTokenForAccount(identity_manager(), account_info.account_id,
+                            "token");
+  maybe_account_info =
+      identity_manager()->FindExtendedAccountInfoByEmailAddress(
+          account_info.email);
   EXPECT_FALSE(maybe_account_info.IsEmpty());
-  EXPECT_EQ(foo_account_info.account_id, maybe_account_info.account_id);
-  EXPECT_EQ(foo_account_info.email, maybe_account_info.email);
-  EXPECT_EQ(foo_account_info.gaia, maybe_account_info.gaia);
+  EXPECT_EQ(account_info.account_id, maybe_account_info.account_id);
+  EXPECT_EQ(account_info.email, maybe_account_info.email);
+  EXPECT_EQ(account_info.gaia, maybe_account_info.gaia);
 }
 
 // Checks that FindExtendedAccountInfoByGaiaId() returns information about the
 // account if the account is found, or an empty account info.
 TEST_F(IdentityManagerTest, FindExtendedAccountInfoByGaiaId) {
-  // Add an account (note: cannot use kTestEmail as it is already inserted
-  // by the fixture common code, so use a different address).
-  const AccountInfo foo_account_info =
-      MakeAccountAvailable(identity_manager(), "foo@bar.com");
+  CoreAccountInfo account_info;
+  account_info.email = kTestEmail2;
+  account_info.gaia = kTestGaiaId2;
+  account_info.account_id = identity_manager()->PickAccountIdForAccount(
+      account_info.gaia, account_info.email);
 
+  // Account is unknown.
   AccountInfo maybe_account_info =
-      identity_manager()->FindExtendedAccountInfoByGaiaId("dummy_value");
+      identity_manager()->FindExtendedAccountInfoByGaiaId(account_info.gaia);
   EXPECT_TRUE(maybe_account_info.IsEmpty());
 
-  maybe_account_info = identity_manager()->FindExtendedAccountInfoByGaiaId(
-      foo_account_info.gaia);
+  // Refresh token is not available.
+  const CoreAccountId account_id =
+      account_tracker()->SeedAccountInfo(account_info.gaia, account_info.email);
+  maybe_account_info =
+      identity_manager()->FindExtendedAccountInfoByGaiaId(account_info.gaia);
+  EXPECT_TRUE(maybe_account_info.IsEmpty());
+
+  // Account with refresh token.
+  SetRefreshTokenForAccount(identity_manager(), account_info.account_id,
+                            "token");
+  maybe_account_info =
+      identity_manager()->FindExtendedAccountInfoByGaiaId(account_info.gaia);
   EXPECT_FALSE(maybe_account_info.IsEmpty());
-  EXPECT_EQ(foo_account_info.account_id, maybe_account_info.account_id);
-  EXPECT_EQ(foo_account_info.email, maybe_account_info.email);
-  EXPECT_EQ(foo_account_info.gaia, maybe_account_info.gaia);
+  EXPECT_EQ(account_info.account_id, maybe_account_info.account_id);
+  EXPECT_EQ(account_info.email, maybe_account_info.email);
+  EXPECT_EQ(account_info.gaia, maybe_account_info.gaia);
+}
+
+TEST_F(IdentityManagerTest, FindExtendedPrimaryAccountInfo) {
+  // AccountInfo found for primary account, even without token.
+  RemoveRefreshTokenForPrimaryAccount(identity_manager());
+  ASSERT_TRUE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));
+  ASSERT_FALSE(identity_manager()->HasPrimaryAccountWithRefreshToken(
+      ConsentLevel::kSignin));
+  AccountInfo extended_info =
+      identity_manager()->FindExtendedPrimaryAccountInfo(ConsentLevel::kSignin);
+  CoreAccountInfo core_info =
+      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kSignin);
+  EXPECT_FALSE(extended_info.IsEmpty());
+  EXPECT_EQ(core_info.account_id, extended_info.account_id);
+  EXPECT_EQ(core_info.email, extended_info.email);
+  EXPECT_EQ(core_info.gaia, extended_info.gaia);
+
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+  // It's not possible to sign out on Ash.
+  ClearPrimaryAccount(identity_manager());
+  SetRefreshTokenForAccount(identity_manager(), core_info.account_id, "token");
+  // No info found if there is no primary account.
+  ASSERT_FALSE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));
+  ASSERT_TRUE(
+      identity_manager()->HasAccountWithRefreshToken(core_info.account_id));
+  EXPECT_TRUE(identity_manager()
+                  ->FindExtendedPrimaryAccountInfo(ConsentLevel::kSignin)
+                  .IsEmpty());
+#endif
 }
 
 // Checks that FindExtendedAccountInfoForAccountWithRefreshTokenByAccountId()
diff --git a/components/strings/components_strings_am.xtb b/components/strings/components_strings_am.xtb
index f2fe202..7610104 100644
--- a/components/strings/components_strings_am.xtb
+++ b/components/strings/components_strings_am.xtb
@@ -1715,7 +1715,7 @@
 <translation id="7378594059915113390">የሚዲያ መቆጣጠሪያዎች</translation>
 <translation id="7378627244592794276">አይ</translation>
 <translation id="7378810950367401542">/</translation>
-<translation id="7386364858855961704">የለም</translation>
+<translation id="7386364858855961704">አይመለከተውም</translation>
 <translation id="7390545607259442187">ካርድ ያረጋግጡ</translation>
 <translation id="7392089738299859607">አድራሻን ያዘምኑ</translation>
 <translation id="7399802613464275309">የደህንነት ፍተሻ</translation>
diff --git a/components/strings/components_strings_be.xtb b/components/strings/components_strings_be.xtb
index d66e188..470fd36 100644
--- a/components/strings/components_strings_be.xtb
+++ b/components/strings/components_strings_be.xtb
@@ -918,7 +918,7 @@
 <translation id="4275830172053184480">Перазапусціце прыладу</translation>
 <translation id="4277028893293644418">Скінуць пароль</translation>
 <translation id="428639260510061158">{NUM_CARDS,plural, =1{Гэта картка захавана ў вашым Уліковым запісе Google}one{Гэтыя карткі захаваны ў вашым Уліковым запісе Google}few{Гэтыя карткі захаваны ў вашым Уліковым запісе Google}many{Гэтыя карткі захаваны ў вашым Уліковым запісе Google}other{Гэтыя карткі захаваны ў вашым Уліковым запісе Google}}</translation>
-<translation id="4287885627794386150">Навучанне FLoC можа выкарыстоўвацца на працягу пробнага перыяду, але яшчэ не было актывавана</translation>
+<translation id="4287885627794386150">Пробная версія функцыі можа выкарыстоўвацца, але яшчэ не актывавана</translation>
 <translation id="4297502707443874121">Мініяцюра старонкі <ph name="THUMBNAIL_PAGE" /></translation>
 <translation id="42981349822642051">Разгарнуць</translation>
 <translation id="4300675098767811073">Некалькі дзірак справа</translation>
@@ -1724,7 +1724,7 @@
 <translation id="7378594059915113390">Элементы кіравання мультымедыйным змесцівам</translation>
 <translation id="7378627244592794276">А вось i не!</translation>
 <translation id="7378810950367401542">/</translation>
-<translation id="7386364858855961704">Непрымяніма</translation>
+<translation id="7386364858855961704">Недастасоўна</translation>
 <translation id="7390545607259442187">Пацвердзіце картку</translation>
 <translation id="7392089738299859607">Абнавіць адрас</translation>
 <translation id="7399802613464275309">Праверка бяспекі</translation>
diff --git a/components/strings/components_strings_gu.xtb b/components/strings/components_strings_gu.xtb
index 1e68b028..d6cb1c70 100644
--- a/components/strings/components_strings_gu.xtb
+++ b/components/strings/components_strings_gu.xtb
@@ -734,6 +734,7 @@
 <translation id="3631244953324577188">બાયોમેટ્રિક્સ</translation>
 <translation id="3633738897356909127">Chrome અપડેટ કરો બટન, તમારા Chrome સેટિંગમાંથી Chrome અપડેટ કરવા માટે Enter દબાવો</translation>
 <translation id="3634530185120165534">ટ્રે 5</translation>
+<translation id="3637662659967048211">Google એકાઉન્ટમાં સાચવો</translation>
 <translation id="3640766068866876100">અનુક્રમણિકા-4x6-Ext</translation>
 <translation id="3642638418806704195">ઍપ્લિકેશન:</translation>
 <translation id="3650584904733503804">માન્યતા સફળ</translation>
@@ -1411,6 +1412,7 @@
 <translation id="6106989379647458772"><ph name="PAGE" /> પરનું વેબપેજ થોડી વાર માટે બંધ હોઈ શકે છે અથવા તે કાયમ માટે નવા વેબ ઍડ્રેસ પર ખસેડવામાં આવ્યું હોઈ શકે છે.</translation>
 <translation id="6107012941649240045">આને રજૂ કરેલું</translation>
 <translation id="610911394827799129">તમારા Google એકાઉન્ટમાં <ph name="BEGIN_LINK" />myactivity.google.com<ph name="END_LINK" /> પર બ્રાઉઝિંગ ઇતિહાસના બીજા સ્વરૂપો હોય શકે છે</translation>
+<translation id="6113594885686374546">યાત્રા બટન ફરી શરૂ કરો, તેમજ તમારી યાત્રા ફરી શરૂ કરવા અને તમારા Chrome ઇતિહાસમાં સંબંધિત પ્રવૃત્તિ જોવા માટે Enter કી દબાવો</translation>
 <translation id="6116338172782435947">ક્લિપબોર્ડ પર કૉપિ કરેલ ટેક્સ્ટ અને છબીઓને જુઓ</translation>
 <translation id="6120179357481664955">તમારું UPI ID યાદ રાખીએ?</translation>
 <translation id="6123290840358279103">વર્ચ્યુઅલ કાર્ડ જુઓ</translation>
@@ -1486,6 +1488,7 @@
 <translation id="6423385022588644828">હવેથી Touch IDનો ઉપયોગ કરીને તમારા કાર્ડ વધુ ઝડપથી કન્ફર્મ કરો</translation>
 <translation id="6425092077175753609">સામગ્રી</translation>
 <translation id="6427730057873428458">ગેટ ફોલ્ડ</translation>
+<translation id="6428146287756735566">તમારા Chrome ઇતિહાસમાં સંબંધિત પ્રવૃત્તિ જોવા માટે, યાત્રા ફરી શરૂ કરો</translation>
 <translation id="6428450836711225518">તમારો ફોન નંબર ચકાસો</translation>
 <translation id="6433490469411711332">સંપર્ક માહિતીમાં ફેરફાર કરો</translation>
 <translation id="6433595998831338502"><ph name="HOST_NAME" /> એ કનેક્ટ કરવાનો ઇનકાર કર્યો.</translation>
@@ -1537,6 +1540,7 @@
 <translation id="6643016212128521049">સાફ કરો</translation>
 <translation id="6645291930348198241">કુકી અને સાઇટ ડેટાનો ઍક્સેસ કરો.</translation>
 <translation id="6646269444027925224">{COUNT,plural, =0{કોઈ નહિ}=1{From 1 site (you won't be signed out of your Google Account)}one{From # sites (you won't be signed out of your Google Account)}other{From # sites (you won't be signed out of your Google Account)}}</translation>
+<translation id="6647197322759179819">યાત્રા ફરી શરૂ કરો</translation>
 <translation id="6648459603387803038">તમારા વ્યવસ્થાપક તમારા બ્રાઉઝર સેટઅપને રિમોટલી બદલી શકે છે. આ ડિવાઇસ પરની પ્રવૃત્તિ Chromeની બહારથી પણ મેનેજ કરી શકાય છે.</translation>
 <translation id="6648524591329069940">Serif ફૉન્ટ</translation>
 <translation id="6651270836885078973">આમના દ્વારા મેનેજ કરવામાં આવે છે:</translation>
@@ -2188,6 +2192,7 @@
 <translation id="9114524666733003316">કાર્ડ કન્ફર્મ કરી રહ્યાં છીએ...</translation>
 <translation id="9114581008513152754">આ બ્રાઉઝર કંપની દ્વારા અથવા અન્ય સંસ્થા દ્વારા મેનેજ કરવામાં આવતું નથી. આ ડિવાઇસ પરની પ્રવૃત્તિ Chromeની બહાર મેનેજ કરી શકાય છે. <ph name="BEGIN_LINK" />વધુ જાણો<ph name="END_LINK" /></translation>
 <translation id="9117930699067497412">નવીન</translation>
+<translation id="9118692854637641831"><ph name="HISTORY_CLUSTERS_SEARCH_FOCUSED_FRIENDLY_MATCH_TEXT" />, Tab કી દબાવો તેમજ તમારી યાત્રા ફરી શરૂ કરવા અને તમારા Chrome ઇતિહાસમાં સંબંધિત પ્રવૃત્તિ જોવા માટે Enter કી દબાવો</translation>
 <translation id="9119042192571987207">અપલોડ કર્યો</translation>
 <translation id="9128016270925453879">પૉલિસી લોડ કરવામાં આવી છે</translation>
 <translation id="9128870381267983090">નેટવર્કથી કનેક્ટ કરો</translation>
diff --git a/components/strings/components_strings_mr.xtb b/components/strings/components_strings_mr.xtb
index e02504f1..6b931b13 100644
--- a/components/strings/components_strings_mr.xtb
+++ b/components/strings/components_strings_mr.xtb
@@ -260,7 +260,7 @@
 <translation id="1800473098294731951">B9</translation>
 <translation id="1803264062614276815">कार्डधारकाचे नाव</translation>
 <translation id="1807246157184219062">फिकट</translation>
-<translation id="1807528111851433570">पत्रक सुरू करा</translation>
+<translation id="1807528111851433570">शीट सुरू करा</translation>
 <translation id="1812527064848182527">लॅंडस्केप</translation>
 <translation id="1814698615734239189">हे ॲप मोबाइल डिव्हाइससाठी डिझाइन केलेले आहे. ते कंपॅटिबिलिटी मोडमध्ये रन होत आहे. आकार बदलण्याला अनुमती दिल्यामुळे ॲप पुन्हा सुरू होण्याच्या समावेशासह समस्या येऊ शकतात.</translation>
 <translation id="1821930232296380041">अवैध विनंती किंवा विनंती मापदंड</translation>
@@ -636,7 +636,7 @@
 <translation id="3329013043687509092">संपृक्तता</translation>
 <translation id="3338095232262050444">सुरक्षित</translation>
 <translation id="3355823806454867987">प्रॉक्सी सेटिंग्ज बदला...</translation>
-<translation id="3360103848165129075">पेमेंट हँडलर पत्रक</translation>
+<translation id="3360103848165129075">पेमेंट हँडलर शीट</translation>
 <translation id="3361596688432910856">Chrome पुढील माहिती <ph name="BEGIN_EMPHASIS" />सेव्ह करणार नाही<ph name="END_EMPHASIS" />:
         <ph name="BEGIN_LIST" />
           <ph name="LIST_ITEM" />तुमचा ब्राउझिंग इतिहास
@@ -813,7 +813,7 @@
 <translation id="3949601375789751990">तुमचा ब्राउझिंग इतिहास येथे दिसतो</translation>
 <translation id="3949870428812919180">सेव्ह केलेल्या पेमेंट पद्धती नाहीत</translation>
 <translation id="3950820424414687140">साइन इन करा</translation>
-<translation id="3961148744525529027">पेमेंट हँडलर पत्रक अर्धे उघडलेले आहे</translation>
+<translation id="3961148744525529027">पेमेंट हँडलर शीट अर्धे उघडलेले आहे</translation>
 <translation id="3962859241508114581">मागील ट्रॅक</translation>
 <translation id="3963721102035795474">वाचक मोड</translation>
 <translation id="3963837677003247395">मॅन्‍युअली सुरू ठेवायचे आहे का?</translation>
@@ -1683,7 +1683,7 @@
 <translation id="7220786058474068424">प्रक्रिया करत आहे</translation>
 <translation id="7221855153210829124">सूचना दर्शवा</translation>
 <translation id="7229659723041939809">तुम्ही आताच एका फसव्या साइटवर तुमचा पासवर्ड एंटर केला. तुम्ही हा पासवर्ड आता जेथे वापरता अशा <ph name="WEBSITE_1" />, <ph name="WEBSITE_2" />, <ph name="WEBSITE_3" /> आणि इतर साइटसाठी तुमचे सेव्ह केलेले पासवर्ड तपासण्याची Chrome शिफारस करते.</translation>
-<translation id="7233592378249864828">खात्री केलेले पत्रक प्रिंट करा</translation>
+<translation id="7233592378249864828">खात्री केलेले शीट प्रिंट करा</translation>
 <translation id="7238585580608191973">SHA-256 बोटाचा ठसा</translation>
 <translation id="7240120331469437312">सर्टिफिकेट विषय वैकल्पिक नाव</translation>
 <translation id="7243010569062352439"><ph name="PASSWORDS" />; <ph name="SIGNIN_DATA" /></translation>
@@ -1798,7 +1798,7 @@
 <translation id="7613889955535752492">कालबाह्य: <ph name="EXPIRATION_MONTH" />/<ph name="EXPIRATION_YEAR" /></translation>
 <translation id="7614494068621678628"><ph name="MANAGE_PASSWORDS_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome सेटिंग्जमध्ये तुमचे पासवर्ड पाहाण्यासाठी आणि व्यवस्थापित करण्यासाठी टॅब आणि त्यानंतर एंटर दाबा</translation>
 <translation id="7616645509853975347">तुमच्या अ‍ॅडमिनिस्ट्रेटरने तुमच्या ब्राउझरवर Chrome एंटरप्राइझ कनेक्टर्स सुरू केली आहेत. या कनेक्टरना तुमच्या काही डेटाचा अ‍ॅक्सेस आहे.</translation>
-<translation id="7619838219691048931">शेवटचे पत्रक</translation>
+<translation id="7619838219691048931">शेवटचे शीट</translation>
 <translation id="762844065391966283">एका वेळी एक</translation>
 <translation id="7633909222644580952">परफॉर्मंस डेटा आणि क्रॅश अहवाल</translation>
 <translation id="7637571805876720304">Chromium वरून क्रेडिट कार्ड काढून टाकायचे?</translation>
@@ -1807,7 +1807,7 @@
 <translation id="7639968568612851608">गडद राखाडी</translation>
 <translation id="7647206758853451655">प्रिंटची गुणवत्ता</translation>
 <translation id="7648992873808071793">या डिव्हाइसवर फाइल संचयित करा</translation>
-<translation id="7653957176542370971">पेमेंट हँडलर पत्रक बंद केलेले आहे</translation>
+<translation id="7653957176542370971">पेमेंट हँडलर शीट बंद केलेले आहे</translation>
 <translation id="7654909834015434372">तुम्ही भाष्ये संपादित करता तेव्हा हा दस्तऐवज त्याच्या मूळ रोटेशनवर परत येईल</translation>
 <translation id="765676359832457558">प्रगत सेटिंग्ज लपवा...</translation>
 <translation id="7658239707568436148">रद्द करा</translation>
@@ -1977,7 +1977,7 @@
 <translation id="8267698848189296333"><ph name="USERNAME" /> म्हणून साइन इन करीत आहे</translation>
 <translation id="8269242089528251720">वेगळे दस्तऐवज/संकलित केलेल्या प्रती</translation>
 <translation id="8275952078857499577">या साइटचे भाषांतर करणे ऑफर करू नका</translation>
-<translation id="8277900682056760511">पेमेंट हँडलर पत्रक उघडलेले आहे</translation>
+<translation id="8277900682056760511">पेमेंट हँडलर शीट उघडलेले आहे</translation>
 <translation id="8281084378435768645">Large-Photo</translation>
 <translation id="8282947398454257691">तुमचा अनन्य डिव्हाइस अभिज्ञापक जाणून घ्या</translation>
 <translation id="8284769179630993263">Chrome सेटिंग्जमध्ये तुमचे सुरक्षित ब्राउझिंग आणि आणखी बरेच काही व्यवस्थापित करा</translation>
@@ -2211,7 +2211,7 @@
 <translation id="9169931577761441333"><ph name="APP_NAME" /> ला होम स्क्रीनवर जोडा</translation>
 <translation id="9170848237812810038">&amp;पूर्ववत करा</translation>
 <translation id="9171296965991013597">ॲप सोडायचे?</translation>
-<translation id="9173282814238175921">एक दस्तऐवज/नवीन पत्रक</translation>
+<translation id="9173282814238175921">एक दस्तऐवज/नवीन शीट</translation>
 <translation id="9173995187295789444">ब्लूटूथ डिव्हाइससाठी स्कॅन करत आहे...</translation>
 <translation id="917450738466192189">सर्व्हरचे सर्टिफिकेट चुकीचे आहे.</translation>
 <translation id="9174917557437862841">टॅब स्विच बटण, या टॅबवर जाण्यासाठी एंटर दाबा</translation>
diff --git a/components/strings/components_strings_ne.xtb b/components/strings/components_strings_ne.xtb
index 9d6bc1f..73fbe1cd 100644
--- a/components/strings/components_strings_ne.xtb
+++ b/components/strings/components_strings_ne.xtb
@@ -729,6 +729,7 @@
 <translation id="3631244953324577188">बायोमेट्रिक्स</translation>
 <translation id="3633738897356909127">'Chrome अपडेट गर्नुहोस्' नामक बटन, आफ्नो Chrome का सेटिङमा गई Chrome अपडेट गर्न Enter थिच्नुहोस्</translation>
 <translation id="3634530185120165534">ट्रे ५</translation>
+<translation id="3637662659967048211">Google खातामा सेभ गर्नुहोस्</translation>
 <translation id="3640766068866876100">Index-4x6-Ext</translation>
 <translation id="3642638418806704195">एप:</translation>
 <translation id="3650584904733503804">प्रमाणीकरण सफल भयो</translation>
@@ -1403,6 +1404,7 @@
 <translation id="6106989379647458772"><ph name="PAGE" /> को वेबपृष्ठ अस्थायी रूपमा बन्द भएको हुन सक्छ वा त्यो स्थायी रूपमा एउटा नयाँ वेब ठेगानामा सरेको हुन सक्छ।</translation>
 <translation id="6107012941649240045">लाई जारी गरिएको</translation>
 <translation id="610911394827799129">तपाईंको Google खाताको <ph name="BEGIN_LINK" />myactivity.google.com<ph name="END_LINK" /> मा ब्राउजिङ इतिहासका अन्य ढाँचाहरू रहेका हुनसक्छन्।</translation>
+<translation id="6113594885686374546">'खोज्ने क्रम सुचारु गर्नुहोस्' बटन, Chrome को ब्राउजिङ इतिहासमा सान्दर्भिक गतिविधि हेर्न खोज्ने क्रम सुचारु गर्न Enter थिच्नुहोस्</translation>
 <translation id="6116338172782435947">क्लिपबोर्डमा प्रतिलिपि गरिएका पाठ र छविहरू हेर्नुहोस्</translation>
 <translation id="6120179357481664955">आफ्नो UPI ID सम्झने हो?</translation>
 <translation id="6124432979022149706">Chrome Enterprise का कनेक्टरहरू</translation>
@@ -1477,6 +1479,7 @@
 <translation id="6423385022588644828">अब उप्रान्त Touch ID प्रयोग गरेर आफ्ना कार्डहरू अझ छिटो पुष्टि गर्नुहोस्</translation>
 <translation id="6425092077175753609">वस्तु</translation>
 <translation id="6427730057873428458">गेट फोल्ड</translation>
+<translation id="6428146287756735566">Chrome को ब्राउजिङ इतिहासमा सान्दर्भिक गतिविधि हेर्न खोज्ने क्रम सुचारु गर्नुहोस्</translation>
 <translation id="6428450836711225518">आफ्नो फोन नम्बरको पुष्टि गर्नुहोस्</translation>
 <translation id="6433490469411711332">सम्पर्क सम्बन्धी जानकारीलाई सम्पादन गर्नुहोस्</translation>
 <translation id="6433595998831338502"><ph name="HOST_NAME" /> ले जडान गर्न अस्वीकार गर्यो।</translation>
@@ -1528,6 +1531,7 @@
 <translation id="6643016212128521049">खालि गर्नुहोस्</translation>
 <translation id="6645291930348198241">कुकीहरू र साइटको डेटा प्रयोग गर्न।</translation>
 <translation id="6646269444027925224">{COUNT,plural, =0{कुनै पनि होइन}=1{१ साइटबाट (तपाईं आफ्नो Google खाताबाट साइन आउट हुनु हुने छैन)}other{# साइटहरूबाट (तपाईं आफ्नो Google खाताबाट साइन आउट हुनु हुने छैन)}}</translation>
+<translation id="6647197322759179819">खोज्ने क्रम सुचारु गर्नुहोस्</translation>
 <translation id="6648459603387803038">तपाईंका एड्मिन टाढैबाट तपाईंको ब्राउजरको सेटअप बदल्न सक्नुहुन्छ। यो डिभाइसका क्रियाकलाप Chrome बाहिरबाट पनि व्यवस्थापन गरिन सक्छ।</translation>
 <translation id="6648524591329069940">Serif फन्ट</translation>
 <translation id="6651270836885078973">व्यवस्थापक:</translation>
@@ -2175,6 +2179,7 @@
 <translation id="9114524666733003316">कार्डको पुष्टि गर्दै...</translation>
 <translation id="9114581008513152754">कुनै कम्पनी वा अन्य सङ्गठनले यो ब्राउजर व्यवस्थापन गर्दैन। यो डिभाइसका क्रियाकलाप Chrome बाहिरबाट व्यवस्थापन गर्न सकिन्छ। <ph name="BEGIN_LINK" />थप जान्नुहोस्<ph name="END_LINK" /></translation>
 <translation id="9117930699067497412">ताजा</translation>
+<translation id="9118692854637641831"><ph name="HISTORY_CLUSTERS_SEARCH_FOCUSED_FRIENDLY_MATCH_TEXT" />, Chrome को ब्राउजिङ इतिहासमा सान्दर्भिक गतिविधि हेर्न खोज्ने क्रम सुचारु गर्न Tab थिच्नुहोस् त्यसपछि Enter थिच्नुहोस्</translation>
 <translation id="9119042192571987207">अपलोड गरिएको</translation>
 <translation id="9128016270925453879">नीतिहरू लोड गरिएका छन्</translation>
 <translation id="9128870381267983090">नेटवर्कमा कनेक्ट गर्नुहोस्</translation>
diff --git a/components/sync/driver/resources/BUILD.gn b/components/sync/driver/resources/BUILD.gn
index 8017305..c8e7dac5 100644
--- a/components/sync/driver/resources/BUILD.gn
+++ b/components/sync/driver/resources/BUILD.gn
@@ -101,7 +101,7 @@
 js_library("search") {
   deps = [
     "//ui/webui/resources/js:util.m",
-    "//ui/webui/resources/js/cr/ui:splitter.m",
+    "//ui/webui/resources/js/cr/ui:splitter",
   ]
 }
 
diff --git a/components/sync/driver/resources/search.js b/components/sync/driver/resources/search.js
index 9d6a76e..47f4127 100644
--- a/components/sync/driver/resources/search.js
+++ b/components/sync/driver/resources/search.js
@@ -5,7 +5,7 @@
 import {decorate} from 'chrome://resources/js/cr/ui.m.js';
 import {ArrayDataModel} from 'chrome://resources/js/cr/ui/array_data_model.m.js';
 import {List} from 'chrome://resources/js/cr/ui/list.m.js';
-import {Splitter} from 'chrome://resources/js/cr/ui/splitter.m.js';
+import {Splitter} from 'chrome://resources/js/cr/ui/splitter.js';
 import {$, getRequiredElement} from 'chrome://resources/js/util.m.js';
 
 import {decorateQuickQueryControls, decorateSearchControls} from './sync_search.js';
diff --git a/components/sync/driver/resources/sync_node_browser.js b/components/sync/driver/resources/sync_node_browser.js
index b33d23da..6fd762d 100644
--- a/components/sync/driver/resources/sync_node_browser.js
+++ b/components/sync/driver/resources/sync_node_browser.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {decorate, define as crUiDefine} from 'chrome://resources/js/cr/ui.m.js';
-import {Splitter} from 'chrome://resources/js/cr/ui/splitter.m.js';
+import {Splitter} from 'chrome://resources/js/cr/ui/splitter.js';
 import {Tree, TreeItem} from 'chrome://resources/js/cr/ui/tree.m.js';
 import {$} from 'chrome://resources/js/util.m.js';
 
diff --git a/components/viz/service/display/surface_aggregator_perftest.cc b/components/viz/service/display/surface_aggregator_perftest.cc
index 5322d27c..87882aa 100644
--- a/components/viz/service/display/surface_aggregator_perftest.cc
+++ b/components/viz/service/display/surface_aggregator_perftest.cc
@@ -2,11 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <unordered_map>
+
 #include "cc/test/fake_output_surface_client.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/quads/compositor_frame.h"
+#include "components/viz/common/quads/render_pass_io.h"
 #include "components/viz/common/quads/surface_draw_quad.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
+#include "components/viz/common/resources/transferable_resource.h"
+#include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/local_surface_id.h"
 #include "components/viz/service/display/aggregated_frame.h"
 #include "components/viz/service/display/display_resource_provider_software.h"
@@ -193,6 +198,7 @@
   }
 
   void SetUpRenderPassListResources(
+      FrameSinkId frame_sink_id,
       CompositorRenderPassList* render_pass_list) {
     std::set<ResourceId> created_resources;
     for (auto& render_pass : *render_pass_list) {
@@ -202,15 +208,36 @@
           if (created_resources.find(resource_id) != created_resources.end()) {
             continue;
           }
-          resource_list_.push_back(TransferableResource::MakeSoftware(
-              SharedBitmap::GenerateId(), quad->rect.size(), RGBA_8888));
-          resource_list_.back().id = resource_id;
+          resource_list_map_[frame_sink_id].push_back(
+              TransferableResource::MakeSoftware(SharedBitmap::GenerateId(),
+                                                 quad->rect.size(), RGBA_8888));
+          resource_list_map_[frame_sink_id].back().id = resource_id;
           created_resources.insert(resource_id);
         }
       }
     }
   }
 
+  void SubmitCompositorFrame(CompositorFrameSinkSupport* frame_sink,
+                             SurfaceId surface_id,
+                             const CompositorRenderPassList& render_pass_list,
+                             std::vector<SurfaceRange> referenced_surfaces) {
+    CompositorRenderPassList local_list;
+    CompositorRenderPass::CopyAllForTest(render_pass_list, &local_list);
+    // Ensure damage encompasses the entire output_rect so everything is
+    // aggregated.
+    auto& last_render_pass = *local_list.back();
+    last_render_pass.damage_rect = last_render_pass.output_rect;
+
+    CompositorFrameBuilder frame_builder;
+    frame_builder.SetRenderPassList(std::move(local_list))
+        .SetTransferableResources(
+            resource_list_map_[surface_id.frame_sink_id()])
+        .SetReferencedSurfaces(std::move(referenced_surfaces));
+    frame_sink->SubmitCompositorFrame(surface_id.local_surface_id(),
+                                      frame_builder.Build());
+  }
+
   void RunSingleSurfaceRenderPassListFromJson(const std::string& tag,
                                               const std::string& site,
                                               uint32_t year,
@@ -219,13 +246,14 @@
     ASSERT_TRUE(CompositorRenderPassListFromJSON(tag, site, year, index,
                                                  &render_pass_list));
     ASSERT_FALSE(render_pass_list.empty());
-    this->SetUpRenderPassListResources(&render_pass_list);
+
+    constexpr FrameSinkId root_frame_sink_id(1, 1);
+    TestSurfaceIdAllocator root_surface_id(root_frame_sink_id);
+    this->SetUpRenderPassListResources(root_frame_sink_id, &render_pass_list);
 
     aggregator_ = std::make_unique<SurfaceAggregator>(
         manager_.surface_manager(), resource_provider_.get(), true, true);
 
-    constexpr FrameSinkId root_frame_sink_id(1, 1);
-    TestSurfaceIdAllocator root_surface_id(root_frame_sink_id);
     auto root_support = std::make_unique<CompositorFrameSinkSupport>(
         nullptr, &manager_, root_frame_sink_id, /*is_root=*/true);
 
@@ -234,19 +262,9 @@
 
     timer_.Reset();
     do {
-      CompositorRenderPassList local_list;
-      CompositorRenderPass::CopyAllForTest(render_pass_list, &local_list);
-      // Ensure damage encompasses the entire output_rect so everything is
-      // aggregated.
-      auto& last_render_pass = *local_list.back();
-      last_render_pass.damage_rect = last_render_pass.output_rect;
-
-      CompositorFrame frame = CompositorFrameBuilder()
-                                  .SetRenderPassList(std::move(local_list))
-                                  .SetTransferableResources(resource_list_)
-                                  .Build();
-      root_support->SubmitCompositorFrame(root_surface_id.local_surface_id(),
-                                          std::move(frame));
+      SubmitCompositorFrame(root_support.get(), root_surface_id,
+                            render_pass_list,
+                            /*referenced_surfaces=*/{});
       auto aggregated = aggregator_->Aggregate(
           root_surface_id, next_fake_display_time, gfx::OVERLAY_TRANSFORM_NONE);
 
@@ -258,12 +276,77 @@
     reporter.AddResult(kMetricSpeedRunsPerS, timer_.LapsPerSecond());
   }
 
+  void RunMultiSurfacePerfTestFromJson(const std::string& name, size_t index) {
+    std::vector<FrameData> frame_data_list;
+    ASSERT_TRUE(
+        FrameDataFromJson("multi_surface_test", name, index, &frame_data_list));
+    ASSERT_FALSE(frame_data_list.empty());
+
+    const int num_surfaces = frame_data_list.size();
+    std::vector<std::unique_ptr<CompositorFrameSinkSupport>> frame_sinks(
+        num_surfaces);
+    for (int i = 0; i < num_surfaces; i++) {
+      auto frame_sink_id = frame_data_list[i].surface_id.frame_sink_id();
+      // The first surface represents the root frame sink.
+      frame_sinks[i] = std::make_unique<CompositorFrameSinkSupport>(
+          nullptr, &manager_, frame_sink_id,
+          /*is_root=*/i == 0);
+      auto& render_pass_list =
+          frame_data_list[i].compositor_frame.render_pass_list;
+      this->SetUpRenderPassListResources(frame_sink_id, &render_pass_list);
+    }
+    // We only need to submit the non-root surfaces (i = [1, N-1]) once, but
+    // we'll submit the root surface every lap.
+    // Loop in reverse order so surfaces are always submitted before their
+    // parents are.
+    for (int i = frame_data_list.size() - 1; i >= 1; --i) {
+      auto surface_id = frame_data_list[i].surface_id;
+      auto& render_pass_list =
+          frame_data_list[i].compositor_frame.render_pass_list;
+      auto& referenced_surfaces =
+          frame_data_list[i].compositor_frame.metadata.referenced_surfaces;
+      SubmitCompositorFrame(frame_sinks[i].get(), surface_id, render_pass_list,
+                            referenced_surfaces);
+    }
+
+    aggregator_ = std::make_unique<SurfaceAggregator>(
+        manager_.surface_manager(), resource_provider_.get(), true, true);
+
+    base::TimeTicks next_fake_display_time =
+        base::TimeTicks() + base::TimeDelta::FromSeconds(1);
+    timer_.Reset();
+    do {
+      // For a single frame test we only need to submit a CompositorFrame for
+      // the root surface.
+      const auto& root_surface_data = frame_data_list[0];
+      auto surface_id = root_surface_data.surface_id;
+      auto& render_pass_list =
+          root_surface_data.compositor_frame.render_pass_list;
+      auto& referenced_surfaces =
+          root_surface_data.compositor_frame.metadata.referenced_surfaces;
+      SubmitCompositorFrame(frame_sinks[0].get(), surface_id, render_pass_list,
+                            referenced_surfaces);
+
+      auto aggregated = aggregator_->Aggregate(root_surface_data.surface_id,
+                                               next_fake_display_time,
+                                               gfx::OVERLAY_TRANSFORM_NONE);
+
+      next_fake_display_time += BeginFrameArgs::DefaultInterval();
+      timer_.NextLap();
+    } while (!timer_.HasTimeLimitExpired());
+
+    auto reporter =
+        SetUpSurfaceAggregatorReporter(name + "_multi_surface_json");
+    reporter.AddResult(kMetricSpeedRunsPerS, timer_.LapsPerSecond());
+  }
+
  protected:
   ServerSharedBitmapManager shared_bitmap_manager_;
   FrameSinkManagerImpl manager_;
   std::unique_ptr<DisplayResourceProvider> resource_provider_;
   std::unique_ptr<SurfaceAggregator> aggregator_;
-  std::vector<TransferableResource> resource_list_;
+  // List of resources for each client_id.
+  std::map<FrameSinkId, std::vector<TransferableResource>> resource_list_map_;
 };
 
 TEST_F(SurfaceAggregatorPerfTest, ManySurfacesOpaque) {
@@ -348,6 +431,14 @@
 TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(yahoo_answers, 74)
 TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST(yahoo_sports, 269)
 
+#define MULTI_SURFACE_PERF_TEST(NAME, FRAME)                      \
+  TEST_F(SurfaceAggregatorPerfTest, NAME##_MultiSurfaceTest) {    \
+    this->RunMultiSurfacePerfTestFromJson(/*name=*/#NAME,         \
+                                          /*frame_index=*/FRAME); \
+  }
+
+MULTI_SURFACE_PERF_TEST(youtube, 358)
+
 #undef TOP_REAL_WORLD_DESKTOP_RENDERER_PERF_TEST
 
 }  // namespace
diff --git a/components/viz/service/display/viz_perf_test.cc b/components/viz/service/display/viz_perf_test.cc
index 0b863b5c..1e71a8b 100644
--- a/components/viz/service/display/viz_perf_test.cc
+++ b/components/viz/service/display/viz_perf_test.cc
@@ -14,35 +14,68 @@
 
 namespace viz {
 
+namespace {
+
+// Creates a path to the JSON file where `group` and `name` are parent folder
+// names, and `frame_index` padded to 4 digits will be the filename. e.g.
+// .../render_pass_data/group/name/0001.json
+absl::optional<base::FilePath> MakeJsonPath(const std::string& group,
+                                            const std::string& name,
+                                            size_t frame_index) {
+  base::FilePath json_path;
+  if (!base::PathService::Get(Paths::DIR_TEST_DATA, &json_path))
+    return absl::nullopt;
+  std::string filename = base::NumberToString(frame_index);
+  while (filename.length() < 4)
+    filename = "0" + filename;
+  filename += ".json";
+  return json_path.Append(FILE_PATH_LITERAL("render_pass_data"))
+      .AppendASCII(group)
+      .AppendASCII(name)
+      .AppendASCII(filename);
+}
+
+absl::optional<base::Value> ReadValueFromJson(const std::string& group,
+                                              const std::string& name,
+                                              size_t frame_index) {
+  auto json_path = MakeJsonPath(group, name, frame_index);
+  if (!json_path)
+    return absl::nullopt;
+  if (!base::PathExists(*json_path))
+    return absl::nullopt;
+  std::string json_text;
+  if (!base::ReadFileToString(*json_path, &json_text))
+    return absl::nullopt;
+  return base::JSONReader::Read(json_text);
+}
+
+}  // namespace
+
 bool CompositorRenderPassListFromJSON(
     const std::string& tag,
     const std::string& site,
     uint32_t year,
     size_t frame_index,
     CompositorRenderPassList* render_pass_list) {
-  base::FilePath json_path;
-  if (!base::PathService::Get(Paths::DIR_TEST_DATA, &json_path))
+  std::string name = site + "_" + base::NumberToString(year);
+  auto dict = ReadValueFromJson(tag, name, frame_index);
+  if (!dict) {
     return false;
-  std::string site_year = site + "_" + base::NumberToString(year);
-  std::string filename = base::NumberToString(frame_index);
-  while (filename.length() < 4)
-    filename = "0" + filename;
-  filename += ".json";
-  json_path = json_path.Append(FILE_PATH_LITERAL("render_pass_data"))
-                  .AppendASCII(tag)
-                  .AppendASCII(site_year)
-                  .AppendASCII(filename);
-  if (!base::PathExists(json_path))
-    return false;
-  std::string json_text;
-  if (!base::ReadFileToString(json_path, &json_text))
-    return false;
-  absl::optional<base::Value> dict = base::JSONReader::Read(json_text);
-  if (!dict.has_value())
-    return false;
+  }
   return CompositorRenderPassListFromDict(dict.value(), render_pass_list);
 }
 
+bool FrameDataFromJson(const std::string& group,
+                       const std::string& name,
+                       size_t frame_index,
+                       std::vector<FrameData>* frame_data_list) {
+  auto list = ReadValueFromJson(group, name, frame_index);
+  if (!list) {
+    return false;
+  }
+  return FrameDataFromList(list.value(), frame_data_list);
+}
+
 namespace {
 
 constexpr char kPerfTestTimeMillis[] = "perf-test-time-ms";
diff --git a/components/viz/service/display/viz_perf_test.h b/components/viz/service/display/viz_perf_test.h
index 8ef6093e..a71bffb3 100644
--- a/components/viz/service/display/viz_perf_test.h
+++ b/components/viz/service/display/viz_perf_test.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_VIZ_SERVICE_DISPLAY_VIZ_PERF_TEST_H_
 
 #include <string>
+#include <vector>
 
 #include "base/timer/lap_timer.h"
 #include "components/viz/common/quads/compositor_render_pass.h"
@@ -13,6 +14,8 @@
 
 namespace viz {
 
+struct FrameData;
+
 // Reads the specified JSON file and parses a CompositorRenderPassList from it,
 // storing the result in |render_pass_list|.
 bool CompositorRenderPassListFromJSON(
@@ -22,7 +25,15 @@
     size_t frame_index,
     CompositorRenderPassList* render_pass_list);
 
-// Viz perf test base class that sets up a lap timer with a specified duration.
+// Reads the specified JSON file and stores the compositor frame data in the
+// output parameter `frame_data_list`.
+bool FrameDataFromJson(const std::string& group,
+                       const std::string& name,
+                       size_t frame_index,
+                       std::vector<FrameData>* frame_data_list);
+
+// Viz perf test base class that sets up a lap timer with a specified
+// duration.
 class VizPerfTest : public testing::Test {
  public:
   VizPerfTest();
diff --git a/components/viz/test/data/mask_with_effect_different_size.png b/components/viz/test/data/mask_with_effect_different_size.png
index 82917eb..2a8c50dd 100644
--- a/components/viz/test/data/mask_with_effect_different_size.png
+++ b/components/viz/test/data/mask_with_effect_different_size.png
Binary files differ
diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h
index e09a4178..de99672d 100644
--- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h
+++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h
@@ -192,9 +192,6 @@
   // orientation.
   content::MouseWheelRailsFilterMac _mouseWheelFilter;
 
-  // Whether the direct manipulation feature is enabled.
-  bool _direct_manipulation_enabled;
-
   // Whether the pen's tip is in contact with the stylus digital tablet.
   bool _has_pen_contact;
 
diff --git a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
index e92152bd..3ff49bb 100644
--- a/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
+++ b/content/app_shim_remote_cocoa/render_widget_host_view_cocoa.mm
@@ -228,8 +228,6 @@
     _isStylusEnteringProximity = false;
     _keyboardLockActive = false;
     _textInputType = ui::TEXT_INPUT_TYPE_NONE;
-    _direct_manipulation_enabled =
-        base::FeatureList::IsEnabled(features::kDirectManipulationStylus);
     _has_pen_contact = false;
   }
   return self;
@@ -763,77 +761,49 @@
   if (type == NSMouseMoved)
     _cursorHidden = NO;
 
-  bool send_touch =
-      _direct_manipulation_enabled &&
-      _pointerType == blink::WebPointerProperties::PointerType::kPen;
+  bool unaccelerated_movement =
+      _mouse_locked && _mouse_lock_unaccelerated_movement;
+  WebMouseEvent event = WebMouseEventBuilder::Build(
+      theEvent, self, _pointerType, unaccelerated_movement);
 
-  // Send touch events when the pen is in contact with the tablet.
-  if (send_touch) {
-    // Because the NSLeftMouseUp event's buttonMask is not
-    // NSEventButtonMaskPenTip, we read |has_pen_contact_| to ensure a
-    // TouchRelease is sent appropriately at the end when the stylus is
-    // no longer in contact with the digitizer.
-    send_touch = _has_pen_contact;
-    if (type == NSLeftMouseDown || type == NSLeftMouseUp ||
-        type == NSLeftMouseDragged) {
-      NSEventButtonMask buttonMask = [theEvent buttonMask];
-      if (buttonMask == NSEventButtonMaskPenTip) {
-        DCHECK(type != NSLeftMouseUp);
-        send_touch = _has_pen_contact = true;
-      } else {
-        _has_pen_contact = false;
-      }
+  if (_mouse_locked &&
+      base::FeatureList::IsEnabled(features::kConsolidatedMovementXY)) {
+    // When mouse is locked, we keep increasing |last_mouse_screen_position|
+    // by movement_x/y so that we can still use PositionInScreen to calculate
+    // movements in blink. We need to keep |last_mouse_screen_position_| from
+    // getting too large because it will lose some precision. So whenever it
+    // exceed the |wrap_around_distance|, we start again from the current
+    // mouse position (locked position), and also send a synthesized event to
+    // update the blink-side status.
+    if (std::abs(_last_mouse_screen_position.x()) > wrap_around_distance ||
+        std::abs(_last_mouse_screen_position.y()) > wrap_around_distance) {
+      NSWindow* window = [self window];
+      NSPoint location = [window mouseLocationOutsideOfEventStream];
+      int window_number = window ? [window windowNumber] : -1;
+      NSEvent* nsevent = [NSEvent mouseEventWithType:NSMouseMoved
+                                            location:location
+                                       modifierFlags:[theEvent modifierFlags]
+                                           timestamp:[theEvent timestamp]
+                                        windowNumber:window_number
+                                             context:nil
+                                         eventNumber:0
+                                          clickCount:[theEvent clickCount]
+                                            pressure:0];
+      WebMouseEvent wrap_around_event =
+          WebMouseEventBuilder::Build(nsevent, self, _pointerType);
+      _last_mouse_screen_position = wrap_around_event.PositionInScreen();
+      wrap_around_event.SetModifiers(
+          event.GetModifiers() |
+          blink::WebInputEvent::Modifiers::kRelativeMotionEvent);
+      _hostHelper->RouteOrProcessMouseEvent(wrap_around_event);
     }
+    event.SetPositionInScreen(
+        _last_mouse_screen_position +
+        gfx::Vector2dF(event.movement_x, event.movement_y));
   }
 
-  if (!send_touch) {
-    bool unaccelerated_movement =
-        _mouse_locked && _mouse_lock_unaccelerated_movement;
-    WebMouseEvent event = WebMouseEventBuilder::Build(
-        theEvent, self, _pointerType, unaccelerated_movement);
-
-    if (_mouse_locked &&
-        base::FeatureList::IsEnabled(features::kConsolidatedMovementXY)) {
-      // When mouse is locked, we keep increasing |last_mouse_screen_position|
-      // by movement_x/y so that we can still use PositionInScreen to calculate
-      // movements in blink. We need to keep |last_mouse_screen_position_| from
-      // getting too large because it will lose some precision. So whenever it
-      // exceed the |wrap_around_distance|, we start again from the current
-      // mouse position (locked position), and also send a synthesized event to
-      // update the blink-side status.
-      if (std::abs(_last_mouse_screen_position.x()) > wrap_around_distance ||
-          std::abs(_last_mouse_screen_position.y()) > wrap_around_distance) {
-        NSWindow* window = [self window];
-        NSPoint location = [window mouseLocationOutsideOfEventStream];
-        int window_number = window ? [window windowNumber] : -1;
-        NSEvent* nsevent = [NSEvent mouseEventWithType:NSMouseMoved
-                                              location:location
-                                         modifierFlags:[theEvent modifierFlags]
-                                             timestamp:[theEvent timestamp]
-                                          windowNumber:window_number
-                                               context:nil
-                                           eventNumber:0
-                                            clickCount:[theEvent clickCount]
-                                              pressure:0];
-        WebMouseEvent wrap_around_event =
-            WebMouseEventBuilder::Build(nsevent, self, _pointerType);
-        _last_mouse_screen_position = wrap_around_event.PositionInScreen();
-        wrap_around_event.SetModifiers(
-            event.GetModifiers() |
-            blink::WebInputEvent::Modifiers::kRelativeMotionEvent);
-        _hostHelper->RouteOrProcessMouseEvent(wrap_around_event);
-      }
-      event.SetPositionInScreen(
-          _last_mouse_screen_position +
-          gfx::Vector2dF(event.movement_x, event.movement_y));
-    }
-
-    _last_mouse_screen_position = event.PositionInScreen();
-    _hostHelper->RouteOrProcessMouseEvent(event);
-  } else {
-    WebTouchEvent event = WebTouchEventBuilder::Build(theEvent, self);
-    _hostHelper->RouteOrProcessTouchEvent(event);
-  }
+  _last_mouse_screen_position = event.PositionInScreen();
+  _hostHelper->RouteOrProcessMouseEvent(event);
 }
 
 - (void)tabletEvent:(NSEvent*)theEvent {
diff --git a/content/browser/accessibility/accessibility_event_recorder_uia_win.cc b/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
index 7be8433..2bd11a3 100644
--- a/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
@@ -8,6 +8,8 @@
 #include <numeric>
 #include <utility>
 
+#include <psapi.h>
+
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -16,6 +18,7 @@
 #include "base/win/scoped_safearray.h"
 #include "base/win/scoped_variant.h"
 #include "base/win/windows_version.h"
+#include "build/build_config.h"
 #include "content/browser/accessibility/browser_accessibility_com_win.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
@@ -30,6 +33,25 @@
 
 namespace {
 
+#if defined(COMPILER_MSVC)
+#define RETURN_ADDRESS() _ReturnAddress()
+#elif defined(COMPILER_GCC) && !defined(OS_NACL)
+#define RETURN_ADDRESS() \
+  __builtin_extract_return_addr(__builtin_return_address(0))
+#else
+#define RETURN_ADDRESS() nullptr
+#endif
+
+static std::pair<uintptr_t, uintptr_t> GetModuleAddressRange(
+    const wchar_t* module_name) {
+  MODULEINFO info;
+  CHECK(GetModuleInformation(GetCurrentProcess(), GetModuleHandle(module_name),
+                             &info, sizeof(info)));
+
+  const uintptr_t start = reinterpret_cast<uintptr_t>(info.lpBaseOfDll);
+  return std::make_pair(start, start + info.SizeOfImage);
+}
+
 std::string UiaIdentifierToStringPretty(int32_t id) {
   auto str = base::WideToUTF8(UiaIdentifierToString(id));
   // Remove UIA_ prefix, and EventId/PropertyId suffixes
@@ -175,66 +197,10 @@
   // Wait for shutdown signal
   shutdown_signal_.Wait();
 
-  // Due to a bug in Windows (fixed in Windows 10 19H1), events are raised
-  // exactly twice for any in-proc off-thread event listeners. We filter out the
-  // duplicate events here, and forward the remaining events to our owner.
   {
     base::AutoLock lock{on_event_lock_};
-    if (event_logs_.size() == 1) {
-      // Only received events on a single thread... perhaps the bug was fixed?
-      // Forward all events.
-      for (auto&& event : event_logs_.begin()->second)
-        owner_->OnEvent(event);
-    } else if (event_logs_.size() == 2) {
-      // Events were raised on two threads, as expected.  Sort the lists and
-      // forward events, eliminating duplicates that occur in both threads.
-      auto&& events_thread1 = event_logs_.begin()->second;
-      auto&& events_thread2 = (++event_logs_.begin())->second;
-
-      std::sort(events_thread1.begin(), events_thread1.end());
-      std::sort(events_thread2.begin(), events_thread2.end());
-
-      auto it1 = events_thread1.begin();
-      auto it2 = events_thread2.begin();
-      while (it1 < events_thread1.end() && it2 < events_thread2.end()) {
-        if (*it1 == *it2) {
-          owner_->OnEvent(*it1);
-          it1++;
-          it2++;
-        } else if (*it1 < *it2) {
-          owner_->OnEvent(*it1);
-          it1++;
-        } else {
-          owner_->OnEvent(*it2);
-          it2++;
-        }
-      }
-      while (it1 < events_thread1.end())
-        owner_->OnEvent(*it1++);
-      while (it2 < events_thread2.end())
-        owner_->OnEvent(*it2++);
-    } else {
-      // Typically we'll get events on exactly two threads (one directly from
-      // UIA, the second from RPC), but sometimes RPC will split its events
-      // across different threads.
-      //
-      // Unfortunately, there is no robust method of eliminating duplicates in
-      // this case.  Tests with intentional duplicates could run afoul of this
-      // logic in rare scenarios; it is recommended that intentionally
-      // duplicated events be avoided in tests, when possible.
-      std::vector<std::string> combined;
-      for (auto&& log : event_logs_)
-        combined.insert(combined.end(), log.second.begin(), log.second.end());
-      std::sort(combined.begin(), combined.end());
-
-      std::string last_event;
-      for (auto&& event : combined) {
-        if (last_event != event)
-          owner_->OnEvent(last_event = event);
-        else
-          last_event = {};
-      }
-    }
+    for (const std::string& event : event_logs_)
+      owner_->OnEvent(event);
   }
 
   // Cleanup
@@ -251,24 +217,27 @@
 }
 
 void AccessibilityEventRecorderUia::Thread::SendShutdownSignal() {
-  // We expect to see the shutdown sentinel exactly twice (due to the Windows
-  // bug detailed in |ThreadMain| and fixed in 19H1), so don't actually shut
-  // down the thread until the second call.
-  if (shutdown_sentinel_received_ ||
-      base::win::GetVersion() >= base::win::Version::WIN10_19H1)
-    shutdown_signal_.Signal();
-  else
-    shutdown_sentinel_received_ = true;
+  shutdown_signal_.Signal();
 }
 
 void AccessibilityEventRecorderUia::Thread::OnEvent(const std::string& event) {
   // We need to synchronize event logging, since UIA event callbacks can be
   // coming from multiple threads.
   base::AutoLock lock{on_event_lock_};
-  event_logs_[base::PlatformThread::CurrentId()].push_back(event);
+  event_logs_.push_back(event);
 }
 
-AccessibilityEventRecorderUia::Thread::EventHandler::EventHandler() {}
+AccessibilityEventRecorderUia::Thread::EventHandler::EventHandler() {
+  // Some events are duplicated between UIAutomationCore.dll and RPCRT4.dll.
+  // Before WIN10_19H1, events are mainly sent from RPCRT4.dll, with a few
+  // duplicates sent from UIAutomationCore.dll.
+  // After WIN10_19H1, events are mainly sent from UIAutomationCore.dll, with a
+  // few duplicates sent from RPCRT4.dll.
+  allowed_module_address_range_ = GetModuleAddressRange(
+      (base::win::GetVersion() >= base::win::Version::WIN10_19H1)
+          ? L"UIAutomationCore.dll"
+          : L"RPCRT4.dll");
+}
 
 AccessibilityEventRecorderUia::Thread::EventHandler::~EventHandler() {}
 
@@ -287,11 +256,23 @@
 STDMETHODIMP
 AccessibilityEventRecorderUia::Thread::EventHandler::HandleFocusChangedEvent(
     IUIAutomationElement* sender) {
-  if (!owner_)
+  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
     return S_OK;
 
   base::win::ScopedSafearray id;
   sender->GetRuntimeId(id.Receive());
+
+  if (auto lock_scope = id.CreateLockScope<VT_I4>()) {
+    // Debounce focus events received from the same |sender|.
+    if (std::equal(lock_scope->begin(), lock_scope->end(),
+                   last_focused_runtime_id_.begin(),
+                   last_focused_runtime_id_.end())) {
+      return S_OK;
+    }
+
+    last_focused_runtime_id_ = {lock_scope->begin(), lock_scope->end()};
+  }
+
   base::win::ScopedVariant id_variant(id.Release());
 
   Microsoft::WRL::ComPtr<IUIAutomationElement> element_found;
@@ -319,17 +300,18 @@
     IUIAutomationElement* sender,
     PROPERTYID property_id,
     VARIANT new_value) {
-  if (owner_) {
-    std::string prop_str = UiaIdentifierToStringPretty(property_id);
-    if (prop_str.empty()) {
-      VLOG(1) << "Ignoring UIA property-changed event " << property_id;
-      return S_OK;
-    }
+  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
+    return S_OK;
 
-    std::string log = base::StringPrintf("%s changed %s", prop_str.c_str(),
-                                         GetSenderInfo(sender).c_str());
-    owner_->OnEvent(log);
+  std::string prop_str = UiaIdentifierToStringPretty(property_id);
+  if (prop_str.empty()) {
+    VLOG(1) << "Ignoring UIA property-changed event " << property_id;
+    return S_OK;
   }
+
+  std::string log = base::StringPrintf("%s changed %s", prop_str.c_str(),
+                                       GetSenderInfo(sender).c_str());
+  owner_->OnEvent(log);
   return S_OK;
 }
 
@@ -338,34 +320,35 @@
     HandleStructureChangedEvent(IUIAutomationElement* sender,
                                 StructureChangeType change_type,
                                 SAFEARRAY* runtime_id) {
-  if (owner_) {
-    std::string type_str;
-    switch (change_type) {
-      case StructureChangeType_ChildAdded:
-        type_str = "ChildAdded";
-        break;
-      case StructureChangeType_ChildRemoved:
-        type_str = "ChildRemoved";
-        break;
-      case StructureChangeType_ChildrenInvalidated:
-        type_str = "ChildrenInvalidated";
-        break;
-      case StructureChangeType_ChildrenBulkAdded:
-        type_str = "ChildrenBulkAdded";
-        break;
-      case StructureChangeType_ChildrenBulkRemoved:
-        type_str = "ChildrenBulkRemoved";
-        break;
-      case StructureChangeType_ChildrenReordered:
-        type_str = "ChildrenReordered";
-        break;
-    }
+  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
+    return S_OK;
 
-    std::string log =
-        base::StringPrintf("StructureChanged/%s %s", type_str.c_str(),
-                           GetSenderInfo(sender).c_str());
-    owner_->OnEvent(log);
+  std::string type_str;
+  switch (change_type) {
+    case StructureChangeType_ChildAdded:
+      type_str = "ChildAdded";
+      break;
+    case StructureChangeType_ChildRemoved:
+      type_str = "ChildRemoved";
+      break;
+    case StructureChangeType_ChildrenInvalidated:
+      type_str = "ChildrenInvalidated";
+      break;
+    case StructureChangeType_ChildrenBulkAdded:
+      type_str = "ChildrenBulkAdded";
+      break;
+    case StructureChangeType_ChildrenBulkRemoved:
+      type_str = "ChildrenBulkRemoved";
+      break;
+    case StructureChangeType_ChildrenReordered:
+      type_str = "ChildrenReordered";
+      break;
   }
+
+  std::string log =
+      base::StringPrintf("StructureChanged/%s %s", type_str.c_str(),
+                         GetSenderInfo(sender).c_str());
+  owner_->OnEvent(log);
   return S_OK;
 }
 
@@ -373,33 +356,45 @@
 AccessibilityEventRecorderUia::Thread::EventHandler::HandleAutomationEvent(
     IUIAutomationElement* sender,
     EVENTID event_id) {
-  if (owner_) {
-    if (event_id == owner_->shutdown_sentinel_) {
-      // This is a sentinel value that tells us the tests are finished.
-      owner_->SendShutdownSignal();
-    } else {
-      std::string event_str = UiaIdentifierToStringPretty(event_id);
-      if (event_str.empty()) {
-        VLOG(1) << "Ignoring UIA automation event " << event_id;
-        return S_OK;
-      }
+  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
+    return S_OK;
 
-      // Remove duplicate menuclosed events with no event data.
-      // The "duplicates" are benign. UIA currently duplicates *all* events for
-      // in-process listeners, and the event-recorder tries to eliminate the
-      // duplicates... but since the recorder sometimes isn't able to retrieve
-      // the role, the duplicate-elimination logic doesn't see them as
-      // duplicates in this case.
-      std::string sender_info =
-          event_id == UIA_MenuClosedEventId ? "" : GetSenderInfo(sender);
-      std::string log =
-          base::StringPrintf("%s %s", event_str.c_str(), sender_info.c_str());
-      owner_->OnEvent(log);
+  if (event_id == owner_->shutdown_sentinel_) {
+    // This is a sentinel value that tells us the tests are finished.
+    owner_->SendShutdownSignal();
+  } else {
+    std::string event_str = UiaIdentifierToStringPretty(event_id);
+    if (event_str.empty()) {
+      VLOG(1) << "Ignoring UIA automation event " << event_id;
+      return S_OK;
     }
+
+    // Remove duplicate menuclosed events with no event data.
+    // The "duplicates" are benign. UIA currently duplicates *all* events for
+    // in-process listeners, and the event-recorder tries to eliminate the
+    // duplicates... but since the recorder sometimes isn't able to retrieve
+    // the role, the duplicate-elimination logic doesn't see them as
+    // duplicates in this case.
+    std::string sender_info =
+        event_id == UIA_MenuClosedEventId ? "" : GetSenderInfo(sender);
+    std::string log =
+        base::StringPrintf("%s %s", event_str.c_str(), sender_info.c_str());
+    owner_->OnEvent(log);
   }
   return S_OK;
 }
 
+// Due to a bug in Windows (fixed in Windows 10 19H1, but found broken in 20H2),
+// events are raised exactly twice for any in-proc off-thread event listeners.
+// To avoid this, in UIA API methods we can pass the RETURN_ADDRESS() to this
+// method to determine whether the caller belongs to a specific platform module.
+bool AccessibilityEventRecorderUia::Thread::EventHandler::
+    IsCallerFromAllowedModule(void* return_address) {
+  const auto address = reinterpret_cast<uintptr_t>(return_address);
+  return address >= allowed_module_address_range_.first &&
+         address < allowed_module_address_range_.second;
+}
+
 std::string AccessibilityEventRecorderUia::Thread::EventHandler::GetSenderInfo(
     IUIAutomationElement* sender) {
   std::string sender_info;
diff --git a/content/browser/accessibility/accessibility_event_recorder_uia_win.h b/content/browser/accessibility/accessibility_event_recorder_uia_win.h
index 8809a4a..01d7c06 100644
--- a/content/browser/accessibility/accessibility_event_recorder_uia_win.h
+++ b/content/browser/accessibility/accessibility_event_recorder_uia_win.h
@@ -9,8 +9,8 @@
 #include <stdint.h>
 #include <uiautomation.h>
 #include <wrl/client.h>
-#include <map>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/atomicops.h"
@@ -67,12 +67,11 @@
     base::OnceClosure initialization_complete_;
     base::OnceClosure shutdown_complete_;
     base::WaitableEvent shutdown_signal_;
-    bool shutdown_sentinel_received_ = false;
 
     // Thread-specific wrapper for OnEvent to handle necessary locking
     void OnEvent(const std::string& event);
     base::Lock on_event_lock_;
-    std::map<base::PlatformThreadId, std::vector<std::string>> event_logs_;
+    std::vector<std::string> event_logs_;
 
     // An implementation of various UIA interfaces that forward event
     // notifications to the owning event recorder.
@@ -119,10 +118,15 @@
       AccessibilityEventRecorderUia::Thread* owner_ = nullptr;
 
      private:
+      std::pair<uintptr_t, uintptr_t> allowed_module_address_range_;
+      bool IsCallerFromAllowedModule(void* return_address);
+
       std::string GetSenderInfo(IUIAutomationElement* sender);
 
       Microsoft::WRL::ComPtr<IUIAutomationElement> root_;
 
+      std::vector<int32_t> last_focused_runtime_id_;
+
       DISALLOW_COPY_AND_ASSIGN(EventHandler);
     };
     Microsoft::WRL::ComPtr<CComObject<EventHandler>> uia_event_handler_;
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index a80ca3a..2fb5600 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -786,13 +786,6 @@
   BrowserAccessibilityStateImpl::GetInstance()->OnAccessibilityApiUsage();
 }
 
-void BrowserAccessibilityManager::SetFocusLocallyForTesting(
-    BrowserAccessibility* node) {
-  ui::AXTreeData data = GetTreeData();
-  data.focus_id = node->GetId();
-  ax_tree()->UpdateData(data);
-}
-
 // static
 void BrowserAccessibilityManager::SetFocusChangeCallbackForTesting(
     base::RepeatingClosure callback) {
@@ -1372,6 +1365,33 @@
   return result;
 }
 
+void BrowserAccessibilityManager::OnTreeDataChanged(
+    ui::AXTree* tree,
+    const ui::AXTreeData& old_data,
+    const ui::AXTreeData& new_data) {
+  DCHECK_EQ(ax_tree(), tree);
+  if (new_data.tree_id == ui::AXTreeIDUnknown() ||
+      new_data.tree_id == ax_tree_id_) {
+    return;  // Tree ID hasn't changed.
+  }
+
+  // Either the tree that is being managed by this manager has just been
+  // created, or it has been destroyed and re-created.
+  connected_to_parent_tree_node_ = false;
+
+  // If the current focus is in the tree that has just been destroyed, then
+  // reset the focus to nullptr. It will be set to the current focus again the
+  // next time there is a focus event.
+  if (ax_tree_id_ != ui::AXTreeIDUnknown() &&
+      ax_tree_id_ == last_focused_node_tree_id_) {
+    SetLastFocusedNode(nullptr);
+  }
+
+  ui::AXTreeManagerMap::GetInstance().RemoveTreeManager(ax_tree_id_);
+  ax_tree_id_ = new_data.tree_id;
+  ui::AXTreeManagerMap::GetInstance().AddTreeManager(ax_tree_id_, this);
+}
+
 void BrowserAccessibilityManager::OnNodeWillBeDeleted(ui::AXTree* tree,
                                                       ui::AXNode* node) {
   DCHECK(node);
@@ -1443,26 +1463,10 @@
     ui::AXTree* tree,
     bool root_changed,
     const std::vector<ui::AXTreeObserver::Change>& changes) {
-  const bool ax_tree_id_changed =
-      GetTreeData().tree_id != ui::AXTreeIDUnknown() &&
-      GetTreeData().tree_id != ax_tree_id_;
-  // When the tree that contains the focus is destroyed and re-created, we
-  // should fire a new focus event. Also, whenever the tree ID or the root of
-  // this tree changes we may need to fire an event on our parent node in the
-  // parent tree to ensure that we're properly connected.
-  if (ax_tree_id_changed && last_focused_node_tree_id_ &&
-      ax_tree_id_ == *last_focused_node_tree_id_) {
-    SetLastFocusedNode(nullptr);
-  }
-  if (ax_tree_id_changed || root_changed)
+  DCHECK_EQ(ax_tree(), tree);
+  if (root_changed)
     connected_to_parent_tree_node_ = false;
 
-  if (ax_tree_id_changed) {
-    ui::AXTreeManagerMap::GetInstance().RemoveTreeManager(ax_tree_id_);
-    ax_tree_id_ = GetTreeData().tree_id;
-    ui::AXTreeManagerMap::GetInstance().AddTreeManager(ax_tree_id_, this);
-  }
-
   // Calls OnDataChanged on newly created, reparented or changed nodes.
   for (const auto& change : changes) {
     ui::AXNode* node = change.node;
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index 0c29d23..08119a55 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -236,10 +236,6 @@
     return hidden_by_interstitial_page_;
   }
 
-  // Pretend that the given node has focus, for testing only. Doesn't
-  // communicate with the renderer and doesn't fire any events.
-  void SetFocusLocallyForTesting(BrowserAccessibility* node);
-
   // For testing only, register a function to be called when focus changes
   // in any BrowserAccessibilityManager.
   static void SetFocusChangeCallbackForTesting(base::RepeatingClosure callback);
@@ -440,6 +436,9 @@
   ui::AXTree* ax_tree() const { return tree_.get(); }
 
   // AXTreeObserver implementation.
+  void OnTreeDataChanged(ui::AXTree* tree,
+                         const ui::AXTreeData& old_data,
+                         const ui::AXTreeData& new_data) override;
   void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
   void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
   void OnNodeCreated(ui::AXTree* tree, ui::AXNode* node) override;
diff --git a/content/browser/accessibility/browser_accessibility_win_unittest.cc b/content/browser/accessibility/browser_accessibility_win_unittest.cc
index 7642483..05dccefa 100644
--- a/content/browser/accessibility/browser_accessibility_win_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_win_unittest.cc
@@ -1227,7 +1227,9 @@
   BrowserAccessibilityWin* combo_box_accessible =
       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
   ASSERT_NE(nullptr, combo_box_accessible);
-  manager->SetFocusLocallyForTesting(combo_box_accessible);
+  ui::AXTreeData data = manager->GetTreeData();
+  data.focus_id = combo_box_accessible->GetId();
+  manager->ax_tree()->UpdateDataForTesting(data);
   ASSERT_EQ(combo_box_accessible,
             ToBrowserAccessibilityWin(manager->GetFocus()));
   BrowserAccessibilityWin* search_box_accessible =
@@ -1884,7 +1886,9 @@
   BrowserAccessibilityWin* combo_box_accessible =
       ToBrowserAccessibilityWin(root_accessible->PlatformGetChild(0));
   ASSERT_NE(nullptr, combo_box_accessible);
-  manager->SetFocusLocallyForTesting(combo_box_accessible);
+  ui::AXTreeData data = manager->GetTreeData();
+  data.focus_id = combo_box_accessible->GetId();
+  manager->ax_tree()->UpdateDataForTesting(data);
   ASSERT_EQ(combo_box_accessible,
             ToBrowserAccessibilityWin(manager->GetFocus()));
   BrowserAccessibilityWin* text_field_accessible =
@@ -1907,7 +1911,9 @@
   EXPECT_EQ(2, caret_offset);
 
   // Move the focus to the text field.
-  manager->SetFocusLocallyForTesting(text_field_accessible);
+  data = manager->GetTreeData();
+  data.focus_id = text_field_accessible->GetId();
+  manager->ax_tree()->UpdateDataForTesting(data);
   ASSERT_EQ(text_field_accessible,
             ToBrowserAccessibilityWin(manager->GetFocus()));
 
@@ -2017,7 +2023,9 @@
   EXPECT_EQ(6, caret_offset);
 
   // Move the focus to the content editable.
-  manager->SetFocusLocallyForTesting(div_editable_accessible);
+  ui::AXTreeData data = manager->GetTreeData();
+  data.focus_id = div_editable_accessible->GetId();
+  manager->ax_tree()->UpdateDataForTesting(data);
   ASSERT_EQ(div_editable_accessible,
             ToBrowserAccessibilityWin(manager->GetFocus()));
 
@@ -2187,7 +2195,9 @@
   EXPECT_EQ(7, caret_offset);
 
   // Move the focus to the content editable.
-  manager->SetFocusLocallyForTesting(div_editable_accessible);
+  ui::AXTreeData data = manager->GetTreeData();
+  data.focus_id = div_editable_accessible->GetId();
+  manager->ax_tree()->UpdateDataForTesting(data);
   ASSERT_EQ(div_editable_accessible,
             ToBrowserAccessibilityWin(manager->GetFocus()));
 
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 54b653f..53477cb0 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -201,6 +201,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityCSSContentVisibilityAutoAriaHidden) {
+  RunCSSTest(FILE_PATH_LITERAL("content-visibility-auto-aria-hidden.html"));
+}
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
                        AccessibilityCSSContentVisibilityHiddenCheckFailure) {
   RunCSSTest(FILE_PATH_LITERAL("content-visibility-hidden-check-failure.html"));
 }
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index a7734981..cfc09d0 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -64,6 +64,9 @@
 
 namespace {
 
+using perfetto::protos::pbzero::ChromeBrowserContext;
+using perfetto::protos::pbzero::ChromeTrackEvent;
+
 void SaveSessionStateOnIOThread(AppCacheServiceImpl* appcache_service) {
   appcache_service->set_force_keep_session_state();
 }
@@ -77,33 +80,23 @@
 }  // namespace
 
 BrowserContext::BrowserContext() {
-  TRACE_EVENT("shutdown", "BrowserContext::BrowserContext",
-              [&](perfetto::EventContext ctx) {
-                auto* event =
-                    ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
-                event->set_chrome_browser_context()->set_ptr(
-                    reinterpret_cast<uint64_t>(this));
-              });
-  TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("shutdown", "Browser.BrowserContext", this,
-                                    "browser_context",
-                                    static_cast<void*>(this));
-
   impl_ = std::make_unique<Impl>(this);
+  TRACE_EVENT("shutdown", "BrowserContext::BrowserContext",
+              ChromeTrackEvent::kChromeBrowserContext, *this);
+  TRACE_EVENT_BEGIN("shutdown", "Browser.BrowserContext",
+                    perfetto::Track::FromPointer(this),
+                    ChromeTrackEvent::kChromeBrowserContext, *this);
 }
 
 BrowserContext::~BrowserContext() {
   TRACE_EVENT("shutdown", "BrowserContext::~BrowserContext",
-              [&](perfetto::EventContext ctx) {
-                auto* event =
-                    ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
-                event->set_chrome_browser_context()->set_ptr(
-                    reinterpret_cast<uint64_t>(this));
-              });
+              ChromeTrackEvent::kChromeBrowserContext, *this);
+
+  // End for ASYNC event "Browser.BrowserContext".
+  TRACE_EVENT_END("shutdown", perfetto::Track::FromPointer(this),
+                  ChromeTrackEvent::kChromeBrowserContext, *this);
 
   impl_.reset();
-
-  TRACE_EVENT_NESTABLE_ASYNC_END1("shutdown", "Browser.BrowserContext", this,
-                                  "browser_context", static_cast<void*>(this));
 }
 
 DownloadManager* BrowserContext::GetDownloadManager() {
@@ -347,6 +340,11 @@
     dict.Add("id", impl()->UniqueId());
 }
 
+void BrowserContext::WriteIntoTrace(
+    perfetto::TracedProto<ChromeBrowserContext> proto) {
+  proto->set_id(impl()->UniqueId());
+}
+
 //////////////////////////////////////////////////////////////////////////////
 // The //content embedder can override the methods below to change or extend
 // how the //content layer interacts with a BrowserContext.  The code below
diff --git a/content/browser/browser_main_runner_impl.cc b/content/browser/browser_main_runner_impl.cc
index fde9208..4edb502 100644
--- a/content/browser/browser_main_runner_impl.cc
+++ b/content/browser/browser_main_runner_impl.cc
@@ -169,7 +169,7 @@
   main_loop_->PreShutdown();
 
   // Finalize the startup tracing session if it is still active.
-  StartupTracingController::GetInstance().WaitUntilStopped();
+  StartupTracingController::GetInstance().ShutdownAndWaitForStopIfNeeded();
 
   {
     // The trace event has to stay between profiler creation and destruction.
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index 65099d93..ecc8ee0e 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -16,6 +16,7 @@
 #include "base/process/launch.h"
 #include "base/process/process_metrics.h"
 #include "base/time/time.h"
+#include "base/tracing/protos/chrome_track_event.pbzero.h"
 #include "build/build_config.h"
 #include "content/public/browser/child_process_launcher_utils.h"
 #include "content/public/common/content_features.h"
@@ -29,6 +30,28 @@
 
 using internal::ChildProcessLauncherHelper;
 
+void ChildProcessLauncherPriority::WriteIntoTrace(
+    perfetto::TracedProto<
+        perfetto::protos::pbzero::ChildProcessLauncherPriority> proto) {
+  proto->set_is_backgrounded(is_background());
+  proto->set_has_pending_views(boost_for_pending_views);
+
+#if defined(OS_ANDROID)
+  using PriorityProto = perfetto::protos::pbzero::ChildProcessLauncherPriority;
+  switch (importance) {
+    case ChildProcessImportance::IMPORTANT:
+      proto->set_importance(PriorityProto::IMPORTANCE_IMPORTANT);
+      break;
+    case ChildProcessImportance::NORMAL:
+      proto->set_importance(PriorityProto::IMPORTANCE_NORMAL);
+      break;
+    case ChildProcessImportance::MODERATE:
+      proto->set_importance(PriorityProto::IMPORTANCE_MODERATE);
+      break;
+  }
+#endif
+}
+
 #if defined(OS_ANDROID)
 bool ChildProcessLauncher::Client::CanUseWarmUpConnection() {
   return true;
diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
index 7b299d9..7517fb03 100644
--- a/content/browser/child_process_launcher.h
+++ b/content/browser/child_process_launcher.h
@@ -23,6 +23,7 @@
 #include "content/public/browser/child_process_termination_info.h"
 #include "content/public/common/result_codes.h"
 #include "mojo/public/cpp/system/invitation.h"
+#include "third_party/perfetto/include/perfetto/tracing/traced_proto.h"
 
 #if defined(OS_ANDROID)
 #include "content/public/browser/android/child_process_importance.h"
@@ -32,6 +33,14 @@
 class CommandLine;
 }
 
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+class ChildProcessLauncherPriority;
+}
+}  // namespace protos
+}  // namespace perfetto
+
 namespace content {
 
 class SandboxedProcessLauncherDelegate;
@@ -88,6 +97,10 @@
     return !(*this == other);
   }
 
+  void WriteIntoTrace(
+      perfetto::TracedProto<
+          perfetto::protos::pbzero::ChildProcessLauncherPriority> proto);
+
   // Prefer |is_background()| to inspecting these fields individually (to ensure
   // all logic uses the same notion of "backgrounded").
 
diff --git a/content/browser/conversions/conversion_host.cc b/content/browser/conversions/conversion_host.cc
index ce6c32d..e288bd75 100644
--- a/content/browser/conversions/conversion_host.cc
+++ b/content/browser/conversions/conversion_host.cc
@@ -261,7 +261,10 @@
   StorableConversion storable_conversion(
       conversion_manager->GetConversionPolicy().GetSanitizedConversionData(
           conversion->conversion_data),
-      conversion_destination, conversion->reporting_origin);
+      conversion_destination, conversion->reporting_origin,
+      conversion_manager->GetConversionPolicy()
+          .GetSanitizedEventSourceTriggerData(
+              conversion->event_source_trigger_data));
 
   if (conversion_page_metrics_)
     conversion_page_metrics_->OnConversion(storable_conversion);
diff --git a/content/browser/conversions/conversion_host.h b/content/browser/conversions/conversion_host.h
index 3be185c..96baab314 100644
--- a/content/browser/conversions/conversion_host.h
+++ b/content/browser/conversions/conversion_host.h
@@ -57,6 +57,8 @@
   FRIEND_TEST_ALL_PREFIXES(ConversionHostTest, ValidConversion_NoBadMessage);
   FRIEND_TEST_ALL_PREFIXES(ConversionHostTest,
                            Conversion_AssociatedWithConversionSite);
+  FRIEND_TEST_ALL_PREFIXES(ConversionHostTest,
+                           Conversion_EventSourceTriggerDataPropagated);
   FRIEND_TEST_ALL_PREFIXES(ConversionHostTest, PerPageConversionMetrics);
   FRIEND_TEST_ALL_PREFIXES(ConversionHostTest,
                            NoManager_NoPerPageConversionMetrics);
diff --git a/content/browser/conversions/conversion_policy.cc b/content/browser/conversions/conversion_policy.cc
index 145247f3..2264883 100644
--- a/content/browser/conversions/conversion_policy.cc
+++ b/content/browser/conversions/conversion_policy.cc
@@ -17,6 +17,10 @@
 // conversion metadata is stripped to these lower bits.
 const int kMaxAllowedConversionValues = 8;
 
+// Maximum number of allowed event source trigger data values. Higher entropy
+// event source trigger data is stripped to these lower bits.
+const int kMaxAllowedEventSourceTriggerDataValues = 2;
+
 }  // namespace
 
 uint64_t ConversionPolicy::NoiseProvider::GetNoisedConversionData(
@@ -32,6 +36,20 @@
   return static_cast<uint64_t>(base::RandInt(0, kMaxAllowedConversionValues));
 }
 
+uint64_t ConversionPolicy::NoiseProvider::GetNoisedEventSourceTriggerData(
+    uint64_t event_source_trigger_data) const {
+  // Return |event_source_trigger_data| without any noise 95% of the time.
+  if (base::RandDouble() > .05)
+    return event_source_trigger_data;
+
+  // 5% of the time return a random number in the allowed range. Note that the
+  // value is noised 5% of the time, but only wrong 5 *
+  // (kMaxAllowedEventSourceTriggerDataValues - 1) /
+  // kMaxAllowedEventSourceTriggerDataValues percent of the time.
+  return static_cast<uint64_t>(
+      base::RandInt(0, kMaxAllowedEventSourceTriggerDataValues));
+}
+
 // static
 std::unique_ptr<ConversionPolicy> ConversionPolicy::CreateForTesting(
     std::unique_ptr<NoiseProvider> noise_provider) {
@@ -62,6 +80,21 @@
   return conversion_data % kMaxAllowedConversionValues;
 }
 
+uint64_t ConversionPolicy::GetSanitizedEventSourceTriggerData(
+    uint64_t event_source_trigger_data) const {
+  // Add noise to the conversion when the value is first sanitized from a
+  // conversion registration event. This noised data will be used for all
+  // associated impressions that convert.
+  if (noise_provider_) {
+    event_source_trigger_data =
+        noise_provider_->GetNoisedEventSourceTriggerData(
+            event_source_trigger_data);
+  }
+
+  // Allow at most 1 bit of entropy in event source trigger data.
+  return event_source_trigger_data % kMaxAllowedEventSourceTriggerDataValues;
+}
+
 uint64_t ConversionPolicy::GetSanitizedImpressionData(
     uint64_t impression_data) const {
   // Impression data is allowed the full 64 bits.
diff --git a/content/browser/conversions/conversion_policy.h b/content/browser/conversions/conversion_policy.h
index ae84fdae..dfc9a0e 100644
--- a/content/browser/conversions/conversion_policy.h
+++ b/content/browser/conversions/conversion_policy.h
@@ -27,10 +27,16 @@
     NoiseProvider() = default;
     virtual ~NoiseProvider() = default;
 
-    // Returns a noise value of |conversion_data|. By default, this reports
+    // Returns a noised value of |conversion_data|. By default, this reports
     // completely random data for 5% of conversions, and sends the real data for
     // 95%. Virtual for testing.
     virtual uint64_t GetNoisedConversionData(uint64_t conversion_data) const;
+
+    // Returns a noised value of |event_source_trigger_data|. By default, this
+    // reports completely random data for 5% of conversions, and sends the real
+    // data for 95%. Virtual for testing.
+    virtual uint64_t GetNoisedEventSourceTriggerData(
+        uint64_t event_source_trigger_data) const;
   };
 
   static std::unique_ptr<ConversionPolicy> CreateForTesting(
@@ -47,6 +53,10 @@
   // from the provided to data to at most 3 bits of information.
   virtual uint64_t GetSanitizedConversionData(uint64_t conversion_data) const;
 
+  // Gets the sanitized event source trigger data for a conversion.
+  virtual uint64_t GetSanitizedEventSourceTriggerData(
+      uint64_t event_source_trigger_data) const;
+
   // Gets the sanitized impression data for an impression.
   virtual uint64_t GetSanitizedImpressionData(uint64_t impression_data) const;
 
diff --git a/content/browser/conversions/conversion_policy_unittest.cc b/content/browser/conversions/conversion_policy_unittest.cc
index c35984a..d320124c 100644
--- a/content/browser/conversions/conversion_policy_unittest.cc
+++ b/content/browser/conversions/conversion_policy_unittest.cc
@@ -21,6 +21,11 @@
   uint64_t GetNoisedConversionData(uint64_t conversion_data) const override {
     return conversion_data;
   }
+
+  uint64_t GetNoisedEventSourceTriggerData(
+      uint64_t event_source_trigger_data) const override {
+    return event_source_trigger_data;
+  }
 };
 
 // Mock ConversionNoiseProvider that always noises values by +1.
@@ -29,6 +34,11 @@
   uint64_t GetNoisedConversionData(uint64_t conversion_data) const override {
     return conversion_data + 1;
   }
+
+  uint64_t GetNoisedEventSourceTriggerData(
+      uint64_t event_source_trigger_data) const override {
+    return event_source_trigger_data + 1;
+  }
 };
 
 }  // namespace
@@ -65,7 +75,7 @@
   }
 }
 
-TEST_F(ConversionPolicyTest, SantizizeConversionData_OutputHasNoise) {
+TEST_F(ConversionPolicyTest, SanitizeConversionData_OutputHasNoise) {
   // The policy should include noise when sanitizing data.
   EXPECT_EQ(5LU, ConversionPolicy::CreateForTesting(
                      std::make_unique<IncrementingNoiseProvider>())
@@ -82,6 +92,46 @@
   }
 }
 
+TEST_F(ConversionPolicyTest,
+       HighEntropyEventSourceTriggerData_StrippedToLowerBits) {
+  uint64_t event_source_trigger_data = 4LU;
+
+  // The policy should strip the data to the lower 1 bit.
+  EXPECT_EQ(
+      0LU,
+      ConversionPolicy::CreateForTesting(std::make_unique<EmptyNoiseProvider>())
+          ->GetSanitizedEventSourceTriggerData(event_source_trigger_data));
+}
+
+TEST_F(ConversionPolicyTest, OneBitEventSourceTriggerData_Unchanged) {
+  std::unique_ptr<ConversionPolicy> policy = ConversionPolicy::CreateForTesting(
+      std::make_unique<EmptyNoiseProvider>());
+  for (uint64_t event_source_trigger_data = 0; event_source_trigger_data < 2;
+       event_source_trigger_data++) {
+    EXPECT_EQ(
+        event_source_trigger_data,
+        policy->GetSanitizedEventSourceTriggerData(event_source_trigger_data));
+  }
+}
+
+TEST_F(ConversionPolicyTest, SanitizeEventSourceTriggerData_OutputHasNoise) {
+  // The policy should include noise when sanitizing data.
+  EXPECT_EQ(0LU, ConversionPolicy::CreateForTesting(
+                     std::make_unique<IncrementingNoiseProvider>())
+                     ->GetSanitizedEventSourceTriggerData(1UL));
+}
+
+// This test will fail flakily if noise is used.
+TEST_F(ConversionPolicyTest, DebugMode_EventSourceTriggerDataNotNoised) {
+  uint64_t event_source_trigger_data = 0UL;
+  for (int i = 0; i < 100; i++) {
+    EXPECT_EQ(
+        event_source_trigger_data,
+        ConversionPolicy(/*debug_mode=*/true)
+            .GetSanitizedEventSourceTriggerData(event_source_trigger_data));
+  }
+}
+
 TEST_F(ConversionPolicyTest, NoExpiryForImpression_DefaultUsed) {
   base::Time impression_time = base::Time::Now();
   EXPECT_EQ(impression_time + base::TimeDelta::FromDays(30),
diff --git a/content/browser/conversions/conversion_report.h b/content/browser/conversions/conversion_report.h
index e634fc1..2a50451 100644
--- a/content/browser/conversions/conversion_report.h
+++ b/content/browser/conversions/conversion_report.h
@@ -32,7 +32,8 @@
   // Impression associated with this conversion report.
   const StorableImpression impression;
 
-  // Data provided at reporting time by the reporting origin.
+  // Data provided at reporting time by the reporting origin. Depending on the
+  // source type, this contains the associated data in the trigger redirect.
   uint64_t conversion_data;
 
   // The time the conversion occurred.
@@ -48,6 +49,9 @@
   // Id assigned by storage to uniquely identify a completed conversion. If
   // null, an ID has not been assigned yet.
   const absl::optional<int64_t> conversion_id;
+
+  // When adding new members, the `ReportsEqual()` testing utility in
+  // `conversion_test_utils.h` should also be updated.
 };
 
 // Only used for logging.
diff --git a/content/browser/conversions/conversion_storage_sql.cc b/content/browser/conversions/conversion_storage_sql.cc
index 618c73d..8f0d24a 100644
--- a/content/browser/conversions/conversion_storage_sql.cc
+++ b/content/browser/conversions/conversion_storage_sql.cc
@@ -211,20 +211,16 @@
 
   base::Time current_time = clock_->Now();
 
-  // TODO(apaseltiner): Support kEvent as well as kNavigation.
-  const StorableImpression::SourceType kSourceType =
-      StorableImpression::SourceType::kNavigation;
-
   // Get all impressions that match this <reporting_origin,
   // conversion_destination> pair. Only get impressions that are active and not
   // past their expiry time.
   const char kGetMatchingImpressionsSql[] =
       "SELECT impression_id, impression_data, impression_origin, "
       "conversion_origin, impression_time, expiry_time, priority, "
-      "attributed_truthfully "
+      "attributed_truthfully, source_type "
       "FROM impressions "
       "WHERE conversion_destination = ? AND reporting_origin = ? "
-      "AND active = 1 AND expiry_time > ? AND source_type = ?"
+      "AND active = 1 AND expiry_time > ? "
       "ORDER BY impression_time DESC";
 
   sql::Statement statement(
@@ -232,7 +228,6 @@
   statement.BindString(0, serialized_conversion_destination);
   statement.BindString(1, SerializeOrigin(reporting_origin));
   statement.BindTime(2, current_time);
-  statement.BindInt(3, static_cast<int>(kSourceType));
 
   std::vector<StorableImpression> impressions;
 
@@ -259,10 +254,12 @@
     StorableImpression::AttributionLogic attribution_logic =
         static_cast<StorableImpression::AttributionLogic>(
             statement.ColumnInt(7));
+    StorableImpression::SourceType source_type =
+        static_cast<StorableImpression::SourceType>(statement.ColumnInt(8));
 
     StorableImpression impression(impression_data, impression_origin,
                                   conversion_origin, reporting_origin,
-                                  impression_time, expiry_time, kSourceType,
+                                  impression_time, expiry_time, source_type,
                                   attribution_source_priority, impression_id);
     impressions.push_back(std::move(impression));
     attribution_logics.insert_or_assign(impression_id, attribution_logic);
@@ -275,7 +272,13 @@
   const StorableImpression& impression_to_attribute =
       delegate_->GetImpressionToAttribute(impressions);
 
-  ConversionReport report(impression_to_attribute, conversion.conversion_data(),
+  const uint64_t conversion_data =
+      impression_to_attribute.source_type() ==
+              StorableImpression::SourceType::kEvent
+          ? conversion.event_source_trigger_data()
+          : conversion.conversion_data();
+
+  ConversionReport report(impression_to_attribute, conversion_data,
                           /*conversion_time=*/current_time,
                           /*report_time=*/current_time,
                           /*conversion_id=*/absl::nullopt);
@@ -332,7 +335,9 @@
   // provide the max number of conversions prior to this new conversion being
   // logged.
   int max_prior_conversions_before_inactive =
-      delegate_->GetMaxConversionsPerImpression(kSourceType) - 1;
+      delegate_->GetMaxConversionsPerImpression(
+          report.impression.source_type()) -
+      1;
 
   // Update the attributed impression.
   impression_update_statement.BindInt(0, max_prior_conversions_before_inactive);
diff --git a/content/browser/conversions/conversion_storage_sql_unittest.cc b/content/browser/conversions/conversion_storage_sql_unittest.cc
index d60a7455..b2d0dea 100644
--- a/content/browser/conversions/conversion_storage_sql_unittest.cc
+++ b/content/browser/conversions/conversion_storage_sql_unittest.cc
@@ -352,7 +352,8 @@
 
   clock()->Advance(base::TimeDelta::FromDays(1));
   StorableConversion conversion(1, net::SchemefulSite(conversion_origin),
-                                reporting_origin);
+                                reporting_origin,
+                                /*event_source_trigger_data=*/0);
   EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   clock()->Advance(base::TimeDelta::FromDays(1));
@@ -396,7 +397,8 @@
 
   clock()->Advance(base::TimeDelta::FromDays(1));
   StorableConversion conversion(1, net::SchemefulSite(conversion_origin),
-                                reporting_origin);
+                                reporting_origin,
+                                /*event_source_trigger_data=*/0);
   EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
 
   clock()->Advance(base::TimeDelta::FromDays(1));
diff --git a/content/browser/conversions/conversion_storage_unittest.cc b/content/browser/conversions/conversion_storage_unittest.cc
index df7a551..0655f3a7 100644
--- a/content/browser/conversions/conversion_storage_unittest.cc
+++ b/content/browser/conversions/conversion_storage_unittest.cc
@@ -155,10 +155,27 @@
           .Build();
   storage()->StoreImpression(impression);
   StorableConversion conversion(1, net::SchemefulSite(GURL("https://a.test")),
-                                impression.reporting_origin());
+                                impression.reporting_origin(),
+                                /*event_source_trigger_data=*/0);
   EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
 }
 
+TEST_F(ConversionStorageTest, EventSourceImpressionsForConversion_Converts) {
+  storage()->StoreImpression(
+      ImpressionBuilder(clock()->Now())
+          .SetSourceType(StorableImpression::SourceType::kEvent)
+          .Build());
+  EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(
+      DefaultConversion(/*event_source_trigger_data=*/456)));
+
+  clock()->Advance(base::TimeDelta::FromMilliseconds(kReportTime));
+
+  std::vector<ConversionReport> actual_reports =
+      storage()->GetConversionsToReport(clock()->Now());
+  EXPECT_EQ(1u, actual_reports.size());
+  EXPECT_EQ(456u, actual_reports[0].conversion_data);
+}
+
 TEST_F(ConversionStorageTest, ImpressionExpired_NoConversionsStored) {
   storage()->StoreImpression(
       ImpressionBuilder(clock()->Now())
@@ -602,14 +619,16 @@
   for (int i = 0; i < 5; i++) {
     auto origin =
         url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i)));
-    StorableConversion conversion(1, net::SchemefulSite(origin), origin);
+    StorableConversion conversion(1, net::SchemefulSite(origin), origin,
+                                  /*event_source_trigger_data=*/0);
     EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
   }
   clock()->Advance(base::TimeDelta::FromDays(1));
   for (int i = 5; i < 10; i++) {
     auto origin =
         url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i)));
-    StorableConversion conversion(1, net::SchemefulSite(origin), origin);
+    StorableConversion conversion(1, net::SchemefulSite(origin), origin,
+                                  /*event_source_trigger_data=*/0);
     EXPECT_TRUE(storage()->MaybeCreateAndStoreConversionReport(conversion));
   }
 
diff --git a/content/browser/conversions/conversion_test_utils.cc b/content/browser/conversions/conversion_test_utils.cc
index 720f58f..88eb2f12 100644
--- a/content/browser/conversions/conversion_test_utils.cc
+++ b/content/browser/conversions/conversion_test_utils.cc
@@ -271,12 +271,13 @@
                             source_type_, priority_, impression_id_);
 }
 
-StorableConversion DefaultConversion() {
+StorableConversion DefaultConversion(uint64_t event_source_trigger_data) {
   StorableConversion conversion(
       /*conversion_data=*/111,
       /*conversion_destination=*/
       net::SchemefulSite(GURL(kDefaultConversionDestination)),
-      /*reporting_origin=*/url::Origin::Create(GURL(kDefaultReportOrigin)));
+      /*reporting_origin=*/url::Origin::Create(GURL(kDefaultReportOrigin)),
+      event_source_trigger_data);
   return conversion;
 }
 
@@ -289,7 +290,7 @@
         impression.impression_data(), impression.impression_origin(),
         impression.conversion_origin(), impression.reporting_origin(),
         impression.impression_time(), impression.expiry_time(),
-        impression.priority());
+        impression.source_type(), impression.priority());
   };
 
   if (tie(expected) != tie(actual)) {
@@ -311,8 +312,10 @@
                            conversion.impression.reporting_origin(),
                            conversion.impression.impression_time(),
                            conversion.impression.expiry_time(),
+                           conversion.impression.source_type(),
                            conversion.impression.priority(),
-                           conversion.conversion_data, conversion.report_time);
+                           conversion.conversion_data, conversion.report_time,
+                           conversion.extra_delay);
   };
 
   if (expected.size() != actual.size())
diff --git a/content/browser/conversions/conversion_test_utils.h b/content/browser/conversions/conversion_test_utils.h
index 7f48bca..e7ebb986 100644
--- a/content/browser/conversions/conversion_test_utils.h
+++ b/content/browser/conversions/conversion_test_utils.h
@@ -5,8 +5,9 @@
 #ifndef CONTENT_BROWSER_CONVERSIONS_CONVERSION_TEST_UTILS_H_
 #define CONTENT_BROWSER_CONVERSIONS_CONVERSION_TEST_UTILS_H_
 
+#include <stdint.h>
+
 #include <list>
-#include <string>
 #include <vector>
 
 #include "base/containers/circular_deque.h"
@@ -250,7 +251,7 @@
 
 // Returns a StorableConversion with default data which matches the default
 // impressions created by ImpressionBuilder.
-StorableConversion DefaultConversion();
+StorableConversion DefaultConversion(uint64_t event_source_trigger_data = 0);
 
 testing::AssertionResult ImpressionsEqual(const StorableImpression& expected,
                                           const StorableImpression& actual);
diff --git a/content/browser/conversions/conversions_browsertest.cc b/content/browser/conversions/conversions_browsertest.cc
index c673348..33d0530 100644
--- a/content/browser/conversions/conversions_browsertest.cc
+++ b/content/browser/conversions/conversions_browsertest.cc
@@ -575,4 +575,101 @@
   SetBrowserClientForTesting(old_browser_client);
 }
 
+IN_PROC_BROWSER_TEST_F(ConversionsBrowserTest,
+                       EventSourceImpressionConversion_ReportSent) {
+  // Expected reports must be registered before the server starts.
+  // 123 in the `registerConversionForOrigin` call below is sanitized to 1 in
+  // the report's `trigger_data`.
+  ExpectedReportWaiter expected_report(
+      GURL("https://a.test/.well-known/attribution-reporting/"
+           "report-attribution"),
+      /*body=*/R"({"source_event_id":"7","trigger_data":"1"})", https_server());
+  ASSERT_TRUE(https_server()->Start());
+
+  GURL impression_url = https_server()->GetURL(
+      "a.test", "/conversions/page_with_impression_creator.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));
+
+  // Create an anchor tag with impression attributes.
+  GURL conversion_url = https_server()->GetURL(
+      "b.test", "/conversions/page_with_conversion_redirect.html");
+  EXPECT_TRUE(
+      ExecJs(web_contents(),
+             JsReplace(R"(
+    createImpressionTagWithRegisterAttributionSource("link" /* id */,
+                        $1 /* url */,
+                        "7" /* impression data */,
+                        $2 /* conversion_destination */);)",
+                       conversion_url, url::Origin::Create(conversion_url))));
+
+  EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));
+
+  // Register a conversion with the original page as the reporting origin.
+  EXPECT_TRUE(
+      ExecJs(web_contents(),
+             JsReplace(R"(registerConversionForOrigin(0 /* conversion_data */,
+                                       $1,
+                                       123 /* event_source_trigger_data */);)",
+                       url::Origin::Create(impression_url))));
+
+  expected_report.WaitForReport();
+}
+
+IN_PROC_BROWSER_TEST_F(ConversionsBrowserTest,
+                       EventSourceImpressionTwoConversions_OneReportSent) {
+  // Expected reports must be registered before the server starts.
+  // 123 in the `registerConversionForOrigin` call below is sanitized to 1 in
+  // the report's `trigger_data`.
+  ExpectedReportWaiter expected_report(
+      GURL("https://a.test/.well-known/attribution-reporting/"
+           "report-attribution"),
+      /*body=*/R"({"source_event_id":"7","trigger_data":"1"})", https_server());
+  ExpectedReportWaiter expected_report_not_sent(
+      GURL("https://a.test/.well-known/attribution-reporting/"
+           "report-attribution"),
+      /*body=*/"", https_server());
+  ASSERT_TRUE(https_server()->Start());
+
+  GURL impression_url = https_server()->GetURL(
+      "a.test", "/conversions/page_with_impression_creator.html");
+  EXPECT_TRUE(NavigateToURL(web_contents(), impression_url));
+
+  // Create an anchor tag with impression attributes.
+  GURL conversion_url = https_server()->GetURL(
+      "b.test", "/conversions/page_with_conversion_redirect.html");
+  EXPECT_TRUE(
+      ExecJs(web_contents(),
+             JsReplace(R"(
+    createImpressionTagWithRegisterAttributionSource("link" /* id */,
+                        $1 /* url */,
+                        "7" /* impression data */,
+                        $2 /* conversion_destination */);)",
+                       conversion_url, url::Origin::Create(conversion_url))));
+
+  EXPECT_TRUE(NavigateToURL(web_contents(), conversion_url));
+
+  // Register two conversions with the original page as the reporting origin.
+  for (int i = 0; i < 2; i++) {
+    EXPECT_TRUE(
+        ExecJs(web_contents(),
+               JsReplace(R"(registerConversionForOrigin(0 /* conversion_data */,
+                                       $1,
+                                       123 /* event_source_trigger_data */);)",
+                         url::Origin::Create(impression_url))));
+  }
+
+  expected_report.WaitForReport();
+
+  // Since we want to verify that a report _isn't_ sent, we can't really wait on
+  // any event here. The best thing we can do is just impose a short delay and
+  // verify the browser didn't send anything. Worst case, this should start
+  // flakily failing if the logic breaks.
+  base::RunLoop run_loop;
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, run_loop.QuitClosure(),
+      base::TimeDelta::FromMilliseconds(100));
+  run_loop.Run();
+  EXPECT_FALSE(expected_report_not_sent.HasRequest());
+}
+
 }  // namespace content
diff --git a/content/browser/conversions/storable_conversion.cc b/content/browser/conversions/storable_conversion.cc
index f8e9bd82..a1c67c2 100644
--- a/content/browser/conversions/storable_conversion.cc
+++ b/content/browser/conversions/storable_conversion.cc
@@ -13,10 +13,12 @@
 StorableConversion::StorableConversion(
     uint64_t conversion_data,
     net::SchemefulSite conversion_destination,
-    url::Origin reporting_origin)
+    url::Origin reporting_origin,
+    uint64_t event_source_trigger_data)
     : conversion_data_(conversion_data),
       conversion_destination_(std::move(conversion_destination)),
-      reporting_origin_(std::move(reporting_origin)) {
+      reporting_origin_(std::move(reporting_origin)),
+      event_source_trigger_data_(event_source_trigger_data) {
   DCHECK(!reporting_origin_.opaque());
   DCHECK(!conversion_destination_.opaque());
 }
diff --git a/content/browser/conversions/storable_conversion.h b/content/browser/conversions/storable_conversion.h
index 082d3ad..b2e2314 100644
--- a/content/browser/conversions/storable_conversion.h
+++ b/content/browser/conversions/storable_conversion.h
@@ -23,7 +23,8 @@
   // navigation origin known by the browser process.
   StorableConversion(uint64_t conversion_data,
                      net::SchemefulSite conversion_destination,
-                     url::Origin reporting_origin);
+                     url::Origin reporting_origin,
+                     uint64_t event_source_trigger_data);
   StorableConversion(const StorableConversion& other);
   StorableConversion& operator=(const StorableConversion& other) = delete;
   ~StorableConversion();
@@ -36,6 +37,10 @@
 
   const url::Origin& reporting_origin() const { return reporting_origin_; }
 
+  uint64_t event_source_trigger_data() const {
+    return event_source_trigger_data_;
+  }
+
  private:
   // Conversion data associated with conversion registration event.
   uint64_t conversion_data_;
@@ -46,6 +51,10 @@
   // Origin of the conversion redirect url, and the origin that will receive any
   // reports.
   url::Origin reporting_origin_;
+
+  // Event source trigger data specified in conversion redirect. Defaults to 0
+  // if not provided.
+  uint64_t event_source_trigger_data_;
 };
 
 }  // namespace content
diff --git a/content/browser/conversions/storable_impression.h b/content/browser/conversions/storable_impression.h
index e10881c8..19a4063 100644
--- a/content/browser/conversions/storable_impression.h
+++ b/content/browser/conversions/storable_impression.h
@@ -93,8 +93,8 @@
   // If null, an ID has not been assigned yet.
   absl::optional<int64_t> impression_id_;
 
-  // When adding new members, the ImpressionsEqual() testing utility in
-  // conversion_test_utils.h should also be updated.
+  // When adding new members, the `ImpressionsEqual()` testing utility in
+  // `conversion_test_utils.h` should also be updated.
 };
 
 }  // namespace content
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index b54c252..2025b14 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -67,6 +67,7 @@
 #include "content/test/content_browser_test_utils_internal.h"
 #include "content/test/fake_network_url_loader_factory.h"
 #include "content/test/test_content_browser_client.h"
+#include "net/base/features.h"
 #include "net/base/filename_util.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/controllable_http_response.h"
@@ -3989,7 +3990,25 @@
                download->GetTargetFilePath().BaseName().value().c_str());
 }
 
-IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeSameSiteCookie) {
+class DownloadContentSameSiteCookieTest
+    : public DownloadContentTest,
+      public ::testing::WithParamInterface<bool> {
+ public:
+  DownloadContentSameSiteCookieTest() {
+    if (DoesCookieSameSiteConsiderRedirectChain()) {
+      inner_feature_list_.InitAndEnableFeature(
+          net::features::kCookieSameSiteConsidersRedirectChain);
+    }
+  }
+
+  bool DoesCookieSameSiteConsiderRedirectChain() { return GetParam(); }
+
+ private:
+  base::test::ScopedFeatureList inner_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(DownloadContentSameSiteCookieTest,
+                       DownloadAttributeSameSiteCookie) {
   base::ScopedAllowBlockingForTesting allow_blocking;
   net::EmbeddedTestServer test_server;
   ASSERT_TRUE(test_server.InitializeAndListen());
@@ -4048,7 +4067,8 @@
   EXPECT_STREQ("B=C", file_contents.c_str());
 
   // OriginOne redirects through OriginTwo. Because the redirect chain contains
-  // a cross-site redirect, SameSite=Strict cookies are not sent.
+  // a cross-site redirect, SameSite=Strict cookies are not sent (if redirect
+  // chains are considered).
   //
   //  Initiator origin: kOriginOne
   //  Redirect chain contains: kOriginTwo
@@ -4063,9 +4083,17 @@
 
   ASSERT_TRUE(
       base::ReadFileToString(download->GetTargetFilePath(), &file_contents));
-  EXPECT_STREQ("B=C", file_contents.c_str());
+  if (DoesCookieSameSiteConsiderRedirectChain()) {
+    EXPECT_STREQ("B=C", file_contents.c_str());
+  } else {
+    EXPECT_STREQ("A=B; B=C", file_contents.c_str());
+  }
 }
 
+INSTANTIATE_TEST_SUITE_P(/* no label */,
+                         DownloadContentSameSiteCookieTest,
+                         ::testing::Bool());
+
 // The file empty.bin is served with a MIME type of application/octet-stream.
 // The content body is empty. Make sure this case is handled properly and we
 // don't regress on http://crbug.com/320394.
diff --git a/content/browser/media/media_internals_audio_focus_helper.h b/content/browser/media/media_internals_audio_focus_helper.h
index 2863eda..c6dc9fd 100644
--- a/content/browser/media/media_internals_audio_focus_helper.h
+++ b/content/browser/media/media_internals_audio_focus_helper.h
@@ -31,6 +31,7 @@
       media_session::mojom::AudioFocusRequestStatePtr session) override;
   void OnFocusLost(
       media_session::mojom::AudioFocusRequestStatePtr session) override;
+  void OnRequestIdReleased(const base::UnguessableToken&) override {}
 
   // Sets whether we should listen to audio focus events.
   void SetEnabled(bool enabled);
diff --git a/content/browser/media/session/audio_focus_delegate.h b/content/browser/media/session/audio_focus_delegate.h
index a4501db..6890c4d 100644
--- a/content/browser/media/session/audio_focus_delegate.h
+++ b/content/browser/media/session/audio_focus_delegate.h
@@ -46,6 +46,9 @@
 
   // Retrieves the current request ID for the associated |MediaSession|.
   virtual const base::UnguessableToken& request_id() const = 0;
+
+  // Inform the AudioFocusManager that this request ID will no longer be used.
+  virtual void ReleaseRequestId() = 0;
 };
 
 }  // namespace content
diff --git a/content/browser/media/session/audio_focus_delegate_android.h b/content/browser/media/session/audio_focus_delegate_android.h
index de3174b..8fe9f26 100644
--- a/content/browser/media/session/audio_focus_delegate_android.h
+++ b/content/browser/media/session/audio_focus_delegate_android.h
@@ -33,6 +33,7 @@
   absl::optional<media_session::mojom::AudioFocusType> GetCurrentFocusType()
       const override;
   const base::UnguessableToken& request_id() const override;
+  void ReleaseRequestId() override {}
 
   // Called when the Android system requests the MediaSession to be suspended.
   // Called by Java through JNI.
diff --git a/content/browser/media/session/audio_focus_delegate_default.cc b/content/browser/media/session/audio_focus_delegate_default.cc
index b7dc5d2..4729b013 100644
--- a/content/browser/media/session/audio_focus_delegate_default.cc
+++ b/content/browser/media/session/audio_focus_delegate_default.cc
@@ -54,6 +54,7 @@
   const base::UnguessableToken& request_id() const override {
     return request_id_;
   }
+  void ReleaseRequestId() override;
 
  private:
   // Finishes an async audio focus request.
@@ -158,6 +159,19 @@
   session_info_ = session_info.Clone();
 }
 
+void AudioFocusDelegateDefault::ReleaseRequestId() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (!base::FeatureList::IsEnabled(
+          media_session::features::kMediaSessionService)) {
+    return;
+  }
+
+  EnsureServiceConnection();
+
+  audio_focus_->RequestIdReleased(request_id_);
+}
+
 void AudioFocusDelegateDefault::FinishAudioFocusRequest(AudioFocusType type,
                                                         bool success) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/media/session/media_session_controller_unittest.cc b/content/browser/media/session/media_session_controller_unittest.cc
index 76131e3..3c9185d0 100644
--- a/content/browser/media/session/media_session_controller_unittest.cc
+++ b/content/browser/media/session/media_session_controller_unittest.cc
@@ -43,6 +43,7 @@
   const base::UnguessableToken& request_id() const override {
     return base::UnguessableToken::Null();
   }
+  void ReleaseRequestId() override {}
 
  private:
   absl::optional<media_session::mojom::AudioFocusType> audio_focus_type_;
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index a7756939..f51d3b4 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -264,6 +264,8 @@
 #endif
 
 void MediaSessionImpl::WebContentsDestroyed() {
+  delegate_->ReleaseRequestId();
+
   // This should only work for tests. In production, all the players should have
   // already been removed before WebContents is destroyed.
 
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc
index a8e154f..cab6248 100644
--- a/content/browser/media/session/media_session_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -91,6 +91,8 @@
 
   MOCK_CONST_METHOD0(request_id, const base::UnguessableToken&());
 
+  MOCK_METHOD(void, ReleaseRequestId, (), (override));
+
   void ResolveRequest(bool result) {
     if (!async_mode_)
       return;
diff --git a/content/browser/media/session/media_session_impl_unittest.cc b/content/browser/media/session/media_session_impl_unittest.cc
index a529333f..dd33955c 100644
--- a/content/browser/media/session/media_session_impl_unittest.cc
+++ b/content/browser/media/session/media_session_impl_unittest.cc
@@ -62,6 +62,8 @@
 
   MOCK_CONST_METHOD0(request_id, const base::UnguessableToken&());
 
+  MOCK_METHOD(void, ReleaseRequestId, (), (override));
+
   MediaSessionInfo::SessionState GetState() const {
     DCHECK(!session_info_.is_null());
     return session_info_->state;
diff --git a/content/browser/net/http_cookie_browsertest.cc b/content/browser/net/http_cookie_browsertest.cc
index ee7dd8e..f2dc5a6 100644
--- a/content/browser/net/http_cookie_browsertest.cc
+++ b/content/browser/net/http_cookie_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/web_contents.h"
@@ -34,10 +35,15 @@
 // See also (tests for cookie access via JavaScript):
 // //content/browser/renderer_host/cookie_browsertest.cc
 
-class HttpCookieBrowserTest : public ContentBrowserTest {
+class HttpCookieBrowserTest : public ContentBrowserTest,
+                              public ::testing::WithParamInterface<bool> {
  public:
-  HttpCookieBrowserTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+  HttpCookieBrowserTest() : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    if (DoesSameSiteConsiderRedirectChain()) {
+      feature_list_.InitAndEnableFeature(
+          net::features::kCookieSameSiteConsidersRedirectChain);
+    }
+  }
 
   ~HttpCookieBrowserTest() override = default;
 
@@ -57,6 +63,8 @@
                            kHostC));
   }
 
+  bool DoesSameSiteConsiderRedirectChain() { return GetParam(); }
+
   const char* kHostA = "a.test";
   const char* kHostB = "b.test";
   const char* kHostC = "c.test";
@@ -217,9 +225,10 @@
 
  private:
   net::test_server::EmbeddedTestServer https_server_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(HttpCookieBrowserTest, SendSameSiteCookies) {
+IN_PROC_BROWSER_TEST_P(HttpCookieBrowserTest, SendSameSiteCookies) {
   SetSameSiteCookies(kHostA);
   SetSameSiteCookies(kHostB);
 
@@ -267,7 +276,7 @@
       net::CookieStringIs(UnorderedElementsAre(Key(kSameSiteNoneCookieName))));
 }
 
-IN_PROC_BROWSER_TEST_F(HttpCookieBrowserTest, SendSameSiteCookies_Redirect) {
+IN_PROC_BROWSER_TEST_P(HttpCookieBrowserTest, SendSameSiteCookies_Redirect) {
   SetSameSiteCookies(kHostA);
 
   WebContentsImpl* web_contents =
@@ -285,29 +294,56 @@
           Key(kSameSiteStrictCookieName), Key(kSameSiteLaxCookieName),
           Key(kSameSiteNoneCookieName), Key(kSameSiteUnspecifiedCookieName))));
 
-  // Main frame cross-site redirect (B->A) sends Lax but not Strict SameSite
-  // cookies...
-  ASSERT_TRUE(NavigateToURL(
-      web_contents,
-      RedirectUrl(https_server(), kHostB,
-                  EchoCookiesUrl(https_server(), kHostA)),
-      /*expected_commit_url=*/EchoCookiesUrl(https_server(), kHostA)));
-  EXPECT_THAT(ExtractFrameContent(web_contents->GetMainFrame()),
-              net::CookieStringIs(UnorderedElementsAre(
-                  Key(kSameSiteLaxCookieName), Key(kSameSiteNoneCookieName),
-                  Key(kSameSiteUnspecifiedCookieName))));
+  if (DoesSameSiteConsiderRedirectChain()) {
+    // Main frame cross-site redirect (B->A) sends Lax but not Strict SameSite
+    // cookies...
+    ASSERT_TRUE(NavigateToURL(
+        web_contents,
+        RedirectUrl(https_server(), kHostB,
+                    EchoCookiesUrl(https_server(), kHostA)),
+        /*expected_commit_url=*/EchoCookiesUrl(https_server(), kHostA)));
+    EXPECT_THAT(ExtractFrameContent(web_contents->GetMainFrame()),
+                net::CookieStringIs(UnorderedElementsAre(
+                    Key(kSameSiteLaxCookieName), Key(kSameSiteNoneCookieName),
+                    Key(kSameSiteUnspecifiedCookieName))));
 
-  // ... even if the first URL is same-site. (A->B->A)
-  ASSERT_TRUE(NavigateToURL(
-      web_contents,
-      RedirectUrl(https_server(), kHostA,
-                  RedirectUrl(https_server(), kHostB,
-                              EchoCookiesUrl(https_server(), kHostA))),
-      /*expected_commit_url=*/EchoCookiesUrl(https_server(), kHostA)));
-  EXPECT_THAT(ExtractFrameContent(web_contents->GetMainFrame()),
-              net::CookieStringIs(UnorderedElementsAre(
-                  Key(kSameSiteLaxCookieName), Key(kSameSiteNoneCookieName),
-                  Key(kSameSiteUnspecifiedCookieName))));
+    // ... even if the first URL is same-site. (A->B->A)
+    ASSERT_TRUE(NavigateToURL(
+        web_contents,
+        RedirectUrl(https_server(), kHostA,
+                    RedirectUrl(https_server(), kHostB,
+                                EchoCookiesUrl(https_server(), kHostA))),
+        /*expected_commit_url=*/EchoCookiesUrl(https_server(), kHostA)));
+    EXPECT_THAT(ExtractFrameContent(web_contents->GetMainFrame()),
+                net::CookieStringIs(UnorderedElementsAre(
+                    Key(kSameSiteLaxCookieName), Key(kSameSiteNoneCookieName),
+                    Key(kSameSiteUnspecifiedCookieName))));
+  } else {
+    // If redirect chains are not considered, then cross-site redirects do not
+    // make the request cross-site.
+    ASSERT_TRUE(NavigateToURL(
+        web_contents,
+        RedirectUrl(https_server(), kHostB,
+                    EchoCookiesUrl(https_server(), kHostA)),
+        /*expected_commit_url=*/EchoCookiesUrl(https_server(), kHostA)));
+    EXPECT_THAT(ExtractFrameContent(web_contents->GetMainFrame()),
+                net::CookieStringIs(UnorderedElementsAre(
+                    Key(kSameSiteStrictCookieName), Key(kSameSiteLaxCookieName),
+                    Key(kSameSiteNoneCookieName),
+                    Key(kSameSiteUnspecifiedCookieName))));
+
+    ASSERT_TRUE(NavigateToURL(
+        web_contents,
+        RedirectUrl(https_server(), kHostA,
+                    RedirectUrl(https_server(), kHostB,
+                                EchoCookiesUrl(https_server(), kHostA))),
+        /*expected_commit_url=*/EchoCookiesUrl(https_server(), kHostA)));
+    EXPECT_THAT(ExtractFrameContent(web_contents->GetMainFrame()),
+                net::CookieStringIs(UnorderedElementsAre(
+                    Key(kSameSiteStrictCookieName), Key(kSameSiteLaxCookieName),
+                    Key(kSameSiteNoneCookieName),
+                    Key(kSameSiteUnspecifiedCookieName))));
+  }
 
   // A same-site redirected iframe (A->A embedded in A) sends all SameSite
   // cookies.
@@ -320,26 +356,51 @@
           Key(kSameSiteStrictCookieName), Key(kSameSiteLaxCookieName),
           Key(kSameSiteNoneCookieName), Key(kSameSiteUnspecifiedCookieName))));
 
-  // A cross-site redirected iframe in a same-site context (B->A embedded in A)
-  // does not send SameSite cookies...
-  EXPECT_THAT(
-      ArrangeFramesAndGetContentFromLeaf(
-          "a.test(%s)", {0},
-          RedirectUrl(https_server(), kHostB,
-                      EchoCookiesUrl(https_server(), kHostA))),
-      net::CookieStringIs(UnorderedElementsAre(Key(kSameSiteNoneCookieName))));
+  if (DoesSameSiteConsiderRedirectChain()) {
+    // A cross-site redirected iframe in a same-site context (B->A embedded in
+    // A) does not send SameSite cookies...
+    EXPECT_THAT(ArrangeFramesAndGetContentFromLeaf(
+                    "a.test(%s)", {0},
+                    RedirectUrl(https_server(), kHostB,
+                                EchoCookiesUrl(https_server(), kHostA))),
+                net::CookieStringIs(
+                    UnorderedElementsAre(Key(kSameSiteNoneCookieName))));
 
-  // ... even if the first URL is same-site. (A->B->A embedded in A)
-  EXPECT_THAT(
-      ArrangeFramesAndGetContentFromLeaf(
-          "a.test(%s)", {0},
-          RedirectUrl(https_server(), kHostA,
-                      RedirectUrl(https_server(), kHostB,
-                                  EchoCookiesUrl(https_server(), kHostA)))),
-      net::CookieStringIs(UnorderedElementsAre(Key(kSameSiteNoneCookieName))));
+    // ... even if the first URL is same-site. (A->B->A embedded in A)
+    EXPECT_THAT(
+        ArrangeFramesAndGetContentFromLeaf(
+            "a.test(%s)", {0},
+            RedirectUrl(https_server(), kHostA,
+                        RedirectUrl(https_server(), kHostB,
+                                    EchoCookiesUrl(https_server(), kHostA)))),
+        net::CookieStringIs(
+            UnorderedElementsAre(Key(kSameSiteNoneCookieName))));
+  } else {
+    // If redirect chains are not considered, then cross-site redirects do not
+    // make the request cross-site.
+    EXPECT_THAT(ArrangeFramesAndGetContentFromLeaf(
+                    "a.test(%s)", {0},
+                    RedirectUrl(https_server(), kHostB,
+                                EchoCookiesUrl(https_server(), kHostA))),
+                net::CookieStringIs(UnorderedElementsAre(
+                    Key(kSameSiteStrictCookieName), Key(kSameSiteLaxCookieName),
+                    Key(kSameSiteNoneCookieName),
+                    Key(kSameSiteUnspecifiedCookieName))));
+
+    EXPECT_THAT(
+        ArrangeFramesAndGetContentFromLeaf(
+            "a.test(%s)", {0},
+            RedirectUrl(https_server(), kHostA,
+                        RedirectUrl(https_server(), kHostB,
+                                    EchoCookiesUrl(https_server(), kHostA)))),
+        net::CookieStringIs(UnorderedElementsAre(
+            Key(kSameSiteStrictCookieName), Key(kSameSiteLaxCookieName),
+            Key(kSameSiteNoneCookieName),
+            Key(kSameSiteUnspecifiedCookieName))));
+  }
 }
 
-IN_PROC_BROWSER_TEST_F(HttpCookieBrowserTest, SetSameSiteCookies) {
+IN_PROC_BROWSER_TEST_P(HttpCookieBrowserTest, SetSameSiteCookies) {
   WebContentsImpl* web_contents =
       static_cast<WebContentsImpl*>(shell()->web_contents());
 
@@ -373,7 +434,7 @@
   ASSERT_EQ(1U, ClearCookies());
 }
 
-IN_PROC_BROWSER_TEST_F(HttpCookieBrowserTest, SetSameSiteCookies_Redirect) {
+IN_PROC_BROWSER_TEST_P(HttpCookieBrowserTest, SetSameSiteCookies_Redirect) {
   WebContentsImpl* web_contents =
       static_cast<WebContentsImpl*>(shell()->web_contents());
 
@@ -420,18 +481,33 @@
                   net::MatchesCookieWithName(kSameSiteUnspecifiedCookieName)));
   ASSERT_EQ(4U, ClearCookies());
 
-  // A cross-site redirected iframe only sets SameSite=None cookies.
-  EXPECT_THAT(ArrangeFramesAndGetCanonicalCookiesForLeaf(
-                  "a.test(%s)",
-                  RedirectUrl(https_server(), kHostB,
-                              SetSameSiteCookiesUrl(https_server(), kHostA)),
-                  https_server()->GetURL(kHostA, "/")),
-              UnorderedElementsAre(
-                  net::MatchesCookieWithName(kSameSiteNoneCookieName)));
-  ASSERT_EQ(1U, ClearCookies());
+  if (DoesSameSiteConsiderRedirectChain()) {
+    // A cross-site redirected iframe only sets SameSite=None cookies.
+    EXPECT_THAT(ArrangeFramesAndGetCanonicalCookiesForLeaf(
+                    "a.test(%s)",
+                    RedirectUrl(https_server(), kHostB,
+                                SetSameSiteCookiesUrl(https_server(), kHostA)),
+                    https_server()->GetURL(kHostA, "/")),
+                UnorderedElementsAre(
+                    net::MatchesCookieWithName(kSameSiteNoneCookieName)));
+    ASSERT_EQ(1U, ClearCookies());
+  } else {
+    EXPECT_THAT(
+        ArrangeFramesAndGetCanonicalCookiesForLeaf(
+            "a.test(%s)",
+            RedirectUrl(https_server(), kHostB,
+                        SetSameSiteCookiesUrl(https_server(), kHostA)),
+            https_server()->GetURL(kHostA, "/")),
+        UnorderedElementsAre(
+            net::MatchesCookieWithName(kSameSiteStrictCookieName),
+            net::MatchesCookieWithName(kSameSiteLaxCookieName),
+            net::MatchesCookieWithName(kSameSiteNoneCookieName),
+            net::MatchesCookieWithName(kSameSiteUnspecifiedCookieName)));
+    ASSERT_EQ(4U, ClearCookies());
+  }
 }
 
-IN_PROC_BROWSER_TEST_F(HttpCookieBrowserTest, SendSamePartyCookies) {
+IN_PROC_BROWSER_TEST_P(HttpCookieBrowserTest, SendSamePartyCookies) {
   SetSamePartyCookies(kHostA);
   SetSamePartyCookies(kHostD);
 
@@ -515,7 +591,7 @@
                   Key(kSamePartyUnspecifiedCookieName))));
 }
 
-IN_PROC_BROWSER_TEST_F(HttpCookieBrowserTest, SetSamePartyCookies) {
+IN_PROC_BROWSER_TEST_P(HttpCookieBrowserTest, SetSamePartyCookies) {
   WebContentsImpl* web_contents =
       static_cast<WebContentsImpl*>(shell()->web_contents());
 
@@ -606,5 +682,9 @@
   ASSERT_EQ(3U, ClearCookies());
 }
 
+INSTANTIATE_TEST_SUITE_P(/* no label */,
+                         HttpCookieBrowserTest,
+                         ::testing::Bool());
+
 }  // namespace
 }  // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 96764279c..f90add2 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -3358,6 +3358,14 @@
   if (did_create_new_document)
     DidCommitNewDocument(params, navigation_request);
 
+  // Set up reporting endpoints for this document.
+  DCHECK(frame_token_.value());
+  if (!reporting_endpoints_.empty()) {
+    GetStoragePartition()->GetNetworkContext()->SetDocumentReportingEndpoints(
+        params.origin, isolation_info_.network_isolation_key(),
+        reporting_endpoints_);
+  }
+
   // When the frame hosts a different document, its state must be replicated
   // via its proxies to the other processes where it appears as remote.
   //
@@ -5246,7 +5254,7 @@
   if (!in_back_forward_cache) {
     TRACE_EVENT0("navigation", "BackForwardCache_EvictAfterDocumentRestored");
     // TODO(carlscab): We should no longer get into this branch thanks to
-    // https://crrev.com/c/2352815. Lets keep this old code for now just in case
+    // https://crrev.com/c/2563674. Lets keep this old code for now just in case
     // and replace with a CHECK once we are confident that is the case.
     base::debug::DumpWithoutCrashing();
     BackForwardCacheMetrics::RecordEvictedAfterDocumentRestored(
@@ -9791,6 +9799,16 @@
   cross_origin_embedder_policy_ =
       navigation_request->cross_origin_embedder_policy();
 
+  reporting_endpoints_.clear();
+  DCHECK(navigation_request);
+
+  if (navigation_request->response() &&
+      navigation_request->response()->parsed_headers &&
+      navigation_request->response()->parsed_headers->reporting_endpoints) {
+    reporting_endpoints_ =
+        *(navigation_request->response()->parsed_headers->reporting_endpoints);
+  }
+
   // We move the PolicyContainerHost of |navigation_request| into the
   // RenderFrameHost unless this is the initial, "fake" navigation to
   // about:blank (because otherwise we would overwrite the PolicyContainerHost
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index c00698b..59cd84d 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -3718,6 +3718,14 @@
   mojo::UniqueReceiverSet<blink::mojom::CodeCacheHost>
       code_cache_host_receivers_;
 
+  // Holds the mapping of names to URLs of reporting endpoints for the current
+  // document, as parsed from the Reporting-Endpoints response header. This data
+  // comes directly from the structured header parser, and does not necessarily
+  // represent a valid reporting configuration. This is passed to the network
+  // service to set up the actual endpoint configuration once the document load
+  // commits.
+  base::flat_map<std::string, std::string> reporting_endpoints_;
+
   // NOTE: This must be the last member.
   base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_{this};
 
diff --git a/content/browser/renderer_host/render_frame_metadata_provider_impl.h b/content/browser/renderer_host/render_frame_metadata_provider_impl.h
index 3d68f96..bfcd03de 100644
--- a/content/browser/renderer_host/render_frame_metadata_provider_impl.h
+++ b/content/browser/renderer_host/render_frame_metadata_provider_impl.h
@@ -60,7 +60,6 @@
 
  private:
   friend class FakeRenderWidgetHostViewAura;
-  friend class DelegatedInkPointTest;
 
   // Paired with the mojom::RenderFrameMetadataObserverClient overrides, these
   // methods are enqueued in |frame_token_message_queue_|. They are invoked when
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index a107b155..67a4e821 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -68,6 +68,8 @@
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "base/trace_event/trace_event.h"
+#include "base/trace_event/typed_macros.h"
+#include "base/tracing/protos/chrome_track_event.pbzero.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "cc/base/switches.h"
@@ -292,6 +294,8 @@
 
 namespace {
 
+using perfetto::protos::pbzero::ChromeTrackEvent;
+
 // Stores the maximum number of renderer processes the content module can
 // create. Only applies if it is set to a non-zero value.
 size_t g_max_renderer_count_override = 0;
@@ -1712,11 +1716,12 @@
       instance_weak_factory_(absl::in_place, this),
       shutdown_exit_code_(-1) {
   CHECK(!browser_context->ShutdownStarted());
-  TRACE_EVENT2("shutdown", "RenderProcessHostImpl", "render_process_host", this,
-               "id", GetID());
-  TRACE_EVENT_NESTABLE_ASYNC_BEGIN2("shutdown", "Browser.RenderProcessHostImpl",
-                                    this, "render_process_host", this,
-                                    "browser_context", browser_context_);
+  TRACE_EVENT("shutdown", "RenderProcessHostImpl",
+              ChromeTrackEvent::kRenderProcessHost, *this);
+  TRACE_EVENT_BEGIN("shutdown", "Browser.RenderProcessHostImpl",
+                    perfetto::Track::FromPointer(this),
+                    ChromeTrackEvent::kRenderProcessHost, *this);
+
   widget_helper_ = new RenderWidgetHelper();
 
   ChildProcessSecurityPolicyImpl::GetInstance()->Add(GetID(), browser_context);
@@ -1826,8 +1831,8 @@
 }
 
 RenderProcessHostImpl::~RenderProcessHostImpl() {
-  TRACE_EVENT2("shutdown", "~RenderProcessHostImpl", "render_process_host",
-               this, "id", GetID());
+  TRACE_EVENT("shutdown", "~RenderProcessHostImpl",
+              ChromeTrackEvent::kRenderProcessHost, *this);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 #ifndef NDEBUG
   DCHECK(is_self_deleted_)
@@ -1858,16 +1863,19 @@
   if (cleanup_network_service_plugin_exceptions_upon_destruction_)
     RemoveNetworkServicePluginExceptions(GetID());
 
-  TRACE_EVENT_NESTABLE_ASYNC_END1("shutdown", "Cleanup in progress", this,
-                                  "render_process_host", this);
-  TRACE_EVENT_NESTABLE_ASYNC_END1("shutdown", "Browser.RenderProcessHostImpl",
-                                  this, "render_process_host", this);
 
   // Manually delete here in order to avoid DeleteOnIOThread trait when
   // kProcessHostOnUI is enabled.
   if (base::FeatureList::IsEnabled(features::kProcessHostOnUI) && gpu_client_) {
     delete gpu_client_.release();
   }
+
+  // "Cleanup in progress"
+  TRACE_EVENT_END("shutdown", perfetto::Track::FromPointer(this),
+                  ChromeTrackEvent::kRenderProcessHost, *this);
+  // "Browser.RenderProcessHostImpl"
+  TRACE_EVENT_END("shutdown", perfetto::Track::FromPointer(this),
+                  ChromeTrackEvent::kRenderProcessHost, *this);
 }
 
 bool RenderProcessHostImpl::Init() {
@@ -2378,6 +2386,22 @@
                                .ToString());
 }
 
+void RenderProcessHostImpl::WriteIntoTrace(
+    perfetto::TracedProto<perfetto::protos::pbzero::RenderProcessHost> proto) {
+  int id = GetID();
+  proto->set_id(id);
+  proto->set_process_lock(ChildProcessSecurityPolicyImpl::GetInstance()
+                              ->GetProcessLock(id)
+                              .ToString());
+  browser_context_->WriteIntoTrace(
+      proto.WriteNestedMessage<perfetto::protos::pbzero::RenderProcessHost::
+                                   FieldMetadata_BrowserContext>());
+
+  // TODO(ssid): Consider moving this to ChildProcessLauncher proto field.
+  if (child_process_launcher_)
+    proto->set_child_process_id(child_process_launcher_->GetProcess().Pid());
+}
+
 void RenderProcessHostImpl::RegisterMojoInterfaces() {
   auto registry = std::make_unique<service_manager::BinderRegistry>();
 
@@ -2815,9 +2839,8 @@
 }
 
 void RenderProcessHostImpl::DisableKeepAliveRefCount() {
-  TRACE_EVENT2("shutdown", "RenderProcessHostImpl::DisableKeepAliveRefCount",
-               "browser_context", browser_context_, "render_process_host",
-               this);
+  TRACE_EVENT("shutdown", "RenderProcessHostImpl::DisableKeepAliveRefCount",
+              ChromeTrackEvent::kRenderProcessHost, *this);
 
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -2885,16 +2908,26 @@
 
 void RenderProcessHostImpl::AddRoute(int32_t routing_id,
                                      IPC::Listener* listener) {
-  TRACE_EVENT2("shutdown", "RenderProcessHostImpl::AddRoute",
-               "render_process_host", this, "routing_id", routing_id);
+  TRACE_EVENT("shutdown", "RenderProcessHostImpl::AddRoute",
+              ChromeTrackEvent::kRenderProcessHost, *this,
+              [&](perfetto::EventContext ctx) {
+                auto* proto = ctx.event<ChromeTrackEvent>()
+                                  ->set_render_process_host_listener_changed();
+                proto->set_routing_id(routing_id);
+              });
   CHECK(!listeners_.Lookup(routing_id))
       << "Found Routing ID Conflict: " << routing_id;
   listeners_.AddWithID(listener, routing_id);
 }
 
 void RenderProcessHostImpl::RemoveRoute(int32_t routing_id) {
-  TRACE_EVENT2("shutdown", "RenderProcessHostImpl::RemoveRoute",
-               "render_process_host", this, "routing_id", routing_id);
+  TRACE_EVENT("shutdown", "RenderProcessHostImpl::RemoveRoute",
+              ChromeTrackEvent::kRenderProcessHost, *this,
+              [&](perfetto::EventContext ctx) {
+                auto* proto = ctx.event<ChromeTrackEvent>()
+                                  ->set_render_process_host_listener_changed();
+                proto->set_routing_id(routing_id);
+              });
   DCHECK(listeners_.Lookup(routing_id) != nullptr);
   listeners_.Remove(routing_id);
   Cleanup();
@@ -3770,8 +3803,8 @@
 }
 
 void RenderProcessHostImpl::Cleanup() {
-  TRACE_EVENT1("shutdown", "RenderProcessHostImpl::Cleanup",
-               "render_process_host", this);
+  TRACE_EVENT("shutdown", "RenderProcessHostImpl::Cleanup",
+              ChromeTrackEvent::kRenderProcessHost, *this);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Keep the one renderer thread around forever in single process mode.
   if (run_renderer_in_process())
@@ -3783,10 +3816,9 @@
   // been made, and guarantee that the RenderProcessHostDestroyed observer
   // callback is always the last callback fired.
   if (within_process_died_observer_) {
-    TRACE_EVENT1(
-        "shutdown",
-        "RenderProcessHostImpl::Cleanup : within_process_died_observer",
-        "render_process_host", this);
+    TRACE_EVENT("shutdown",
+                "RenderProcessHostImpl::Cleanup : within_process_died_observer",
+                ChromeTrackEvent::kRenderProcessHost, *this);
     delayed_cleanup_needed_ = true;
     return;
   }
@@ -3802,23 +3834,32 @@
   // Until there are no other owners of this object, we can't delete
   // ourselves.
   if (!listeners_.IsEmpty()) {
-    TRACE_EVENT2("shutdown", "RenderProcessHostImpl::Cleanup : Has listeners.",
-                 "render_process_host", this, "listener_count",
-                 listeners_.size());
+    TRACE_EVENT(
+        "shutdown", "RenderProcessHostImpl::Cleanup : Has listeners.",
+        ChromeTrackEvent::kRenderProcessHost, *this,
+        [&](perfetto::EventContext ctx) {
+          auto* proto =
+              ctx.event<ChromeTrackEvent>()->set_render_process_host_cleanup();
+          proto->set_listener_count(listeners_.size());
+        });
     return;
   } else if (keep_alive_ref_count_ != 0) {
-    TRACE_EVENT2("shutdown",
-                 "RenderProcessHostImpl::Cleanup : Have keep_alive_ref.",
-                 "render_process_host", this, "keep_alive_ref_count_",
-                 keep_alive_ref_count_);
+    TRACE_EVENT(
+        "shutdown", "RenderProcessHostImpl::Cleanup : Have keep_alive_ref.",
+        ChromeTrackEvent::kRenderProcessHost, *this,
+        [&](perfetto::EventContext ctx) {
+          auto* proto =
+              ctx.event<ChromeTrackEvent>()->set_render_process_host_cleanup();
+          proto->set_keep_alive_ref_count(keep_alive_ref_count_);
+        });
     return;
   }
 
-  TRACE_EVENT1("shutdown", "RenderProcessHostImpl::Cleanup : Starting cleanup.",
-               "render_process_host", this);
-  TRACE_EVENT_NESTABLE_ASYNC_BEGIN2("shutdown", "Cleanup in progress", this,
-                                    "render_process_host", this,
-                                    "browser_context", browser_context_);
+  TRACE_EVENT("shutdown", "RenderProcessHostImpl::Cleanup : Starting cleanup.",
+              ChromeTrackEvent::kRenderProcessHost, *this);
+  TRACE_EVENT_BEGIN("shutdown", "Cleanup in progress",
+                    perfetto::Track::FromPointer(this),
+                    ChromeTrackEvent::kRenderProcessHost, *this);
 
   if (is_initialized_) {
     GetIOThreadTaskRunner({})->PostTask(
@@ -3993,19 +4034,26 @@
 
 // static
 void RenderProcessHostImpl::RegisterHost(int host_id, RenderProcessHost* host) {
-  TRACE_EVENT2("shutdown", "RenderProcessHostImpl::RegisterHost",
-               "render_process_host", host, "host_id", host_id);
+  TRACE_EVENT(
+      "shutdown", "RenderProcessHostImpl::RegisterHost",
+      [&](perfetto::EventContext ctx) {
+        ctx.event<ChromeTrackEvent>()->set_render_process_host()->set_id(
+            host_id);
+      });
   GetAllHosts().AddWithID(host, host_id);
 }
 
 // static
 void RenderProcessHostImpl::UnregisterHost(int host_id) {
   RenderProcessHost* host = GetAllHosts().Lookup(host_id);
-  TRACE_EVENT2("shutdown", "RenderProcessHostImpl::UnregisterHost",
-               "render_process_host", host, "host_id", host_id);
-
   if (!host)
     return;
+  TRACE_EVENT(
+      "shutdown", "RenderProcessHostImpl::UnregisterHost",
+      [&](perfetto::EventContext ctx) {
+        ctx.event<ChromeTrackEvent>()->set_render_process_host()->set_id(
+            host_id);
+      });
 
   GetAllHosts().Remove(host_id);
 
@@ -4894,9 +4942,9 @@
       priority_.is_background() != priority.is_background();
   const bool visibility_state_changed = priority_.visible != priority.visible;
 
-  TRACE_EVENT2("renderer_host", "RenderProcessHostImpl::UpdateProcessPriority",
-               "should_background", priority.is_background(),
-               "has_pending_views", priority.boost_for_pending_views);
+  TRACE_EVENT("renderer_host", "RenderProcessHostImpl::UpdateProcessPriority",
+              ChromeTrackEvent::kRenderProcessHost, *this,
+              ChromeTrackEvent::kChildProcessLauncherPriority, priority);
   priority_ = priority;
 
   // Control the background state from the browser process, otherwise the task
@@ -4907,13 +4955,6 @@
   if (!run_renderer_in_process()) {
     DCHECK(child_process_launcher_.get());
     DCHECK(!child_process_launcher_->IsStarting());
-    // Make sure to keep the pid in the trace so we can tell which process is
-    // being modified.
-    TRACE_EVENT2(
-        "renderer_host",
-        "RenderProcessHostImpl::UpdateProcessPriority.SetProcessPriority",
-        "pid", child_process_launcher_->GetProcess().Pid(),
-        "priority_is_background", priority.is_background());
     child_process_launcher_->SetProcessPriority(priority_);
   }
 
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 59ee682..c70224e 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -84,6 +84,7 @@
 #include "third_party/blink/public/mojom/plugins/plugin_registry.mojom-forward.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom-forward.h"
 #include "third_party/blink/public/mojom/webdatabase/web_database.mojom-forward.h"
+#include "third_party/perfetto/include/perfetto/tracing/traced_proto.h"
 #include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
@@ -104,6 +105,14 @@
 class SystemTracingService;
 }
 
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+class RenderProcessHost;
+}
+}  // namespace protos
+}  // namespace perfetto
+
 namespace viz {
 class GpuClient;
 }
@@ -296,6 +305,9 @@
     child_process_activity_time_ = base::TimeTicks::Now();
   }
 
+  void WriteIntoTrace(
+      perfetto::TracedProto<perfetto::protos::pbzero::RenderProcessHost> proto);
+
   // Return the set of previously stored frame tokens for a |new_routing_id|.
   // The frame tokens were stored on the IO thread via the
   // RenderMessageFilter::GenerateFrameRoutingID mojo call. Returns false if
diff --git a/content/browser/renderer_host/render_view_host_delegate.cc b/content/browser/renderer_host/render_view_host_delegate.cc
index 42de6d8..9874982a 100644
--- a/content/browser/renderer_host/render_view_host_delegate.cc
+++ b/content/browser/renderer_host/render_view_host_delegate.cc
@@ -10,12 +10,6 @@
   return nullptr;
 }
 
-bool RenderViewHostDelegate::OnMessageReceived(
-    RenderViewHostImpl* render_view_host,
-    const IPC::Message& message) {
-  return false;
-}
-
 WebContents* RenderViewHostDelegate::GetAsWebContents() {
   return nullptr;
 }
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h
index b877d0b7..ba6cb540 100644
--- a/content/browser/renderer_host/render_view_host_delegate.h
+++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -14,10 +14,6 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "net/base/load_states.h"
 
-namespace IPC {
-class Message;
-}
-
 namespace blink {
 namespace web_pref {
 struct WebPreferences;
@@ -53,10 +49,6 @@
   // there is no corresponding delegate.
   virtual RenderViewHostDelegateView* GetDelegateView();
 
-  // This is used to give the delegate a chance to filter IPC messages.
-  virtual bool OnMessageReceived(RenderViewHostImpl* render_view_host,
-                                 const IPC::Message& message);
-
   // Return this object cast to a WebContents, if it is one. If the object is
   // not a WebContents, returns NULL. DEPRECATED: Be sure to include brettw or
   // jam as reviewers before you use this method. http://crbug.com/82582
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 8748d842..2e43f4e 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -771,11 +771,7 @@
 // RenderViewHostImpl, IPC message handlers:
 
 bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) {
-  // Crash reports trigerred by the IPC messages below should be associated
-  // with URL of the main frame.
-  ScopedActiveURL scoped_active_url(this);
-
-  return delegate_->OnMessageReceived(this, msg);
+  return false;
 }
 
 void RenderViewHostImpl::OnDidContentsPreferredSizeChange(
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index b2e08a8..893c804 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -14,7 +14,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
-#include "base/trace_event/trace_event.h"
 #include "components/viz/common/features.h"
 #include "components/viz/common/hit_test/hit_test_region_list.h"
 #include "components/viz/common/quads/surface_draw_quad.h"
@@ -28,7 +27,6 @@
 #include "content/public/browser/render_widget_host_iterator.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "ui/base/layout.h"
-#include "ui/compositor/compositor.h"
 #include "ui/gfx/geometry/dip_util.h"
 
 namespace {
@@ -643,14 +641,6 @@
     mouse_capture_target_ = target;
   }
 
-  if (target) {
-    ui::EventType type = mouse_event.GetTypeAsUiEventType();
-    bool hovering =
-        (type ^ ui::ET_MOUSE_DRAGGED) && (type ^ ui::ET_MOUSE_PRESSED);
-    ForwardDelegatedInkPoint(target, root_view, mouse_event, mouse_event,
-                             hovering);
-  }
-
   DCHECK(target_location.has_value());
   blink::WebMouseEvent event = mouse_event;
   event.SetPositionInWidget(target_location->x(), target_location->y());
@@ -875,11 +865,6 @@
     base::debug::DumpWithoutCrashing();
   }
 
-  if (touch_target_) {
-    ForwardDelegatedInkPoint(touch_target_, root_view, touch_event,
-                             touch_event.touches[0], touch_event.hovering);
-  }
-
   TouchEventAckQueue::TouchEventSource event_source =
       is_emulated_touchevent
           ? TouchEventAckQueue::TouchEventSource::EmulatedTouchEvent
@@ -2003,71 +1988,4 @@
   event_targeter_->SetIsAutoScrollInProgress(is_autoscroll_in_progress);
 }
 
-bool IsMoveEvent(ui::EventType type) {
-  return type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED ||
-         type == ui::ET_TOUCH_MOVED;
-}
-
-void RenderWidgetHostInputEventRouter::ForwardDelegatedInkPoint(
-    RenderWidgetHostViewBase* target_view,
-    RenderWidgetHostViewBase* root_view,
-    const blink::WebInputEvent& input_event,
-    const blink::WebPointerProperties& pointer_properties,
-    bool hovering) {
-  const absl::optional<cc::DelegatedInkBrowserMetadata>& metadata =
-      target_view->host()
-          ->render_frame_metadata_provider()
-          ->LastRenderFrameMetadata()
-          .delegated_ink_metadata;
-
-  if (IsMoveEvent(input_event.GetTypeAsUiEventType()) && metadata &&
-      hovering == metadata.value().delegated_ink_is_hovering) {
-    if (!delegated_ink_point_renderer_.is_bound()) {
-      ui::Compositor* compositor = target_view->GetCompositor();
-
-      // The remote can't be bound if the compositor is null, so bail if that
-      // is the case so we don't crash by trying to use an unbound remote.
-      if (!compositor)
-        return;
-
-      TRACE_EVENT_INSTANT0("input",
-                           "Binding mojo interface for delegated ink points.",
-                           TRACE_EVENT_SCOPE_THREAD);
-      compositor->SetDelegatedInkPointRenderer(
-          delegated_ink_point_renderer_.BindNewPipeAndPassReceiver());
-      delegated_ink_point_renderer_.reset_on_disconnect();
-    }
-
-    gfx::PointF position = pointer_properties.PositionInWidget();
-    root_view->TransformPointToRootSurface(&position);
-    position.Scale(target_view->GetDeviceScaleFactor());
-
-    gfx::DelegatedInkPoint delegated_ink_point(
-        position, input_event.TimeStamp(), pointer_properties.id);
-    TRACE_EVENT_INSTANT1("input",
-                         "Forwarding delegated ink point from browser.",
-                         TRACE_EVENT_SCOPE_THREAD, "delegated point",
-                         delegated_ink_point.ToString());
-
-    // Calling this will result in IPC calls to get |delegated_ink_point| to
-    // viz. The decision to do this here was made with the understanding that
-    // the IPC overhead will result in a minor increase in latency for getting
-    // this event to the renderer. However, by sending it here, the event is
-    // given the greatest possible chance to make it to viz before
-    // DrawAndSwap() is called, allowing more points to be drawn as part of
-    // the delegated ink trail, and thus reducing user perceived latency.
-    delegated_ink_point_renderer_->StoreDelegatedInkPoint(delegated_ink_point);
-    ended_delegated_ink_trail_ = false;
-  } else if (delegated_ink_point_renderer_.is_bound() &&
-             !ended_delegated_ink_trail_) {
-    // Let viz know that the most recent point it received from us is probably
-    // the last point the user is inking, so it shouldn't predict anything
-    // beyond it.
-    TRACE_EVENT_INSTANT0("input", "Delegated ink trail ended",
-                         TRACE_EVENT_SCOPE_THREAD);
-    delegated_ink_point_renderer_->ResetPrediction();
-    ended_delegated_ink_trail_ = true;
-  }
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.h b/content/browser/renderer_host/render_widget_host_input_event_router.h
index b850e9e..3550833 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.h
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.h
@@ -23,10 +23,8 @@
 #include "content/browser/renderer_host/render_widget_host_view_base_observer.h"
 #include "content/browser/renderer_host/render_widget_targeter.h"
 #include "content/common/content_export.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
 #include "ui/gfx/geometry/vector2d_conversions.h"
-#include "ui/gfx/mojom/delegated_ink_point_renderer.mojom.h"
 #include "ui/gfx/transform.h"
 
 namespace blink {
@@ -34,7 +32,6 @@
 class WebInputEvent;
 class WebMouseEvent;
 class WebMouseWheelEvent;
-class WebPointerProperties;
 class WebTouchEvent;
 }
 
@@ -335,18 +332,6 @@
   void SetTouchscreenGestureTarget(RenderWidgetHostViewBase* target,
                                    bool moved_recently = false);
 
-  void ForwardDelegatedInkPoint(
-      RenderWidgetHostViewBase* target_view,
-      RenderWidgetHostViewBase* root_view,
-      const blink::WebInputEvent& input_event,
-      const blink::WebPointerProperties& pointer_properties,
-      bool hovering);
-
-  void FlushForTest() { delegated_ink_point_renderer_.FlushForTesting(); }
-  bool IsDelegatedInkRendererBoundForTest() {
-    return delegated_ink_point_renderer_.is_bound();
-  }
-
   FrameSinkIdOwnerMap owner_map_;
   TargetMap touchscreen_gesture_target_map_;
   RenderWidgetHostViewBase* touch_target_ = nullptr;
@@ -444,18 +429,6 @@
   // Used to prevent multiple dumps.
   bool has_dumped_ = false;
 
-  // Remote end of the connection for sending delegated ink points to viz to
-  // support the delegated ink trails feature.
-  mojo::Remote<gfx::mojom::DelegatedInkPointRenderer>
-      delegated_ink_point_renderer_;
-  // Used to know if we have already told viz to reset prediction because the
-  // final point of the delegated ink trail has been sent. True when prediction
-  // has already been reset for the most recent trail, false otherwise. This
-  // flag helps make sure that we don't send more IPCs than necessary to viz to
-  // reset prediction. Sending extra IPCs wouldn't impact correctness, but can
-  // impact performance due to the IPC overhead.
-  bool ended_delegated_ink_trail_ = false;
-
   base::WeakPtrFactory<RenderWidgetHostInputEventRouter> weak_ptr_factory_{
       this};
 
@@ -477,8 +450,6 @@
                            InputEventRouterWheelTargetTest);
   FRIEND_TEST_ALL_PREFIXES(SitePerProcessMacBrowserTest,
                            InputEventRouterTouchpadGestureTargetTest);
-  FRIEND_TEST_ALL_PREFIXES(SitePerProcessDelegatedInkBrowserTest,
-                           MetadataAndPointGoThroughOOPIF);
 };
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc b/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
index 9db386e..fe41ab1 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
@@ -31,13 +31,6 @@
 #include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
 #include "third_party/blink/public/mojom/input/touch_event.mojom.h"
 
-#if defined(USE_AURA)
-#include "ui/aura/test/aura_test_helper.h"
-#include "ui/aura/test/test_screen.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/gfx/mojom/delegated_ink_point_renderer.mojom.h"
-#endif  // defined(USE_AURA)
-
 namespace content {
 
 namespace {
@@ -107,15 +100,10 @@
 
   void Reset() { last_gesture_seen_ = blink::WebInputEvent::Type::kUndefined; }
 
-  void SetCompositor(ui::Compositor* compositor) { compositor_ = compositor; }
-  ui::Compositor* GetCompositor() override { return compositor_; }
-
  private:
   blink::WebInputEvent::Type last_gesture_seen_ =
       blink::WebInputEvent::Type::kUndefined;
   uint32_t unique_id_for_last_touch_ack_ = 0;
-
-  ui::Compositor* compositor_;
 };
 
 class StubHitTestQuery : public viz::HitTestQuery {
@@ -348,8 +336,6 @@
       RenderWidgetHostViewBase* gesture_target,
       bool should_cancel);
 
-  void FlushInkRenderer() { rwhier()->FlushForTest(); }
-
   BrowserTaskEnvironment task_environment_;
 
   MockRenderWidgetHostDelegate delegate_;
@@ -1059,505 +1045,4 @@
   base::RunLoop().RunUntilIdle();
 }
 
-#if defined(USE_AURA)
-// Mock the DelegatedInkPointRenderer to grab the delegated ink points as they
-// are shipped off to viz from the browser process.
-class MockDelegatedInkPointRenderer
-    : public gfx::mojom::DelegatedInkPointRenderer {
- public:
-  explicit MockDelegatedInkPointRenderer(
-      mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer> receiver)
-      : receiver_(this, std::move(receiver)) {}
-
-  void StoreDelegatedInkPoint(const gfx::DelegatedInkPoint& point) override {
-    delegated_ink_point_ = point;
-  }
-
-  bool HasDelegatedInkPoint() { return delegated_ink_point_.has_value(); }
-
-  gfx::DelegatedInkPoint GetDelegatedInkPoint() {
-    gfx::DelegatedInkPoint point = delegated_ink_point_.value();
-    delegated_ink_point_.reset();
-    return point;
-  }
-
-  void ClearDelegatedInkPoint() { delegated_ink_point_.reset(); }
-
-  void ResetPrediction() override { prediction_reset_ = true; }
-  bool GetPredictionState() {
-    bool state = prediction_reset_;
-    prediction_reset_ = false;
-    return state;
-  }
-
-  void FlushForTesting() { receiver_.FlushForTesting(); }
-
-  void ResetReceiver() { receiver_.reset(); }
-  bool ReceiverIsBound() { return receiver_.is_bound(); }
-
- private:
-  mojo::Receiver<gfx::mojom::DelegatedInkPointRenderer> receiver_;
-  absl::optional<gfx::DelegatedInkPoint> delegated_ink_point_;
-  bool prediction_reset_ = false;
-};
-
-// MockCompositor class binds the mojo interfaces so that the ink points are
-// shipped to the browser process. Uses values from the real compositor to be
-// created, but a fake FrameSinkId must be used so that it hasn't already been
-// registered.
-class MockCompositor : public ui::Compositor {
- public:
-  explicit MockCompositor(ui::Compositor* compositor)
-      : ui::Compositor(viz::FrameSinkId(5, 5),
-                       compositor->context_factory(),
-                       compositor->task_runner(),
-                       compositor->is_pixel_canvas()) {}
-
-  void SetDelegatedInkPointRenderer(
-      mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer> receiver)
-      override {
-    delegated_ink_point_renderer_ =
-        std::make_unique<MockDelegatedInkPointRenderer>(std::move(receiver));
-  }
-
-  MockDelegatedInkPointRenderer* delegated_ink_point_renderer() {
-    return delegated_ink_point_renderer_.get();
-  }
-
- private:
-  std::unique_ptr<MockDelegatedInkPointRenderer> delegated_ink_point_renderer_;
-};
-
-enum TestEvent { kMouseEvent, kTouchEvent };
-enum HoveringState { kHovering, kNotHovering };
-
-class DelegatedInkPointTest
-    : public RenderWidgetHostInputEventRouterTest,
-      public testing::WithParamInterface<std::tuple<TestEvent, HoveringState>> {
- public:
-  DelegatedInkPointTest() = default;
-
-  void SetUp() override {
-    RenderWidgetHostInputEventRouterTest::SetUp();
-
-    aura_test_helper_ = std::make_unique<aura::test::AuraTestHelper>(
-        ImageTransportFactory::GetInstance()->GetContextFactory());
-    aura_test_helper_->SetUp();
-
-    compositor_ = std::make_unique<MockCompositor>(
-        aura_test_helper_->GetHost()->compositor());
-    view_root_->SetCompositor(compositor_.get());
-  }
-
-  void TearDown() override {
-    aura_test_helper_->TearDown();
-    compositor_.reset();
-    RenderWidgetHostInputEventRouterTest::TearDown();
-  }
-
-  TestEvent GetEventParam() { return std::get<0>(GetParam()); }
-  HoveringState GetHoverParam() { return std::get<1>(GetParam()); }
-
-  void SetInkMetadataFlagOnRenderFrameMetadata(bool delegated_ink) {
-    SetInkMetadataFlagOnSpecificHost(delegated_ink, widget_host_root_.get());
-  }
-
-  void SetInkMetadataFlagOnSpecificHost(bool delegated_ink,
-                                        RenderWidgetHostImpl* widget_host) {
-    cc::RenderFrameMetadata metadata;
-    if (delegated_ink) {
-      metadata.delegated_ink_metadata = cc::DelegatedInkBrowserMetadata(
-          GetHoverParam() == HoveringState::kHovering);
-    }
-    widget_host->render_frame_metadata_provider()
-        ->SetLastRenderFrameMetadataForTest(metadata);
-  }
-
-  void SendEvent(bool match_test_hovering_state,
-                 gfx::PointF point,
-                 base::TimeTicks timestamp = base::TimeTicks::Now()) {
-    SendEvent(match_test_hovering_state, point, timestamp,
-              /*use_enter_event*/ false, /*use_exit_event*/ false);
-  }
-
-  void SendEvent(bool match_test_hovering_state,
-                 const gfx::PointF& point,
-                 base::TimeTicks timestamp,
-                 bool use_enter_event,
-                 bool use_exit_event) {
-    DCHECK(!(use_enter_event && use_exit_event));
-
-    // Hovering creates and sends ui::MouseEvents with
-    // ET_MOUSE_{MOVED,ENTERED,EXITED} types, so do the same here in hovering
-    // scenarios.
-    if (GetEventParam() == TestEvent::kTouchEvent &&
-        !Hovering(match_test_hovering_state)) {
-      blink::WebInputEvent::Type event_type =
-          blink::WebInputEvent::Type::kTouchMove;
-      blink::WebTouchPoint::State touch_state =
-          blink::WebTouchPoint::State::kStateMoved;
-      if (use_enter_event) {
-        event_type = blink::WebInputEvent::Type::kTouchStart;
-        touch_state = blink::WebTouchPoint::State::kStatePressed;
-        // Set this now so that if we are going to send a enter event anyway,
-        // we don't send two.
-        sent_touch_press_ = true;
-      }
-      if (use_exit_event) {
-        event_type = blink::WebInputEvent::Type::kTouchEnd;
-        touch_state = blink::WebTouchPoint::State::kStateReleased;
-      }
-
-      // Touch needs a pressed event first to properly handle future move
-      // events.
-      SendTouchPress(point);
-
-      blink::WebTouchEvent touch_event(
-          event_type, blink::WebInputEvent::kNoModifiers, timestamp);
-      touch_event.touches_length = 1;
-      touch_event.touches[0].id = kPointerId;
-      touch_event.touches[0].SetPositionInWidget(point);
-      touch_event.touches[0].state = touch_state;
-      touch_event.unique_touch_event_id = GetTouchId();
-
-      rwhier()->RouteTouchEvent(view_root_.get(), &touch_event,
-                                ui::LatencyInfo(ui::SourceEventType::TOUCH));
-
-      // Need to send a new press event after ending the previous touch.
-      if (use_exit_event)
-        sent_touch_press_ = false;
-    } else {
-      blink::WebInputEvent::Type event_type =
-          blink::WebInputEvent::Type::kMouseMove;
-      if (use_enter_event)
-        event_type = blink::WebInputEvent::Type::kMouseEnter;
-      if (use_exit_event)
-        event_type = blink::WebInputEvent::Type::kMouseLeave;
-
-      int modifiers = 0;
-      if (!Hovering(match_test_hovering_state))
-        modifiers = blink::WebInputEvent::kLeftButtonDown;
-
-      blink::WebMouseEvent mouse_event(event_type, modifiers, timestamp,
-                                       kPointerId);
-      mouse_event.SetPositionInWidget(point);
-
-      rwhier()->RouteMouseEvent(view_root_.get(), &mouse_event,
-                                ui::LatencyInfo(ui::SourceEventType::MOUSE));
-    }
-  }
-
-  void SetDeviceScaleFactor(float dsf) {
-    aura_test_helper_->GetTestScreen()->SetDeviceScaleFactor(dsf);
-  }
-
-  MockCompositor* compositor() { return compositor_.get(); }
-
-  int32_t GetExpectedPointerId() const { return kPointerId; }
-
- private:
-  void SendTouchPress(const gfx::PointF& requested_touch_location) {
-    DCHECK(GetEventParam() == TestEvent::kTouchEvent);
-    if (sent_touch_press_)
-      return;
-
-    // Location of the press event doesn't matter, so long as it doesn't exactly
-    // match the location of the subsequent move event. If they match, then the
-    // move event is dropped.
-    gfx::PointF point(requested_touch_location.x() + 2.f,
-                      requested_touch_location.y() + 2.f);
-
-    // Send a TouchStart/End sequence.
-    blink::WebTouchEvent press(
-        blink::WebInputEvent::Type::kTouchStart,
-        blink::WebInputEvent::kNoModifiers,
-        blink::WebInputEvent::GetStaticTimeStampForTests());
-    press.touches_length = 1;
-    press.touches[0].id = kPointerId;
-    press.touches[0].SetPositionInWidget(point);
-    press.touches[0].state = blink::WebTouchPoint::State::kStatePressed;
-    press.unique_touch_event_id = GetTouchId();
-
-    rwhier()->RouteTouchEvent(view_root_.get(), &press,
-                              ui::LatencyInfo(ui::SourceEventType::TOUCH));
-    sent_touch_press_ = true;
-  }
-
-  bool Hovering(bool match_test_hovering_state) {
-    return (GetHoverParam() == HoveringState::kHovering &&
-            match_test_hovering_state) ||
-           (GetHoverParam() == HoveringState::kNotHovering &&
-            !match_test_hovering_state);
-  }
-
-  // Unique touch id is unique per event, so always increment before providing
-  // a new one.
-  int GetTouchId() { return ++unique_touch_id_; }
-
-  // Pointer id to use in these tests. It must be consistent throughout a single
-  // test for some of the touch variations.
-  const int32_t kPointerId = 5;
-
-  // Touch events are ignored if a press isn't sent first, so use this to track
-  // if we have already sent a touch press event yet or not.
-  bool sent_touch_press_ = false;
-
-  // Most recently used unique touch id for blink::WebTouchEvents
-  int unique_touch_id_ = 0;
-
-  // Helper for creating a compositor and setting the device scale factor.
-  std::unique_ptr<aura::test::AuraTestHelper> aura_test_helper_;
-
-  // Mock compositor used for getting the delegated ink points that are
-  // forwarded.
-  std::unique_ptr<MockCompositor> compositor_;
-};
-
-struct DelegatedInkPointTestPassToString {
-  std::string operator()(
-      const testing::TestParamInfo<std::tuple<TestEvent, HoveringState>> type)
-      const {
-    std::string suffix;
-
-    if (std::get<0>(type.param) == TestEvent::kMouseEvent)
-      suffix.append("Mouse");
-    else
-      suffix.append("Touch");
-
-    if (std::get<1>(type.param) == HoveringState::kHovering)
-      suffix.append("Hovering");
-    else
-      suffix.append("NotHovering");
-
-    return suffix;
-  }
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    DelegatedInkTrails,
-    DelegatedInkPointTest,
-    testing::Combine(
-        testing::Values(TestEvent::kMouseEvent, TestEvent::kTouchEvent),
-        testing::Values(HoveringState::kHovering, HoveringState::kNotHovering)),
-    DelegatedInkPointTestPassToString());
-
-// Tests to confirm that input events are correctly forwarded to the UI
-// Compositor when DelegatedInkTrails should be drawn, and stops forwarding when
-// they no longer should be drawn.
-TEST_P(DelegatedInkPointTest, EventForwardedToCompositor) {
-  // First confirm that the flag is false by default and the point is not sent.
-  SendEvent(true, gfx::PointF(15, 15));
-  MockDelegatedInkPointRenderer* delegated_ink_point_renderer =
-      compositor()->delegated_ink_point_renderer();
-
-  EXPECT_FALSE(delegated_ink_point_renderer);
-
-  // Then set it to true and confirm that the DelegatedInkPointRenderer is
-  // initialized, the connection is made and the point makes it to the renderer.
-  SetInkMetadataFlagOnRenderFrameMetadata(true);
-  gfx::DelegatedInkPoint expected_point(
-      gfx::PointF(10, 10), base::TimeTicks::Now(), GetExpectedPointerId());
-  SendEvent(true, expected_point.point(), expected_point.timestamp());
-
-  delegated_ink_point_renderer = compositor()->delegated_ink_point_renderer();
-  EXPECT_TRUE(delegated_ink_point_renderer);
-  delegated_ink_point_renderer->FlushForTesting();
-
-  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  gfx::DelegatedInkPoint actual_point =
-      delegated_ink_point_renderer->GetDelegatedInkPoint();
-  EXPECT_EQ(expected_point.point(), actual_point.point());
-  EXPECT_EQ(expected_point.timestamp(), actual_point.timestamp());
-  EXPECT_EQ(GetExpectedPointerId(), actual_point.pointer_id());
-
-  // Then try changing the scale factor to confirm it affects the point
-  // correctly.
-  const float scale = 2.6f;
-  SetDeviceScaleFactor(scale);
-  gfx::PointF unscaled_point(15, 15);
-  base::TimeTicks unscaled_time = base::TimeTicks::Now();
-
-  SendEvent(true, unscaled_point, unscaled_time);
-  delegated_ink_point_renderer->FlushForTesting();
-
-  unscaled_point.Scale(scale);
-  expected_point = gfx::DelegatedInkPoint(unscaled_point, unscaled_time,
-                                          GetExpectedPointerId());
-
-  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  actual_point = delegated_ink_point_renderer->GetDelegatedInkPoint();
-  EXPECT_EQ(expected_point.point(), actual_point.point());
-  EXPECT_EQ(expected_point.timestamp(), actual_point.timestamp());
-  EXPECT_EQ(GetExpectedPointerId(), actual_point.pointer_id());
-
-  // Confirm that prediction is reset when the API is no longer being used and
-  // |delegated_ink_metadata| is not set.
-  SetInkMetadataFlagOnRenderFrameMetadata(false);
-
-  SendEvent(true, gfx::PointF(25, 25));
-  delegated_ink_point_renderer->FlushForTesting();
-
-  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  EXPECT_TRUE(delegated_ink_point_renderer->GetPredictionState());
-
-  // Finally, confirm that nothing is sent after the prediction has been reset
-  // when the delegated ink flag on the render frame metadata is false.
-  SendEvent(true, gfx::PointF(46, 46));
-  delegated_ink_point_renderer->FlushForTesting();
-
-  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  EXPECT_FALSE(delegated_ink_point_renderer->GetPredictionState());
-}
-
-// Confirm that the interface is rebound if the receiver disconnects.
-TEST_P(DelegatedInkPointTest, MojoInterfaceReboundOnDisconnect) {
-  // First make sure the connection exists.
-  SetInkMetadataFlagOnRenderFrameMetadata(true);
-  SendEvent(true, gfx::PointF(15, 15));
-
-  MockDelegatedInkPointRenderer* delegated_ink_point_renderer =
-      compositor()->delegated_ink_point_renderer();
-
-  EXPECT_TRUE(delegated_ink_point_renderer);
-  EXPECT_TRUE(delegated_ink_point_renderer->ReceiverIsBound());
-
-  // Reset the receiver and flush the remote to confirm it is no longer bound.
-  delegated_ink_point_renderer->ResetReceiver();
-  FlushInkRenderer();
-
-  EXPECT_FALSE(delegated_ink_point_renderer->ReceiverIsBound());
-
-  // Confirm that it now gets reconnected correctly.
-  SendEvent(true, gfx::PointF(25, 25));
-
-  delegated_ink_point_renderer = compositor()->delegated_ink_point_renderer();
-
-  EXPECT_TRUE(delegated_ink_point_renderer);
-  EXPECT_TRUE(delegated_ink_point_renderer->ReceiverIsBound());
-}
-
-// Test to confirm that forwarding points to viz will stop and prediction is
-// reset if the state of hovering differs between what is expected and the
-// received points.
-TEST_P(DelegatedInkPointTest, StopForwardingOnHoverStateChange) {
-  // First send a point and make sure it makes it to the renderer.
-  SetInkMetadataFlagOnRenderFrameMetadata(true);
-  SendEvent(true, gfx::PointF(15, 15));
-
-  MockDelegatedInkPointRenderer* delegated_ink_point_renderer =
-      compositor()->delegated_ink_point_renderer();
-  EXPECT_TRUE(delegated_ink_point_renderer);
-  delegated_ink_point_renderer->FlushForTesting();
-
-  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  delegated_ink_point_renderer->ClearDelegatedInkPoint();
-
-  // Now send a point that doesn't match the state of hovering on the metadata
-  // to confirm that it isn't sent and ResetPrediction is called.
-  SendEvent(false, gfx::PointF(20, 20));
-  delegated_ink_point_renderer->FlushForTesting();
-
-  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  EXPECT_TRUE(delegated_ink_point_renderer->GetPredictionState());
-
-  // Send another that doesn't match to confirm the end trail point is only sent
-  // once.
-  SendEvent(false, gfx::PointF(25, 25));
-  delegated_ink_point_renderer->FlushForTesting();
-  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-
-  // Send one that does match again to confirm that points will start sending
-  // again if the hovering state starts matching again.
-  SendEvent(true, gfx::PointF(30, 30));
-  delegated_ink_point_renderer->FlushForTesting();
-
-  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  EXPECT_FALSE(delegated_ink_point_renderer->GetPredictionState());
-}
-
-// Confirm that only move events are forwarded, not enter/exit or equivalent
-// events.
-TEST_P(DelegatedInkPointTest, IgnoreEnterAndExitEvents) {
-  // First set everything up and try forwarding a point, confirming that it is
-  // sent as expected.
-  SetInkMetadataFlagOnRenderFrameMetadata(true);
-  gfx::DelegatedInkPoint expected_point(
-      gfx::PointF(10, 10), base::TimeTicks::Now(), GetExpectedPointerId());
-  SendEvent(true, expected_point.point(), expected_point.timestamp());
-
-  MockDelegatedInkPointRenderer* delegated_ink_point_renderer =
-      compositor()->delegated_ink_point_renderer();
-  EXPECT_TRUE(delegated_ink_point_renderer);
-  delegated_ink_point_renderer->FlushForTesting();
-
-  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  gfx::DelegatedInkPoint actual_point =
-      delegated_ink_point_renderer->GetDelegatedInkPoint();
-  EXPECT_EQ(expected_point.point(), actual_point.point());
-  EXPECT_EQ(expected_point.timestamp(), actual_point.timestamp());
-  EXPECT_EQ(GetExpectedPointerId(), actual_point.pointer_id());
-
-  // Now try with an exit event.
-  SendEvent(true, gfx::PointF(42, 19), base::TimeTicks::Now(),
-            /*use_enter_event=*/false, /*use_exit_event=*/true);
-  delegated_ink_point_renderer->FlushForTesting();
-  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-
-  // Try sending an enter event and confirm it is not forwarded.
-  SendEvent(true, gfx::PointF(12, 12), base::TimeTicks::Now(),
-            /*use_enter_event=*/true, /*use_exit_event=*/false);
-  delegated_ink_point_renderer->FlushForTesting();
-  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-
-  // Finally, confirm that sending move events will work again without issue.
-  expected_point = gfx::DelegatedInkPoint(
-      gfx::PointF(20, 21), base::TimeTicks::Now(), GetExpectedPointerId());
-  SendEvent(true, expected_point.point(), expected_point.timestamp());
-
-  delegated_ink_point_renderer->FlushForTesting();
-
-  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  actual_point = delegated_ink_point_renderer->GetDelegatedInkPoint();
-  EXPECT_EQ(expected_point.point(), actual_point.point());
-  EXPECT_EQ(expected_point.timestamp(), actual_point.timestamp());
-  EXPECT_EQ(GetExpectedPointerId(), actual_point.pointer_id());
-}
-
-// This test confirms that points can be forwarded when using delegated ink in
-// a child frame, such as an OOPIF.
-TEST_P(DelegatedInkPointTest, ForwardPointsToChildFrame) {
-  // Make the child frame, set the delegated ink flag on it, give it a
-  // compositor, and set it as the hit test result so that the input router
-  // sends points to it.
-  ChildViewState child = MakeChildView(view_root_.get());
-  SetInkMetadataFlagOnSpecificHost(true, child.widget_host.get());
-  child.view->SetCompositor(compositor());
-  view_root_->SetHittestResult(child.view.get(), false);
-
-  // Send a point and confirm that it is forwarded, meaning that it correctly
-  // checked the metadata flag on the child frame's widget.
-  gfx::DelegatedInkPoint expected_point(
-      gfx::PointF(10, 10), base::TimeTicks::Now(), GetExpectedPointerId());
-  SendEvent(true, expected_point.point(), expected_point.timestamp(), false,
-            false);
-
-  MockDelegatedInkPointRenderer* delegated_ink_point_renderer =
-      compositor()->delegated_ink_point_renderer();
-  EXPECT_TRUE(delegated_ink_point_renderer);
-  delegated_ink_point_renderer->FlushForTesting();
-
-  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
-  gfx::DelegatedInkPoint actual_point =
-      delegated_ink_point_renderer->GetDelegatedInkPoint();
-  EXPECT_EQ(expected_point.point(), actual_point.point());
-  EXPECT_EQ(expected_point.timestamp(), actual_point.timestamp());
-  EXPECT_EQ(GetExpectedPointerId(), actual_point.pointer_id());
-
-  // Reset's the hit test result on the root so that we don't crash on
-  // destruction.
-  rwhier()->OnRenderWidgetHostViewBaseDestroyed(child.view.get());
-}
-
-#endif  // defined(USE_AURA)
-
 }  // namespace content
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 053eaaf..d8365d0 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1855,10 +1855,10 @@
 }
 
 viz::FrameSinkId RenderWidgetHostViewAura::GetRootFrameSinkId() {
-  if (!GetCompositor())
+  if (!window_ || !window_->GetHost() || !window_->GetHost()->compositor())
     return viz::FrameSinkId();
 
-  return GetCompositor()->frame_sink_id();
+  return window_->GetHost()->compositor()->frame_sink_id();
 }
 
 viz::SurfaceId RenderWidgetHostViewAura::GetCurrentSurfaceId() const {
@@ -2377,7 +2377,7 @@
   UpdateLegacyWin();
 #endif
 
-  delegated_frame_host_->AttachToCompositor(GetCompositor());
+    delegated_frame_host_->AttachToCompositor(window_->GetHost()->compositor());
 }
 
 void RenderWidgetHostViewAura::RemovingFromRootWindow() {
@@ -2730,11 +2730,4 @@
     tooltip_observer_for_testing_->OnTooltipTextUpdated(tooltip_text);
 }
 
-ui::Compositor* RenderWidgetHostViewAura::GetCompositor() {
-  if (!window_ || !window_->GetHost())
-    return nullptr;
-
-  return window_->GetHost()->compositor();
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h
index ea2aee1..b881bdc 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.h
+++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -373,8 +373,6 @@
 
   MouseWheelPhaseHandler* GetMouseWheelPhaseHandler() override;
 
-  ui::Compositor* GetCompositor() override;
-
  protected:
   ~RenderWidgetHostViewAura() override;
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index a90ac41..466e96ff 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -120,6 +120,7 @@
 #include "ui/events/keycodes/keyboard_code_conversion.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/mojom/delegated_ink_point_renderer.mojom.h"
 #include "ui/gfx/selection_bound.h"
 #include "ui/wm/core/window_util.h"
 
@@ -285,6 +286,9 @@
     return event_handler()->pointer_state();
   }
 
+  // Flush the mojo remote on the event handler for testing purposes.
+  void FlushForTest() { event_handler()->FlushForTest(); }
+
   void SetRenderFrameMetadata(cc::RenderFrameMetadata metadata) {
     host()->render_frame_metadata_provider()->SetLastRenderFrameMetadataForTest(
         metadata);
@@ -6754,4 +6758,428 @@
 
 #endif  // defined(OS_WIN)
 
+// Mock the DelegatedInkPointRenderer to grab the delegated ink points as they
+// are shipped off to viz from the browser process.
+class MockDelegatedInkPointRenderer
+    : public gfx::mojom::DelegatedInkPointRenderer {
+ public:
+  explicit MockDelegatedInkPointRenderer(
+      mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer> receiver)
+      : receiver_(this, std::move(receiver)) {}
+
+  void StoreDelegatedInkPoint(const gfx::DelegatedInkPoint& point) override {
+    delegated_ink_point_ = point;
+  }
+
+  bool HasDelegatedInkPoint() { return delegated_ink_point_.has_value(); }
+
+  gfx::DelegatedInkPoint GetDelegatedInkPoint() {
+    gfx::DelegatedInkPoint point = delegated_ink_point_.value();
+    delegated_ink_point_.reset();
+    return point;
+  }
+
+  void ClearDelegatedInkPoint() { delegated_ink_point_.reset(); }
+
+  void ResetPrediction() override { prediction_reset_ = true; }
+  bool GetPredictionState() {
+    bool state = prediction_reset_;
+    prediction_reset_ = false;
+    return state;
+  }
+
+  void FlushForTesting() { receiver_.FlushForTesting(); }
+
+  void ResetReceiver() { receiver_.reset(); }
+  bool ReceiverIsBound() { return receiver_.is_bound(); }
+
+ private:
+  mojo::Receiver<gfx::mojom::DelegatedInkPointRenderer> receiver_;
+  absl::optional<gfx::DelegatedInkPoint> delegated_ink_point_;
+  bool prediction_reset_ = false;
+};
+
+// MockCompositor class binds the mojo interfaces so that the ink points are
+// shipped to the browser process. Uses values from the real compositor to be
+// created, but a fake FrameSinkId must be used so that it hasn't already been
+// registered.
+class MockCompositor : public ui::Compositor {
+ public:
+  explicit MockCompositor(ui::Compositor* compositor)
+      : ui::Compositor(viz::FrameSinkId(5, 5),
+                       compositor->context_factory(),
+                       compositor->task_runner(),
+                       compositor->is_pixel_canvas()) {}
+
+  void SetDelegatedInkPointRenderer(
+      mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer> receiver)
+      override {
+    delegated_ink_point_renderer_ =
+        std::make_unique<MockDelegatedInkPointRenderer>(std::move(receiver));
+  }
+
+  MockDelegatedInkPointRenderer* delegated_ink_point_renderer() {
+    return delegated_ink_point_renderer_.get();
+  }
+
+ private:
+  std::unique_ptr<MockDelegatedInkPointRenderer> delegated_ink_point_renderer_;
+};
+
+enum TestEvent { kMouseEvent, kTouchEvent };
+enum HoveringState { kHovering, kNotHovering };
+
+class DelegatedInkPointTest
+    : public RenderWidgetHostViewAuraTest,
+      public testing::WithParamInterface<std::tuple<TestEvent, HoveringState>> {
+ public:
+  DelegatedInkPointTest() = default;
+
+  void SetUp() override {
+    RenderWidgetHostViewAuraTest::SetUp();
+
+    InitViewForFrame(nullptr);
+    aura_test_helper_->GetTestScreen()->SetDeviceScaleFactor(1.0f);
+
+    real_compositor_ = view_->GetNativeView()->layer()->GetCompositor();
+    compositor_ = std::make_unique<MockCompositor>(
+        aura_test_helper_->GetHost()->compositor());
+    view_->GetNativeView()->layer()->SetCompositorForTesting(compositor_.get());
+  }
+
+  void TearDown() override {
+    // Restore the view's compositor to the old value so it no longer references
+    // the MockCompositor that is about to be destructed. This also ensures
+    // that the view can be properly destroyed by TearDown().
+    view_->GetNativeView()->layer()->SetCompositorForTesting(real_compositor_);
+    compositor_.reset();
+    RenderWidgetHostViewAuraTest::TearDown();
+  }
+
+  TestEvent GetEventParam() { return std::get<0>(GetParam()); }
+  HoveringState GetHoverParam() { return std::get<1>(GetParam()); }
+
+  void SetInkMetadataFlagOnRenderFrameMetadata(bool delegated_ink) {
+    cc::RenderFrameMetadata metadata;
+    if (delegated_ink) {
+      metadata.delegated_ink_metadata = cc::DelegatedInkBrowserMetadata(
+          GetHoverParam() == HoveringState::kHovering);
+    }
+    view_->SetRenderFrameMetadata(metadata);
+  }
+
+  void SendEvent(bool match_test_hovering_state,
+                 gfx::PointF point,
+                 base::TimeTicks timestamp = ui::EventTimeForNow()) {
+    SendEvent(match_test_hovering_state, point, timestamp,
+              /*use_enter_event*/ false, /*use_exit_event*/ false);
+  }
+
+  void SendEvent(bool match_test_hovering_state,
+                 const gfx::PointF& point,
+                 base::TimeTicks timestamp,
+                 bool use_enter_event,
+                 bool use_exit_event) {
+    DCHECK(!(use_enter_event && use_exit_event));
+
+    // Hovering creates and sends ui::MouseEvents with
+    // ET_MOUSE_{MOVED,ENTERED,EXITED} types, so do the same here in hovering
+    // scenarios.
+    if (GetEventParam() == TestEvent::kTouchEvent &&
+        !Hovering(match_test_hovering_state)) {
+      ui::EventType event_type = ui::ET_TOUCH_MOVED;
+      if (use_enter_event)
+        event_type = ui::ET_TOUCH_PRESSED;
+      if (use_exit_event)
+        event_type = ui::ET_TOUCH_RELEASED;
+
+      // Touch needs a pressed event first to properly handle future move
+      // events.
+      SendTouchPress(point);
+
+      ui::TouchEvent touch_event(
+          event_type, point, point, timestamp,
+          ui::PointerDetails(ui::EventPointerType::kTouch, kPointerId));
+      view_->OnTouchEvent(&touch_event);
+
+      // Need to send a new press event after ending the previous touch.
+      if (use_exit_event)
+        sent_touch_press_ = false;
+    } else {
+      ui::EventPointerType pointer_type = ui::EventPointerType::kMouse;
+      if (GetEventParam() == TestEvent::kTouchEvent) {
+        DCHECK(Hovering(match_test_hovering_state));
+        pointer_type = ui::EventPointerType::kPen;
+      }
+
+      ui::EventType event_type = ui::ET_MOUSE_MOVED;
+      if (use_enter_event)
+        event_type = ui::ET_MOUSE_ENTERED;
+      if (use_exit_event)
+        event_type = ui::ET_MOUSE_EXITED;
+
+      int flags =
+          Hovering(match_test_hovering_state) ? 0 : ui::EF_LEFT_MOUSE_BUTTON;
+      ui::MouseEvent mouse_event(event_type, point, point, timestamp, flags, 0,
+                                 ui::PointerDetails(pointer_type, kPointerId));
+      view_->OnMouseEvent(&mouse_event);
+    }
+  }
+
+  MockCompositor* compositor() { return compositor_.get(); }
+
+  int32_t GetExpectedPointerId() const { return kPointerId; }
+
+ private:
+  void SendTouchPress(const gfx::PointF& requested_touch_location) {
+    DCHECK(GetEventParam() == TestEvent::kTouchEvent);
+    if (sent_touch_press_)
+      return;
+
+    // Location of the press event doesn't matter, so long as it doesn't exactly
+    // match the location of the subsequent move event. If they match, then the
+    // move event is dropped.
+    gfx::PointF point(requested_touch_location.x() + 2.f,
+                      requested_touch_location.y() + 2.f);
+
+    ui::TouchEvent press(
+        ui::ET_TOUCH_PRESSED, point, point, ui::EventTimeForNow(),
+        ui::PointerDetails(ui::EventPointerType::kTouch, kPointerId));
+
+    view_->OnTouchEvent(&press);
+    sent_touch_press_ = true;
+  }
+
+  bool Hovering(bool match_test_hovering_state) {
+    return (GetHoverParam() == HoveringState::kHovering &&
+            match_test_hovering_state) ||
+           (GetHoverParam() == HoveringState::kNotHovering &&
+            !match_test_hovering_state);
+  }
+
+  // Pointer id to use in these tests. It must be consistent throughout a single
+  // test for some of the touch variations.
+  const int32_t kPointerId = 5;
+
+  // Touch events are ignored if a press isn't sent first, so use this to track
+  // if we have already sent a touch press event yet or not.
+  bool sent_touch_press_ = false;
+
+  // The real compositor that was contained by the |view_| and must be replaced
+  // before tear down, and the mock compositor used for getting the delegated
+  // ink points.
+  ui::Compositor* real_compositor_;
+  std::unique_ptr<MockCompositor> compositor_;
+};
+
+struct DelegatedInkPointTestPassToString {
+  std::string operator()(
+      const testing::TestParamInfo<std::tuple<TestEvent, HoveringState>> type)
+      const {
+    std::string suffix;
+
+    if (std::get<0>(type.param) == TestEvent::kMouseEvent)
+      suffix.append("Mouse");
+    else
+      suffix.append("Touch");
+
+    if (std::get<1>(type.param) == HoveringState::kHovering)
+      suffix.append("Hovering");
+    else
+      suffix.append("NotHovering");
+
+    return suffix;
+  }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    DelegatedInkTrails,
+    DelegatedInkPointTest,
+    testing::Combine(
+        testing::Values(TestEvent::kMouseEvent, TestEvent::kTouchEvent),
+        testing::Values(HoveringState::kHovering, HoveringState::kNotHovering)),
+    DelegatedInkPointTestPassToString());
+
+// Tests to confirm that input events are correctly forwarded to the UI
+// Compositor when DelegatedInkTrails should be drawn, and stops forwarding when
+// they no longer should be drawn.
+TEST_P(DelegatedInkPointTest, EventForwardedToCompositor) {
+  // First confirm that the flag is false by default and the point is not sent.
+  SendEvent(true, gfx::PointF(15, 15));
+  MockDelegatedInkPointRenderer* delegated_ink_point_renderer =
+      compositor()->delegated_ink_point_renderer();
+
+  EXPECT_FALSE(delegated_ink_point_renderer);
+
+  // Then set it to true and confirm that the DelegatedInkPointRenderer is
+  // initialized, the connection is made and the point makes it to the renderer.
+  SetInkMetadataFlagOnRenderFrameMetadata(true);
+  gfx::DelegatedInkPoint expected_point(
+      gfx::PointF(10, 10), base::TimeTicks::Now(), GetExpectedPointerId());
+  SendEvent(true, expected_point.point(), expected_point.timestamp());
+
+  delegated_ink_point_renderer = compositor()->delegated_ink_point_renderer();
+  EXPECT_TRUE(delegated_ink_point_renderer);
+  delegated_ink_point_renderer->FlushForTesting();
+
+  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+  gfx::DelegatedInkPoint actual_point =
+      delegated_ink_point_renderer->GetDelegatedInkPoint();
+  EXPECT_EQ(expected_point.point(), actual_point.point());
+  EXPECT_EQ(expected_point.timestamp(), actual_point.timestamp());
+  EXPECT_EQ(GetExpectedPointerId(), actual_point.pointer_id());
+
+  // Then try changing the scale factor to confirm it affects the point
+  // correctly.
+  const float scale = 2.6f;
+  aura_test_helper_->GetTestScreen()->SetDeviceScaleFactor(scale);
+  gfx::PointF unscaled_point(15, 15);
+  base::TimeTicks unscaled_time = base::TimeTicks::Now();
+
+  SendEvent(true, unscaled_point, unscaled_time);
+  delegated_ink_point_renderer->FlushForTesting();
+
+  unscaled_point.Scale(scale);
+  expected_point = gfx::DelegatedInkPoint(unscaled_point, unscaled_time,
+                                          GetExpectedPointerId());
+
+  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+  actual_point = delegated_ink_point_renderer->GetDelegatedInkPoint();
+  EXPECT_EQ(expected_point.point(), actual_point.point());
+  EXPECT_EQ(expected_point.timestamp(), actual_point.timestamp());
+  EXPECT_EQ(GetExpectedPointerId(), actual_point.pointer_id());
+
+  // Confirm that prediction is reset when the API is no longer being used and
+  // |delegated_ink_metadata| is not set.
+  SetInkMetadataFlagOnRenderFrameMetadata(false);
+
+  SendEvent(true, gfx::PointF(25, 25));
+  delegated_ink_point_renderer->FlushForTesting();
+
+  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+  EXPECT_TRUE(delegated_ink_point_renderer->GetPredictionState());
+
+  // Finally, confirm that nothing is sent after the prediction has been reset
+  // when the delegated ink flag on the render frame metadata is false.
+  SendEvent(true, gfx::PointF(46, 46));
+  delegated_ink_point_renderer->FlushForTesting();
+
+  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+  EXPECT_FALSE(delegated_ink_point_renderer->GetPredictionState());
+}
+
+// Confirm that the interface is rebound if the receiver disconnects.
+TEST_P(DelegatedInkPointTest, MojoInterfaceReboundOnDisconnect) {
+  // First make sure the connection exists.
+  SetInkMetadataFlagOnRenderFrameMetadata(true);
+  SendEvent(true, gfx::PointF(15, 15));
+
+  MockDelegatedInkPointRenderer* delegated_ink_point_renderer =
+      compositor()->delegated_ink_point_renderer();
+
+  EXPECT_TRUE(delegated_ink_point_renderer);
+  EXPECT_TRUE(delegated_ink_point_renderer->ReceiverIsBound());
+
+  // Reset the receiver and flush the remote to confirm it is no longer bound.
+  delegated_ink_point_renderer->ResetReceiver();
+  view_->FlushForTest();
+
+  EXPECT_FALSE(delegated_ink_point_renderer->ReceiverIsBound());
+
+  // Confirm that it now gets reconnected correctly.
+  SendEvent(true, gfx::PointF(25, 25));
+
+  delegated_ink_point_renderer = compositor()->delegated_ink_point_renderer();
+
+  EXPECT_TRUE(delegated_ink_point_renderer);
+  EXPECT_TRUE(delegated_ink_point_renderer->ReceiverIsBound());
+}
+
+// Test to confirm that forwarding points to viz will stop and prediction is
+// reset if the state of hovering differs between what is expected and the
+// received points.
+TEST_P(DelegatedInkPointTest, StopForwardingOnHoverStateChange) {
+  // First send a point and make sure it makes it to the renderer.
+  SetInkMetadataFlagOnRenderFrameMetadata(true);
+  SendEvent(true, gfx::PointF(15, 15));
+
+  MockDelegatedInkPointRenderer* delegated_ink_point_renderer =
+      compositor()->delegated_ink_point_renderer();
+  EXPECT_TRUE(delegated_ink_point_renderer);
+  delegated_ink_point_renderer->FlushForTesting();
+
+  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+  delegated_ink_point_renderer->ClearDelegatedInkPoint();
+
+  // Now send a point that doesn't match the state of hovering on the metadata
+  // to confirm that it isn't sent and ResetPrediction is called.
+  SendEvent(false, gfx::PointF(20, 20));
+  delegated_ink_point_renderer->FlushForTesting();
+
+  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+  EXPECT_TRUE(delegated_ink_point_renderer->GetPredictionState());
+
+  // Send another that doesn't match to confirm the end trail point is only sent
+  // once.
+  SendEvent(false, gfx::PointF(25, 25));
+  delegated_ink_point_renderer->FlushForTesting();
+  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+
+  // Send one that does match again to confirm that points will start sending
+  // again if the hovering state starts matching again.
+  SendEvent(true, gfx::PointF(30, 30));
+  delegated_ink_point_renderer->FlushForTesting();
+
+  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+  EXPECT_FALSE(delegated_ink_point_renderer->GetPredictionState());
+}
+
+// Confirm that only move events are forwarded, not enter/exit or equivalent
+// events.
+TEST_P(DelegatedInkPointTest, IgnoreEnterAndExitEvents) {
+  // First set everything up and try forwarding a point, confirming that it is
+  // sent as expected.
+  SetInkMetadataFlagOnRenderFrameMetadata(true);
+  gfx::DelegatedInkPoint expected_point(
+      gfx::PointF(10, 10), base::TimeTicks::Now(), GetExpectedPointerId());
+  SendEvent(true, expected_point.point(), expected_point.timestamp());
+
+  MockDelegatedInkPointRenderer* delegated_ink_point_renderer =
+      compositor()->delegated_ink_point_renderer();
+  EXPECT_TRUE(delegated_ink_point_renderer);
+  delegated_ink_point_renderer->FlushForTesting();
+
+  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+  gfx::DelegatedInkPoint actual_point =
+      delegated_ink_point_renderer->GetDelegatedInkPoint();
+  EXPECT_EQ(expected_point.point(), actual_point.point());
+  EXPECT_EQ(expected_point.timestamp(), actual_point.timestamp());
+  EXPECT_EQ(GetExpectedPointerId(), actual_point.pointer_id());
+
+  // Try sending an enter event and confirm it is not forwarded.
+  SendEvent(true, gfx::PointF(12, 12), base::TimeTicks::Now(),
+            /*use_enter_event*/ true, /*use_exit_event*/ false);
+  delegated_ink_point_renderer->FlushForTesting();
+  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+
+  // Now try with an exit event.
+  SendEvent(true, gfx::PointF(42, 19), base::TimeTicks::Now(),
+            /*use_enter_event*/ false, /*use_exit_event*/ true);
+  delegated_ink_point_renderer->FlushForTesting();
+  EXPECT_FALSE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+
+  // Finally, confirm that sending move events will work again without issue.
+  expected_point = gfx::DelegatedInkPoint(
+      gfx::PointF(20, 21), base::TimeTicks::Now(), GetExpectedPointerId());
+  SendEvent(true, expected_point.point(), expected_point.timestamp());
+
+  delegated_ink_point_renderer->FlushForTesting();
+
+  EXPECT_TRUE(delegated_ink_point_renderer->HasDelegatedInkPoint());
+  actual_point = delegated_ink_point_renderer->GetDelegatedInkPoint();
+  EXPECT_EQ(expected_point.point(), actual_point.point());
+  EXPECT_EQ(expected_point.timestamp(), actual_point.timestamp());
+  EXPECT_EQ(GetExpectedPointerId(), actual_point.pointer_id());
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc
index 3e66a34e..ec0d6b0 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.cc
+++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -988,8 +988,4 @@
       transformed_point);
 }
 
-ui::Compositor* RenderWidgetHostViewBase::GetCompositor() {
-  return nullptr;
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index 6c9aa1e..9e207025 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -54,7 +54,6 @@
 }
 
 namespace ui {
-class Compositor;
 enum class DomCode;
 class LatencyInfo;
 class TouchEvent;
@@ -545,8 +544,6 @@
 
   void SetTooltipObserverForTesting(TooltipObserver* observer);
 
-  virtual ui::Compositor* GetCompositor();
-
  protected:
   explicit RenderWidgetHostViewBase(RenderWidgetHost* host);
   ~RenderWidgetHostViewBase() override;
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index 4e26cd2ff..14a83f94 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -1003,10 +1003,4 @@
   host()->SynchronizeVisualProperties();
 }
 
-ui::Compositor* RenderWidgetHostViewChildFrame::GetCompositor() {
-  if (!GetRootView())
-    return nullptr;
-  return GetRootView()->GetCompositor();
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.h b/content/browser/renderer_host/render_widget_host_view_child_frame.h
index fdcd303..a32e3e0 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.h
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.h
@@ -256,8 +256,6 @@
     return weak_factory_.GetWeakPtr();
   }
 
-  ui::Compositor* GetCompositor() override;
-
  protected:
   ~RenderWidgetHostViewChildFrame() override;
 
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
index 68ba083..f4dbe7b3e 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -364,6 +364,69 @@
   }
 }
 
+bool IsMoveEvent(ui::EventType type) {
+  return type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED ||
+         type == ui::ET_TOUCH_MOVED;
+}
+
+void RenderWidgetHostViewEventHandler::ForwardDelegatedInkPoint(
+    ui::LocatedEvent* event,
+    bool hovering,
+    int32_t pointer_id) {
+  const cc::RenderFrameMetadata& last_metadata =
+      host_->render_frame_metadata_provider()->LastRenderFrameMetadata();
+  if (IsMoveEvent(event->type()) &&
+      last_metadata.delegated_ink_metadata.has_value() &&
+      hovering == last_metadata.delegated_ink_metadata.value()
+                      .delegated_ink_is_hovering) {
+    if (!delegated_ink_point_renderer_.is_bound()) {
+      ui::Compositor* compositor = window_ && window_->layer()
+                                       ? window_->layer()->GetCompositor()
+                                       : nullptr;
+
+      // The remote can't be bound if the compositor is null, so bail if that
+      // is the case so we don't crash by trying to use an unbound remote.
+      if (!compositor)
+        return;
+
+      TRACE_EVENT_INSTANT0("input",
+                           "Binding mojo interface for delegated ink points.",
+                           TRACE_EVENT_SCOPE_THREAD);
+      compositor->SetDelegatedInkPointRenderer(
+          delegated_ink_point_renderer_.BindNewPipeAndPassReceiver());
+      delegated_ink_point_renderer_.reset_on_disconnect();
+    }
+
+    gfx::PointF point = event->root_location_f();
+    point.Scale(host_view_->GetDeviceScaleFactor());
+    gfx::DelegatedInkPoint delegated_ink_point(point, event->time_stamp(),
+                                               pointer_id);
+    TRACE_EVENT_INSTANT1("input",
+                         "Forwarding delegated ink point from browser.",
+                         TRACE_EVENT_SCOPE_THREAD, "delegated point",
+                         delegated_ink_point.ToString());
+
+    // Calling this will result in IPC calls to get |delegated_ink_point| to
+    // viz. The decision to do this here was made with the understanding that
+    // the IPC overhead will result in a minor increase in latency for getting
+    // this event to the renderer. However, by sending it here, the event is
+    // given the greatest possible chance to make it to viz before
+    // DrawAndSwap() is called, allowing more points to be drawn as part of
+    // the delegated ink trail, and thus reducing user perceived latency.
+    delegated_ink_point_renderer_->StoreDelegatedInkPoint(delegated_ink_point);
+    ended_delegated_ink_trail_ = false;
+  } else if (delegated_ink_point_renderer_.is_bound() &&
+             !ended_delegated_ink_trail_) {
+    // Let viz know that the most recent point it received from us is probably
+    // the last point the user is inking, so it shouldn't predict anything
+    // beyond it.
+    TRACE_EVENT_INSTANT0("input", "Delegated ink trail ended",
+                         TRACE_EVENT_SCOPE_THREAD);
+    delegated_ink_point_renderer_->ResetPrediction();
+    ended_delegated_ink_trail_ = true;
+  }
+}
+
 void RenderWidgetHostViewEventHandler::OnMouseEvent(ui::MouseEvent* event) {
   TRACE_EVENT0("input", "RenderWidgetHostViewBase::OnMouseEvent");
 
@@ -407,6 +470,9 @@
     bool is_selection_popup = NeedsInputGrab(popup_child_host_view_);
     if (CanRendererHandleEvent(event, mouse_locked_, is_selection_popup) &&
         !(event->flags() & ui::EF_FROM_TOUCH)) {
+      bool hovering = (event->type() ^ ui::ET_MOUSE_DRAGGED) &&
+                      (event->type() ^ ui::ET_MOUSE_PRESSED);
+      ForwardDelegatedInkPoint(event, hovering, event->pointer_details().id);
 
       // Confirm existing composition text on mouse press, to make sure
       // the input caret won't be moved with an ongoing composition text.
@@ -531,6 +597,9 @@
   if (handled)
     return;
 
+  ForwardDelegatedInkPoint(event, event->hovering(),
+                           event->pointer_details().id);
+
   if (had_no_pointer)
     delegate_->selection_controller_client()->OnTouchDown();
   if (!pointer_state_.GetPointerCount())
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.h b/content/browser/renderer_host/render_widget_host_view_event_handler.h
index 6d41f4d..65dbc6313 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.h
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.h
@@ -12,12 +12,14 @@
 #include "content/browser/renderer_host/input/mouse_wheel_phase_handler.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/native_web_keyboard_event.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/mojom/input/pointer_lock_result.mojom.h"
 #include "ui/aura/scoped_enable_unadjusted_mouse_events.h"
 #include "ui/aura/scoped_keyboard_hook.h"
 #include "ui/events/event_handler.h"
 #include "ui/events/gestures/motion_event_aura.h"
+#include "ui/gfx/mojom/delegated_ink_point_renderer.mojom.h"
 #include "ui/latency/latency_info.h"
 
 namespace aura {
@@ -252,6 +254,15 @@
 
   void HandleMouseWheelEvent(ui::MouseEvent* event);
 
+  // Forward the location and timestamp of the event to viz if a delegated ink
+  // trail is requested.
+  void ForwardDelegatedInkPoint(ui::LocatedEvent* event,
+                                bool hovering,
+                                int32_t pointer_id);
+
+  // Flush the remote for testing purposes.
+  void FlushForTest() { delegated_ink_point_renderer_.FlushForTesting(); }
+
   // Whether return characters should be passed on to the RenderWidgetHostImpl.
   bool accept_return_character_ = false;
 
@@ -314,6 +325,18 @@
 
   std::unique_ptr<HitTestDebugKeyEventObserver> debug_observer_;
 
+  // Remote end of the connection for sending delegated ink points to viz to
+  // support the delegated ink trails feature.
+  mojo::Remote<gfx::mojom::DelegatedInkPointRenderer>
+      delegated_ink_point_renderer_;
+  // Used to know if we have already told viz to reset prediction because the
+  // final point of the delegated ink trail has been sent. True when prediction
+  // has already been reset for the most recent trail, false otherwise. This
+  // flag helps make sure that we don't send more IPCs than necessary to viz to
+  // reset prediction. Sending extra IPCs wouldn't impact correctness, but can
+  // impact performance due to the IPC overhead.
+  bool ended_delegated_ink_trail_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewEventHandler);
 };
 
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index ea91436..74b55dea 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -482,8 +482,6 @@
     mock_clock_.Advance(base::TimeDelta::FromMilliseconds(100));
     ui::SetEventTickClockForTesting(&mock_clock_);
     RenderViewHostImplTestHarness::SetUp();
-    base::test::ScopedFeatureList feature_list;
-    feature_list.InitAndEnableFeature(features::kDirectManipulationStylus);
 
     browser_context_ = std::make_unique<TestBrowserContext>();
     process_host_ =
@@ -1092,78 +1090,6 @@
             GetPointerType(events));
 }
 
-TEST_F(RenderWidgetHostViewMacTest, PointerEventWithPenTypeSendAsTouch) {
-  // Send a NSEvent of NSTabletProximity type which has a device type of pen.
-  NSEvent* event = MockTabletEventWithParams(kCGEventTabletProximity, true,
-                                             NSPenPointingDevice);
-  [rwhv_mac_->GetInProcessNSView() tabletEvent:event];
-  // Flush and clear other messages (e.g. begin frames) the RWHVMac also sends.
-  base::RunLoop().RunUntilIdle();
-  static_cast<RenderWidgetHostImpl*>(rwhv_mac_->GetRenderWidgetHost())
-      ->input_router()
-      ->ForceSetTouchActionAuto();
-
-  event = MockMouseEventWithParams(
-      kCGEventLeftMouseDown, {6, 9}, kCGMouseButtonLeft,
-      kCGEventMouseSubtypeTabletPoint, false, true);
-  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
-  base::RunLoop().RunUntilIdle();
-  MockWidgetInputHandler::MessageVector events =
-      host_->GetAndResetDispatchedMessages();
-  ASSERT_EQ("TouchStart", GetMessageNames(events));
-  EXPECT_EQ(blink::WebPointerProperties::PointerType::kPen,
-            GetPointerType(events));
-  events.clear();
-  base::RunLoop().RunUntilIdle();
-  events = host_->GetAndResetDispatchedMessages();
-
-  event = MockMouseEventWithParams(
-      kCGEventLeftMouseDragged, {16, 29}, kCGMouseButtonLeft,
-      kCGEventMouseSubtypeTabletPoint, false, true);
-  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
-  base::RunLoop().RunUntilIdle();
-  events = host_->GetAndResetDispatchedMessages();
-  ASSERT_EQ("TouchMove", GetMessageNames(events));
-  EXPECT_EQ(blink::WebPointerProperties::PointerType::kPen,
-            GetPointerType(events));
-
-  events.clear();
-  base::RunLoop().RunUntilIdle();
-  events = host_->GetAndResetDispatchedMessages();
-
-  event = MockMouseEventWithParams(kCGEventLeftMouseUp, {16, 29},
-                                   kCGMouseButtonLeft,
-                                   kCGEventMouseSubtypeTabletPoint, false);
-  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
-  base::RunLoop().RunUntilIdle();
-  events = host_->GetAndResetDispatchedMessages();
-  ASSERT_EQ("TouchEnd", GetMessageNames(events));
-  EXPECT_EQ(blink::WebPointerProperties::PointerType::kPen,
-            static_cast<const blink::WebTouchEvent&>(
-                events[0]->ToEvent()->Event()->Event())
-                .touches[0]
-                .pointer_type);
-
-  events.clear();
-  base::RunLoop().RunUntilIdle();
-  events = host_->GetAndResetDispatchedMessages();
-  ASSERT_EQ("GestureScrollEnd", GetMessageNames(events));
-
-  event =
-      MockMouseEventWithParams(kCGEventLeftMouseDown, {6, 9},
-                               kCGMouseButtonLeft, kCGEventMouseSubtypeDefault);
-  [rwhv_mac_->GetInProcessNSView() mouseEvent:event];
-  base::RunLoop().RunUntilIdle();
-  events = host_->GetAndResetDispatchedMessages();
-  ASSERT_EQ("MouseDown", GetMessageNames(events));
-  EXPECT_EQ(blink::WebPointerProperties::PointerType::kMouse,
-            GetPointerType(events));
-
-  events.clear();
-  base::RunLoop().RunUntilIdle();
-  events = host_->GetAndResetDispatchedMessages();
-}
-
 TEST_F(RenderWidgetHostViewMacTest, SendMouseMoveOnShowingContextMenu) {
   rwhv_mac_->SetShowingContextMenu(true);
   base::RunLoop().RunUntilIdle();
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 556a1838..05e9add2 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -18,7 +18,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
-#include "components/viz/common/features.h"
 #include "components/viz/test/host_frame_sink_manager_test_api.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/renderer_host/cursor_manager.h"
@@ -7329,130 +7328,4 @@
   EXPECT_EQ(kSlowHitTestFlags, hit_test_data[2].flags);
 }
 
-#if defined(USE_AURA)
-class SitePerProcessDelegatedInkBrowserTest
-    : public SitePerProcessHitTestBrowserTest {
- public:
-  SitePerProcessDelegatedInkBrowserTest() = default;
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    SitePerProcessHitTestBrowserTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
-                                    "DelegatedInkTrails");
-  }
-};
-
-// Test confirms that a point hitting an OOPIF that is requesting delegated ink
-// trails results in the metadata being correctly sent to the child's
-// RenderWidgetHost and is usable for sending delegated ink points.
-IN_PROC_BROWSER_TEST_F(SitePerProcessDelegatedInkBrowserTest,
-                       MetadataAndPointGoThroughOOPIF) {
-  // Delegated ink is only supported on Skia Renderer for now.
-  if (!features::IsUsingSkiaRenderer())
-    return;
-
-  GURL main_url(embedded_test_server()->GetURL(
-      "/frame_tree/page_with_positioned_frame.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetFrameTree()
-                            ->root();
-  ASSERT_EQ(1U, root->child_count());
-
-  FrameTreeNode* child = root->child_at(0);
-
-  GURL site_url(embedded_test_server()->GetURL("baz.com", "/title1.html"));
-  EXPECT_EQ(site_url, child->current_url());
-  EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
-            child->current_frame_host()->GetSiteInstance());
-
-  // Make sure the child frame is indeed a OOPIF
-  EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
-
-  EXPECT_TRUE(ExecJs(child->current_frame_host(), R"(
-      let presenter = null;
-      navigator.ink.requestPresenter('delegated-ink-trail').then(e => {
-        presenter = e;
-      });
-      let style = { color: 'green', diameter: 21 };
-
-      window.addEventListener('pointermove' , evt => {
-        presenter.updateInkTrailStartPoint(evt, style);
-        document.write('Force a new frame so that an updated ' +
-        'RenderFrameMetadata is sent to the browser process.');
-      });
-      )"));
-
-  RenderWidgetHostImpl* root_rwh =
-      root->current_frame_host()->GetRenderWidgetHost();
-  RenderWidgetHostImpl* child_rwh =
-      child->current_frame_host()->GetRenderWidgetHost();
-
-  // Create listeners for mouse events.
-  RenderWidgetHostMouseEventMonitor main_frame_monitor(root_rwh);
-  RenderWidgetHostMouseEventMonitor child_frame_monitor(child_rwh);
-
-  WaitForHitTestData(child->current_frame_host());
-
-  RenderWidgetHostViewBase* root_view =
-      static_cast<RenderWidgetHostViewBase*>(root_rwh->GetView());
-  RenderWidgetHostViewBase* rwhv_child =
-      static_cast<RenderWidgetHostViewBase*>(child_rwh->GetView());
-
-  RenderWidgetHostInputEventRouter* router =
-      web_contents()->GetInputEventRouter();
-
-  EXPECT_FALSE(router->IsDelegatedInkRendererBoundForTest());
-
-  // Target MouseMove to child frame.
-  blink::WebMouseEvent mouse_event(
-      blink::WebInputEvent::Type::kMouseMove,
-      blink::WebInputEvent::kNoModifiers,
-      blink::WebInputEvent::GetStaticTimeStampForTests());
-  SetWebEventPositions(&mouse_event, gfx::Point(55, 55), root_view);
-
-  RouteMouseEventAndWaitUntilDispatch(router, root_view, rwhv_child,
-                                      &mouse_event);
-
-  // Dispatch twice because the router generates an extra MouseLeave for the
-  // main frame.
-  main_frame_monitor.ResetEventReceived();
-  child_frame_monitor.ResetEventReceived();
-
-  RouteMouseEventAndWaitUntilDispatch(router, root_view, rwhv_child,
-                                      &mouse_event);
-  EXPECT_FALSE(main_frame_monitor.EventWasReceived());
-  EXPECT_TRUE(child_frame_monitor.EventWasReceived());
-
-  // Wait for the child to receive and process input so that the new
-  // RenderFrameMetadata is sent to the browser process.
-  {
-    auto observer = std::make_unique<MainThreadFrameObserver>(child_rwh);
-    observer->Wait();
-  }
-
-  // Confirm that the metadata is what we expect and accessible from the child's
-  // RenderWidgetHost.
-  const cc::RenderFrameMetadata& last_metadata =
-      child_rwh->render_frame_metadata_provider()->LastRenderFrameMetadata();
-  EXPECT_TRUE(last_metadata.delegated_ink_metadata.has_value());
-  EXPECT_TRUE(
-      last_metadata.delegated_ink_metadata.value().delegated_ink_is_hovering);
-
-  // Send one more mouse move event and confirm that it causes the forwarding
-  // to occur, which will result in the |delegated_ink_point_renderer_| mojom
-  // remote being bound.
-  main_frame_monitor.ResetEventReceived();
-  child_frame_monitor.ResetEventReceived();
-  SetWebEventPositions(&mouse_event, gfx::Point(57, 57), root_view);
-  RouteMouseEventAndWaitUntilDispatch(router, root_view, rwhv_child,
-                                      &mouse_event);
-
-  EXPECT_FALSE(main_frame_monitor.EventWasReceived());
-  EXPECT_TRUE(child_frame_monitor.EventWasReceived());
-  EXPECT_TRUE(router->IsDelegatedInkRendererBoundForTest());
-}
-#endif  // USE_AURA
-
 }  // namespace content
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc
index ab16fbf..4b76522 100644
--- a/content/browser/storage_partition_impl_unittest.cc
+++ b/content/browser/storage_partition_impl_unittest.cc
@@ -1892,7 +1892,8 @@
             .SetExpiry(base::TimeDelta::FromDays(2))
             .Build());
     conversion_manager->HandleConversion(
-        StorableConversion(123, net::SchemefulSite(conv), reporter));
+        StorableConversion(123, net::SchemefulSite(conv), reporter,
+                           /*event_source_trigger_data=*/0));
   }
 
   EXPECT_EQ(5u, GetConversionsToReportForTesting(conversion_manager,
diff --git a/content/browser/tracing/startup_tracing_browsertest.cc b/content/browser/tracing/startup_tracing_browsertest.cc
index 44c9db4..20a45fd 100644
--- a/content/browser/tracing/startup_tracing_browsertest.cc
+++ b/content/browser/tracing/startup_tracing_browsertest.cc
@@ -326,6 +326,12 @@
   CheckOutput(GetExpectedPath(), GetOutputType());
 }
 
+IN_PROC_BROWSER_TEST_P(StartupTracingTest, ContinueAtShutdown) {
+  EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "title1.html")));
+  StartupTracingController::GetInstance()
+      .set_continue_on_shutdown_for_testing();
+}
+
 class EmergencyStopTracingTest : public StartupTracingTest {};
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/content/browser/tracing/startup_tracing_controller.cc b/content/browser/tracing/startup_tracing_controller.cc
index 7cc95b6..5957d39 100644
--- a/content/browser/tracing/startup_tracing_controller.cc
+++ b/content/browser/tracing/startup_tracing_controller.cc
@@ -511,6 +511,15 @@
   run_loop.Run();
 }
 
+void StartupTracingController::ShutdownAndWaitForStopIfNeeded() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (should_continue_on_shutdown_)
+    return;
+
+  WaitUntilStopped();
+}
+
 // static
 void StartupTracingController::EmergencyStop() {
   if (GetIOThreadTaskRunner({})->RunsTasksInCurrentSequence()) {
diff --git a/content/browser/tracing/startup_tracing_controller.h b/content/browser/tracing/startup_tracing_controller.h
index fcbbc4d28..492a036c 100644
--- a/content/browser/tracing/startup_tracing_controller.h
+++ b/content/browser/tracing/startup_tracing_controller.h
@@ -31,6 +31,7 @@
 
   void StartIfNeeded();
   void WaitUntilStopped();
+  void ShutdownAndWaitForStopIfNeeded();
 
   // 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.
@@ -68,6 +69,10 @@
 
   bool is_finished_for_testing() const { return state_ == State::kStopped; }
 
+  void set_continue_on_shutdown_for_testing() {
+    should_continue_on_shutdown_ = true;
+  }
+
  private:
   void Stop(base::OnceClosure on_finished_callback);
 
@@ -92,6 +97,8 @@
 
   std::string default_basename_;
   bool basename_for_test_set_ = false;
+  // Used for testing only
+  bool should_continue_on_shutdown_ = false;
 
   TempFilePolicy temp_file_policy_ = TempFilePolicy::kUseTemporaryFile;
 };
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 5572f096..15621d2 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1179,21 +1179,6 @@
   return GetRenderManager();
 }
 
-bool WebContentsImpl::OnMessageReceived(RenderViewHostImpl* render_view_host,
-                                        const IPC::Message& message) {
-  OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnMessageReceived",
-                        "render_view_host", render_view_host);
-  for (auto& observer : observers_.observer_list()) {
-    // TODO(nick, creis): https://crbug.com/758026: Replace all uses of this
-    // variant of OnMessageReceived with the version that takes a
-    // RenderFrameHost, and then delete it.
-    if (observer.OnMessageReceived(message))
-      return true;
-  }
-
-  return false;
-}
-
 bool WebContentsImpl::OnMessageReceived(RenderFrameHostImpl* render_frame_host,
                                         const IPC::Message& message) {
   OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnMessageReceived",
@@ -2616,8 +2601,7 @@
 // TODO(dtapuska): Enable barrel button selection drag support on Android.
 // crbug.com/758042
 #if defined(OS_WIN)
-  prefs.barrel_button_for_drag_enabled =
-      base::FeatureList::IsEnabled(features::kDirectManipulationStylus);
+  prefs.barrel_button_for_drag_enabled = true;
 #endif  // defined(OS_WIN)
 
   prefs.enable_scroll_animator =
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index f5318f70..9152a4d 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -813,8 +813,6 @@
 
   // RenderViewHostDelegate ----------------------------------------------------
   RenderViewHostDelegateView* GetDelegateView() override;
-  bool OnMessageReceived(RenderViewHostImpl* render_view_host,
-                         const IPC::Message& message) override;
   // RenderFrameHostDelegate has the same method, so list it there because this
   // interface is going away.
   // WebContents* GetAsWebContents() override;
diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h
index e42b740..2d9cc0c 100644
--- a/content/public/browser/browser_context.h
+++ b/content/public/browser/browser_context.h
@@ -25,6 +25,7 @@
 #include "third_party/blink/public/mojom/blob/blob.mojom-forward.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom-forward.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom-forward.h"
+#include "third_party/perfetto/include/perfetto/tracing/traced_proto.h"
 #include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
 
 #if !defined(OS_ANDROID)
@@ -61,6 +62,14 @@
 class VariationsClient;
 }  // namespace variations
 
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+class ChromeBrowserContext;
+}
+}  // namespace protos
+}  // namespace perfetto
+
 namespace content {
 
 class BackgroundFetchDelegate;
@@ -270,6 +279,11 @@
   // Write a representation of this object into a trace.
   void WriteIntoTrace(perfetto::TracedValue context);
 
+  // Write a representation of this object into tracing proto.
+  void WriteIntoTrace(
+      perfetto::TracedProto<perfetto::protos::pbzero::ChromeBrowserContext>
+          context);
+
   //////////////////////////////////////////////////////////////////////////////
   // The //content embedder can override the methods below to change or extend
   // how the //content layer interacts with a BrowserContext.
diff --git a/content/public/browser/web_contents_observer.cc b/content/public/browser/web_contents_observer.cc
index b6754c23..b068bd5 100644
--- a/content/public/browser/web_contents_observer.cc
+++ b/content/public/browser/web_contents_observer.cc
@@ -43,10 +43,6 @@
   return false;
 }
 
-bool WebContentsObserver::OnMessageReceived(const IPC::Message& message) {
-  return false;
-}
-
 void WebContentsObserver::ResetWebContents() {
   static_cast<WebContentsImpl*>(web_contents_)->RemoveObserver(this);
   web_contents_ = nullptr;
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h
index 6c2b6e4..3fc353f 100644
--- a/content/public/browser/web_contents_observer.h
+++ b/content/public/browser/web_contents_observer.h
@@ -18,7 +18,6 @@
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/reload_type.h"
 #include "content/public/browser/visibility.h"
-#include "ipc/ipc_listener.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/network/public/mojom/fetch_api.mojom-forward.h"
 #include "services/service_manager/public/cpp/bind_source_info.h"
@@ -77,7 +76,7 @@
 //
 // TODO(creis, jochen): Hide the fact that there are several RenderViewHosts
 // from the WebContentsObserver API. http://crbug.com/173325
-class CONTENT_EXPORT WebContentsObserver : public IPC::Listener {
+class CONTENT_EXPORT WebContentsObserver {
  public:
   // Frames and Views ----------------------------------------------------------
 
@@ -703,13 +702,6 @@
   virtual void OnServiceWorkerAccessed(NavigationHandle* navigation_handle,
                                        const GURL& scope,
                                        AllowServiceWorkerResult allowed) {}
-  // IPC::Listener implementation.
-  // DEPRECATED: Use (i.e. override) the other overload instead:
-  //     virtual bool OnMessageReceived(const IPC::Message& message,
-  //                                    RenderFrameHost* render_frame_host);
-  // TODO(https://crbug.com/758026): Delete this overload when possible.
-  bool OnMessageReceived(const IPC::Message& message) override;
-
   WebContents* web_contents() const;
 
  protected:
@@ -722,7 +714,7 @@
   // observing.
   WebContentsObserver();
 
-  ~WebContentsObserver() override;
+  virtual ~WebContentsObserver();
 
   // Start observing a different WebContents; used with the default constructor.
   void Observe(WebContents* web_contents);
diff --git a/content/public/common/content_switch_dependent_feature_overrides.cc b/content/public/common/content_switch_dependent_feature_overrides.cc
index 763db06..47fb1cc8 100644
--- a/content/public/common/content_switch_dependent_feature_overrides.cc
+++ b/content/public/common/content_switch_dependent_feature_overrides.cc
@@ -29,8 +29,12 @@
       {switches::kAppCacheForceEnabled,
        std::cref(blink::features::kAppCacheRequireOriginTrial),
        base::FeatureList::OVERRIDE_DISABLE_FEATURE},
+
       // Overrides for --enable-experimental-web-platform-features.
       {switches::kEnableExperimentalWebPlatformFeatures,
+       std::cref(net::features::kCookieSameSiteConsidersRedirectChain),
+       base::FeatureList::OVERRIDE_ENABLE_FEATURE},
+      {switches::kEnableExperimentalWebPlatformFeatures,
        std::cref(network::features::kCrossOriginEmbedderPolicyCredentialless),
        base::FeatureList::OVERRIDE_ENABLE_FEATURE},
       {switches::kEnableExperimentalWebPlatformFeatures,
@@ -84,6 +88,9 @@
 
       // Overrides for --enable-experimental-cookie-features.
       {switches::kEnableExperimentalCookieFeatures,
+       std::cref(net::features::kCookieSameSiteConsidersRedirectChain),
+       base::FeatureList::OVERRIDE_ENABLE_FEATURE},
+      {switches::kEnableExperimentalCookieFeatures,
        std::cref(net::features::kCookiesWithoutSameSiteMustBeSecure),
        base::FeatureList::OVERRIDE_ENABLE_FEATURE},
       {switches::kEnableExperimentalCookieFeatures,
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index 99f0ec20..7373213 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -429,7 +429,6 @@
   reset_token_ = reset_token;
   serializer_->Reset();
   pending_events_.clear();
-  dirty_objects_.clear();
 
   const WebDocument& document = GetMainDocument();
   if (!document.IsNull()) {
@@ -451,13 +450,11 @@
 void RenderAccessibilityImpl::MarkWebAXObjectDirty(
     const WebAXObject& obj,
     bool subtree,
-    ax::mojom::Action event_from_action,
-    std::vector<ui::AXEventIntent> event_intents) {
+    ax::mojom::Action event_from_action) {
   DirtyObject dirty_object;
   dirty_object.obj = obj;
   dirty_object.event_from = ax::mojom::EventFrom::kAction;
   dirty_object.event_from_action = event_from_action;
-  dirty_object.event_intents = event_intents;
   dirty_objects_.push_back(dirty_object);
 
   if (subtree)
@@ -512,10 +509,6 @@
   if (IsImmediateProcessingRequiredForEvent(event))
     event_schedule_mode_ = EventScheduleMode::kProcessEventsImmediately;
 
-  if (ShouldSerializeNodeForEvent(obj, event))
-    MarkWebAXObjectDirty(obj, false, event.event_from_action,
-                         event.event_intents);
-
   ScheduleSendPendingAccessibilityEvents();
 }
 
@@ -603,17 +596,6 @@
   if (obj.IsDetached())
     return false;
 
-  // If we were to return true for kLayoutComplete, that means calling
-  // MarkWebAXObjectDirty on the root node (since kLayoutComplete is fired
-  // on the root), and because the root is focusable, MarkWebAXObjectDirty
-  // sets the event schedule mode to kProcessEventsImmediately, which is
-  // inefficient. So it's best to not actually serialize a node in response
-  // to a kLayoutComplete event, but note that it will still ensure that a
-  // lower-priority update is scheduled, which will serialize any nodes whose
-  // bounding boxes have changed.
-  if (event.event_type == ax::mojom::Event::kLayoutComplete)
-    return false;
-
   if (event.event_type == ax::mojom::Event::kTextSelectionChanged &&
       !obj.IsAtomicTextField()) {
     // Selection changes on non-atomic text fields cause no change to the
@@ -787,7 +769,6 @@
     pending_events_.insert(
         pending_events_.begin(),
         ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kLayoutComplete));
-    MarkWebAXObjectDirty(root_obj, false);
 
     // If loaded and has some content, insert load complete at the top, so that
     // screen readers are informed a new document is ready.
@@ -893,18 +874,88 @@
       continue;
     }
 
+    auto obj = WebAXObject::FromWebDocumentByID(document, event.id);
+
+    // Make sure the object still exists.
+    // TODO(accessibility) Change this to CheckValidity() if there aren't crash
+    // reports of illegal lifecycle changes from WebDisallowTransitionScope.
+    if (!obj.MaybeUpdateLayoutAndCheckValidity())
+      continue;
+
+      // Make sure it's a descendant of our root node - exceptions include the
+      // scroll area that's the parent of the main document (we ignore it), and
+      // possibly nodes attached to a different document.
+      // TODO(accessibility) Remove once it's clear this never triggers.
+#if defined(AX_FAIL_FAST_BUILD)
+    SANITIZER_CHECK(tree_source_->IsInTree(obj))
+        << "\n* Object not in tree: " << obj.ToString(true).Utf8();
+#endif
+
+    // If it's ignored, find the first ancestor that's not ignored.
+    //
+    // Note that "IsDetached()" also calls "IsNull()". Additionally,
+    // "ParentObject()" always gets the first ancestor that is included in tree
+    // (ignored or unignored), so it will never return objects that are not
+    // included in the tree at all.
+    if (!obj.IsDetached() && !obj.AccessibilityIsIncludedInTree())
+      obj = obj.ParentObject();
+    for (; !obj.IsDetached() && obj.AccessibilityIsIgnored();
+         obj = obj.ParentObject()) {
+      // There are 3 states of nodes that we care about here.
+      // (x) Unignored, included in tree
+      // [x] Ignored, included in tree
+      // <x> Ignored, excluded from tree
+      //
+      // Consider the following tree :
+      // ++(0) Role::kRootWebArea
+      // ++++<1> Role::kNone
+      // ++++++[2] Role::kGenericContainer <body>
+      // ++++++++[3] Role::kGenericContainer with 'visibility: hidden'
+      //
+      // If we modify [3] to be 'visibility: visible', we will receive
+      // Event::kChildrenChanged here for the Ignored parent [2].
+      // We must re-serialize the Unignored parent node (0) due to this
+      // change, but we must also re-serialize [2] since its children
+      // have changed. <1> was never part of the ax tree, and therefore
+      // does not need to be serialized.
+      // Note that [3] will be serialized to (3) during :
+      // |AXTreeSerializer<>::SerializeChangedNodes| when node [2] is
+      // being serialized, since it will detect the Ignored state had
+      // changed.
+      //
+      // Similarly, during Event::kTextChanged, if any Ignored,
+      // but included in tree ancestor uses NameFrom::kContents,
+      // they must also be re-serialized in case the name changed.
+      if (ShouldSerializeNodeForEvent(obj, event)) {
+        DirtyObject dirty_object;
+        dirty_object.obj = obj;
+        dirty_object.event_from = event.event_from;
+        dirty_object.event_from_action = event.event_from_action;
+        dirty_object.event_intents = event.event_intents;
+        dirty_objects.push_back(dirty_object);
+      }
+    }
+
     events.push_back(event);
 
     VLOG(1) << "Accessibility event: " << ui::ToString(event.event_type)
             << " on node id " << event.id;
+
+    // Some events don't cause any changes to their associated objects.
+    if (ShouldSerializeNodeForEvent(obj, event)) {
+      DirtyObject dirty_object;
+      dirty_object.obj = obj;
+      dirty_object.event_from = event.event_from;
+      dirty_object.event_from_action = event.event_from_action;
+      dirty_object.event_intents = event.event_intents;
+      dirty_objects.push_back(dirty_object);
+    }
   }
 
   // Now serialize all dirty objects. Keep track of IDs serialized
   // so we don't have to serialize the same node twice.
   std::set<int32_t> already_serialized_ids;
-  for (size_t i = 0; i < dirty_objects.size(); ++i) {
-    DirtyObject current_dirty_object = dirty_objects[i];
-
+  for (const DirtyObject& current_dirty_object : dirty_objects) {
     auto obj = current_dirty_object.obj;
     // Dirty objects can be added using MarkWebAXObjectDirty(obj) from other
     // parts of the code as well, so we need to ensure the object still exists.
@@ -926,52 +977,6 @@
     if (already_serialized_ids.find(obj.AxID()) != already_serialized_ids.end())
       continue;
 
-    // If it's ignored, find the first ancestor that's not ignored and
-    // mark all ancestors along the way as dirty.
-    if (obj.AccessibilityIsIgnored()) {
-      WebAXObject ancestor = obj;
-      for (; !ancestor.IsDetached() && ancestor.AccessibilityIsIgnored();
-           ancestor = ancestor.ParentObject()) {
-        // There are 3 states of nodes that we care about here.
-        // (x) Unignored, included in tree
-        // [x] Ignored, included in tree
-        // <x> Ignored, excluded from tree
-        //
-        // Consider the following tree :
-        // ++(0) Role::kRootWebArea
-        // ++++<1> Role::kNone
-        // ++++++[2] Role::kGenericContainer <body>
-        // ++++++++[3] Role::kGenericContainer with 'visibility: hidden'
-        //
-        // If we modify [3] to be 'visibility: visible', we will receive
-        // Event::kChildrenChanged here for the Ignored parent [2].
-        // We must re-serialize the Unignored parent node (0) due to this
-        // change, but we must also re-serialize [2] since its children
-        // have changed. <1> was never part of the ax tree, and therefore
-        // does not need to be serialized.
-        // Note that [3] will be serialized to (3) during :
-        // |AXTreeSerializer<>::SerializeChangedNodes| when node [2] is
-        // being serialized, since it will detect the Ignored state had
-        // changed.
-        //
-        // Similarly, during Event::kTextChanged, if any Ignored,
-        // but included in tree ancestor uses NameFrom::kContents,
-        // they must also be re-serialized in case the name changed.
-        DirtyObject dirty_object;
-        dirty_object.obj = ancestor;
-        dirty_object.event_from = current_dirty_object.event_from;
-        dirty_object.event_from_action = current_dirty_object.event_from_action;
-        dirty_object.event_intents = current_dirty_object.event_intents;
-        dirty_objects.push_back(dirty_object);
-      }
-      DirtyObject dirty_object;
-      dirty_object.obj = ancestor;
-      dirty_object.event_from = current_dirty_object.event_from;
-      dirty_object.event_from_action = current_dirty_object.event_from_action;
-      dirty_object.event_intents = current_dirty_object.event_intents;
-      dirty_objects.push_back(dirty_object);
-    }
-
     ui::AXTreeUpdate update;
     update.event_from = current_dirty_object.event_from;
     update.event_from_action = current_dirty_object.event_from_action;
diff --git a/content/renderer/accessibility/render_accessibility_impl.h b/content/renderer/accessibility/render_accessibility_impl.h
index f1004a5e..6365bdf 100644
--- a/content/renderer/accessibility/render_accessibility_impl.h
+++ b/content/renderer/accessibility/render_accessibility_impl.h
@@ -113,8 +113,7 @@
   void MarkWebAXObjectDirty(
       const blink::WebAXObject& obj,
       bool subtree,
-      ax::mojom::Action event_from_action = ax::mojom::Action::kNone,
-      std::vector<ui::AXEventIntent> event_intents = {});
+      ax::mojom::Action event_from_action = ax::mojom::Action::kNone);
 
   void HandleAXEvent(const ui::AXEvent& event);
 
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index 84323f4..0f55b53 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -432,7 +432,7 @@
   WebDocument document = GetMainFrame()->GetDocument();
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
   GetRenderAccessibilityImpl()->HandleAXEvent(
-      ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kChildrenChanged));
+      ui::AXEvent(root_obj.AxID(), ax::mojom::Event::kLayoutComplete));
   SendPendingAccessibilityEvents();
   EXPECT_EQ(1, CountAccessibilityNodesSentToBrowser());
   {
diff --git a/content/renderer/media/batching_media_log.cc b/content/renderer/media/batching_media_log.cc
index 3023fb1..8d8c5cb2 100644
--- a/content/renderer/media/batching_media_log.cc
+++ b/content/renderer/media/batching_media_log.cc
@@ -8,14 +8,11 @@
 
 #include "base/bind.h"
 #include "base/json/json_writer.h"
-#include "base/location.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "base/time/default_tick_clock.h"
-#include "content/public/common/content_client.h"
-#include "content/public/renderer/content_renderer_client.h"
-#include "content/public/renderer/render_thread.h"
 #include "media/base/logging_override_if_enabled.h"
 
 namespace {
@@ -60,8 +57,6 @@
       last_ipc_send_time_(tick_clock_->NowTicks()),
       ipc_send_pending_(false),
       logged_rate_limit_warning_(false) {
-  DCHECK(RenderThread::Get())
-      << "BatchingMediaLog must be constructed on the render thread";
   // Pre-bind the WeakPtr on the right thread since we'll receive calls from
   // other threads and don't want races.
   weak_this_ = weak_factory_.GetWeakPtr();
diff --git a/content/renderer/media/render_media_event_handler.cc b/content/renderer/media/render_media_event_handler.cc
index 220d293d..fef52e4 100644
--- a/content/renderer/media/render_media_event_handler.cc
+++ b/content/renderer/media/render_media_event_handler.cc
@@ -12,7 +12,11 @@
   GetMediaInternalRecordLogRemote().Log(events_to_send);
 }
 
-RenderMediaEventHandler::RenderMediaEventHandler() = default;
+RenderMediaEventHandler::RenderMediaEventHandler() {
+  DCHECK(RenderThread::Get())
+      << "RenderMediaEventHandler must be constructed on the render thread";
+}
+
 RenderMediaEventHandler::~RenderMediaEventHandler() = default;
 
 // This media log doesn't care, since the RenderThread outlives us for
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index a61bf4db..19aba494 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -631,9 +631,9 @@
 // but the system default is true.
 #if defined(OS_MAC)
   is_elastic_overscroll_enabled_ = true;
-#elif defined(OS_WIN)
+#elif defined(OS_WIN) || defined(OS_ANDROID)
   is_elastic_overscroll_enabled_ =
-      base::FeatureList::IsEnabled(features::kElasticOverscrollWin);
+      base::FeatureList::IsEnabled(features::kElasticOverscroll);
 #else
   is_elastic_overscroll_enabled_ = false;
 #endif
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 074db7fb..2b4cf709 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -1034,11 +1034,15 @@
 // sent back to the Browser via Mojo objects bound to |owner_task_runner|.
 std::unique_ptr<media::MediaLog> RendererBlinkPlatformImpl::GetMediaLog(
     blink::MediaInspectorContext* inspector_context,
-    scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner) {
+    scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner,
+    bool is_on_worker) {
   std::vector<std::unique_ptr<BatchingMediaLog::EventHandler>> handlers;
 
   // For chrome://media-internals.
-  handlers.push_back(std::make_unique<RenderMediaEventHandler>());
+  // This should only be created in the main Window context, and not from
+  // a worker context.
+  if (!is_on_worker)
+    handlers.push_back(std::make_unique<RenderMediaEventHandler>());
 
   // For devtools' media tab.
   handlers.push_back(
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 6ce06a9..8275928 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -242,7 +242,8 @@
       scoped_refptr<network::SharedURLLoaderFactory> factory) override;
   std::unique_ptr<media::MediaLog> GetMediaLog(
       blink::MediaInspectorContext* inspector_context,
-      scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner) override;
+      scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner,
+      bool is_on_worker) override;
   media::GpuVideoAcceleratorFactories* GetGpuFactories() override;
   scoped_refptr<base::SingleThreadTaskRunner> MediaThreadTaskRunner() override;
   media::DecoderFactory* GetMediaDecoderFactory() override;
diff --git a/content/test/data/accessibility/css/content-visibility-auto-aria-hidden-expected-blink.txt b/content/test/data/accessibility/css/content-visibility-auto-aria-hidden-expected-blink.txt
new file mode 100644
index 0000000..42459b5
--- /dev/null
+++ b/content/test/data/accessibility/css/content-visibility-auto-aria-hidden-expected-blink.txt
@@ -0,0 +1,6 @@
+rootWebArea name='Done'
+++genericContainer ignored
+++++genericContainer ignored
+++++++genericContainer
+++++++++genericContainer
+++++++++++staticText name='Not hidden'
diff --git a/content/test/data/accessibility/css/content-visibility-auto-aria-hidden.html b/content/test/data/accessibility/css/content-visibility-auto-aria-hidden.html
new file mode 100644
index 0000000..4c5bfefa6
--- /dev/null
+++ b/content/test/data/accessibility/css/content-visibility-auto-aria-hidden.html
@@ -0,0 +1,25 @@
+<!--
+@WAIT-FOR:Done
+-->
+<html>
+<head>
+<style>
+.auto {
+  content-visibility: auto;
+}
+.push-content-down {
+  height: 5000px;
+}
+</style>
+</head>
+<body>
+<div class="push-content-down" role="none"></div>
+<div class="auto"><div aria-hidden="true">aria-hidden</div><div>Not hidden</div></div>
+<script>
+function runTest() {
+  document.title = 'Done';
+}
+window.onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest));
+</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-auralinux.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-auralinux.txt
index 7dbfe02..45182e87 100644
--- a/content/test/data/accessibility/event/aria-combo-box-expand-expected-auralinux.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-expand-expected-auralinux.txt
@@ -1,5 +1,12 @@
+FOCUS-EVENT:FALSE role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+FOCUS-EVENT:TRUE role=ROLE_COMBO_BOX name='(null)' EDITABLE,ENABLED,EXPANDABLE,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,SINGLE-LINE,VISIBLE,SUPPORTS-AUTOCOMPLETION,SELECTABLE-TEXT
+STATE-CHANGE:FOCUSED:FALSE role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:FOCUSED:TRUE role=ROLE_COMBO_BOX name='(null)' EDITABLE,ENABLED,EXPANDABLE,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,SINGLE-LINE,VISIBLE,SUPPORTS-AUTOCOMPLETION,SELECTABLE-TEXT
+TEXT-CARET-MOVED role=ROLE_COMBO_BOX name='(null)' EDITABLE,ENABLED,EXPANDABLE,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,SINGLE-LINE,VISIBLE,SUPPORTS-AUTOCOMPLETION,SELECTABLE-TEXT
+=== Start Continuation ===
+STATE-CHANGE:EXPANDED:TRUE role=ROLE_COMBO_BOX name='(null)' EDITABLE,ENABLED,EXPANDABLE,EXPANDED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,SINGLE-LINE,VISIBLE,SUPPORTS-AUTOCOMPLETION,SELECTABLE-TEXT
+=== Start Continuation ===
 FOCUS-EVENT:TRUE role=ROLE_LIST_ITEM name='Apple' ENABLED,FOCUSABLE,FOCUSED,SELECTABLE,SELECTED,SENSITIVE,SHOWING,VISIBLE
 SELECTION-CHANGED role=ROLE_LIST_BOX name='(null)' ENABLED,SENSITIVE,SHOWING,VERTICAL,VISIBLE
-STATE-CHANGE:EXPANDED:TRUE role=ROLE_COMBO_BOX name='(null)' EDITABLE,ENABLED,EXPANDABLE,EXPANDED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,SINGLE-LINE,VISIBLE,SUPPORTS-AUTOCOMPLETION,SELECTABLE-TEXT
 STATE-CHANGE:FOCUSED:TRUE role=ROLE_LIST_ITEM name='Apple' ENABLED,FOCUSABLE,FOCUSED,SELECTABLE,SELECTED,SENSITIVE,SHOWING,VISIBLE
 STATE-CHANGE:SELECTED:TRUE role=ROLE_LIST_ITEM name='Apple' ENABLED,FOCUSABLE,FOCUSED,SELECTABLE,SELECTED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt
index 343feae3..fee02ce 100644
--- a/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-expand-expected-mac.txt
@@ -1,4 +1,9 @@
+AXFocusedUIElementChanged on AXComboBox
+AXSelectedTextChanged on AXComboBox AXTextSelectionDirection=AXTextSelectionDirectionUnknown AXTextSelectionGranularity=AXTextSelectionGranularityUnknown AXTextStateChangeType=AXTextStateChangeTypeSelectionMove
+AXSelectedTextChanged on AXWebArea AXTextSelectionDirection=AXTextSelectionDirectionUnknown AXTextSelectionGranularity=AXTextSelectionGranularityUnknown AXTextStateChangeType=AXTextStateChangeTypeSelectionMove
+=== Start Continuation ===
 AXExpandedChanged on AXComboBox
+=== Start Continuation ===
 AXFocusedUIElementChanged on AXStaticText AXValue="Apple"
 AXSelectedChildrenChanged on AXComboBox
-AXSelectedChildrenChanged on AXList
\ No newline at end of file
+AXSelectedChildrenChanged on AXList
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win.txt
index aadb6d6..72b6a31 100644
--- a/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win.txt
@@ -1,7 +1,10 @@
+AutomationFocusChanged on role=combobox
+Text_TextSelectionChanged on role=combobox
+=== Start Continuation ===
 AriaProperties changed on role=combobox
+ExpandCollapseExpandCollapseState changed on role=combobox
+=== Start Continuation ===
 AriaProperties changed on role=option, name=Apple
 AutomationFocusChanged on role=option, name=Apple
-AutomationFocusChanged on role=option, name=Apple
-ExpandCollapseExpandCollapseState changed on role=combobox
 SelectionItemIsSelected changed on role=option, name=Apple
 SelectionItem_ElementSelected on role=option, name=Apple
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win7.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win7.txt
deleted file mode 100644
index 2cbca25..0000000
--- a/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win7.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-AriaProperties changed on role=combobox
-AriaProperties changed on role=option, name=Apple
-AutomationFocusChanged on role=combobox
-AutomationFocusChanged on role=option, name=Apple
-AutomationFocusChanged on role=option, name=Apple
-ExpandCollapseExpandCollapseState changed on role=combobox
-SelectionItemIsSelected changed on role=option, name=Apple
-SelectionItem_ElementSelected on role=option, name=Apple
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-win.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-win.txt
index 99969e2..5db16f2 100644
--- a/content/test/data/accessibility/event/aria-combo-box-expand-expected-win.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-expand-expected-win.txt
@@ -1,6 +1,12 @@
+EVENT_OBJECT_FOCUS on <input> role=ROLE_SYSTEM_COMBOBOX FOCUSED,COLLAPSED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SELECTABLE_TEXT,IA2_STATE_SINGLE_LINE,IA2_STATE_SUPPORTS_AUTOCOMPLETION
+EVENT_OBJECT_LOCATIONCHANGE role=ROLE_SYSTEM_CARET  window_class=Chrome_WidgetWin_0
+EVENT_OBJECT_SHOW role=ROLE_SYSTEM_CARET  window_class=Chrome_WidgetWin_0
+IA2_EVENT_TEXT_CARET_MOVED on <input> role=ROLE_SYSTEM_COMBOBOX FOCUSED,COLLAPSED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SELECTABLE_TEXT,IA2_STATE_SINGLE_LINE,IA2_STATE_SUPPORTS_AUTOCOMPLETION
+=== Start Continuation ===
+EVENT_OBJECT_STATECHANGE on <input> role=ROLE_SYSTEM_COMBOBOX FOCUSED,EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SELECTABLE_TEXT,IA2_STATE_SINGLE_LINE,IA2_STATE_SUPPORTS_AUTOCOMPLETION
+=== Start Continuation ===
 EVENT_OBJECT_FOCUS on <li#option1> role=ROLE_SYSTEM_LISTITEM name="Apple" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=1 SetSize=3
 EVENT_OBJECT_SELECTION on <li#option1> role=ROLE_SYSTEM_LISTITEM name="Apple" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=1 SetSize=3
 EVENT_OBJECT_SELECTIONWITHIN on <ul#list> role=ROLE_SYSTEM_LIST IA2_STATE_VERTICAL SetSize=3
-EVENT_OBJECT_STATECHANGE on <input> role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SELECTABLE_TEXT,IA2_STATE_SINGLE_LINE,IA2_STATE_SUPPORTS_AUTOCOMPLETION
 EVENT_OBJECT_STATECHANGE on <li#option1> role=ROLE_SYSTEM_LISTITEM name="Apple" SELECTED,FOCUSED,FOCUSABLE,SELECTABLE PosInSet=1 SetSize=3
-IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on <input> role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SELECTABLE_TEXT,IA2_STATE_SINGLE_LINE,IA2_STATE_SUPPORTS_AUTOCOMPLETION
\ No newline at end of file
+IA2_EVENT_ACTIVE_DESCENDANT_CHANGED on <input> role=ROLE_SYSTEM_COMBOBOX EXPANDED,FOCUSABLE,HASPOPUP IA2_STATE_EDITABLE,IA2_STATE_SELECTABLE_TEXT,IA2_STATE_SINGLE_LINE,IA2_STATE_SUPPORTS_AUTOCOMPLETION
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand.html b/content/test/data/accessibility/event/aria-combo-box-expand.html
index 7e88c0d..9623859 100644
--- a/content/test/data/accessibility/event/aria-combo-box-expand.html
+++ b/content/test/data/accessibility/event/aria-combo-box-expand.html
@@ -1,7 +1,3 @@
-<!--
-@MAC-RUN-UNTIL-EVENT:AXSelectedChildrenChanged
-@MAC-RUN-UNTIL-EVENT:AXExpandedChanged
--->
 <!DOCTYPE html>
 <html>
 <body>
@@ -13,11 +9,16 @@
 <li id="option3" role="option">Banana</li>
 </ul>
 <script>
-  document.querySelector('input').focus();
+  var go_passes = [
+    () => document.querySelector('input').focus(),
+    () => document.querySelector('input').setAttribute('aria-expanded', 'true'),
+    () => document.querySelector('input').setAttribute('aria-activedescendant', 'option1'),
+  ];
+
+  let current_pass = 0;
   function go() {
-    var combo_box = document.querySelector('input');
-    combo_box.setAttribute('aria-expanded', 'true');
-    combo_box.setAttribute('aria-activedescendant', 'option1');
+    go_passes[current_pass++].call();
+    return current_pass < go_passes.length;
   }
 </script>
 </body>
diff --git a/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win.txt b/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win.txt
index e065920..c4b6a6069 100644
--- a/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win.txt
@@ -1,7 +1,6 @@
 AriaProperties changed on role=option, name=Apple
 AriaProperties changed on role=option, name=Orange
 AutomationFocusChanged on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Orange
 SelectionItemIsSelected changed on role=option, name=Apple
 SelectionItemIsSelected changed on role=option, name=Orange
 SelectionItem_ElementSelected on role=option, name=Orange
@@ -9,7 +8,6 @@
 AriaProperties changed on role=option, name=Banana
 AriaProperties changed on role=option, name=Orange
 AutomationFocusChanged on role=option, name=Banana
-AutomationFocusChanged on role=option, name=Banana
 SelectionItemIsSelected changed on role=option, name=Banana
 SelectionItemIsSelected changed on role=option, name=Orange
 SelectionItem_ElementSelected on role=option, name=Banana
diff --git a/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win7.txt b/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win7.txt
deleted file mode 100644
index 5813e2a..0000000
--- a/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win7.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-AriaProperties changed on role=option, name=Apple
-AriaProperties changed on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Apple
-AutomationFocusChanged on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Orange
-SelectionItemIsSelected changed on role=option, name=Apple
-SelectionItemIsSelected changed on role=option, name=Orange
-SelectionItem_ElementSelected on role=option, name=Orange
-=== Start Continuation ===
-AriaProperties changed on role=option, name=Banana
-AriaProperties changed on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Banana
-AutomationFocusChanged on role=option, name=Banana
-AutomationFocusChanged on role=option, name=Orange
-SelectionItemIsSelected changed on role=option, name=Banana
-SelectionItemIsSelected changed on role=option, name=Orange
-SelectionItem_ElementSelected on role=option, name=Banana
diff --git a/content/test/data/accessibility/event/expanded-changed-expected-mac.txt b/content/test/data/accessibility/event/expanded-changed-expected-mac.txt
index 3724521..5312fbbc 100644
--- a/content/test/data/accessibility/event/expanded-changed-expected-mac.txt
+++ b/content/test/data/accessibility/event/expanded-changed-expected-mac.txt
@@ -1 +1,2 @@
-AXExpandedChanged on AXLink AXDescription="Toggle"
\ No newline at end of file
+=== Start Continuation ===
+AXExpandedChanged on AXLink AXDescription="Toggle"
diff --git a/content/test/data/accessibility/event/expanded-changed-expected-uia-win.txt b/content/test/data/accessibility/event/expanded-changed-expected-uia-win.txt
index 53e0dc7..0b2f7da0 100644
--- a/content/test/data/accessibility/event/expanded-changed-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/expanded-changed-expected-uia-win.txt
@@ -1,6 +1,7 @@
-AriaProperties changed on role=link, name=Toggle
 AriaProperties changed on role=list, name=list
 AriaProperties changed on role=listitem, name=list item 1
 AriaProperties changed on role=listitem, name=list item 2
 AriaProperties changed on role=listitem, name=list item 3
-ExpandCollapseExpandCollapseState changed on role=link, name=Toggle
\ No newline at end of file
+=== Start Continuation ===
+AriaProperties changed on role=link, name=Toggle
+ExpandCollapseExpandCollapseState changed on role=link, name=Toggle
diff --git a/content/test/data/accessibility/event/expanded-changed.html b/content/test/data/accessibility/event/expanded-changed.html
index d39c742..97d8870 100644
--- a/content/test/data/accessibility/event/expanded-changed.html
+++ b/content/test/data/accessibility/event/expanded-changed.html
@@ -13,9 +13,15 @@
   <li aria-label="list item 3">ListItem 3</li>
 </ul>
 <script>
+  var go_passes = [
+    () => document.getElementById('list').style.visibility = 'visible',
+    () => document.getElementById('title').setAttribute('aria-expanded', 'true'),
+  ];
+
+  let current_pass = 0;
   function go() {
-    document.getElementById('list').style.visibility = 'visible';
-    document.getElementById('title').setAttribute('aria-expanded', 'true');
+    go_passes[current_pass++].call();
+    return current_pass < go_passes.length;
   }
 </script>
 </body>
diff --git a/content/test/data/accessibility/event/focus-listbox-multiselect-expected-uia-win.txt b/content/test/data/accessibility/event/focus-listbox-multiselect-expected-uia-win.txt
index fef719d..3e39e79 100644
--- a/content/test/data/accessibility/event/focus-listbox-multiselect-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/focus-listbox-multiselect-expected-uia-win.txt
@@ -1,7 +1,6 @@
 AriaProperties changed on role=option, name=b
 AriaProperties changed on role=option, name=c
 AutomationFocusChanged on role=option, name=c
-AutomationFocusChanged on role=option, name=c
 SelectionItemIsSelected changed on role=option, name=b
 SelectionItemIsSelected changed on role=option, name=c
 SelectionItem_ElementSelected on role=option, name=c
diff --git a/content/test/data/accessibility/event/listbox-next-expected-uia-win.txt b/content/test/data/accessibility/event/listbox-next-expected-uia-win.txt
index 957e79b..7065466 100644
--- a/content/test/data/accessibility/event/listbox-next-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/listbox-next-expected-uia-win.txt
@@ -1,7 +1,6 @@
 AriaProperties changed on role=option, name=Apple
 AriaProperties changed on role=option, name=Orange
 AutomationFocusChanged on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Orange
 SelectionItemIsSelected changed on role=option, name=Apple
 SelectionItemIsSelected changed on role=option, name=Orange
 SelectionItem_ElementSelected on role=option, name=Orange
diff --git a/content/test/data/accessibility/event/listbox-next-expected-uia-win7.txt b/content/test/data/accessibility/event/listbox-next-expected-uia-win7.txt
deleted file mode 100644
index d5397e6..0000000
--- a/content/test/data/accessibility/event/listbox-next-expected-uia-win7.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-AriaProperties changed on role=option, name=Apple
-AriaProperties changed on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Apple
-AutomationFocusChanged on role=option, name=Orange
-AutomationFocusChanged on role=option, name=Orange
-SelectionItemIsSelected changed on role=option, name=Apple
-SelectionItemIsSelected changed on role=option, name=Orange
-SelectionItem_ElementSelected on role=option, name=Orange
diff --git a/content/test/data/accessibility/event/menulist-collapse-expected-uia-win.txt b/content/test/data/accessibility/event/menulist-collapse-expected-uia-win.txt
index 0b3357b..d8ebbce6 100644
--- a/content/test/data/accessibility/event/menulist-collapse-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/menulist-collapse-expected-uia-win.txt
@@ -1,5 +1,4 @@
 AutomationFocusChanged on role=combobox
-AutomationFocusChanged on role=combobox
 === Start Continuation ===
 AriaProperties changed on role=combobox
 SelectionItemIsSelected changed on role=combobox
diff --git a/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win.txt b/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win.txt
index fa79854..9da1adf 100644
--- a/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/menulist-collapse-next-expected-uia-win.txt
@@ -1,5 +1,4 @@
 AutomationFocusChanged on role=combobox
-AutomationFocusChanged on role=combobox
 === Start Continuation ===
 AriaProperties changed on role=combobox
 AriaProperties changed on role=listitem, name=Orange
diff --git a/content/test/data/accessibility/event/menulist-focus-expected-uia-win.txt b/content/test/data/accessibility/event/menulist-focus-expected-uia-win.txt
index 3cd5d17..50c9f72 100644
--- a/content/test/data/accessibility/event/menulist-focus-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/menulist-focus-expected-uia-win.txt
@@ -1,2 +1 @@
 AutomationFocusChanged on role=combobox
-AutomationFocusChanged on role=combobox
diff --git a/content/test/data/accessibility/event/subtree-reparented-via-aria-owns-expected-auralinux.txt b/content/test/data/accessibility/event/subtree-reparented-via-aria-owns-expected-auralinux.txt
index 5325c31d..a542fbe 100644
--- a/content/test/data/accessibility/event/subtree-reparented-via-aria-owns-expected-auralinux.txt
+++ b/content/test/data/accessibility/event/subtree-reparented-via-aria-owns-expected-auralinux.txt
@@ -1 +1,4 @@
-PARENT-CHANGED PARENT:(role=ROLE_PANEL name='(null)') role=ROLE_LANDMARK name='(null)' ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:ADD index:0 CHILD:(role=ROLE_LANDMARK) role=ROLE_PANEL ENABLED,SENSITIVE,SHOWING,VISIBLE
+CHILDREN-CHANGED:REMOVE index:1 CHILD:(role=ROLE_LANDMARK) role=ROLE_DOCUMENT_WEB ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
diff --git a/content/test/data/accessibility/event/subtree-reparented-via-aria-owns-expected-win.txt b/content/test/data/accessibility/event/subtree-reparented-via-aria-owns-expected-win.txt
index 9632e1cb..5a902d09 100644
--- a/content/test/data/accessibility/event/subtree-reparented-via-aria-owns-expected-win.txt
+++ b/content/test/data/accessibility/event/subtree-reparented-via-aria-owns-expected-win.txt
@@ -1,4 +1,6 @@
+EVENT_OBJECT_HIDE on <div#search> role=ROLE_SYSTEM_GROUPING
 EVENT_OBJECT_REORDER on <#document> role=ROLE_SYSTEM_DOCUMENT value~=[doc-url] FOCUSED,FOCUSABLE
 EVENT_OBJECT_REORDER on <div#group> role=ROLE_SYSTEM_GROUPING
+EVENT_OBJECT_SHOW on <div#search> role=ROLE_SYSTEM_GROUPING
 IA2_EVENT_TEXT_INSERTED on <div#group> role=ROLE_SYSTEM_GROUPING new_text={'<obj>' start=0 end=1}
-IA2_EVENT_TEXT_REMOVED on <#document> role=ROLE_SYSTEM_DOCUMENT value~=[doc-url] FOCUSED,FOCUSABLE old_text={'<obj>' start=1 end=2}
+IA2_EVENT_TEXT_REMOVED on <#document> role=ROLE_SYSTEM_DOCUMENT value~=[doc-url] FOCUSED,FOCUSABLE old_text={'<obj>' start=1 end=2}
\ No newline at end of file
diff --git a/content/test/data/accessibility/event/tbody-focus-expected-uia-win.txt b/content/test/data/accessibility/event/tbody-focus-expected-uia-win.txt
index 44f41197..eaa209f 100644
--- a/content/test/data/accessibility/event/tbody-focus-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/tbody-focus-expected-uia-win.txt
@@ -1,2 +1 @@
 AutomationFocusChanged on role=rowgroup, name=tbody
-AutomationFocusChanged on role=rowgroup, name=tbody
diff --git a/content/test/data/accessibility/event/tfoot-focus-expected-uia-win.txt b/content/test/data/accessibility/event/tfoot-focus-expected-uia-win.txt
index 437da270..8d3b7fb 100644
--- a/content/test/data/accessibility/event/tfoot-focus-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/tfoot-focus-expected-uia-win.txt
@@ -1,2 +1 @@
 AutomationFocusChanged on role=rowgroup, name=tfoot
-AutomationFocusChanged on role=rowgroup, name=tfoot
diff --git a/content/test/data/accessibility/event/thead-focus-expected-uia-win.txt b/content/test/data/accessibility/event/thead-focus-expected-uia-win.txt
index 45470dda..40aef32 100644
--- a/content/test/data/accessibility/event/thead-focus-expected-uia-win.txt
+++ b/content/test/data/accessibility/event/thead-focus-expected-uia-win.txt
@@ -1,2 +1 @@
 AutomationFocusChanged on role=rowgroup, name=thead
-AutomationFocusChanged on role=rowgroup, name=thead
diff --git a/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc b/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
index 54216e7..95f957d9 100644
--- a/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
+++ b/content/test/fuzzer/code_cache_host_mojolpm_fuzzer.cc
@@ -24,6 +24,7 @@
 #include "content/public/test/test_content_client_initializer.h"
 #include "content/test/fuzzer/code_cache_host_mojolpm_fuzzer.pb.h"
 #include "mojo/core/embedder/embedder.h"
+#include "storage/browser/quota/quota_manager.h"
 #include "storage/browser/quota/special_storage_policy.h"
 #include "storage/browser/test/mock_special_storage_policy.h"
 #include "third_party/blink/public/mojom/loader/code_cache.mojom-mojolpm.h"
@@ -213,7 +214,9 @@
       std::make_unique<content::CacheStorageControlWrapper>(
           content::GetIOThreadTaskRunner({}), browser_context_->GetPath(),
           browser_context_->GetSpecialStoragePolicy(),
-          /*quota_manager_proxy=*/nullptr,
+          browser_context_->GetDefaultStoragePartition()
+              ->GetQuotaManager()
+              ->proxy(),
           /*blob_storage_context=*/mojo::NullRemote());
 
   generated_code_cache_context_ =
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 2ff25483..f0fc28e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -279,9 +279,9 @@
 # Flaky on AMD Win7
 crbug.com/989050 [ angle-d3d11 win7 amd ] conformance2/textures/misc/tex-unpack-params-imagedata.html [ RetryOnFailure ]
 
-# Passthrough command decoder / Linux / OpenGL / NVIDIA
-crbug.com/766918 [ angle-opengl linux passthrough nvidia ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
-crbug.com/965648 [ angle-opengl linux passthrough nvidia ] deqp/functional/gles3/shaderstruct.html [ RetryOnFailure ]
+# Win / Intel
+crbug.com/1215624 [ win intel angle-d3d11 ] conformance2/textures/video/* [ RetryOnFailure ]
+crbug.com/1215624 [ win intel angle-d3d11 ] conformance2/textures/webgl_canvas/tex-2d-rgba8-rgba-unsigned_byte.html [ RetryOnFailure ]
 
 ####################
 # Mac failures     #
@@ -604,6 +604,8 @@
 
 # Linux / OpenGL / NVIDIA failures
 crbug.com/angleproject/4555 [ linux angle-opengl passthrough nvidia ] conformance/misc/type-conversion-test.html [ Skip ]
+crbug.com/766918 [ angle-opengl linux passthrough nvidia ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
+crbug.com/965648 [ angle-opengl linux passthrough nvidia ] deqp/functional/gles3/shaderstruct.html [ RetryOnFailure ]
 
 # Linux GTX 1660
 crbug.com/1115314 [ linux nvidia-0x2184 angle-opengl passthrough ] deqp/functional/gles3/fbocompleteness.html [ Failure ]
@@ -716,9 +718,6 @@
 crbug.com/angleproject/3686 [ android angle-opengles ] deqp/functional/gles3/multisample/fbo_8_samples.html [ Failure ]
 crbug.com/angleproject/3686 [ android angle-opengles ] deqp/functional/gles3/multisample/fbo_max_samples.html [ Failure ]
 
-# Flakily hitting a fatal V8 compiler issue.
-crbug.com/1213266 [ android android-pixel-4 ] deqp/functional/gles3/* [ RetryOnFailure ]
-
 crbug.com/1037958 [ mac amd-0x679e ] conformance2/transform_feedback/switching-objects.html [ RetryOnFailure ]
 
 # Conflicting expectations to test that the
diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc
index cea0a32..88bd15d 100644
--- a/content/test/test_render_view_host.cc
+++ b/content/test/test_render_view_host.cc
@@ -234,10 +234,6 @@
   return display_feature_;
 }
 
-ui::Compositor* TestRenderWidgetHostView::GetCompositor() {
-  return compositor_;
-}
-
 TestRenderViewHost::TestRenderViewHost(
     FrameTree* frame_tree,
     SiteInstance* instance,
diff --git a/content/test/test_render_view_host.h b/content/test/test_render_view_host.h
index 0529c4ec..5482020 100644
--- a/content/test/test_render_view_host.h
+++ b/content/test/test_render_view_host.h
@@ -118,7 +118,6 @@
   viz::SurfaceId GetCurrentSurfaceId() const override;
   std::unique_ptr<SyntheticGestureTarget> CreateSyntheticGestureTarget()
       override;
-  ui::Compositor* GetCompositor() override;
 
   bool is_showing() const { return is_showing_; }
   bool is_occluded() const { return is_occluded_; }
@@ -130,8 +129,6 @@
 
   const WebCursor& last_cursor() const { return last_cursor_; }
 
-  void SetCompositor(ui::Compositor* compositor) { compositor_ = compositor; }
-
  protected:
   // RenderWidgetHostViewBase:
   void UpdateBackgroundColor() override;
@@ -157,8 +154,6 @@
 #endif
 
   absl::optional<DisplayFeature> display_feature_;
-
-  ui::Compositor* compositor_ = nullptr;
 };
 
 // TestRenderViewHost ----------------------------------------------------------
diff --git a/docs/security/faq.md b/docs/security/faq.md
index 428acc5..912df6a1 100644
--- a/docs/security/faq.md
+++ b/docs/security/faq.md
@@ -677,6 +677,11 @@
 See our dedicated [Service Worker Security
 FAQ](https://chromium.googlesource.com/chromium/src/+/main/docs/security/service-worker-security-faq.md).
 
+<a name="TOC-What-is-the-security-story-for-Extensions-"></a>
+## What is the security story for Extensions?
+
+See our dedicated [Extensions Security FAQ](https://chromium.googlesource.com/chromium/src/+/main/extensions/docs/security_faq.md).
+
 <a name="TOC-What-about-URL-spoofs-using-Internationalized-Domain-Names-IDN-"></a>
 ## What about URL spoofs using Internationalized Domain Names (IDN)?
 
@@ -692,30 +697,7 @@
 <a name="TOC-Chrome-silently-syncs-extensions-across-devices.-Is-this-a-security-vulnerability-"></a>
 ## Chrome silently syncs extensions across devices. Is this a security vulnerability?
 
-If an attacker has access to one of a victim's devices, the attacker can install
-an extension which will be synced to the victim's other sync-enabled
-devices. Similarly, an attacker who phishes a victim's Google credentials can
-sign in to Chrome as the victim and install an extension, which will be synced
-to the victim's other sync-enabled devices. Sync thereby enables an attacker to
-elevate phished credentials or physical access to persistent access on all of a
-victim's sync-enabled devices.
-
-To mitigate this issue, Chrome only syncs extensions that have been installed
-from the Chrome Web Store. Extensions in the Chrome Web Store are monitored for
-abusive behavior.
-
-In the future, we may pursue further mitigations. However, because an attacker
-must already have the victim's Google credentials and/or [physical access to a
-device](#TOC-Why-aren-t-physically-local-attacks-in-Chrome-s-threat-model-), we
-don't consider this attack a security vulnerability.
-
-We **do** consider it a vulnerability if an attacker can get an extension to
-sync to a victim's device without either of the above preconditions. For
-example, we consider it a vulnerability if an attacker could craft a request to
-Google's sync servers that causes an extension to be installed to a user's
-device, or if an attacker could entice a victim to visit a webpage that causes
-an extension to be installed on their device(s). Please report such bugs via
-https://bugs.chromium.org/p/chromium/issues/entry?template=Security+Bug.
+This topic has been moved to the [Extensions Security FAQ](https://chromium.googlesource.com/chromium/src/+/main/extensions/docs/security_faq.md).
 
 <a name="TOC-Are-PDF-files-static-content-in-Chromium-"></a>
 ## Are PDF files static content in Chromium?
diff --git a/docs/standards/DIR_METADATA b/docs/standards/DIR_METADATA
new file mode 100644
index 0000000..d09af66
--- /dev/null
+++ b/docs/standards/DIR_METADATA
@@ -0,0 +1,9 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+team_email: "spec-mentors@chromium.org"
diff --git a/docs/standards/README.md b/docs/standards/README.md
new file mode 100644
index 0000000..ad05972
--- /dev/null
+++ b/docs/standards/README.md
@@ -0,0 +1,13 @@
+# Developing web standards within Chromium
+
+The [Chromium process for launching
+features](https://www.chromium.org/blink/launching-features) requires that
+web-exposed features go through (though not always complete) a public
+standardization process.
+
+We provide a [specification mentorship
+program](https://www.chromium.org/blink/spec-mentors) to help you go through
+this process.
+
+If you notice misbehavior in a standardization venue, see their [codes of
+conduct](conduct.md).
diff --git a/docs/standards/conduct.md b/docs/standards/conduct.md
new file mode 100644
index 0000000..004ade9
--- /dev/null
+++ b/docs/standards/conduct.md
@@ -0,0 +1,74 @@
+# Codes of Conduct
+
+Chromium contributors participate in many organizations that standardize Web
+APIs. These organizations have rules that govern participants' behavior. We need
+to follow these rules, and we can expect that others in these bodies will also
+follow the rules.
+
+This document links to each organization's code of conduct and any contact
+information that organization provides for navigating difficulties. For
+organizations without a code of conduct, we're happy to help them develop one.
+
+If you notice someone breaking a venue's code of conduct, you have several options for handling the situation:
+
+1. If you feel comfortable, call out the behavior in the discussion and give an
+   opportunity for it to be addressed. For example:
+
+   > "This comment is not constructive/does not help move us to consensus/makes
+   an incorrect assumption/is not actionable.  Could you please re-state or
+   rephrase your comments accordingly?"
+1. If you do not feel comfortable raising the concern directly, or the behavior
+   is not fixed or acknowledged, disengage with the discussion and escalate the
+   issue:
+   1. To your [spec mentor](https://www.chromium.org/blink/spec-mentors) if you have one.
+   1. To a relevant group chair.
+   1. To the SDO's ombuds group as identified below and/or in their code of conduct.
+
+
+[TOC]
+
+
+## W3C
+
+See the [Code of Ethics and Professional
+Conduct](https://www.w3.org/Consortium/cepc/) and the
+[procedures](https://www.w3.org/Consortium/pwe/#Procedures) used to enforce it.
+The ombuds may be contacted
+[individually](https://www.w3.org/Consortium/pwe/#ombuds) or collectively at
+<ombuds@w3.org>.
+
+## WHATWG
+
+See the [Code of Conduct](https://whatwg.org/code-of-conduct). Escalate via
+<sg@whatwg.org>.
+
+## IETF
+
+See the [Guidelines for Conduct](https://www.rfc-editor.org/rfc/rfc7154.html)
+and the [Anti-Harassment
+Procedures](https://www.rfc-editor.org/rfc/rfc7776.html).
+
+Escalate via [individual ombudspeople](https://www.ietf.org/contact/ombudsteam/)
+or <ombudsteam@ietf.org>.
+
+## ECMA TC39
+
+See the [Code of Conduct](https://tc39.es/code-of-conduct/). Report violations via <tc39-conduct-reports@googlegroups.com>.
+
+## Khronos
+
+See the
+[https://www.khronos.org/events/code-of-conduct](https://www.khronos.org/events/code-of-conduct).
+Their escalation path is more tailored to events than standardization activity,
+but you can [file an anonymous
+report](https://docs.google.com/forms/d/1nr1B_TOMIhSXh1yG8VsvGpEO9p6GWY-3kxJu-rnA5dU/viewform).
+
+## FIDO
+
+The [FIDO Alliance](https://fidoalliance.org/) does not currently have a code of conduct.
+
+## AO Media
+
+The [Alliance for Open Media](https://aomedia.org/) does not currently have a code of conduct.
+
+
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
index 68d2372..613c704 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -667,7 +667,7 @@
   }
 
   // From here the lifecycle of this request is driven by subsequent events on
-  // either |proxy_loader_binding_|, |proxy_client_binding_|, or
+  // either |proxied_loader_receiver_|, |proxied_client_receiver_|, or
   // |header_client_receiver_|.
 }
 
diff --git a/extensions/browser/content_script_tracker.cc b/extensions/browser/content_script_tracker.cc
index cd719013..ba90b446 100644
--- a/extensions/browser/content_script_tracker.cc
+++ b/extensions/browser/content_script_tracker.cc
@@ -248,6 +248,7 @@
     const content::RenderProcessHost& process,
     const ExtensionId& extension_id) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(!extension_id.empty());
 
   // Check if we've been notified about the content script injection via
   // ReadyToCommitNavigation or WillExecuteCode methods.
diff --git a/extensions/browser/extension_message_filter.cc b/extensions/browser/extension_message_filter.cc
index 9ee2211..b225535 100644
--- a/extensions/browser/extension_message_filter.cc
+++ b/extensions/browser/extension_message_filter.cc
@@ -100,9 +100,13 @@
       return true;
 
     case MessagingEndpoint::Type::kTab:
-      // TODO(https://crbug.com/1212918: Re-enable the enforcement after
-      // investigating and fixing the root cause of bad message reports coming
-      // from the end users.
+      if (source_endpoint.extension_id.has_value() &&
+          !ContentScriptTracker::DidProcessRunContentScriptFromExtension(
+              process, source_endpoint.extension_id.value())) {
+        bad_message::ReceivedBadMessage(
+            &process, bad_message::EMF_INVALID_EXTENSION_ID_FOR_CONTENT_SCRIPT);
+        return false;
+      }
       return true;
   }
 }
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
index 0cdb37a0..99f6fa9 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
@@ -205,13 +205,9 @@
 // potential race between the cross-origin renderer initiated navigation and
 // the navigation to "about:blank" started from the browser.
 //
-// Disabled on Linux and Mac due to flakiness: https://crbug.com/1182355.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) || defined(OS_MAC)
-#define MAYBE_NavigationRaceFromEmbedder DISABLED_NavigationRaceFromEmbedder
-#else
-#define MAYBE_NavigationRaceFromEmbedder NavigationRaceFromEmbedder
-#endif
-IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, MAYBE_NavigationRaceFromEmbedder) {
+// Disabled on all platforms due to flakiness: https://crbug.com/1182355.
+IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
+                       DISABLED_NavigationRaceFromEmbedder) {
   const std::string kTestName = "test_navigation_race_embedder";
   auto cross_origin_url =
       embedded_test_server()->GetURL("b.com", "/test_page.html").spec();
diff --git a/extensions/common/api/virtual_keyboard_private.json b/extensions/common/api/virtual_keyboard_private.json
index adcd418..70bc634 100644
--- a/extensions/common/api/virtual_keyboard_private.json
+++ b/extensions/common/api/virtual_keyboard_private.json
@@ -117,6 +117,10 @@
             "$ref": "DisplayFormat",
             "name": "displayFormat",
             "description": "The display format associated with this item."
+          },
+          "timeCopied": {
+            "type": "number",
+            "description": "The time that this item was added to the clipboard, in milliseconds since the epoch."
           }
         }
       }
diff --git a/extensions/docs/security_faq.md b/extensions/docs/security_faq.md
new file mode 100644
index 0000000..6662d4a0
--- /dev/null
+++ b/extensions/docs/security_faq.md
@@ -0,0 +1,387 @@
+# Extensions Security FAQ
+
+[TOC]
+
+## Purpose
+
+This document outlines many common questions we've received about extensions
+security and what we do or do not consider to be security bugs. This is
+primarily written for an audience of Chromium developers and security
+researchers.
+
+It is analogous to the general
+[Chrome Security FAQ](https://chromium.googlesource.com/chromium/src/+/main/docs/security/faq.md).
+
+## Future Additions
+
+Add canonical examples of bugs for each of these (particularly WontFix bugs for
+examples of what is not considered a security bug).
+
+## FAQ
+
+### I've found / written an extension that can access sensitive user data, like passwords and emails. Is this a security bug in Chromium?
+
+In most cases, this is _not_ a security bug. Extensions are designed to have
+access to user data if and only if they have the appropriate
+[permissions](https://developer.chrome.com/docs/extensions/mv3/declare_permissions/).
+For instance, an extension may be able to access a user's data on a set of sites
+(or all sites) if the extension requests the appropriate permissions in its
+manifest file. Before an extension can use these capabilities, they must be
+granted by the user - as an example, the install prompt may say that the
+extension can "Read and change your data on google.com".
+
+If the extension is only able to access user data through permissions the
+extension has requested (for instance, it is able to access data on google.com
+because it specified host or content script permissions that match google.com),
+then this access is working as intended. If an extension is able to access data
+_without_ appropriate permission, then this would be a security bug. Please
+report any such bugs [here][new-security-bug].
+
+### Why do you allow extensions to access sensitive user data? Surely there's no reason to allow it to read my passwords!
+
+It may seem alarming that certain extensions can access certain types of
+sensitive data, such as passwords. However, this access can be critical to
+extensions' functionality. Consider, for instance, password managers (which
+store and retrieve your passwords) - these extensions, fundamentally, must
+access this type of sensitive data. There is very little data that we can
+deterministically say should never be available to extensions.
+
+When installing extensions, users should be sure to look both at the permissions
+requested by the extension (displayed in the installation prompt) and also at
+the extension's privacy policy and other disclosures (which may be linked from
+the extension's listing on the
+[Chrome Web Store](https://chrome.google.com/webstore)).
+
+### If extensions can read sensitive user data, what prevents an extension from stealing my sensitive data?
+
+First, extensions should only be allowed to access data that they have
+permission to (and these permissions must be approved by the user before they
+can be used, either at installation or at runtime). Users should only install
+extensions and grant permissions to extensions they trust.
+
+In addition to these platform mitigations, developers distributing through the
+Chrome Web Store are required to adhere to a number of different policies, which
+describe which types of behaviors are allowed. For instance, the
+[user data](https://developer.chrome.com/docs/webstore/user_data/) FAQ here
+describes the types of data and permissions that extensions are allowed to
+gather and use.
+
+Google also subjects extensions to a combination of automated and manual review
+systems.
+
+### I've found / written an extension that can access site data without any host patterns specified in the "permissions" or "host\_permissions" manifest keys. Is this a security bug?
+
+In most cases, this is _not_ a security bug. Extensions can also access certain
+sites with content scripts (specified in the `content\_scripts` key) or with the
+`activeTab` permission. Hosts specified in `content\_scripts` (under the
+`matches` key) are displayed to the user in permission requests in the same way
+as host permissions requested under `permissions` or `host\_permissions` - that
+is, it indicates the extension can "Read and change your data on <site>".
+ActiveTab requires a user to explicitly invoke the extension on a page before
+the extension can access data on the page, which is a form of runtime permission
+grant (similar to how "sharing" a page with an app on Android grants the app
+access to the content of the page).
+
+If an extension is able to access site data without any API or permission that
+allows this access, then it may be a security bug. Please report any such bugs
+[here][new-security-bug].
+
+### Are extensions able to keep running after they have been uninstalled?
+
+An extension is not able to directly keep running after it has been uninstalled
+(or disabled) by the user or the browser. However, any changes made by an
+extension to a currently loaded site (e.g. script injection or data
+modification) will remain in place after the extension is uninstalled until the
+user leaves the site (e.g. by navigating away or refreshing), and potentially
+beyond. This has a few implications:
+
+*   Exfiltrated data is still exfiltrated. If an extension sent data from the
+    local device to a remote server, Chromium cannot delete the data on that
+    remote server.
+*   Content scripts (and other injected scripts) will continue running on pages
+    they have already been injected in after the extension is disabled. Since
+    Chromium cannot "un-inject" these scripts, they may continue running until
+    the user leaves the site.
+*   Data modified by the extension remains modified. This includes data like
+    cookies, which may influence how a site operates. Additionally, extensions
+    may have mutated the cache, installed service workers on sites, or similar
+    modifications. These will persist.
+
+Other extension behavior, such as running background scripts, network handlers,
+URL overrides, proxy settings, and preference modifications should be reverted
+upon uninstallation.
+
+### I've found an extension that violates Chrome Web Store policy. Is this considered a bug in Chromium?
+
+Individual extensions are generally not considered a part of Chromium, so
+extensions violating Chrome Web Store policies (including distribution policies)
+are not considered security bugs in Chromium itself.
+
+You can report the extension with the "Report abuse" link in the extension's
+entry on the Chrome Web Store.
+<!-- TODO(devlin): Link to
+     https://support.google.com/chrome_webstore/answer/7508032?hl=en when we
+     have a p-link for this. Otherwise, it violates
+     https://crbug.com/679462. -->
+
+### Is adding a malicious extension to a user's profile considered a security bug?
+
+Chromium
+[does not consider physically-local attacks to be security bugs][physically-local-attacks].
+This includes attacks like loading extensions on a user's machine. As such,
+attacks like loading extensions physically through loading an unpacked
+extension in the chrome://extensions page, via malicious software that executes
+outside of the Chromium browser, or updating enterprise policies to load
+extensions are all not within Chromium's threat model.
+
+Adding a malicious extension to the user's profile is only a security bug if you
+find a way to add the extension without direct access to the user's profile and
+bypassing the normal extension installation flow. For instance, if an extension
+could be installed (without user consent) when the user visits a malicious site,
+this would be considered a security bug. Please report any such bugs
+[here][new-security-bug].
+
+### Chrome silently syncs extensions across devices. Is this a security vulnerability?
+
+If an attacker has access to one of a victim‘s devices, the attacker can install
+an extension which will be synced to the victim’s other sync-enabled devices.
+Similarly, an attacker who phishes a victim‘s Google credentials can sign in to
+Chrome as the victim and install an extension, which will be synced to the
+victim’s other sync-enabled devices. Sync thereby enables an attacker to elevate
+phished credentials or physical access to persistent access on all of a victim's
+sync-enabled devices.
+
+To mitigate this issue, Chrome only syncs extensions that have been installed
+from the Chrome Web Store. Extensions in the Chrome Web Store are monitored for
+abusive behavior.
+
+In the future, we may pursue further mitigations. However, because an attacker
+must already have the victim‘s Google credentials and/or
+[physical access to a device][physically-local-attacks], we don’t consider this
+attack a security vulnerability.
+
+We **do** consider it a vulnerability if an attacker can get an extension to
+sync to a victim‘s device without either of the above preconditions. For
+example, we consider it a vulnerability if an attacker could craft a request to
+Google’s sync servers without proper credentials that causes an extension to be
+installed to a user's device, or if an attacker could entice a victim to visit a
+webpage that causes an extension to be installed on their device(s). Please
+report any such bugs [here][new-security-bug].
+
+### Why do some permissions (and APIs) requested by developers not display a permission warning?
+
+Permissions and APIs may not have an associated warning for a number of reasons.
+There is not a 1-to-1 mapping of permissions listed in the manifest and warnings
+shown to the user.
+
+Some permissions may grant innocuous capabilities to extensions. For instance,
+the `storage` permission allows an extension to store _its own data_. This does
+not grant the extension access to any additional data; further, this type of
+behavior is already possible using open web APIs like `localStorage` and Indexed
+DB. Since this does not provide the extension any access to any new data or
+dangerous capabilities, the API does not have an associated warning.
+
+Other permissions, like `webRequest`, may only apply to sites the extension has
+access to. The webRequest API allows extensions to monitor (and potentially
+modify) network requests made by web pages. However, an extension can _only_
+intercept these requests for sites it has access to. Requesting access to a site
+already displays a permission warning ("Read and change your data on <site>");
+the webRequest API does not provide any additional access. This is also the case
+for the `scripting` permission and others.
+
+Some APIs may also have runtime permission prompts, such as dialogs or choosers,
+that are shown to the user in context. Since these APIs do not immediately grant
+any data access to extensions, no permission warning is shown.
+
+Finally, some permissions may be subsumed by other, more powerful, permissions.
+For instance, if an extension has access to the history API (which allows
+extensions to read and change a user's browsing history), we do not also show a
+warning for the `topSites` API (which allows extensions to see the top sites a
+user has visited). Since the history API is strictly more powerful, it subsumes
+the `topSites` API. You can read more
+[here](https://chromium.googlesource.com/chromium/src/+/main/extensions/docs/permissions.md#Determining-Permission-Warnings).
+
+### Why do optional permissions not display a permission warning at install time?
+
+Permissions listed in the `optional\_permissions` key in the manifest are not
+granted to the extension at install-time. Instead, they are granted through the
+use of the
+[Permissions API](https://developer.chrome.com/docs/extensions/reference/permissions/).
+When the extension requests new capabilities (i.e., permissions that have not
+been previously granted and are not superseded by other granted permissions), a
+dialog is shown to the user, allowing them to grant or refuse the permissions.
+
+### If an extension is updated to include new permissions, are users notified?
+
+If an extension updates and includes new permissions that are not already
+contained within its current granted capabilities, the extension is disabled on
+users' machines and the user is notified (and asked if they'd like to grant the
+new permissions and enable the extension). You can read more
+[here](https://chromium.googlesource.com/chromium/src/+/main/extensions/docs/permissions.md#Permission-Increases).
+
+This does not apply to newly-requested optional permissions, which are not
+granted by default.
+
+### I've found / written an extension that can execute code from a remote server. Is this a security bug?
+
+This is not considered a security bug in Chromium
+([example](https://crbug.com/1025017)). Prior to Manifest Version 3, extensions
+were allowed to execute code that was not contained in the extension package
+(also called remotely-hosted code); this is a pattern that is extremely common
+in web development. Beginning in Manifest Version 3, all logic must be contained
+within the extension package for developers distributing through the Chrome Web
+store; however, this is a _policy_ requirement (described
+[here](https://developer.chrome.com/docs/webstore/program_policies/)), and is
+not enforced by the Chromium browser.
+
+While the platform includes some restrictions through the inclusion of a default
+content security policy for extensions, this is not meant to be a guaranteed
+deterrent, and does not prevent all types of remote code execution. For
+instance, it is impossible for the Chromium browser to guard against an
+extension that includes an interpreter that processes remotely-fetched JSON
+commands, even though this type of behavior is prohibited by policy in Manifest
+V3.
+
+### Why do we allow extensions to open or close chrome:-scheme pages?
+
+Web pages with the chrome:-scheme (such as chrome://settings) are generally
+protected from extensions - extensions are not allowed to read or change data
+on these pages (without the use of the `--extensions-on-chrome-urls` command
+line flag). However, extensions _are_ allowed to open and close these pages
+through APIs like the tabs and windows APIs. This is critical for certain types
+of extensions, such as tab and session managers, bookmark managers, and history
+managers.
+
+### Why are extensions allowed to bypass a web page's Content Security Policy?
+
+Extensions are considered more privileged than the web pages they are allowed to
+run on. As such, they are allowed to circumvent restrictions put in place by
+those web pages. This can be critical for extension functionality.
+
+### Is executing in the main world of a document a security bug?
+
+No ([example](https://bugs.chromium.org/p/chromium/issues/detail?id=760419)). By
+default, extension scripts (like content scripts and those injected with
+[tabs.executeScript()](https://developer.chrome.com/docs/extensions/reference/tabs/#method-executeScript)
+or
+[scripting.executeScript()](https://developer.chrome.com/docs/extensions/reference/scripting/#method-executeScript)
+execute in an isolated world. However, this is _not_ intended as a security
+boundary to protect the main page from the extension (rather, it is a soft
+boundary in the other direction, _slightly_ protecting the extension from the
+webpage and other extensions active on the page). More than anything else, this
+is designed to prevent collision of JavaScript variables, so that `foo` in the
+content script does not reference `foo` from the main world.
+
+Extensions can trivially execute in the main world of a document if they so
+desire - for instance, by appending a `<script>` element. Assuming the extension
+has access to the site, it can already read and change the data on that site. As
+such, injecting in the main world does not represent any increased capability or
+access, and is not a security bug.
+
+### Is a web page accessing content script data in an isolated world considered a security bug?
+
+Isolated worlds are separate JavaScript execution contexts in V8. By default,
+script in one world cannot access the data in another. Circumventing this _may_
+be a security bug, depending on the attack.
+
+**Process-based attacks:** Since isolated worlds are necessarily in the same
+process as the main world, any attack that allows the attacker to read
+process-level data (such as a Spectre or Meltdown attack, or other renderer
+compromise) could potentially access content script data or access APIs exposed
+to content scripts.
+
+**Shared variable attacks:** Certain content is shared between the isolated
+world and the main world. For instance, references to an undeclared variable can
+result in accessing a frame with the same name from the window object.
+Similarly, the content of the DOM is shared between different JS worlds (though
+the JS objects are distinct to each). Attackers may be able to "trick" a content
+script into using these variables as a flavor of XSS - an example of this is
+[this Project Zero bug](https://bugs.chromium.org/p/project-zero/issues/detail?id=1225).
+These are bugs in the particular extension, rather than in Chromium. Please
+report them to the extension developer.
+
+Other attacks that cross from the main world into an extension's isolated world
+may be considered security bugs; please report any such bugs
+[here][new-security-bug].
+
+### Extensions can modify, relax, or remove site security by removing security-sensitive headers like Content-Security-Policy, HTTP Strict-Transport-Security, Access-Control-Allow-Origin, and others. Is this a security bug?
+
+Assuming the extension has access to the site, this is not considered a security
+bug. If the extension has access to a site, it can read and change all data
+associated with that site, including affecting how it can be shared with other
+parties.
+
+However, modifying security-sensitive headers is generally discouraged, unless
+absolutely necessary (such as for developer tools).
+
+### An extension is able to open a native application. Is this a security bug?
+
+_Maybe_. Extensions are allowed to open (and potentially communicate with)
+native applications in a variety of ways. One of the main ways is the
+[nativeMessaging API](https://developer.chrome.com/docs/extensions/nativeMessaging/),
+which allows for communication with installed native applications that have
+registered a manifest on the user's machine. Extensions are also able to open
+native applications with the
+[downloads.open()](https://developer.chrome.com/docs/extensions/reference/downloads/#method-open)
+method. Finally, extensions are allowed to navigate to file:-scheme URLs, which
+could, depending on the user's system configuration, result in execution. Using
+these methods to open a native application is not considered a security bug.
+
+If an extension is able to open a native application or execute native code in
+another way, it may be a security bug; please report any such bugs
+[here][new-security-bug].
+
+### An extension is able to read file contents from the local machine. Is this a security bug?
+
+Extensions can read file contents if they are granted file permission by the
+user. This is toggled in the chrome://extensions page for the given extension
+(under "Allow access to file URLs"; note that this is the default for unpacked
+extensions). If this setting is enabled, extensions can read all files on disk.
+Additionally, extensions are allowed to read any files that were explicitly
+shared with them, such as through the HTML5 Filesystem API. Additionally,
+extensions (with appropriate API permissions) can read the URLs of tabs and
+history entries, including file:-scheme URLs; however, this should not allow
+access to the _contents _of the file.
+
+If an extension is able to read file contents from the local machine in another
+way, this may be a security bug; please report any such bugs
+[here][new-security-bug].
+
+### An extension is able to read and store data from incognito browsing. Is this a security bug?
+
+Extensions are allowed to run in an incognito profile if the "Allow in
+incognito" setting is enabled for the given extension in the
+chrome://extensions page. In this scenario, the extension has access to all the
+types of data it does in normal browsing - such as URLs and the contents of
+websites. Chrome does not limit what an extension does with this data.
+
+If an extension is able to access incognito contexts without this setting
+enabled, this may be a security bug; please report any such bugs
+[here][new-security-bug].
+
+### I've found a security bug in an extension. Is this a security bug in Chromium?
+
+This depends on the extension.
+
+**Component Extensions:** Some extensions (called "component extensions") are
+bundled by the Chromium browser and implement core browser functionality. An
+example of this is the CryptoToken extension (used in security key
+authentication). If you find a security bug in a component extension, this _is_
+considered a security bug in Chromium. Please report any such bugs
+[here][new-security-bug].
+
+**Google Extensions:** A security bug in an extension developed by Google, but
+not distributed directly with the Chrome browser, would likely not be considered
+a bug in Chromium. However, they may be covered by the
+[Google Vulnerability Reward Program](https://www.google.com/about/appsecurity/reward-program/).
+
+**Other Extensions:** A security bug in a third-party extension would not be
+considered a security bug in Chromium. Some third-party extensions may have
+their own vulnerability reward programs; please check with the extension
+developer. It may also be eligible for a reward through the Developer Data
+Protection Reward Program; visit
+[this site](https://www.google.com/about/appsecurity/ddprp/) for more
+information.
+
+[new-security-bug]: https://bugs.chromium.org/p/chromium/issues/entry?template=Security+Bug
+[physically-local-attacks]: https://chromium.googlesource.com/chromium/src/+/main/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model
diff --git a/fuchsia/cipd/README.md b/fuchsia/cipd/README.md
index 9a7a851..92d1908 100644
--- a/fuchsia/cipd/README.md
+++ b/fuchsia/cipd/README.md
@@ -6,15 +6,6 @@
 
 ## Example usage
 
-To create a CIPD package, run the following command from the build output
-directory. In this example, "http.yaml" is being built for arm64:
+The most recent package can be discovered by searching for the "canary" ref:
 
-```
-$ cipd create --pkg-def gen/fuchsia/cipd/http/http.yaml
-              -ref latest
-              -tag version:$(cat gen/fuchsia/cipd/build_id.txt)
-```
-
-The most recent package can be discovered by searching for the "latest" ref:
-
-`$ cipd describe chromium/fuchsia/$PACKAGE_NAME-$TARGET_ARCH -version latest`
+`$ cipd describe chromium/fuchsia/$PACKAGE_NAME-$TARGET_ARCH -version canary`
diff --git a/fuchsia/engine/web_instance_host/web_instance_host.cc b/fuchsia/engine/web_instance_host/web_instance_host.cc
index d59eeef8..df91c66 100644
--- a/fuchsia/engine/web_instance_host/web_instance_host.cc
+++ b/fuchsia/engine/web_instance_host/web_instance_host.cc
@@ -120,20 +120,22 @@
   return directory.Unbind().TakeChannel();
 }
 
-void AppendFeature(base::StringPiece features_flag,
-                   base::StringPiece feature_string,
-                   base::CommandLine* command_line) {
-  if (!command_line->HasSwitch(features_flag)) {
-    command_line->AppendSwitchNative(std::string(features_flag),
-                                     feature_string);
+// Appends |value| to the value of |switch_name| in the |command_line|.
+// The switch is assumed to consist of comma-separated values. If |switch_name|
+// is already set in |command_line| then a comma will be appended, followed by
+// |value|, otherwise the switch will be set to |value|.
+void AppendToSwitch(base::StringPiece switch_name,
+                    base::StringPiece value,
+                    base::CommandLine* command_line) {
+  if (!command_line->HasSwitch(switch_name)) {
+    command_line->AppendSwitchNative(switch_name, value);
     return;
   }
 
-  std::string new_feature_string = base::StrCat(
-      {command_line->GetSwitchValueASCII(features_flag), ",", feature_string});
-  command_line->RemoveSwitch(features_flag);
-  command_line->AppendSwitchNative(std::string(features_flag),
-                                   new_feature_string);
+  std::string new_value = base::StrCat(
+      {command_line->GetSwitchValueASCII(switch_name), ",", value});
+  command_line->RemoveSwitch(switch_name);
+  command_line->AppendSwitchNative(switch_name, new_value);
 }
 
 // File names must not contain directory separators, nor match the special
@@ -247,12 +249,12 @@
     if (origin == switches::kAllowRunningInsecureContent) {
       launch_args->AppendSwitch(switches::kAllowRunningInsecureContent);
     } else if (origin == kDisableMixedContentAutoupgradeOrigin) {
-      AppendFeature(switches::kDisableFeatures,
-                    kMixedContentAutoupgradeFeatureName, launch_args);
+      AppendToSwitch(switches::kDisableFeatures,
+                     kMixedContentAutoupgradeFeatureName, launch_args);
     } else {
       // Pass the rest of the list to the Context process.
-      AppendFeature(network::switches::kUnsafelyTreatInsecureOriginAsSecure,
-                    origin, launch_args);
+      AppendToSwitch(network::switches::kUnsafelyTreatInsecureOriginAsSecure,
+                     origin, launch_args);
     }
   }
 }
@@ -543,10 +545,11 @@
 
   if (use_overlays_for_video) {
     // Overlays are only available if OutputPresenterFuchsia is in use.
-    AppendFeature(switches::kEnableFeatures,
-                  features::kUseSkiaOutputDeviceBufferQueue.name, &launch_args);
-    AppendFeature(switches::kEnableFeatures,
-                  features::kUseRealBuffersForPageFlipTest.name, &launch_args);
+    AppendToSwitch(switches::kEnableFeatures,
+                   features::kUseSkiaOutputDeviceBufferQueue.name,
+                   &launch_args);
+    AppendToSwitch(switches::kEnableFeatures,
+                   features::kUseRealBuffersForPageFlipTest.name, &launch_args);
     launch_args.AppendSwitchASCII(switches::kEnableHardwareOverlays,
                                   "underlay");
     launch_args.AppendSwitch(switches::kUseOverlaysForVideo);
@@ -563,8 +566,8 @@
     launch_args.AppendSwitch(switches::kUseVulkan);
     const std::vector<base::StringPiece> enabled_features = {
         features::kUseSkiaRenderer.name, features::kVulkan.name};
-    AppendFeature(switches::kEnableFeatures,
-                  base::JoinString(enabled_features, ","), &launch_args);
+    AppendToSwitch(switches::kEnableFeatures,
+                   base::JoinString(enabled_features, ","), &launch_args);
 
     // SkiaRenderer requires out-of-process rasterization be enabled.
     launch_args.AppendSwitch(switches::kEnableOopRasterization);
@@ -625,8 +628,8 @@
       return ZX_ERR_INVALID_ARGS;
     }
 
-    AppendFeature(switches::kDisableFeatures,
-                  features::kEnableSoftwareOnlyVideoCodecs.name, &launch_args);
+    AppendToSwitch(switches::kDisableFeatures,
+                   features::kEnableSoftwareOnlyVideoCodecs.name, &launch_args);
   }
 
   if (!HandleCdmDataDirectoryParam(&params, &launch_args, &launch_info)) {
diff --git a/fuchsia/runners/common/web_component.cc b/fuchsia/runners/common/web_component.cc
index fe1ffad..1bed5bfc 100644
--- a/fuchsia/runners/common/web_component.cc
+++ b/fuchsia/runners/common/web_component.cc
@@ -4,14 +4,10 @@
 
 #include "fuchsia/runners/common/web_component.h"
 
-#include <fuchsia/logger/cpp/fidl.h>
-#include <fuchsia/sys/cpp/fidl.h>
 #include <fuchsia/ui/views/cpp/fidl.h>
-#include <lib/fidl/cpp/binding_set.h>
 #include <lib/fit/function.h>
 #include <lib/sys/cpp/component_context.h>
 #include <lib/ui/scenic/cpp/view_ref_pair.h>
-#include <utility>
 
 #include "base/bind.h"
 #include "base/fuchsia/fuchsia_logging.h"
diff --git a/fuchsia/runners/common/web_component.h b/fuchsia/runners/common/web_component.h
index 62471b3..98b7119 100644
--- a/fuchsia/runners/common/web_component.h
+++ b/fuchsia/runners/common/web_component.h
@@ -10,7 +10,6 @@
 #include <fuchsia/ui/app/cpp/fidl.h>
 #include <fuchsia/web/cpp/fidl.h>
 #include <lib/fidl/cpp/binding.h>
-#include <lib/fidl/cpp/binding_set.h>
 #include <memory>
 #include <string>
 #include <utility>
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 90e5de37..e3a6455 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -23,12 +23,47 @@
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gl/ca_renderer_layer_params.h"
 #include "ui/gl/dc_renderer_layer_params.h"
-#include "ui/gl/gl_utils.h"
 #include "ui/gl/gl_version_info.h"
 
 namespace gpu {
 namespace gles2 {
 
+// Temporarily allows compilation of shaders that use the
+// ARB_texture_rectangle/ANGLE_texture_rectangle extension. We don't want to
+// expose the extension to WebGL user shaders but we still need to use it for
+// parts of the implementation on macOS. Note that the extension is always
+// enabled on macOS and this only controls shader compilation.
+class GLES2DecoderPassthroughImpl::
+    ScopedEnableTextureRectangleInShaderCompiler {
+ public:
+  ScopedEnableTextureRectangleInShaderCompiler(
+      const ScopedEnableTextureRectangleInShaderCompiler&) = delete;
+  ScopedEnableTextureRectangleInShaderCompiler& operator=(
+      const ScopedEnableTextureRectangleInShaderCompiler&) = delete;
+
+  // This class is a no-op except on macOS.
+#if !defined(OS_MAC)
+  explicit ScopedEnableTextureRectangleInShaderCompiler(
+      GLES2DecoderPassthroughImpl* decoder) {}
+
+ private:
+#else
+  explicit ScopedEnableTextureRectangleInShaderCompiler(
+      GLES2DecoderPassthroughImpl* decoder)
+      : decoder_(decoder) {
+    if (decoder_->feature_info_->IsWebGLContext())
+      decoder_->api_->glEnableFn(GL_TEXTURE_RECTANGLE_ANGLE);
+  }
+  ~ScopedEnableTextureRectangleInShaderCompiler() {
+    if (decoder_->feature_info_->IsWebGLContext())
+      decoder_->api_->glDisableFn(GL_TEXTURE_RECTANGLE_ANGLE);
+  }
+
+ private:
+  GLES2DecoderPassthroughImpl* decoder_;
+#endif
+};
+
 namespace {
 
 template <typename ClientType, typename ServiceType, typename GenFunction>
@@ -4470,8 +4505,7 @@
     GLboolean unpack_flip_y,
     GLboolean unpack_premultiply_alpha,
     GLboolean unpack_unmultiply_alpha) {
-  gl::ScopedEnableTextureRectangleInShaderCompiler enable(
-      feature_info_->IsWebGLContext() ? api() : nullptr);
+  ScopedEnableTextureRectangleInShaderCompiler enable(this);
   BindPendingImageForClientIDIfNeeded(source_id);
   api()->glCopyTextureCHROMIUMFn(
       GetTextureServiceID(api(), source_id, resources_, false), source_level,
@@ -4499,8 +4533,7 @@
     GLboolean unpack_flip_y,
     GLboolean unpack_premultiply_alpha,
     GLboolean unpack_unmultiply_alpha) {
-  gl::ScopedEnableTextureRectangleInShaderCompiler enable(
-      feature_info_->IsWebGLContext() ? api() : nullptr);
+  ScopedEnableTextureRectangleInShaderCompiler enable(this);
   BindPendingImageForClientIDIfNeeded(source_id);
   api()->glCopySubTextureCHROMIUMFn(
       GetTextureServiceID(api(), source_id, resources_, false), source_level,
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 5ec629fd..471fe03 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -18120,6 +18120,76 @@
       }
     }
     builders {
+      name: "Win x64 Builder (reclient)(cross)"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows-10"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      recipe {
+        name: "chromium"
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        properties_j: "$build/reclient:{\"instance\":\"rbe-chromium-trusted\",\"metrics_project\":\"chromium-reclient-metrics\"}"
+        properties_j: "$kitchen:{\"devshell\":true,\"emulate_gce\":true,\"git_auth\":true}"
+        properties_j: "$recipe_engine/isolated:{\"server\":\"https://isolateserver.appspot.com\"}"
+        properties_j: "$recipe_engine/resultdb/test_presentation:{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]}"
+        properties_j: "builder_group:\"chromium.fyi\""
+      }
+      execution_timeout_secs: 36000
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium.chromium_tests.use_rbe_cas"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.gtests_local"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.junit_tests"
+        value: 100
+      }
+      experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -41448,7 +41518,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
-        properties_j: "$build/goma:{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true}"
+        properties_j: "$build/goma:{\"enable_ats\":true,\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "$recipe_engine/isolated:{\"server\":\"https://isolateserver.appspot.com\"}"
         properties_j: "$recipe_engine/resultdb/test_presentation:{\"column_keys\":[],\"grouping_keys\":[\"status\",\"v.test_suite\"]}"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index c4054b1..64480e6f 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -6478,6 +6478,11 @@
     category: "win"
     short_name: "re"
   }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Win x64 Builder (reclient)(cross)"
+    category: "win"
+    short_name: "re x"
+  }
   header {
     oncalls {
       name: "Chromium"
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index deeec9f..01585dde 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -3868,6 +3868,16 @@
   }
 }
 job {
+  id: "Win x64 Builder (reclient)(cross)"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Win x64 Builder (reclient)(cross)"
+  }
+}
+job {
   id: "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)"
   realm: "ci"
   acls {
@@ -7055,6 +7065,7 @@
   triggers: "Win x64 Builder"
   triggers: "Win x64 Builder (dbg)"
   triggers: "Win x64 Builder (reclient)"
+  triggers: "Win x64 Builder (reclient)(cross)"
   triggers: "Win10 FYI x64 SkiaRenderer Dawn Release (NVIDIA)"
   triggers: "Windows deterministic"
   triggers: "android-10-arm64-rel"
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 93658b5c1..834e6faf 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -3979,6 +3979,20 @@
     os = os.WINDOWS_DEFAULT,
 )
 
+ci.fyi_windows_builder(
+    name = "Win x64 Builder (reclient)(cross)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "win",
+        short_name = "re x",
+    ),
+    goma_backend = None,
+    reclient_instance = "rbe-chromium-trusted",
+    configure_kitchen = True,
+    kitchen_emulate_gce = True,
+    os = os.WINDOWS_DEFAULT,
+)
+
 ci.fyi_celab_builder(
     name = "win-celab-builder-rel",
     console_view_entry = consoles.console_view_entry(
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 18ac6c9da..cb780f6 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -1917,6 +1917,7 @@
 try_.gpu_chromium_android_builder(
     name = "android_optional_gpu_tests_rel",
     branch_selector = branches.STANDARD_MILESTONE,
+    goma_jobs = goma.jobs.J150,
     main_list_view = "try",
     tryjob = try_.job(
         location_regexp = [
diff --git a/ios/chrome/app/strings/resources/ios_strings_ar.xtb b/ios/chrome/app/strings/resources/ios_strings_ar.xtb
index 517790aa..c86e32c 100644
--- a/ios/chrome/app/strings/resources/ios_strings_ar.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_ar.xtb
@@ -397,7 +397,7 @@
 <translation id="5090832849094901128">لن يؤدي حذف كلمة المرور هذه إلى حذف حسابك على <ph name="WEBSITE" />.</translation>
 <translation id="5094827893301452931">‏اكتملت مشاركة Tweet.</translation>
 <translation id="5118764316110575523">غير مفعَّل</translation>
-<translation id="5127805178023152808">المزامنة غير مفعّلة</translation>
+<translation id="5127805178023152808">المزامنة متوقفة.</translation>
 <translation id="5132942445612118989">مزامنة كلمات المرور والسجلّ والمزيد على جميع الأجهزة</translation>
 <translation id="5140288047769711648">‏سيتذكّر Chrome كلمة المرور هذه بالنيابة عنك. وبالتالي لن تضطرّ لتذكُّرها كلّ مرّة.</translation>
 <translation id="5150492518600715772">الإرسال إلى جهازك</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_en-GB.xtb b/ios/chrome/app/strings/resources/ios_strings_en-GB.xtb
index 8e116d0..c032cb26 100644
--- a/ios/chrome/app/strings/resources/ios_strings_en-GB.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_en-GB.xtb
@@ -103,7 +103,7 @@
 <translation id="1952172573699511566">Websites will show text in your preferred language, when possible.</translation>
 <translation id="1952728750904661634">Sign in with managed account</translation>
 <translation id="1965935827552890526">Finish what you were doing in your other open Chrome window.</translation>
-<translation id="1973912524893600642">Keep data</translation>
+<translation id="1973912524893600642">Keep Data</translation>
 <translation id="1974060860693918893">Advanced</translation>
 <translation id="1989112275319619282">Browse</translation>
 <translation id="2015722694326466240">To see passwords, you must first set a passcode on your device.</translation>
@@ -198,7 +198,7 @@
 <translation id="2964349545761222050">Block third-party cookies</translation>
 <translation id="2969979262385602596">Failed to sign in. Please try again later.</translation>
 <translation id="2975121486251958312">Only Incognito mode is available</translation>
-<translation id="2982481275546140226">Clear data</translation>
+<translation id="2982481275546140226">Clear Data</translation>
 <translation id="298306318844797842">Add Payment Method...</translation>
 <translation id="2989805286512600854">Open in New Tab</translation>
 <translation id="3037605927509011580">Aw, Snap!</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_gu.xtb b/ios/chrome/app/strings/resources/ios_strings_gu.xtb
index a98d9d9..e008c8a4 100644
--- a/ios/chrome/app/strings/resources/ios_strings_gu.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_gu.xtb
@@ -492,6 +492,7 @@
 <translation id="6059830886158432458">તમારી સ્ટોરી અને પ્રવૃત્તિને અહીં નિયંત્રિત કરો</translation>
 <translation id="6066301408025741299">રદ કરવા માટે ટેપ કરો.</translation>
 <translation id="60829778314739003">સ્વીકારો અને આગળ વધો</translation>
+<translation id="6084848228346514841">કોઈ ટૅબ પસંદ કરો</translation>
 <translation id="6108923351542677676">સેટઅપની પ્રક્રિયા ચાલુ છે...</translation>
 <translation id="6119050551270742952">વર્તમાન વેબપેજ છૂપા મોડમાં છે</translation>
 <translation id="6122191549521593678">ઓનલાઇન</translation>
@@ -782,6 +783,7 @@
 <ph name="BEGIN_LINK" />વધુ જાણો<ph name="END_LINK" /></translation>
 <translation id="8820817407110198400">બુકમાર્ક</translation>
 <translation id="8840513115188359703">તમે તમારા Google એકાઉન્ટમાંથી સાઇન આઉટ થશો નહિ.</translation>
+<translation id="8868471676553493380">{count,plural, =1{{count} ટૅબ}one{{count} ટૅબ}other{{count} ટૅબ}}</translation>
 <translation id="8870413625673593573">તાજેતરમાં બંધ કરેલા</translation>
 <translation id="8876882697946675716">તમારા ડિવાઇસને સિંક કરેલું રાખો</translation>
 <translation id="8881801611828450202"><ph name="SEARCH_ENGINE" /> પર આ છબી શોધો</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_hy.xtb b/ios/chrome/app/strings/resources/ios_strings_hy.xtb
index 2681fece..7567bd3 100644
--- a/ios/chrome/app/strings/resources/ios_strings_hy.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_hy.xtb
@@ -750,7 +750,7 @@
 <translation id="8503813439785031346">Օգտանուն</translation>
 <translation id="850600235656508448">Բացել ինկոգնիտո ռեժիմում</translation>
 <translation id="8510057420705599706">Դուք դուրս կգրվեք հաշվից, և համաժամացումը կանջատվի, սակայն ձեր էջանիշները, պատմությունը, գաղտնաբառերը և Chrome-ի այլ տվյալներ կպահպանվեն այս սարքում։</translation>
-<translation id="8517375800490286174">Բաց կոդով հավելվածների արտոնագրեր</translation>
+<translation id="8517375800490286174">Բաց կոդով հավելվածների լիցենզիաներ</translation>
 <translation id="8524799873541103884"><ph name="INCOGNITO" /> Ներդիրներ <ph name="FIRST_VISIBLE_TAB" /> – <ph name="LAST_VISIBLE_TAB" />՝ <ph name="NUMBER_OF_OPEN_TABS" />-ից</translation>
 <translation id="8529767659511976195">Նոր</translation>
 <translation id="8532105204136943229">Ժամկետի սպառման տարեթիվը</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_ne.xtb b/ios/chrome/app/strings/resources/ios_strings_ne.xtb
index 205f7f1..74e600fa 100644
--- a/ios/chrome/app/strings/resources/ios_strings_ne.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_ne.xtb
@@ -492,6 +492,7 @@
 <translation id="6059830886158432458">यहाँ आफ्नो समाचार फिडमा देखिने सामग्री तथा क्रियाकलाप नियन्त्रण गर्नुहोस्</translation>
 <translation id="6066301408025741299">रद्द गर्न ट्याप गर्नुहोस्।</translation>
 <translation id="60829778314739003">स्वीकार गर्नुहोस् र जारी राख्नुहोस्</translation>
+<translation id="6084848228346514841">ट्याबहरू चयन गर्नुहोस्</translation>
 <translation id="6108923351542677676">सेटअप प्रगतिमा छ...</translation>
 <translation id="6119050551270742952">हालको वेबपृष्ठ इन्कोग्निटो मोडमा छ</translation>
 <translation id="6122191549521593678">अनलाइन</translation>
@@ -782,6 +783,7 @@
 <ph name="BEGIN_LINK" />थप जान्नुहोस्<ph name="END_LINK" /></translation>
 <translation id="8820817407110198400">बुकमार्कहरू</translation>
 <translation id="8840513115188359703">तपाईं आफ्नो Google खाताबाट साइन आउट हुनु हुने छैन।</translation>
+<translation id="8868471676553493380">{count,plural, =1{{count} ट्याब}other{{count} वटा ट्याब}}</translation>
 <translation id="8870413625673593573">हालै बन्द गरिएको</translation>
 <translation id="8876882697946675716">आफ्ना डिभाइसहरू सिंक गरिराख्नुहोस्</translation>
 <translation id="8881801611828450202"><ph name="SEARCH_ENGINE" /> मा यो छवि खोज्नुहोस्</translation>
diff --git a/ios/chrome/app/strings/resources/ios_strings_pa.xtb b/ios/chrome/app/strings/resources/ios_strings_pa.xtb
index 0c86f6ac..759ab0e4 100644
--- a/ios/chrome/app/strings/resources/ios_strings_pa.xtb
+++ b/ios/chrome/app/strings/resources/ios_strings_pa.xtb
@@ -781,7 +781,7 @@
 <ph name="BEGIN_LINK" />ਹੋਰ ਜਾਣੋ<ph name="END_LINK" /></translation>
 <translation id="8820817407110198400">Bookmarks</translation>
 <translation id="8840513115188359703">ਤੁਸੀਂ ਆਪਣੇ 'Google ਖਾਤੇ' ਤੋਂ ਸਾਈਨ-ਆਊਟ ਨਹੀਂ ਹੋਵੋਗੇ।</translation>
-<translation id="8868471676553493380">{count,plural, =1{{count} Tab}one{{count} Tab}other{{count} Tabs}}</translation>
+<translation id="8868471676553493380">{count,plural, =1{{count} ਟੈਬ}one{{count} ਟੈਬ}other{{count} ਟੈਬਾਂ}}</translation>
 <translation id="8870413625673593573">ਹੁਣੇ ਜਿਹੇ ਬੰਦ ਕੀਤੀਆਂ</translation>
 <translation id="8876882697946675716">ਆਪਣੇ ਡੀਵਾਈਸਾਂ ਦਾ ਆਪਸ ਵਿੱਚ ਸਮਕਾਲੀਕਰਨ ਕਰਕੇ ਰੱਖੋ</translation>
 <translation id="8881801611828450202">ਇਸ ਚਿੱਤਰ ਲਈ <ph name="SEARCH_ENGINE" /> ਖੋਜੋ</translation>
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/BUILD.gn
index 05019059..97721d3 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/BUILD.gn
@@ -36,6 +36,7 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
+    "save_address_profile_infobar_banner_interaction_handler_unittest.mm",
     "save_address_profile_infobar_modal_interaction_handler_unittest.mm",
     "save_address_profile_infobar_modal_overlay_request_callback_installer_unittest.mm",
   ]
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h
index b6b8fbf..5db5bc40 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h
@@ -9,6 +9,10 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
 
+namespace autofill {
+class AutofillSaveUpdateAddressProfileDelegateIOS;
+}
+
 // Helper object that updates the model layer for interaction events with the
 // SaveAddressProfile infobar banner UI.
 class SaveAddressProfileInfobarBannerInteractionHandler
@@ -18,8 +22,13 @@
   ~SaveAddressProfileInfobarBannerInteractionHandler() override;
 
   // InfobarBannerInteractionHandler:
-  void BannerVisibilityChanged(InfoBarIOS* infobar, bool visible) override {}
-  void BannerDismissedByUser(InfoBarIOS* infobar) override {}
+  void BannerVisibilityChanged(InfoBarIOS* infobar, bool visible) override;
+  void BannerDismissedByUser(InfoBarIOS* infobar) override;
+
+ private:
+  // Returns the SaveAddressProfile delegate from |infobar|.
+  autofill::AutofillSaveUpdateAddressProfileDelegateIOS* GetInfobarDelegate(
+      InfoBarIOS* infobar);
 };
 
 #endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_AUTOFILL_ADDRESS_PROFILE_SAVE_ADDRESS_PROFILE_INFOBAR_BANNER_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.mm
index 2c41a3a..c981c67 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h"
 
+#include "components/autofill/core/browser/autofill_save_update_address_profile_delegate_ios.h"
 #include "ios/chrome/browser/infobars/infobar_ios.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.h"
 
@@ -23,3 +24,20 @@
 
 SaveAddressProfileInfobarBannerInteractionHandler::
     ~SaveAddressProfileInfobarBannerInteractionHandler() = default;
+
+void SaveAddressProfileInfobarBannerInteractionHandler::BannerVisibilityChanged(
+    InfoBarIOS* infobar,
+    bool visible) {
+  if (!visible) {
+    autofill::AutofillSaveUpdateAddressProfileDelegateIOS::FromInfobarDelegate(
+        infobar->delegate())
+        ->MessageTimeout();
+  }
+}
+
+void SaveAddressProfileInfobarBannerInteractionHandler::BannerDismissedByUser(
+    InfoBarIOS* infobar) {
+  autofill::AutofillSaveUpdateAddressProfileDelegateIOS::FromInfobarDelegate(
+      infobar->delegate())
+      ->MessageDeclined();
+}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler_unittest.mm
new file mode 100644
index 0000000..4d4a7716
--- /dev/null
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler_unittest.mm
@@ -0,0 +1,70 @@
+// 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 "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h"
+
+#include "base/guid.h"
+#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
+#include "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h"
+#import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
+#import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
+#include "ios/chrome/browser/infobars/test/mock_infobar_delegate.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test fixture for SaveAddressProfileInfobarBannerInteractionHandler.
+class SaveAddressProfileInfobarBannerInteractionHandlerTest
+    : public PlatformTest {
+ public:
+  SaveAddressProfileInfobarBannerInteractionHandlerTest()
+      : delegate_factory_(),
+        profile_(base::GenerateGUID(), "https://www.example.com/") {
+    infobar_ = std::make_unique<InfoBarIOS>(
+        InfobarType::kInfobarTypeSaveAutofillAddressProfile,
+        MockAutofillSaveUpdateAddressProfileDelegateIOSFactory::
+            CreateMockAutofillSaveUpdateAddressProfileDelegateIOSFactory(
+                profile_));
+  }
+
+  MockAutofillSaveUpdateAddressProfileDelegateIOS& mock_delegate() {
+    return *static_cast<MockAutofillSaveUpdateAddressProfileDelegateIOS*>(
+        infobar_->delegate());
+  }
+
+ protected:
+  SaveAddressProfileInfobarBannerInteractionHandler handler_;
+  MockAutofillSaveUpdateAddressProfileDelegateIOSFactory delegate_factory_;
+  autofill::AutofillProfile profile_;
+  std::unique_ptr<InfoBarIOS> infobar_;
+};
+
+// Tests that user_decision is set to message timeout on BannerVisibilityChanged
+// with parameter value as false.
+TEST_F(SaveAddressProfileInfobarBannerInteractionHandlerTest,
+       BannerVisibilityFalse) {
+  handler_.BannerVisibilityChanged(infobar_.get(), false);
+  EXPECT_EQ(mock_delegate().user_decision(),
+            autofill::AutofillClient::SaveAddressProfileOfferUserDecision::
+                kMessageTimeout);
+}
+
+// Tests that user_decision is set to message declined on BannerDismissedByUser.
+TEST_F(SaveAddressProfileInfobarBannerInteractionHandlerTest,
+       BannerDismissedByUser) {
+  handler_.BannerDismissedByUser(infobar_.get());
+  EXPECT_EQ(mock_delegate().user_decision(),
+            autofill::AutofillClient::SaveAddressProfileOfferUserDecision::
+                kMessageDeclined);
+
+  handler_.BannerVisibilityChanged(infobar_.get(), false);
+  // Expect the user decision to be message declined even when
+  // BannerVisibilityChanged is called.
+  EXPECT_EQ(mock_delegate().user_decision(),
+            autofill::AutofillClient::SaveAddressProfileOfferUserDecision::
+                kMessageDeclined);
+}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h
index 70470c39..2d48ff45 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h
@@ -30,6 +30,10 @@
   virtual void SaveEditedProfile(InfoBarIOS* infobar,
                                  NSDictionary* profileData);
 
+  // Instructs the handler to inform the delegate that the view has been
+  // cancelled.
+  virtual void CancelModal(InfoBarIOS* infobar, BOOL fromEditModal);
+
  private:
   // InfobarModalInteractionHandler:
   std::unique_ptr<InfobarModalOverlayRequestCallbackInstaller>
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.mm
index e81b919..09ec419 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.mm
@@ -37,7 +37,18 @@
     std::u16string data = base::SysNSStringToUTF16(profileData[key]);
     GetInfoBarDelegate(infobar)->SetProfileInfo(type, data);
   }
-  infobar->set_accepted(GetInfoBarDelegate(infobar)->EditAccepted());
+  GetInfoBarDelegate(infobar)->EditAccepted();
+  infobar->set_accepted(true);
+}
+
+void SaveAddressProfileInfobarModalInteractionHandler::CancelModal(
+    InfoBarIOS* infobar,
+    BOOL fromEditModal) {
+  if (fromEditModal) {
+    GetInfoBarDelegate(infobar)->EditDeclined();
+  } else {
+    GetInfoBarDelegate(infobar)->Cancel();
+  }
 }
 
 #pragma mark - Private
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler_unittest.mm
index b8e11aa..c98c1f9a 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler_unittest.mm
@@ -53,6 +53,20 @@
 
 TEST_F(SaveAddressProfileInfobarModalInteractionHandlerTest,
        SaveEditedProfile) {
-  EXPECT_CALL(mock_delegate(), EditAccepted()).WillOnce(testing::Return(true));
+  EXPECT_CALL(mock_delegate(), EditAccepted());
   handler_.SaveEditedProfile(infobar_.get(), @{}.mutableCopy);
 }
+
+TEST_F(SaveAddressProfileInfobarModalInteractionHandlerTest, EditDeclined) {
+  handler_.CancelModal(infobar_.get(), /*fromEditModal=*/YES);
+  EXPECT_EQ(mock_delegate().user_decision(),
+            autofill::AutofillClient::SaveAddressProfileOfferUserDecision::
+                kEditDeclined);
+}
+
+TEST_F(SaveAddressProfileInfobarModalInteractionHandlerTest, Cancel) {
+  handler_.CancelModal(infobar_.get(), /*fromEditModal=*/NO);
+  EXPECT_EQ(
+      mock_delegate().user_decision(),
+      autofill::AutofillClient::SaveAddressProfileOfferUserDecision::kDeclined);
+}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer.h
index 34abf92..c9fd884 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer.h
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer.h
@@ -29,6 +29,12 @@
   void SaveEditedProfileDetailsCallback(OverlayRequest* request,
                                         OverlayResponse* response);
 
+  // Used as a callback for OverlayResponses dispatched through |request|'s
+  // callback manager.  The OverlayDispatchCallback is created with an
+  // OverlayResponseSupport that guarantees that |response| is created with a
+  // save_address_profile_infobar_modal_responses::CancelViewAction.
+  void CancelModalCallback(OverlayRequest* request, OverlayResponse* response);
+
   // OverlayRequestCallbackInstaller:
   void InstallCallbacksInternal(OverlayRequest* request) override;
 
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer.mm
index b381969..b13997eb 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer.mm
@@ -22,6 +22,7 @@
 using autofill_address_profile_infobar_overlays::
     SaveAddressProfileModalRequestConfig;
 using save_address_profile_infobar_modal_responses::EditedProfileSaveAction;
+using save_address_profile_infobar_modal_responses::CancelViewAction;
 
 SaveAddressProfileInfobarModalOverlayRequestCallbackInstaller::
     SaveAddressProfileInfobarModalOverlayRequestCallbackInstaller(
@@ -47,8 +48,18 @@
   }
 
   EditedProfileSaveAction* info = response->GetInfo<EditedProfileSaveAction>();
-  interaction_handler_->SaveEditedProfile(GetOverlayRequestInfobar(request),
-                                          info->profile_data());
+  interaction_handler_->SaveEditedProfile(infobar, info->profile_data());
+}
+
+void SaveAddressProfileInfobarModalOverlayRequestCallbackInstaller::
+    CancelModalCallback(OverlayRequest* request, OverlayResponse* response) {
+  InfoBarIOS* infobar = GetOverlayRequestInfobar(request);
+  if (!infobar) {
+    return;
+  }
+
+  CancelViewAction* info = response->GetInfo<CancelViewAction>();
+  interaction_handler_->CancelModal(infobar, info->edit_view_is_dismissed());
 }
 
 #pragma mark - OverlayRequestCallbackInstaller
@@ -65,4 +76,11 @@
               SaveEditedProfileDetailsCallback,
           weak_factory_.GetWeakPtr(), request),
       EditedProfileSaveAction::ResponseSupport()));
+
+  manager->AddDispatchCallback(OverlayDispatchCallback(
+      base::BindRepeating(
+          &SaveAddressProfileInfobarModalOverlayRequestCallbackInstaller::
+              CancelModalCallback,
+          weak_factory_.GetWeakPtr(), request),
+      CancelViewAction::ResponseSupport()));
 }
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer_unittest.mm
index 9cc2364..19e76ad 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_overlay_request_callback_installer_unittest.mm
@@ -31,6 +31,7 @@
 using autofill_address_profile_infobar_overlays::
     SaveAddressProfileModalRequestConfig;
 using save_address_profile_infobar_modal_responses::EditedProfileSaveAction;
+using save_address_profile_infobar_modal_responses::CancelViewAction;
 
 // Test fixture for
 // SaveAddressProfileInfobarModalOverlayRequestCallbackInstaller.
@@ -92,3 +93,11 @@
   request_->GetCallbackManager()->DispatchResponse(
       OverlayResponse::CreateWithInfo<EditedProfileSaveAction>(empty));
 }
+
+TEST_F(SaveAddressProfileInfobarModalOverlayRequestCallbackInstallerTest,
+       CancelAction) {
+  BOOL fakeFromEditModal = NO;
+  EXPECT_CALL(mock_handler_, CancelModal(infobar_, fakeFromEditModal));
+  request_->GetCallbackManager()->DispatchResponse(
+      OverlayResponse::CreateWithInfo<CancelViewAction>(fakeFromEditModal));
+}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h
index b7a2fd6..b99ed1db 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_update_address_profile_delegate_ios.h
@@ -25,8 +25,7 @@
   ~MockAutofillSaveUpdateAddressProfileDelegateIOS() override;
 
   MOCK_METHOD0(Accept, bool());
-  MOCK_METHOD0(InfoBarDismissed, void());
-  MOCK_METHOD0(EditAccepted, bool());
+  MOCK_METHOD0(EditAccepted, void());
 };
 
 class MockAutofillSaveUpdateAddressProfileDelegateIOSFactory {
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_address_profile_modal_infobar_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_address_profile_modal_infobar_interaction_handler.h
index 696c1a9..cc09123c 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_address_profile_modal_infobar_interaction_handler.h
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_address_profile_modal_infobar_interaction_handler.h
@@ -21,6 +21,7 @@
 
   MOCK_METHOD2(SaveEditedProfile,
                void(InfoBarIOS* infobar, NSDictionary* profileData));
+  MOCK_METHOD2(CancelModal, void(InfoBarIOS* infobar, BOOL fromEditView));
 };
 
 #endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_SAVE_ADDRESS_PROFILE_MODAL_INFOBAR_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_responses.h b/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_responses.h
index c50aea50..a8eaa4c 100644
--- a/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_responses.h
+++ b/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_responses.h
@@ -27,6 +27,21 @@
   NSDictionary* profile_data_;
 };
 
+// Response info used to create dispatched OverlayResponses once the user
+// cancels the modal.
+class CancelViewAction : public OverlayResponseInfo<CancelViewAction> {
+ public:
+  ~CancelViewAction() override;
+
+  BOOL edit_view_is_dismissed() const { return edit_view_is_dismissed_; }
+
+ private:
+  OVERLAY_USER_DATA_SETUP(CancelViewAction);
+  CancelViewAction(BOOL edit_view_is_dismissed);
+
+  BOOL edit_view_is_dismissed_;
+};
+
 }  // namespace save_address_profile_infobar_modal_responses
 
 #endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_MODAL_SAVE_ADDRESS_PROFILE_INFOBAR_MODAL_OVERLAY_RESPONSES_H_
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_responses.mm b/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_responses.mm
index 0c0f1b7d..c5f1137 100644
--- a/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_responses.mm
+++ b/ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_responses.mm
@@ -19,4 +19,13 @@
 
 EditedProfileSaveAction::~EditedProfileSaveAction() = default;
 
+#pragma mark - CancelViewAction
+
+OVERLAY_USER_DATA_SETUP_IMPL(CancelViewAction);
+
+CancelViewAction::CancelViewAction(BOOL edit_view_is_dismissed)
+    : edit_view_is_dismissed_(edit_view_is_dismissed) {}
+
+CancelViewAction::~CancelViewAction() = default;
+
 }  // save_address_profile_infobar_modal_responses
diff --git a/ios/chrome/browser/reading_list/offline_page_tab_helper_unittest.mm b/ios/chrome/browser/reading_list/offline_page_tab_helper_unittest.mm
index a78a63b..9922e1ba 100644
--- a/ios/chrome/browser/reading_list/offline_page_tab_helper_unittest.mm
+++ b/ios/chrome/browser/reading_list/offline_page_tab_helper_unittest.mm
@@ -151,14 +151,10 @@
 class OfflinePageTabHelperTest : public web::WebTest {
  public:
   void SetUp() override {
-    web::WebTest::SetUp();
-    TestChromeBrowserState::Builder test_cbs_builder;
-    base::FilePath test_data_dir;
-    ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
-    test_data_dir = test_data_dir.AppendASCII(kTestDirectory);
-    test_cbs_builder.SetPath(test_data_dir);
-    chrome_browser_state_ = test_cbs_builder.Build();
-    fake_web_state_.SetBrowserState(chrome_browser_state_.get());
+    // Ensure that the EXPECT_TRUE in CreateBrowserState() passed.
+    ASSERT_NO_FATAL_FAILURE(web::WebTest::SetUp());
+
+    fake_web_state_.SetBrowserState(GetBrowserState());
     fake_web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
     reading_list_model_ = std::make_unique<ReadingListModelImpl>(
@@ -170,8 +166,16 @@
                                             reading_list_model_.get());
   }
 
+  std::unique_ptr<web::BrowserState> CreateBrowserState() override {
+    TestChromeBrowserState::Builder test_cbs_builder;
+    base::FilePath test_data_dir;
+    EXPECT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
+    test_data_dir = test_data_dir.AppendASCII(kTestDirectory);
+    test_cbs_builder.SetPath(test_data_dir);
+    return test_cbs_builder.Build();
+  }
+
  protected:
-  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<ReadingListModelImpl> reading_list_model_;
   web::FakeWebState fake_web_state_;
 };
@@ -180,14 +184,10 @@
 // ReadingListModel.
 class OfflinePageTabHelperDelayedModelTest : public web::WebTest {
   void SetUp() override {
-    web::WebTest::SetUp();
-    TestChromeBrowserState::Builder test_cbs_builder;
-    base::FilePath test_data_dir;
-    ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
-    test_data_dir = test_data_dir.AppendASCII(kTestDirectory);
-    test_cbs_builder.SetPath(test_data_dir);
-    chrome_browser_state_ = test_cbs_builder.Build();
-    fake_web_state_.SetBrowserState(chrome_browser_state_.get());
+    // Ensure that the EXPECT_TRUE in CreateBrowserState() passed.
+    ASSERT_NO_FATAL_FAILURE(web::WebTest::SetUp());
+
+    fake_web_state_.SetBrowserState(GetBrowserState());
     fake_web_state_.SetNavigationManager(
         std::make_unique<web::FakeNavigationManager>());
     fake_reading_list_model_ = std::make_unique<FakeReadingListModel>();
@@ -202,8 +202,16 @@
                                             fake_reading_list_model_.get());
   }
 
+  std::unique_ptr<web::BrowserState> CreateBrowserState() override {
+    TestChromeBrowserState::Builder test_cbs_builder;
+    base::FilePath test_data_dir;
+    EXPECT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
+    test_data_dir = test_data_dir.AppendASCII(kTestDirectory);
+    test_cbs_builder.SetPath(test_data_dir);
+    return test_cbs_builder.Build();
+  }
+
  protected:
-  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<FakeReadingListModel> fake_reading_list_model_;
   web::FakeWebState fake_web_state_;
   std::unique_ptr<ReadingListEntry> entry_;
diff --git a/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm b/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm
index 3518d97..aa93408 100644
--- a/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm
+++ b/ios/chrome/browser/safe_browsing/safe_browsing_egtest.mm
@@ -137,6 +137,20 @@
   _safeURL2 = self.testServer->GetURL("/echo_also_safe");
   _safeContent2 = "also_safe";
 
+  if (@available(iOS 15.1, *)) {
+  } else {
+    if (@available(iOS 14.5, *)) {
+      // Workaround https://bugs.webkit.org/show_bug.cgi?id=226323, which breaks
+      // some back/forward navigations between pages that share a renderer
+      // process. Use 'localhost' instead of '127.0.0.1' for safe URLs to
+      // prevent sharing renderer processes with unsafe URLs.
+      GURL::Replacements replacements;
+      replacements.SetHostStr("localhost");
+      _safeURL1 = _safeURL1.ReplaceComponents(replacements);
+      _safeURL2 = _safeURL2.ReplaceComponents(replacements);
+    }
+  }
+
   // |appConfigurationForTestCase| is called during [super setUp], and
   // depends on the URLs initialized above.
   [super setUp];
diff --git a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge_unittests.mm b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge_unittests.mm
index 00ae940..edf5087d 100644
--- a/ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge_unittests.mm
+++ b/ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge_unittests.mm
@@ -159,8 +159,6 @@
 
   friend TestGaiaAuthFetcherIOSNSURLSessionBridge;
 
-  // Browser state for the tests.
-  std::unique_ptr<ChromeBrowserState> browser_state_;
   // Instance used for the tests.
   std::unique_ptr<TestGaiaAuthFetcherIOSNSURLSessionBridge>
       ns_url_session_bridge_;
@@ -196,10 +194,11 @@
 #pragma mark - GaiaAuthFetcherIOSNSURLSessionBridgeTest
 
 void GaiaAuthFetcherIOSNSURLSessionBridgeTest::SetUp() {
+  ChromeWebTest::SetUp();
+
   delegate_.reset(new FakeGaiaAuthFetcherIOSBridgeDelegate());
-  browser_state_ = TestChromeBrowserState::Builder().Build();
   ns_url_session_bridge_.reset(new TestGaiaAuthFetcherIOSNSURLSessionBridge(
-      delegate_.get(), browser_state_.get(), this));
+      delegate_.get(), GetBrowserState(), this));
   url_session_configuration_ =
       NSURLSessionConfiguration.ephemeralSessionConfiguration;
   url_session_configuration_.HTTPShouldSetCookies = YES;
@@ -220,6 +219,7 @@
 
 void GaiaAuthFetcherIOSNSURLSessionBridgeTest::TearDown() {
   ASSERT_OCMOCK_VERIFY((id)url_session_mock_);
+  ChromeWebTest::TearDown();
 }
 
 NSURLSession* GaiaAuthFetcherIOSNSURLSessionBridgeTest::CreateNSURLSession(
@@ -242,7 +242,7 @@
   std::vector<net::CanonicalCookie> cookies_out;
   base::RunLoop run_loop;
   network::mojom::CookieManager* cookie_manager =
-      browser_state_->GetCookieManager();
+      GetBrowserState()->GetCookieManager();
   cookie_manager->GetAllCookies(base::BindOnce(base::BindLambdaForTesting(
       [&run_loop,
        &cookies_out](const std::vector<net::CanonicalCookie>& cookies) {
@@ -301,7 +301,7 @@
 bool GaiaAuthFetcherIOSNSURLSessionBridgeTest::SetCookiesInCookieManager(
     NSArray<NSHTTPCookie*>* cookies) {
   network::mojom::CookieManager* cookie_manager =
-      browser_state_->GetCookieManager();
+      GetBrowserState()->GetCookieManager();
   for (NSHTTPCookie* cookie in cookies) {
     std::unique_ptr<net::CanonicalCookie> canonical_cookie =
         net::CanonicalCookieFromSystemCookie(cookie, base::Time::Now());
diff --git a/ios/chrome/browser/ssl/ios_ssl_error_handler_unittest.mm b/ios/chrome/browser/ssl/ios_ssl_error_handler_unittest.mm
index 6b71024..a7fbe9d 100644
--- a/ios/chrome/browser/ssl/ios_ssl_error_handler_unittest.mm
+++ b/ios/chrome/browser/ssl/ios_ssl_error_handler_unittest.mm
@@ -36,8 +36,7 @@
 class IOSSSLErrorHandlerTest : public web::WebTestWithWebState {
  protected:
   IOSSSLErrorHandlerTest()
-      : browser_state_(builder_.Build()),
-        cert_(net::ImportCertFromFile(net::GetTestCertsDirectory(),
+      : cert_(net::ImportCertFromFile(net::GetTestCertsDirectory(),
                                       kTestCertFileName)) {}
 
   // web::WebTestWithWebState overrides:
@@ -64,15 +63,17 @@
     AddPendingItem(GURL(kTestHostName),
                    ui::PageTransition::PAGE_TRANSITION_TYPED);
   }
-  web::BrowserState* GetBrowserState() override { return browser_state_.get(); }
+
+  std::unique_ptr<web::BrowserState> CreateBrowserState() override {
+    TestChromeBrowserState::Builder builder;
+    return builder.Build();
+  }
 
   // Returns certificate for testing.
   scoped_refptr<net::X509Certificate> cert() { return cert_; }
 
  private:
   network::TestURLLoaderFactory test_loader_factory_;
-  TestChromeBrowserState::Builder builder_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
   scoped_refptr<net::X509Certificate> cert_;
 };
 
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 11e88ed..dbfc569 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -331,6 +331,7 @@
     "//ios/chrome/browser:utils",
     "//ios/chrome/browser/ui/ntp:constants",
     "//ios/chrome/browser/ui/settings:constants",
+    "//ios/chrome/browser/ui/start_surface:feature_flags",
     "//ios/chrome/browser/ui/toolbar/public:constants",
     "//ios/chrome/test:eg_test_support+eg2",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index c583ae0..b79eaee 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -15,6 +15,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h"
+#import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -127,6 +128,7 @@
   AppLaunchConfiguration config;
   config.additional_args.push_back(std::string("--") +
                                    switches::kEnableDiscoverFeed);
+  config.features_disabled.push_back(kStartSurface);
   return config;
 }
 
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index afc1ac09..bf350d8 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -1584,12 +1584,6 @@
     return;
   }
 
-  // Suppress iPad web sign-in.
-  // TODO(crbug.com/1211794): Remove iPad suppression once the UI is adapted.
-  if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
-    return;
-  }
-
   self.signinCoordinator = [SigninCoordinator
       consistencyPromoSigninCoordinatorWithBaseViewController:baseViewController
                                                       browser:self.mainInterface
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/BUILD.gn
index 1ce3b31..7e4eb8db 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/BUILD.gn
@@ -21,6 +21,7 @@
     "//ios/chrome/browser/overlays/public/common/infobars",
     "//ios/chrome/browser/overlays/public/infobar_modal",
     "//ios/chrome/browser/ui/infobars/modals",
+    "//ios/chrome/browser/ui/infobars/modals:public",
     "//ios/chrome/browser/ui/overlays:coordinators",
     "//ios/chrome/browser/ui/overlays/infobar_modal:coordinators",
     "//ios/chrome/browser/ui/overlays/infobar_modal:mediators",
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/save_address_profile_infobar_modal_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/save_address_profile_infobar_modal_overlay_mediator.mm
index 7ba26d4..8ea2b038 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/save_address_profile_infobar_modal_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/save_address_profile_infobar_modal_overlay_mediator.mm
@@ -4,10 +4,13 @@
 
 #import "ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/save_address_profile_infobar_modal_overlay_mediator.h"
 
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_responses.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_edit_address_profile_modal_consumer.h"
+#import "ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_save_address_profile_modal_consumer.h"
 #import "ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_coordinator+modal_configuration.h"
 #import "ios/chrome/browser/ui/overlays/overlay_request_mediator+subclassing.h"
@@ -20,11 +23,14 @@
 using autofill_address_profile_infobar_overlays::
     SaveAddressProfileModalRequestConfig;
 using save_address_profile_infobar_modal_responses::EditedProfileSaveAction;
+using save_address_profile_infobar_modal_responses::CancelViewAction;
 
 @interface SaveAddressProfileInfobarModalOverlayMediator ()
 // The save address profile modal config from the request.
 @property(nonatomic, assign, readonly)
     SaveAddressProfileModalRequestConfig* config;
+// YES if the edit modal is being shown.
+@property(nonatomic, assign) BOOL currentViewIsEditView;
 @end
 
 @implementation SaveAddressProfileInfobarModalOverlayMediator
@@ -91,6 +97,8 @@
   if (!self.request) {
     return;
   }
+
+  self.currentViewIsEditView = YES;
   [self.saveAddressProfileMediatorDelegate showEditView];
 }
 
@@ -103,4 +111,13 @@
   [self dismissOverlay];
 }
 
+- (void)dismissInfobarModal:(id)infobarModal {
+  [self dispatchResponse:OverlayResponse::CreateWithInfo<CancelViewAction>(
+                             self.currentViewIsEditView)];
+  base::RecordAction(base::UserMetricsAction(kInfobarModalCancelButtonTapped));
+  [self dismissOverlay];
+
+  self.currentViewIsEditView = NO;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/save_address_profile_infobar_modal_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/save_address_profile_infobar_modal_overlay_mediator_unittest.mm
index 8b55889..aeb5081 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/save_address_profile_infobar_modal_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/autofill_address_profile/save_address_profile_infobar_modal_overlay_mediator_unittest.mm
@@ -29,13 +29,15 @@
 using autofill_address_profile_infobar_overlays::
     SaveAddressProfileModalRequestConfig;
 using save_address_profile_infobar_modal_responses::EditedProfileSaveAction;
+using save_address_profile_infobar_modal_responses::CancelViewAction;
 
 // Test fixture for SaveAddressProfileInfobarModalOverlayMediator.
 class SaveAddressProfileInfobarModalOverlayMediatorTest : public PlatformTest {
  public:
   SaveAddressProfileInfobarModalOverlayMediatorTest()
       : callback_installer_(&callback_receiver_,
-                            {EditedProfileSaveAction::ResponseSupport()}),
+                            {EditedProfileSaveAction::ResponseSupport(),
+                             CancelViewAction::ResponseSupport()}),
         mediator_delegate_(
             OCMStrictProtocolMock(@protocol(OverlayRequestMediatorDelegate))) {
     autofill::AutofillProfile profile = autofill::test::GetFullProfile();
@@ -83,3 +85,12 @@
   OCMExpect([mediator_delegate_ stopOverlayForMediator:mediator_]);
   [mediator_ saveEditedProfileWithData:@{}.mutableCopy];
 }
+
+// Tests that calling dismissInfobarModal triggers a CancelViewAction response.
+TEST_F(SaveAddressProfileInfobarModalOverlayMediatorTest, CancelAction) {
+  EXPECT_CALL(
+      callback_receiver_,
+      DispatchCallback(request_.get(), CancelViewAction::ResponseSupport()));
+  OCMExpect([mediator_delegate_ stopOverlayForMediator:mediator_]);
+  [mediator_ dismissInfobarModal:nil];
+}
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
index 33477b2..ba02c134 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
@@ -94,7 +94,12 @@
 
 class PopupMenuMediatorTest : public ChromeWebTest {
  public:
-  PopupMenuMediatorTest() : ChromeWebTest(std::make_unique<ChromeWebClient>()) {
+  PopupMenuMediatorTest()
+      : ChromeWebTest(std::make_unique<ChromeWebClient>()) {}
+
+  void SetUp() override {
+    ChromeWebTest::SetUp();
+
     reading_list_model_.reset(new ReadingListModelImpl(
         nullptr, nullptr, base::DefaultClock::GetInstance()));
     popup_menu_ = OCMClassMock([PopupMenuTableViewController class]);
@@ -105,9 +110,7 @@
     SetUpWebStateList();
 
     // Set up the TestBrowser.
-    TestChromeBrowserState::Builder browser_state_builder;
-    browser_state_ = browser_state_builder.Build();
-    browser_ = std::make_unique<TestBrowser>(browser_state_.get(),
+    browser_ = std::make_unique<TestBrowser>(GetTestChromeBrowserState(),
                                              web_state_list_.get());
     // Set up the OverlayPresenter.
     OverlayPresenter::FromBrowser(browser_.get(),
@@ -115,13 +118,24 @@
         ->SetPresentationContext(&presentation_context_);
   }
 
-  // Explicitly disconnect the mediator so there won't be any WebStateList
-  // observers when web_state_list_ gets dealloc.
-  ~PopupMenuMediatorTest() override {
+  void TearDown() override {
+    // Explicitly disconnect the mediator so there won't be any WebStateList
+    // observers when web_state_list_ gets dealloc.
     [mediator_ disconnect];
+
+    ChromeWebTest::TearDown();
+  }
+
+  std::unique_ptr<web::BrowserState> CreateBrowserState() override {
+    TestChromeBrowserState::Builder builder;
+    return builder.Build();
   }
 
  protected:
+  TestChromeBrowserState* GetTestChromeBrowserState() {
+    return static_cast<TestChromeBrowserState*>(GetBrowserState());
+  }
+
   PopupMenuMediator* CreateMediator(PopupMenuType type,
                                     BOOL is_incognito,
                                     BOOL trigger_incognito_hint) {
@@ -156,9 +170,9 @@
   }
 
   void SetUpBookmarks() {
-    browser_state_->CreateBookmarkModel(false);
-    bookmark_model_ =
-        ios::BookmarkModelFactory::GetForBrowserState(browser_state_.get());
+    GetTestChromeBrowserState()->CreateBookmarkModel(false);
+    bookmark_model_ = ios::BookmarkModelFactory::GetForBrowserState(
+        GetTestChromeBrowserState());
     bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model_);
     mediator_.bookmarkModel = bookmark_model_;
   }
@@ -252,7 +266,6 @@
   FakeOverlayPresentationContext presentation_context_;
   std::unique_ptr<WebStateList> web_state_list_;
   FakeWebStateListDelegate web_state_list_delegate_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
   std::unique_ptr<Browser> browser_;
   PopupMenuMediator* mediator_;
   BookmarkModel* bookmark_model_;
diff --git a/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm b/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm
index afe4dae..9176f6cd0 100644
--- a/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/block_popups_table_view_controller.mm
@@ -292,7 +292,11 @@
   for (NSIndexPath* indexPath in indexPaths) {
     size_t urlIndex = indexPath.item;
     std::string urlToRemove;
-    _exceptions.GetString(urlIndex, &urlToRemove);
+    base::Value::ListView exceptions_view = _exceptions.GetList();
+    if (urlIndex < exceptions_view.size() &&
+        exceptions_view[urlIndex].is_string()) {
+      urlToRemove = exceptions_view[urlIndex].GetString();
+    }
 
     // Remove the exception for the site by resetting its popup setting to the
     // default.
@@ -303,7 +307,7 @@
             CONTENT_SETTING_DEFAULT);
 
     // Remove the site from |_exceptions|.
-    _exceptions.Remove(urlIndex, NULL);
+    _exceptions.EraseListIter(exceptions_view.begin() + urlIndex);
   }
   [self.tableView performBatchUpdates:^{
     NSInteger exceptionsSection = [self.tableViewModel
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller.mm b/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller.mm
index f552798..de38f4eb 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_table_view_controller.mm
@@ -177,7 +177,7 @@
   [model addItem:[self handoffDetailItem]
       toSectionWithIdentifier:SectionIdentifierWebServices];
 
-  // Do not show the incognito authentication setting when Incongito mode is
+  // Do not show the incognito authentication setting when Incognito mode is
   // disabled.
   if (base::FeatureList::IsEnabled(kIncognitoAuthentication) &&
       !IsIncognitoModeDisabled(_browserState->GetPrefs())) {
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_egtest.mm b/ios/chrome/browser/ui/start_surface/start_surface_egtest.mm
index e1f1fe93..ce05d00 100644
--- a/ios/chrome/browser/ui/start_surface/start_surface_egtest.mm
+++ b/ios/chrome/browser/ui/start_surface/start_surface_egtest.mm
@@ -24,9 +24,6 @@
 
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config;
-  config.additional_args.push_back(
-      std::string("--enable-features=StartSurface:"
-                  "ReturnToStartSurfaceInactiveDurationInSeconds/0"));
   config.relaunch_policy = ForceRelaunchByCleanShutdown;
   return config;
 }
diff --git a/ios/chrome/browser/web/lookalike_url_app_interface.mm b/ios/chrome/browser/web/lookalike_url_app_interface.mm
index b2ec43e..1c1e8b1 100644
--- a/ios/chrome/browser/web/lookalike_url_app_interface.mm
+++ b/ios/chrome/browser/web/lookalike_url_app_interface.mm
@@ -46,6 +46,16 @@
     if (response_url.path() == kLookalikePagePathForTesting) {
       GURL::Replacements safeReplacements;
       safeReplacements.SetPathStr("echo");
+      if (@available(iOS 15.1, *)) {
+      } else {
+        if (@available(iOS 14.5, *)) {
+          // Workaround https://bugs.webkit.org/show_bug.cgi?id=226323, which
+          // breaks some back/forward navigations between pages that share a
+          // renderer process. Use 'localhost' instead of '127.0.0.1' for the
+          // safe URL to prevent sharing renderer processes with unsafe URLs.
+          safeReplacements.SetHostStr("localhost");
+        }
+      }
       lookalike_container->SetLookalikeUrlInfo(
           response_url.ReplaceComponents(safeReplacements), response_url,
           LookalikeUrlMatchType::kSkeletonMatchTop5k);
diff --git a/ios/chrome/browser/web/lookalike_url_egtest.mm b/ios/chrome/browser/web/lookalike_url_egtest.mm
index 0531dfc..23b8a24 100644
--- a/ios/chrome/browser/web/lookalike_url_egtest.mm
+++ b/ios/chrome/browser/web/lookalike_url_egtest.mm
@@ -96,6 +96,19 @@
       l10n_util::GetStringUTF8(IDS_LOOKALIKE_URL_PRIMARY_PARAGRAPH);
   _lookalikeBlockingPageNoSuggestionContent = l10n_util::GetStringUTF8(
       IDS_LOOKALIKE_URL_PRIMARY_PARAGRAPH_NO_SUGGESTED_URL);
+
+  if (@available(iOS 15.1, *)) {
+  } else {
+    if (@available(iOS 14.5, *)) {
+      // Workaround https://bugs.webkit.org/show_bug.cgi?id=226323, which breaks
+      // some back/forward navigations between pages that share a renderer
+      // process. Use 'localhost' instead of '127.0.0.1' for the safe URL to
+      // prevent sharing renderer processes with unsafe URLs.
+      GURL::Replacements replacements;
+      replacements.SetHostStr("localhost");
+      _safeURL = _safeURL.ReplaceComponents(replacements);
+    }
+  }
 }
 
 - (void)tearDown {
diff --git a/ios/web/js_messaging/crw_wk_script_message_router_unittest.mm b/ios/web/js_messaging/crw_wk_script_message_router_unittest.mm
index 789d831..fd66b2b2 100644
--- a/ios/web/js_messaging/crw_wk_script_message_router_unittest.mm
+++ b/ios/web/js_messaging/crw_wk_script_message_router_unittest.mm
@@ -87,15 +87,15 @@
 // Test fixture for CRWWKScriptMessageRouter.
 class CRWWKScriptMessageRouterTest : public web::WebTest {
  public:
-  CRWWKScriptMessageRouterTest()
-      : web_client_(base::WrapUnique(new web::WebClient())) {}
+  CRWWKScriptMessageRouterTest() = default;
 
  protected:
   void SetUp() override {
     web::WebTest::SetUp();
 
     web::WKWebViewConfigurationProvider& configuration_provider =
-        web::WKWebViewConfigurationProvider::FromBrowserState(&browser_state_);
+        web::WKWebViewConfigurationProvider::FromBrowserState(
+            GetBrowserState());
     WKWebViewConfiguration* configuration =
         configuration_provider.GetWebViewConfiguration();
     // Mock WKUserContentController object.
@@ -117,9 +117,9 @@
     name1_ = [@"name1" copy];
     name2_ = [@"name2" copy];
     name3_ = [@"name3" copy];
-    web_view1_ = web::BuildWKWebView(CGRectZero, &browser_state_);
-    web_view2_ = web::BuildWKWebView(CGRectZero, &browser_state_);
-    web_view3_ = web::BuildWKWebView(CGRectZero, &browser_state_);
+    web_view1_ = web::BuildWKWebView(CGRectZero, GetBrowserState());
+    web_view2_ = web::BuildWKWebView(CGRectZero, GetBrowserState());
+    web_view3_ = web::BuildWKWebView(CGRectZero, GetBrowserState());
   }
   void TearDown() override {
     [user_content_controller_ checkExpectations];
@@ -143,11 +143,6 @@
   WKWebView* web_view1_;
   WKWebView* web_view2_;
   WKWebView* web_view3_;
-
- private:
-  // WebClient and BrowserState for testing.
-  web::ScopedTestingWebClient web_client_;
-  web::FakeBrowserState browser_state_;
 };
 
 // Tests CRWWKScriptMessageRouter designated initializer.
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index cb7af00..7a3a559 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -857,10 +857,13 @@
     return policy_match;
   }
 
-  // Return an owned BrowserState in order to set off the record state.
-  BrowserState* GetBrowserState() override { return &browser_state_; }
+  std::unique_ptr<BrowserState> CreateBrowserState() override {
+    return std::make_unique<FakeBrowserState>();
+  }
 
-  FakeBrowserState browser_state_;
+  FakeBrowserState* GetFakeBrowserState() {
+    return static_cast<FakeBrowserState*>(GetBrowserState());
+  }
 };
 
 // Tests that App specific URLs in iframes are allowed if the main frame is App
@@ -880,7 +883,7 @@
 // Tests that URL is allowed in OffTheRecord mode when the
 // |kBlockUniversalLinksInOffTheRecordMode| feature is disabled.
 TEST_F(CRWWebControllerPolicyDeciderTest, AllowOffTheRecordNavigation) {
-  browser_state_.SetOffTheRecord(true);
+  GetFakeBrowserState()->SetOffTheRecord(true);
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndDisableFeature(
       web::features::kBlockUniversalLinksInOffTheRecordMode);
@@ -897,7 +900,7 @@
 // and the BLOCK_UNIVERSAL_LINKS_IN_OFF_THE_RECORD_MODE buildflag is set.
 TEST_F(CRWWebControllerPolicyDeciderTest,
        AllowOffTheRecordNavigationBlockUniversalLinks) {
-  browser_state_.SetOffTheRecord(true);
+  GetFakeBrowserState()->SetOffTheRecord(true);
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
       web::features::kBlockUniversalLinksInOffTheRecordMode);
@@ -1301,7 +1304,7 @@
  protected:
   void SetUp() override {
     WebTestWithWebController::SetUp();
-    FakeBrowserState browser_state;
+
     web_view_ = [[CRWFakeWKWebViewObserverCount alloc] init];
     CRWFakeWebViewContentView* webViewContentView =
         [[CRWFakeWebViewContentView alloc]
diff --git a/ios/web/web_state/web_state_impl_unittest.mm b/ios/web/web_state/web_state_impl_unittest.mm
index a2a643f..e6edce30 100644
--- a/ios/web/web_state/web_state_impl_unittest.mm
+++ b/ios/web/web_state/web_state_impl_unittest.mm
@@ -29,7 +29,6 @@
 #import "ios/web/public/session/crw_session_storage.h"
 #import "ios/web/public/session/serializable_user_data_manager.h"
 #import "ios/web/public/test/fakes/async_web_state_policy_decider.h"
-#include "ios/web/public/test/fakes/fake_browser_state.h"
 #import "ios/web/public/test/fakes/fake_java_script_dialog_presenter.h"
 #import "ios/web/public/test/fakes/fake_navigation_context.h"
 #import "ios/web/public/test/fakes/fake_web_frame.h"
diff --git a/media/blink/key_system_config_selector.cc b/media/blink/key_system_config_selector.cc
index 0acaf77d..1175e7e4 100644
--- a/media/blink/key_system_config_selector.cc
+++ b/media/blink/key_system_config_selector.cc
@@ -12,6 +12,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "media/base/cdm_config.h"
 #include "media/base/key_system_names.h"
 #include "media/base/key_systems.h"
@@ -657,14 +658,18 @@
   // permission has already been denied. This would happen anyway later.
   EmeFeatureSupport distinctive_identifier_support =
       key_systems_->GetDistinctiveIdentifierSupport(key_system);
+#if !defined(OS_ANDROID)
   // NOTE: This is an additional action we are taking here that is not in the
   // spec currently.  Specifically, we are not allowing a distinctive identifier
-  // for cross-origin frames.
+  // for cross-origin frames. We do not do this on Android because there is no
+  // CDM selection available to Chrome that doesn't require a distinct
+  // identifier.
   if (web_frame_delegate_->IsCrossOriginToMainFrame()) {
     if (distinctive_identifier_support == EmeFeatureSupport::ALWAYS_ENABLED)
       return CONFIGURATION_NOT_SUPPORTED;
     distinctive_identifier_support = EmeFeatureSupport::NOT_SUPPORTED;
   }
+#endif  // !defined(OS_ANDROID)
   EmeConfigRule di_rule = GetDistinctiveIdentifierConfigRule(
       distinctive_identifier_support, distinctive_identifier);
   if (!config_state->IsRuleSupported(di_rule)) {
diff --git a/media/blink/key_system_config_selector_unittest.cc b/media/blink/key_system_config_selector_unittest.cc
index ea82741..3b6d68b 100644
--- a/media/blink/key_system_config_selector_unittest.cc
+++ b/media/blink/key_system_config_selector_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/strings/pattern.h"
+#include "build/build_config.h"
 #include "media/base/cdm_config.h"
 #include "media/base/eme_constants.h"
 #include "media/base/key_systems.h"
@@ -733,7 +734,13 @@
   config.distinctive_identifier = MediaKeysRequirement::kOptional;
   configs_.push_back(config);
 
+#if defined(OS_ANDROID)
+  SelectConfigRequestsPermissionAndReturnsConfig();
+  EXPECT_EQ(MediaKeysRequirement::kRequired, config_.distinctive_identifier);
+  EXPECT_TRUE(cdm_config_.allow_distinctive_identifier);
+#else
   SelectConfigReturnsError();
+#endif  // defined(OS_ANDROID)
 }
 
 TEST_F(KeySystemConfigSelectorTest,
@@ -746,7 +753,13 @@
   config.distinctive_identifier = MediaKeysRequirement::kRequired;
   configs_.push_back(config);
 
+#if defined(OS_ANDROID)
+  SelectConfigRequestsPermissionAndReturnsConfig();
+  EXPECT_EQ(MediaKeysRequirement::kRequired, config_.distinctive_identifier);
+  EXPECT_TRUE(cdm_config_.allow_distinctive_identifier);
+#else
   SelectConfigReturnsError();
+#endif  // defined(OS_ANDROID)
 }
 
 // --- persistentState ---
@@ -1248,9 +1261,13 @@
   config.video_capabilities = video_capabilities;
   configs_.push_back(config);
 
+#if defined(OS_ANDROID)
+  SelectConfigRequestsPermissionAndReturnsConfig();
+#else
   SelectConfigReturnsConfig();
-  ASSERT_EQ(1u, config_.video_capabilities.size());
+#endif  // defined(OS_ANDROID)
   EXPECT_EQ(MediaKeysRequirement::kNotAllowed, config_.distinctive_identifier);
+  ASSERT_EQ(1u, config_.video_capabilities.size());
 }
 
 TEST_F(KeySystemConfigSelectorTest,
diff --git a/net/base/features.cc b/net/base/features.cc
index 54f8b3e..630d372d 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -239,5 +239,8 @@
     base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // defined(OS_POSIX) || defined(OS_FUCHSIA)
 
+const base::Feature kCookieSameSiteConsidersRedirectChain{
+    "CookieSameSiteConsidersRedirectChain", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace net
diff --git a/net/base/features.h b/net/base/features.h
index b91cc925..b97d7dd 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -348,6 +348,14 @@
 NET_EXPORT extern const base::Feature kUdpSocketPosixAlwaysUpdateBytesReceived;
 #endif  // defined(OS_POSIX) || defined(OS_FUCHSIA)
 
+// When this feature is enabled, redirected requests will be considered
+// cross-site for the purpose of SameSite cookies if any redirect hop was
+// cross-site to the target URL, even if the original initiator of the
+// redirected request was same-site with the target URL (and the
+// site-for-cookies).
+// See spec changes in https://github.com/httpwg/http-extensions/pull/1348
+NET_EXPORT extern const base::Feature kCookieSameSiteConsidersRedirectChain;
+
 }  // namespace features
 }  // namespace net
 
diff --git a/net/cookies/canonical_cookie.cc b/net/cookies/canonical_cookie.cc
index 83232c7..adba3ec 100644
--- a/net/cookies/canonical_cookie.cc
+++ b/net/cookies/canonical_cookie.cc
@@ -475,7 +475,6 @@
 }
 
 // static
-// TODO(crbug.com/957184): This should ideally return a CookieInclusionStatus.
 std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
     const GURL& url,
     const std::string& name,
@@ -489,49 +488,88 @@
     bool http_only,
     CookieSameSite same_site,
     CookiePriority priority,
-    bool same_party) {
+    bool same_party,
+    CookieInclusionStatus* status) {
+  // Put a pointer on the stack so the rest of the function can assign to it if
+  // the default nullptr is passed in.
+  CookieInclusionStatus blank_status;
+  if (status == nullptr) {
+    status = &blank_status;
+  }
+  *status = CookieInclusionStatus();
+
   // Validate consistency of passed arguments.
   if (ParsedCookie::ParseTokenString(name) != name ||
-      ParsedCookie::ParseValueString(value) != value ||
-      !ParsedCookie::IsValidCookieAttributeValue(name) ||
-      !ParsedCookie::IsValidCookieAttributeValue(value) ||
-      ParsedCookie::ParseValueString(domain) != domain ||
-      ParsedCookie::ParseValueString(path) != path) {
-    return nullptr;
+      !ParsedCookie::IsValidCookieAttributeValue(name)) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+  } else if (ParsedCookie::ParseValueString(value) != value ||
+             !ParsedCookie::IsValidCookieAttributeValue(value)) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+  } else if (ParsedCookie::ParseValueString(path) != path) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+  } else if (name.empty() && value.empty()) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
   }
 
-  if (name.empty() && value.empty())
-    return nullptr;
-
-  // This validation step must happen before GetCookieDomainWithString, so it
-  // doesn't fail DCHECKs.
-  if (!cookie_util::DomainIsHostOnly(url.host()))
-    return nullptr;
+  if (ParsedCookie::ParseValueString(domain) != domain) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
+  }
 
   std::string cookie_domain;
-  if (!cookie_util::GetCookieDomainWithString(url, domain, &cookie_domain))
-    return nullptr;
+  // This validation step must happen before GetCookieDomainWithString, so it
+  // doesn't fail DCHECKs.
+  if (!cookie_util::DomainIsHostOnly(url.host())) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
+  } else if (!cookie_util::GetCookieDomainWithString(url, domain,
+                                                     &cookie_domain)) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
+  }
 
-  CookieSourceScheme source_scheme = url.SchemeIsCryptographic()
-                                         ? CookieSourceScheme::kSecure
-                                         : CookieSourceScheme::kNonSecure;
+  CookieSourceScheme source_scheme = CookieSourceScheme::kNonSecure;
+  // This validation step must happen before SchemeIsCryptographic, so it
+  // doesn't fail DCHECKs.
+  if (!url.is_valid()) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
+  } else {
+    source_scheme = url.SchemeIsCryptographic()
+                        ? CookieSourceScheme::kSecure
+                        : CookieSourceScheme::kNonSecure;
+  }
 
   // Get the port, this will get a default value if a port isn't provided.
   int source_port = url.EffectiveIntPort();
 
   std::string cookie_path = CanonicalCookie::CanonPathWithString(url, path);
-  if (!path.empty() && cookie_path != path)
-    return nullptr;
+  if (!path.empty() && cookie_path != path) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+  }
 
   if (!IsCookiePrefixValid(GetCookiePrefix(name), url, secure, domain,
                            cookie_path)) {
-    return nullptr;
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_INVALID_PREFIX);
   }
 
-  if (!IsCookieSamePartyValid(same_party, secure, same_site))
-    return nullptr;
+  if (!IsCookieSamePartyValid(same_party, secure, same_site)) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY);
+  }
 
-  if (!last_access_time.is_null() && creation_time.is_null())
+  if (!last_access_time.is_null() && creation_time.is_null()) {
+    status->AddExclusionReason(
+        net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE);
+  }
+
+  if (!status->IsInclude())
     return nullptr;
 
   // Canonicalize path again to make sure it escapes characters as needed.
diff --git a/net/cookies/canonical_cookie.h b/net/cookies/canonical_cookie.h
index f716ed3f..d012b081 100644
--- a/net/cookies/canonical_cookie.h
+++ b/net/cookies/canonical_cookie.h
@@ -89,8 +89,9 @@
 
   // Create a canonical cookie based on sanitizing the passed inputs in the
   // context of the passed URL.  Returns a null unique pointer if the inputs
-  // cannot be sanitized.  If a cookie is created, |cookie->IsCanonical()|
-  // will be true.
+  // cannot be sanitized.  If `status` is provided it will have any relevant
+  // CookieInclusionStatus rejection reasons set. If a cookie is created,
+  // `cookie->IsCanonical()` will be true.
   static std::unique_ptr<CanonicalCookie> CreateSanitizedCookie(
       const GURL& url,
       const std::string& name,
@@ -104,7 +105,8 @@
       bool http_only,
       CookieSameSite same_site,
       CookiePriority priority,
-      bool same_party);
+      bool same_party,
+      CookieInclusionStatus* status = nullptr);
 
   // FromStorage is a factory method which is meant for creating a new
   // CanonicalCookie using properties of a previously existing cookie
diff --git a/net/cookies/canonical_cookie_unittest.cc b/net/cookies/canonical_cookie_unittest.cc
index 871d85fa..a07e60f 100644
--- a/net/cookies/canonical_cookie_unittest.cc
+++ b/net/cookies/canonical_cookie_unittest.cc
@@ -2212,13 +2212,14 @@
   base::Time one_hour_ago = base::Time::Now() - base::TimeDelta::FromHours(1);
   base::Time one_hour_from_now =
       base::Time::Now() + base::TimeDelta::FromHours(1);
-
+  CookieInclusionStatus status;
   std::unique_ptr<CanonicalCookie> cc;
+
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_EQ("A", cc->Name());
   EXPECT_EQ("B", cc->Value());
@@ -2233,87 +2234,97 @@
   EXPECT_EQ(COOKIE_PRIORITY_MEDIUM, cc->Priority());
   EXPECT_FALSE(cc->IsSameParty());
   EXPECT_FALSE(cc->IsDomainCookie());
+  EXPECT_TRUE(status.IsInclude());
 
   // Creation date
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
       two_hours_ago, base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_EQ(two_hours_ago, cc->CreationDate());
+  EXPECT_TRUE(status.IsInclude());
 
   // Last access date
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
       two_hours_ago, base::Time(), one_hour_ago, false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_EQ(one_hour_ago, cc->LastAccessDate());
+  EXPECT_TRUE(status.IsInclude());
 
   // Expiry
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
       base::Time(), one_hour_from_now, base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_EQ(one_hour_from_now, cc->ExpiryDate());
+  EXPECT_TRUE(status.IsInclude());
 
   // Secure
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), true /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_TRUE(cc->IsSecure());
+  EXPECT_TRUE(status.IsInclude());
 
   // Httponly
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       true /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_TRUE(cc->IsHttpOnly());
+  EXPECT_TRUE(status.IsInclude());
 
   // Same site
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::LAX_MODE, COOKIE_PRIORITY_DEFAULT,
-      false /*same_party*/);
+      false /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_EQ(CookieSameSite::LAX_MODE, cc->SameSite());
+  EXPECT_TRUE(status.IsInclude());
 
   // Priority
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW,
-      false /*same_party*/);
+      false /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_EQ(COOKIE_PRIORITY_LOW, cc->Priority());
+  EXPECT_TRUE(status.IsInclude());
 
   // Domain cookie
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", "www.foo.com", "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_TRUE(cc->IsDomainCookie());
+  EXPECT_TRUE(status.IsInclude());
 
   // SameParty
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), true /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW,
-      true /*same_party*/);
+      true /*same_party*/, &status);
   EXPECT_TRUE(cc);
   EXPECT_TRUE(cc->IsSameParty());
+  EXPECT_TRUE(status.IsInclude());
 }
 
 // Make sure sanitization and blocking of cookies works correctly.
@@ -2322,103 +2333,135 @@
   base::Time one_hour_ago = base::Time::Now() - base::TimeDelta::FromHours(1);
   base::Time one_hour_from_now =
       base::Time::Now() + base::TimeDelta::FromHours(1);
+  CookieInclusionStatus status;
 
   // Simple path and domain variations.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A", "B", std::string(), "/foo",
       one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/bar"), "C", "D", "www.foo.com", "/",
       two_hours_ago, base::Time(), one_hour_ago, false /*secure*/,
       true /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "E", "F", std::string(), std::string(),
       base::Time(), base::Time(), base::Time(), true /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
 
   // Test the file:// protocol.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("file:///"), "A", "B", std::string(), "/foo", one_hour_ago,
       one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/,
       CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
-      false /*same_party*/));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("file:///home/user/foo.txt"), "A", "B", std::string(), "/foo",
       one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("file:///home/user/foo.txt"), "A", "B", "home", "/foo", one_hour_ago,
       one_hour_from_now, base::Time(), false /*secure*/, false /*httponly*/,
       CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
-      false /*same_party*/));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
 
   // Test that malformed attributes fail to set the cookie.
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), " A", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A;", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A=", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A\x07", "B", std::string(), "/foo",
       one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com"), "A", " B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com"), "A", "\x0fZ", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com"), "A", "B", "www.foo.com ", "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A", "B", "foo.ozzzzzzle", "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A", "B", std::string(), "foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com"), "A", "B", std::string(), "/foo ",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A", "B", "%2Efoo.com", "/foo",
       one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://domaintest.%E3%81%BF%E3%82%93%E3%81%AA"), "A", "B",
       "domaintest.%E3%81%BF%E3%82%93%E3%81%AA", "/foo", base::Time(),
       base::Time(), base::Time(), false /*secure*/, false /*httponly*/,
       CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
-      false /*same_party*/));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
 
   std::unique_ptr<CanonicalCookie> cc;
 
@@ -2428,133 +2471,163 @@
       GURL("http://www.foo.com/foo"), "A", "B", "www.foo.com", "/foo",
       one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   ASSERT_TRUE(cc);
   EXPECT_TRUE(cc->IsDomainCookie());
   EXPECT_EQ(".www.foo.com", cc->Domain());
+  EXPECT_TRUE(status.IsInclude());
 
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A", "B", ".www.foo.com", "/foo",
       one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   ASSERT_TRUE(cc);
   EXPECT_TRUE(cc->IsDomainCookie());
   EXPECT_EQ(".www.foo.com", cc->Domain());
+  EXPECT_TRUE(status.IsInclude());
 
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A", "B", ".foo.com", "/foo",
       one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   ASSERT_TRUE(cc);
   EXPECT_TRUE(cc->IsDomainCookie());
   EXPECT_EQ(".foo.com", cc->Domain());
+  EXPECT_TRUE(status.IsInclude());
 
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com/foo"), "A", "B", ".www2.www.foo.com", "/foo",
       one_hour_ago, one_hour_from_now, base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   EXPECT_FALSE(cc);
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
 
   // Secure/URL Scheme mismatch.
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com"), "A", "B", std::string(), "/foo ",
       base::Time(), base::Time(), base::Time(), true /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
 
   // Null creation date/non-null last access date conflict.
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com"), "A", "B", std::string(), "/foo", base::Time(),
       base::Time(), base::Time::Now(), false /*secure*/, false /*httponly*/,
       CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
-      false /*same_party*/));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
 
   // Domain doesn't match URL
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com"), "A", "B", "www.bar.com", "/", base::Time(),
       base::Time(), base::Time(), false /*secure*/, false /*httponly*/,
       CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
-      false /*same_party*/));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
 
   // Path with unusual characters escaped.
   cc = CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com"), "A", "B", std::string(), "/foo",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false /*same_party*/);
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status);
   ASSERT_TRUE(cc);
   EXPECT_EQ("/foo%7F", cc->Path());
+  EXPECT_TRUE(status.IsInclude());
 
   // Empty name and value.
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://www.foo.com"), "", "", std::string(), "/", base::Time(),
       base::Time(), base::Time(), false /*secure*/, false /*httponly*/,
       CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
-      false /*same_party*/));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE}));
 
   // A __Secure- cookie must be Secure.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "__Secure-A", "B", ".www.foo.com", "/",
       two_hours_ago, one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "__Secure-A", "B", ".www.foo.com", "/",
       two_hours_ago, one_hour_from_now, one_hour_ago, false, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_PREFIX}));
 
   // A __Host- cookie must be Secure.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/",
       two_hours_ago, one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/",
       two_hours_ago, one_hour_from_now, one_hour_ago, false, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_PREFIX}));
 
   // A __Host- cookie must have path "/".
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/",
       two_hours_ago, one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/foo",
       two_hours_ago, one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_PREFIX}));
 
   // A __Host- cookie must not specify a domain.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "__Host-A", "B", std::string(), "/",
       two_hours_ago, one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "__Host-A", "B", ".www.foo.com", "/",
       two_hours_ago, one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_PREFIX}));
+
   // Without __Host- prefix, this is a valid host cookie because it does not
   // specify a domain.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", std::string(), "/", two_hours_ago,
       one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
+
   // Without __Host- prefix, this is a valid domain (not host) cookie.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", ".www.foo.com", "/", two_hours_ago,
       one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
 
   // The __Host- prefix should not prevent otherwise-valid host cookies from
   // being accepted.
@@ -2562,24 +2635,29 @@
       GURL("https://127.0.0.1"), "A", "B", std::string(), "/", two_hours_ago,
       one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://127.0.0.1"), "__Host-A", "B", std::string(), "/",
       two_hours_ago, one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
+
   // Host cookies should not specify domain unless it is an IP address that
   // matches the URL.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://127.0.0.1"), "A", "B", "127.0.0.1", "/", two_hours_ago,
       one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://127.0.0.1"), "__Host-A", "B", "127.0.0.1", "/",
       two_hours_ago, one_hour_from_now, one_hour_ago, true, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      false));
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
 
   // SameParty attribute requires Secure and forbids SameSite=Strict.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
@@ -2587,21 +2665,28 @@
       one_hour_from_now, one_hour_ago, true /*secure*/, false,
       CookieSameSite::NO_RESTRICTION, CookiePriority::COOKIE_PRIORITY_DEFAULT,
       true /*same_party*/));
+  EXPECT_TRUE(status.IsInclude());
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", ".www.foo.com", "/", two_hours_ago,
       one_hour_from_now, one_hour_ago, false /*secure*/, false,
       CookieSameSite::LAX_MODE, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      true /*same_party*/));
+      true /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", ".www.foo.com", "/", two_hours_ago,
       one_hour_from_now, one_hour_ago, true /*secure*/, false,
       CookieSameSite::STRICT_MODE, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      true /*same_party*/));
+      true /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("https://www.foo.com"), "A", "B", ".www.foo.com", "/", two_hours_ago,
       one_hour_from_now, one_hour_ago, false /*secure*/, false,
       CookieSameSite::STRICT_MODE, CookiePriority::COOKIE_PRIORITY_DEFAULT,
-      true /*same_party*/));
+      true /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY}));
 
   // Check that CreateSanitizedCookie can gracefully fail on inputs that would
   // crash cookie_util::GetCookieDomainWithString due to failing
@@ -2611,36 +2696,60 @@
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://..."), "A", "B", "...", "/", base::Time(), base::Time(),
       base::Time(), false /*secure*/, false /*httponly*/,
-      CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false));
+      CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://."), "A", "B", std::string(), "/", base::Time(),
       base::Time(), base::Time(), false /*secure*/, false /*httponly*/,
-      CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false));
+      CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
   EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
       GURL("http://.chromium.org"), "A", "B", ".chromium.org", "/",
       base::Time(), base::Time(), base::Time(), false /*secure*/,
       false /*httponly*/, CookieSameSite::NO_RESTRICTION,
-      COOKIE_PRIORITY_DEFAULT, false));
+      COOKIE_PRIORITY_DEFAULT, false /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
 
   // Check that a file URL with an IPv6 host, and matching IPv6 domain, are
   // valid.
   EXPECT_TRUE(CanonicalCookie::CreateSanitizedCookie(
       GURL("file://[A::]"), "A", "B", "[A::]", "", base::Time(), base::Time(),
       base::Time(), false /*secure*/, false /*httponly*/,
-      CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false));
+      CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+      false /*same_party*/, &status));
+  EXPECT_TRUE(status.IsInclude());
 
   // On Windows, URLs beginning with two backslashes are considered file
   // URLs. On other platforms, they are invalid.
   auto double_backslash_ipv6_cookie = CanonicalCookie::CreateSanitizedCookie(
       GURL("\\\\[A::]"), "A", "B", "[A::]", "", base::Time(), base::Time(),
       base::Time(), false /*secure*/, false /*httponly*/,
-      CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT, false);
+      CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_DEFAULT,
+      false /*same_party*/, &status);
 #if defined(OS_WIN)
   EXPECT_TRUE(double_backslash_ipv6_cookie);
   EXPECT_TRUE(double_backslash_ipv6_cookie->IsCanonical());
+  EXPECT_TRUE(status.IsInclude());
 #else
   EXPECT_FALSE(double_backslash_ipv6_cookie);
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
 #endif
+
+  // Confirm multiple error types can be set.
+  EXPECT_FALSE(CanonicalCookie::CreateSanitizedCookie(
+      GURL(""), "", "", "", "", base::Time(), base::Time(), base::Time::Now(),
+      true /*secure*/, true /*httponly*/, CookieSameSite::STRICT_MODE,
+      COOKIE_PRIORITY_DEFAULT, true /*same_party*/, &status));
+  EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+      {CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE,
+       CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN,
+       CookieInclusionStatus::EXCLUDE_INVALID_SAMEPARTY}));
 }
 
 TEST(CanonicalCookieTest, FromStorage) {
diff --git a/net/cookies/cookie_util.cc b/net/cookies/cookie_util.cc
index e1ecfcbd5..5300a57 100644
--- a/net/cookies/cookie_util.cc
+++ b/net/cookies/cookie_util.cc
@@ -146,8 +146,12 @@
       url_chain.size() == 1u ||
       base::ranges::all_of(url_chain, is_same_site_with_site_for_cookies);
 
-  if (same_site_initiator && same_site_redirect_chain)
+  if (same_site_initiator &&
+      (!base::FeatureList::IsEnabled(
+           features::kCookieSameSiteConsidersRedirectChain) ||
+       same_site_redirect_chain)) {
     return {ContextType::SAME_SITE_STRICT, false};
+  }
 
   if (is_http) {
     base::UmaHistogramBoolean("Cookie.SameSiteContextAffectedByBugfix1166211",
diff --git a/net/cookies/cookie_util_unittest.cc b/net/cookies/cookie_util_unittest.cc
index 166b397..e41c755 100644
--- a/net/cookies/cookie_util_unittest.cc
+++ b/net/cookies/cookie_util_unittest.cc
@@ -363,17 +363,25 @@
 // Tests for the various ComputeSameSiteContextFor*() functions. The first
 // boolean test param is whether the results of the computations are evaluated
 // schemefully. The second boolean test param is whether the fix for
-// crbug.com/1166211 is enabled.
+// crbug.com/1166211 is enabled. The third boolean param is whether SameSite
+// considers redirect chains.
 class CookieUtilComputeSameSiteContextTest
-    : public ::testing::TestWithParam<std::tuple<bool, bool>> {
+    : public ::testing::TestWithParam<std::tuple<bool, bool, bool>> {
  public:
   CookieUtilComputeSameSiteContextTest() {
-    // No need to explicitly enable the feature because it is enabled by
-    // default.
-    if (!IsBugfix1166211Enabled()) {
-      feature_list_.InitAndDisableFeature(
-          features::kSameSiteCookiesBugfix1166211);
+    std::vector<base::Feature> enabled_features;
+    std::vector<base::Feature> disabled_features;
+    // No need to explicitly enable the Bugfix1166211 feature because it
+    // is enabled by default.
+    if (!IsBugfix1166211Enabled())
+      disabled_features.push_back(features::kSameSiteCookiesBugfix1166211);
+    // No need to explicitly disable the redirect chain feature because it
+    // is disabled by default.
+    if (DoesSameSiteConsiderRedirectChain()) {
+      enabled_features.push_back(
+          features::kCookieSameSiteConsidersRedirectChain);
     }
+    feature_list_.InitWithFeatures(enabled_features, disabled_features);
   }
   ~CookieUtilComputeSameSiteContextTest() override = default;
 
@@ -384,6 +392,10 @@
 
   bool IsBugfix1166211Enabled() const { return std::get<1>(GetParam()); }
 
+  bool DoesSameSiteConsiderRedirectChain() const {
+    return std::get<2>(GetParam());
+  }
+
   // Returns the proper gtest matcher to use for the schemeless/schemeful mode.
   auto ContextTypeIs(ContextType context_type) const {
     return ContextTypeIsWithSchemefulMode(context_type, IsSchemeful());
@@ -967,38 +979,66 @@
     bool url_chain_is_same_site;
     bool site_for_cookies_is_same_site;
     bool initiator_is_same_site;
+    // These are the expected context types considering redirect chains:
     ContextType expected_context_type;  // for non-main-frame-nav requests.
     ContextType expected_context_type_for_main_frame_navigation;
+    // These are the expected context types not considering redirect chains:
+    ContextType expected_context_type_without_chain;
+    ContextType expected_context_type_for_main_frame_navigation_without_chain;
   } kTestCases[] = {
+      // If the url chain is same-site, then the result is the same with or
+      // without considering the redirect chain.
       {"GET", true, true, true, ContextType::SAME_SITE_STRICT,
+       ContextType::SAME_SITE_STRICT, ContextType::SAME_SITE_STRICT,
        ContextType::SAME_SITE_STRICT},
-      {"GET", true, true, false, incorrectly_lax, ContextType::SAME_SITE_LAX},
+      {"GET", true, true, false, incorrectly_lax, ContextType::SAME_SITE_LAX,
+       incorrectly_lax, ContextType::SAME_SITE_LAX},
       {"GET", true, false, true, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE,
        ContextType::CROSS_SITE},
       {"GET", true, false, false, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE,
        ContextType::CROSS_SITE},
-      {"GET", false, true, true, incorrectly_lax, ContextType::SAME_SITE_LAX},
-      {"GET", false, true, false, incorrectly_lax, ContextType::SAME_SITE_LAX},
+      // If the url chain is cross-site, then the result will differ depending
+      // on whether the redirect chain is considered, when the site-for-cookies
+      // and initiator are both same-site.
+      {"GET", false, true, true, incorrectly_lax, ContextType::SAME_SITE_LAX,
+       ContextType::SAME_SITE_STRICT, ContextType::SAME_SITE_STRICT},
+      {"GET", false, true, false, incorrectly_lax, ContextType::SAME_SITE_LAX,
+       incorrectly_lax, ContextType::SAME_SITE_LAX},
       {"GET", false, false, true, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE,
        ContextType::CROSS_SITE},
       {"GET", false, false, false, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE,
        ContextType::CROSS_SITE},
+      // If the url chain is same-site, then the result is the same with or
+      // without considering the redirect chain.
       {"POST", true, true, true, ContextType::SAME_SITE_STRICT,
+       ContextType::SAME_SITE_STRICT, ContextType::SAME_SITE_STRICT,
        ContextType::SAME_SITE_STRICT},
       {"POST", true, true, false, incorrectly_lax_unsafe,
+       ContextType::SAME_SITE_LAX_METHOD_UNSAFE, incorrectly_lax_unsafe,
        ContextType::SAME_SITE_LAX_METHOD_UNSAFE},
       {"POST", true, false, true, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE,
        ContextType::CROSS_SITE},
       {"POST", true, false, false, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE,
        ContextType::CROSS_SITE},
+      // If the url chain is cross-site, then the result will differ depending
+      // on whether the redirect chain is considered, when the site-for-cookies
+      // and initiator are both same-site.
       {"POST", false, true, true, incorrectly_lax_unsafe,
-       ContextType::SAME_SITE_LAX_METHOD_UNSAFE},
+       ContextType::SAME_SITE_LAX_METHOD_UNSAFE, ContextType::SAME_SITE_STRICT,
+       ContextType::SAME_SITE_STRICT},
       {"POST", false, true, false, incorrectly_lax_unsafe,
+       ContextType::SAME_SITE_LAX_METHOD_UNSAFE, incorrectly_lax_unsafe,
        ContextType::SAME_SITE_LAX_METHOD_UNSAFE},
       {"POST", false, false, true, ContextType::CROSS_SITE,
-       ContextType::CROSS_SITE},
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE},
       {"POST", false, false, false, ContextType::CROSS_SITE,
-       ContextType::CROSS_SITE},
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE},
   };
 
   for (const auto& test_case : kTestCases) {
@@ -1011,6 +1051,15 @@
     std::vector<absl::optional<url::Origin>> initiators =
         test_case.initiator_is_same_site ? GetSameSiteInitiators()
                                          : GetCrossSiteInitiators();
+    ContextType expected_context_type =
+        DoesSameSiteConsiderRedirectChain()
+            ? test_case.expected_context_type
+            : test_case.expected_context_type_without_chain;
+    ContextType expected_context_type_for_main_frame_navigation =
+        DoesSameSiteConsiderRedirectChain()
+            ? test_case.expected_context_type_for_main_frame_navigation
+            : test_case
+                  .expected_context_type_for_main_frame_navigation_without_chain;
     for (const std::vector<GURL>& url_chain : url_chains) {
       for (const SiteForCookies& site_for_cookies : sites_for_cookies) {
         for (const absl::optional<url::Origin>& initiator : initiators) {
@@ -1018,7 +1067,7 @@
                           test_case.method, url_chain, site_for_cookies,
                           initiator, false /* is_main_frame_navigation */,
                           false /* force_ignore_site_for_cookies */),
-                      ContextTypeIs(test_case.expected_context_type))
+                      ContextTypeIs(expected_context_type))
               << UrlChainToString(url_chain) << " "
               << site_for_cookies.ToDebugString() << " "
               << (initiator ? initiator->Serialize() : "nullopt");
@@ -1029,8 +1078,7 @@
                   test_case.method, url_chain, site_for_cookies, initiator,
                   true /* is_main_frame_navigation */,
                   false /* force_ignore_site_for_cookies */),
-              ContextTypeIs(
-                  test_case.expected_context_type_for_main_frame_navigation))
+              ContextTypeIs(expected_context_type_for_main_frame_navigation))
               << UrlChainToString(url_chain) << " "
               << site_for_cookies.ToDebugString() << " "
               << (initiator ? initiator->Serialize() : "nullopt");
@@ -1220,18 +1268,34 @@
     bool url_chain_is_same_site;
     bool site_for_cookies_is_same_site;
     bool initiator_is_same_site;
+    // These are the expected context types considering redirect chains:
     ContextType expected_context_type;  // for non-main-frame-nav requests.
     ContextType expected_context_type_for_main_frame_navigation;
+    // These are the expected context types not considering redirect chains:
+    ContextType expected_context_type_without_chain;
+    ContextType expected_context_type_for_main_frame_navigation_without_chain;
   } kTestCases[] = {
-      {true, true, true, ContextType::SAME_SITE_LAX,
-       ContextType::SAME_SITE_LAX},
-      {true, true, false, incorrectly_lax, ContextType::SAME_SITE_LAX},
-      {true, false, true, ContextType::CROSS_SITE, ContextType::CROSS_SITE},
-      {true, false, false, ContextType::CROSS_SITE, ContextType::CROSS_SITE},
-      {false, true, true, incorrectly_lax, ContextType::SAME_SITE_LAX},
-      {false, true, false, incorrectly_lax, ContextType::SAME_SITE_LAX},
-      {false, false, true, ContextType::CROSS_SITE, ContextType::CROSS_SITE},
-      {false, false, false, ContextType::CROSS_SITE, ContextType::CROSS_SITE},
+      // If the url chain is same-site, then the result is the same with or
+      // without considering the redirect chain.
+      {true, true, true, ContextType::SAME_SITE_LAX, ContextType::SAME_SITE_LAX,
+       ContextType::SAME_SITE_LAX, ContextType::SAME_SITE_LAX},
+      {true, true, false, incorrectly_lax, ContextType::SAME_SITE_LAX,
+       incorrectly_lax, ContextType::SAME_SITE_LAX},
+      {true, false, true, ContextType::CROSS_SITE, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE},
+      {true, false, false, ContextType::CROSS_SITE, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE},
+      // If the url chain is cross-site, then the result will differ depending
+      // on whether the redirect chain is considered, when the site-for-cookies
+      // and initiator are both same-site.
+      {false, true, true, incorrectly_lax, ContextType::SAME_SITE_LAX,
+       ContextType::SAME_SITE_LAX, ContextType::SAME_SITE_LAX},
+      {false, true, false, incorrectly_lax, ContextType::SAME_SITE_LAX,
+       incorrectly_lax, ContextType::SAME_SITE_LAX},
+      {false, false, true, ContextType::CROSS_SITE, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE},
+      {false, false, false, ContextType::CROSS_SITE, ContextType::CROSS_SITE,
+       ContextType::CROSS_SITE, ContextType::CROSS_SITE},
   };
   for (const auto& test_case : kTestCases) {
     std::vector<std::vector<GURL>> url_chains =
@@ -1243,6 +1307,15 @@
     std::vector<absl::optional<url::Origin>> initiators =
         test_case.initiator_is_same_site ? GetSameSiteInitiators()
                                          : GetCrossSiteInitiators();
+    ContextType expected_context_type =
+        DoesSameSiteConsiderRedirectChain()
+            ? test_case.expected_context_type
+            : test_case.expected_context_type_without_chain;
+    ContextType expected_context_type_for_main_frame_navigation =
+        DoesSameSiteConsiderRedirectChain()
+            ? test_case.expected_context_type_for_main_frame_navigation
+            : test_case
+                  .expected_context_type_for_main_frame_navigation_without_chain;
     for (const std::vector<GURL>& url_chain : url_chains) {
       for (const SiteForCookies& site_for_cookies : sites_for_cookies) {
         for (const absl::optional<url::Origin>& initiator : initiators) {
@@ -1250,7 +1323,7 @@
                           url_chain, site_for_cookies, initiator,
                           false /* is_main_frame_navigation */,
                           false /* force_ignore_site_for_cookies */),
-                      ContextTypeIs(test_case.expected_context_type))
+                      ContextTypeIs(expected_context_type))
               << UrlChainToString(url_chain) << " "
               << site_for_cookies.ToDebugString() << " "
               << (initiator ? initiator->Serialize() : "nullopt");
@@ -1261,8 +1334,7 @@
                   url_chain, site_for_cookies, initiator,
                   true /* is_main_frame_navigation */,
                   false /* force_ignore_site_for_cookies */),
-              ContextTypeIs(
-                  test_case.expected_context_type_for_main_frame_navigation))
+              ContextTypeIs(expected_context_type_for_main_frame_navigation))
               << UrlChainToString(url_chain) << " "
               << site_for_cookies.ToDebugString() << " "
               << (initiator ? initiator->Serialize() : "nullopt");
@@ -1367,6 +1439,7 @@
 INSTANTIATE_TEST_SUITE_P(/* no label */,
                          CookieUtilComputeSameSiteContextTest,
                          ::testing::Combine(::testing::Bool(),
+                                            ::testing::Bool(),
                                             ::testing::Bool()));
 
 TEST(CookieUtilTest, AdaptCookieAccessResultToBool) {
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 798f85a..47cb01bf 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -1243,13 +1243,14 @@
     return rv;
 
 #if BUILDFLAG(ENABLE_REPORTING)
+  // Note: This just handles the legacy Report-To header, which is still
+  // required for NEL. The newer Reporting-Endpoints header is processed in
+  // network::PopulateParsedHeaders().
+  ProcessReportToHeader();
+
   // Note: Unless there is a pre-existing NEL policy for this origin, any NEL
   // reports generated before the NEL header is processed here will just be
   // dropped by the NetworkErrorLoggingService.
-  ProcessReportToHeader();
-  if (base::FeatureList::IsEnabled(net::features::kDocumentReporting)) {
-    ProcessReportingEndpointsHeader();
-  }
   ProcessNetworkErrorLoggingHeader();
 
   // Generate NEL report here if we have to report an HTTP error (4xx or 5xx
@@ -1382,26 +1383,6 @@
 }
 
 #if BUILDFLAG(ENABLE_REPORTING)
-void HttpNetworkTransaction::ProcessReportingEndpointsHeader() {
-  std::string value;
-  if (!response_.headers->GetNormalizedHeader("Reporting-Endpoints", &value))
-    return;
-
-  ReportingService* service = session_->reporting_service();
-  if (!service)
-    return;
-
-  // Only accept Reporting-Endpoints headers on HTTPS connections that have no
-  // certificate errors.
-  if (!response_.ssl_info.is_valid())
-    return;
-  if (IsCertStatusError(response_.ssl_info.cert_status))
-    return;
-
-  service->ProcessReportingEndpointsHeader(url::Origin::Create(url_),
-                                           network_isolation_key_, value);
-}
-
 void HttpNetworkTransaction::ProcessReportToHeader() {
   std::string value;
   if (!response_.headers->GetNormalizedHeader("Report-To", &value))
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 34289b7..fa449f0 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -19952,7 +19952,7 @@
   }
 
   // Makes an HTTPS request that should install a valid Reporting policy
-  // using either Report-To header or Reporting-Endpoints header.
+  // using Report-To header.
   void RequestPolicy(CertStatus cert_status = 0) {
     HttpRequestInfo request;
     request.method = "GET";
@@ -19968,15 +19968,10 @@
     };
 
     MockRead reporting_header;
-    if (UseDocumentReporting()) {
-      reporting_header = MockRead(
-          "Reporting-Endpoints: nel=\"https://www.example.org/upload/\"\r\n");
-    } else {
-      reporting_header = MockRead(
-          "Report-To: {\"group\": \"nel\", \"max_age\": 86400, "
-          "\"endpoints\": [{\"url\": "
-          "\"https://www.example.org/upload/\"}]}\r\n");
-    }
+    reporting_header = MockRead(
+        "Report-To: {\"group\": \"nel\", \"max_age\": 86400, "
+        "\"endpoints\": [{\"url\": "
+        "\"https://www.example.org/upload/\"}]}\r\n");
     MockRead data_reads[] = {
         MockRead("HTTP/1.0 200 OK\r\n"),
         std::move(reporting_header),
diff --git a/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc b/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc
index 72a8dda68..61a794d 100644
--- a/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc
+++ b/net/proxy_resolution/win/windows_system_proxy_resolver_unittest.cc
@@ -260,65 +260,30 @@
     callback_info_ = std::make_unique<WINHTTP_ASYNC_RESULT>();
     callback_info_->dwError = info_error;
   }
-  void AddToProxyResults(const ProxyServer& proxy_server,
-                         bool bypass = false,
-                         bool skip_port = false) {
+  void AddBypassToProxyResults() {
     EXPECT_LT(proxy_result_.cEntries, kMaxProxyEntryLimit - 1);
+    AllocateProxyResultEntriesIfNeeded();
+    proxy_result_.pEntries[proxy_result_.cEntries].fBypass = TRUE;
+    proxy_result_.cEntries++;
+  }
+  void AddDirectToProxyResults() {
+    EXPECT_LT(proxy_result_.cEntries, kMaxProxyEntryLimit - 1);
+    AllocateProxyResultEntriesIfNeeded();
+    proxy_result_.cEntries++;
+  }
+  void AddToProxyResults(INTERNET_SCHEME scheme,
+                         std::wstring proxy_host,
+                         INTERNET_PORT port) {
+    EXPECT_LT(proxy_result_.cEntries, kMaxProxyEntryLimit - 1);
+    AllocateProxyResultEntriesIfNeeded();
 
-    // Assign memory as needed.
-    if (proxy_result_.cEntries == 0) {
-      proxy_result_.pEntries =
-          new WINHTTP_PROXY_RESULT_ENTRY[kMaxProxyEntryLimit];
-      std::memset(proxy_result_.pEntries, 0,
-                  kMaxProxyEntryLimit * sizeof(WINHTTP_PROXY_RESULT_ENTRY));
+    proxy_list_.push_back(std::move(proxy_host));
+    wchar_t* proxy_host_raw = const_cast<wchar_t*>(proxy_list_.back().data());
 
-      // The memory of the strings above will be backed by a vector of strings.
-      proxy_list_.reserve(kMaxProxyEntryLimit);
-    }
-
-    if (bypass) {
-      proxy_result_.pEntries[proxy_result_.cEntries].fBypass = TRUE;
-    } else if (!proxy_server.is_direct()) {
-      // Now translate the ProxyServer into a WINHTTP_PROXY_RESULT_ENTRY and
-      // assign.
-      proxy_result_.pEntries[proxy_result_.cEntries].fProxy = TRUE;
-
-      switch (proxy_server.scheme()) {
-        case ProxyServer::Scheme::SCHEME_HTTP:
-          proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme =
-              INTERNET_SCHEME_HTTP;
-          break;
-        case ProxyServer::Scheme::SCHEME_HTTPS:
-          proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme =
-              INTERNET_SCHEME_HTTPS;
-          break;
-        case ProxyServer::Scheme::SCHEME_SOCKS4:
-          proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme =
-              INTERNET_SCHEME_SOCKS;
-          break;
-        default:
-          ADD_FAILURE()
-              << "Of the possible proxy schemes returned by WinHttp, Chrome "
-                 "supports HTTP(S) and SOCKS4. The ProxyServer::Scheme that "
-                 "triggered this message is: "
-              << proxy_server.scheme();
-          break;
-      }
-
-      std::wstring proxy_host(proxy_server.host_port_pair().host().begin(),
-                              proxy_server.host_port_pair().host().end());
-      proxy_list_.push_back(proxy_host);
-
-      wchar_t* proxy_host_raw = const_cast<wchar_t*>(proxy_list_.back().data());
-      proxy_result_.pEntries[proxy_result_.cEntries].pwszProxy = proxy_host_raw;
-
-      if (skip_port)
-        proxy_result_.pEntries[proxy_result_.cEntries].ProxyPort =
-            INTERNET_DEFAULT_PORT;
-      else
-        proxy_result_.pEntries[proxy_result_.cEntries].ProxyPort =
-            proxy_server.host_port_pair().port();
-    }
+    proxy_result_.pEntries[proxy_result_.cEntries].fProxy = TRUE;
+    proxy_result_.pEntries[proxy_result_.cEntries].ProxyScheme = scheme;
+    proxy_result_.pEntries[proxy_result_.cEntries].pwszProxy = proxy_host_raw;
+    proxy_result_.pEntries[proxy_result_.cEntries].ProxyPort = port;
 
     proxy_result_.cEntries++;
   }
@@ -372,6 +337,19 @@
     callback_info_.reset();
   }
 
+  void AllocateProxyResultEntriesIfNeeded() {
+    if (proxy_result_.cEntries != 0)
+      return;
+
+    proxy_result_.pEntries =
+        new WINHTTP_PROXY_RESULT_ENTRY[kMaxProxyEntryLimit];
+    std::memset(proxy_result_.pEntries, 0,
+                kMaxProxyEntryLimit * sizeof(WINHTTP_PROXY_RESULT_ENTRY));
+
+    // The memory of the strings above will be backed by a vector of strings.
+    proxy_list_.reserve(kMaxProxyEntryLimit);
+  }
+
   // Data configurable by tests to simulate errors and results from WinHttp.
   bool open_success_ = true;
   bool set_timeouts_success_ = true;
@@ -442,55 +420,6 @@
 
   bool InitializeResolver() { return proxy_resolver_->Initialize(); }
 
-  void AddNoPortProxyToResults() {
-    const ProxyServer proxy_result =
-        ProxyServer::FromPacString("PROXY foopy:8080");
-    winhttp_api_wrapper_->AddToProxyResults(proxy_result, /*bypass=*/false,
-                                            /*skip_port=*/true);
-  }
-
-  void AddDirectProxyToResults(ProxyList* out_proxy_list) {
-    winhttp_api_wrapper_->AddToProxyResults(ProxyServer::Direct());
-    out_proxy_list->AddProxyServer(ProxyServer::Direct());
-  }
-
-  void AddBypassedProxyToResults(ProxyList* out_proxy_list) {
-    winhttp_api_wrapper_->AddToProxyResults(ProxyServer::Direct(),
-                                            /*bypass=*/true);
-    out_proxy_list->AddProxyServer(ProxyServer::Direct());
-  }
-
-  void AddHTTPProxyToResults(ProxyList* out_proxy_list) {
-    const ProxyServer proxy_result =
-        ProxyServer::FromPacString("PROXY foopy:8080");
-    winhttp_api_wrapper_->AddToProxyResults(proxy_result);
-    out_proxy_list->AddProxyServer(proxy_result);
-  }
-
-  void AddHTTPSProxyToResults(ProxyList* out_proxy_list) {
-    const ProxyServer proxy_result =
-        ProxyServer::FromPacString("HTTPS foopy:8443");
-    winhttp_api_wrapper_->AddToProxyResults(proxy_result);
-    out_proxy_list->AddProxyServer(proxy_result);
-  }
-
-  void AddSOCKSProxyToResults(ProxyList* out_proxy_list) {
-    const ProxyServer proxy_result =
-        ProxyServer::FromPacString("SOCKS4 foopy:8080");
-    winhttp_api_wrapper_->AddToProxyResults(proxy_result);
-    out_proxy_list->AddProxyServer(proxy_result);
-  }
-
-  void AddIDNProxyToResults(ProxyList* out_proxy_list) {
-    const ProxyServer proxy_result =
-        ProxyServer::FromPacString("HTTPS föopy:8080");
-    winhttp_api_wrapper_->AddToProxyResults(proxy_result);
-
-    const ProxyServer expected_proxy_result =
-        ProxyServer::FromPacString("HTTPS xn--fopy-5jr83a:8080");
-    out_proxy_list->AddProxyServer(expected_proxy_result);
-  }
-
   void PerformGetProxyForUrlAndValidateResult(const ProxyList& proxy_list,
                                               int net_error,
                                               int windows_error) {
@@ -517,8 +446,10 @@
   }
 
   void DoProxyConfigTest(const ProxyConfig& proxy_config) {
+    winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_HTTPS, L"foopy",
+                                             8443);
     ProxyList proxy_list;
-    AddHTTPSProxyToResults(&proxy_list);
+    proxy_list.AddProxyServer(ProxyServer::FromPacString("HTTPS foopy:8443"));
 
     std::wstring pac_url;
     if (proxy_config.has_pac_url())
@@ -626,7 +557,8 @@
 }
 
 TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlFailOnDefaultPort) {
-  AddNoPortProxyToResults();
+  winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_HTTP, L"foopy",
+                                           INTERNET_DEFAULT_PORT);
   DoFailedGetProxyForUrlTest(ERR_FAILED, 0);
 }
 
@@ -660,8 +592,11 @@
 }
 
 TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlCancelAndRestart) {
+  winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_HTTPS, L"foopy",
+                                           8443);
   ProxyList expected_proxy_list;
-  AddHTTPSProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(
+      ProxyServer::FromPacString("HTTPS foopy:8443"));
 
   ASSERT_TRUE(InitializeResolver());
   TestCompletionCallback unused_callback;
@@ -738,52 +673,81 @@
 }
 
 TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlDirect) {
+  winhttp_api_wrapper()->AddDirectToProxyResults();
   ProxyList expected_proxy_list;
-  AddDirectProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(ProxyServer::Direct());
   DoGetProxyForUrlTest(expected_proxy_list);
 }
 
 TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlBypass) {
+  winhttp_api_wrapper()->AddBypassToProxyResults();
   ProxyList expected_proxy_list;
-  AddBypassedProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(ProxyServer::Direct());
   DoGetProxyForUrlTest(expected_proxy_list);
 }
 
 TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlHTTP) {
+  winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_HTTP, L"foopy",
+                                           8080);
   ProxyList expected_proxy_list;
-  AddHTTPProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(
+      ProxyServer::FromPacString("PROXY foopy:8080"));
   DoGetProxyForUrlTest(expected_proxy_list);
 }
 
 TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlHTTPS) {
+  winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_HTTPS, L"foopy",
+                                           8443);
   ProxyList expected_proxy_list;
-  AddHTTPSProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(
+      ProxyServer::FromPacString("HTTPS foopy:8443"));
   DoGetProxyForUrlTest(expected_proxy_list);
 }
 
 TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlSOCKS) {
+  winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_SOCKS, L"foopy",
+                                           8080);
   ProxyList expected_proxy_list;
-  AddSOCKSProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(
+      ProxyServer::FromPacString("SOCKS4 foopy:8080"));
   DoGetProxyForUrlTest(expected_proxy_list);
 }
 
 TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlIDNProxy) {
+  winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_HTTPS, L"föopy",
+                                           8080);
+
+  // Expect L"föopy" to be ascii-encoded as "xn--fopy-5qa".
   ProxyList expected_proxy_list;
-  AddIDNProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(
+      ProxyServer::FromPacString("HTTPS xn--fopy-5qa:8080"));
+
   DoGetProxyForUrlTest(expected_proxy_list);
 }
 
 TEST_F(WindowsSystemProxyResolverTest, GetProxyForUrlMultipleResults) {
+  winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_HTTPS, L"foopy",
+                                           8443);
+  winhttp_api_wrapper()->AddDirectToProxyResults();
+
   ProxyList expected_proxy_list;
-  AddHTTPSProxyToResults(&expected_proxy_list);
-  AddDirectProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(
+      ProxyServer::FromPacString("HTTPS foopy:8443"));
+  expected_proxy_list.AddProxyServer(ProxyServer::Direct());
+
   DoGetProxyForUrlTest(expected_proxy_list);
 }
 
 TEST_F(WindowsSystemProxyResolverTest, MultipleCallsToGetProxyForUrl) {
+  winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_HTTPS, L"foopy",
+                                           8443);
+  winhttp_api_wrapper()->AddDirectToProxyResults();
+
   ProxyList expected_proxy_list;
-  AddHTTPSProxyToResults(&expected_proxy_list);
-  AddDirectProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(
+      ProxyServer::FromPacString("HTTPS foopy:8443"));
+  expected_proxy_list.AddProxyServer(ProxyServer::Direct());
+
   ASSERT_TRUE(InitializeResolver());
 
   TestCompletionCallback unused_callback;
@@ -818,9 +782,15 @@
 
 TEST_F(WindowsSystemProxyResolverTest,
        MultipleCallsToGetProxyForUrlWithOneCancellation) {
+  winhttp_api_wrapper()->AddToProxyResults(INTERNET_SCHEME_HTTPS, L"foopy",
+                                           8443);
+  winhttp_api_wrapper()->AddDirectToProxyResults();
+
   ProxyList expected_proxy_list;
-  AddHTTPSProxyToResults(&expected_proxy_list);
-  AddDirectProxyToResults(&expected_proxy_list);
+  expected_proxy_list.AddProxyServer(
+      ProxyServer::FromPacString("HTTPS foopy:8443"));
+  expected_proxy_list.AddProxyServer(ProxyServer::Direct());
+
   ASSERT_TRUE(InitializeResolver());
 
   // This extra scope is needed so that the MockProxyResolutionRequests destruct
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index a6688ce..e20205b 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -1944,7 +1944,8 @@
   quic::test::QuicStreamPeer::SendBuffer(stream).SaveStreamData(iov, 1, 0, 4);
   quic::test::QuicStreamPeer::SetStreamBytesWritten(4, stream);
   session_->WritevData(stream->id(), 4, 0, quic::NO_FIN,
-                       quic::NOT_RETRANSMISSION, absl::nullopt);
+                       quic::NOT_RETRANSMISSION,
+                       quic::ENCRYPTION_FORWARD_SECURE);
 
   EXPECT_TRUE(quic_data2.AllReadDataConsumed());
   EXPECT_TRUE(quic_data2.AllWriteDataConsumed());
diff --git a/net/quic/quic_chromium_client_stream_test.cc b/net/quic/quic_chromium_client_stream_test.cc
index 6509df58..731f378 100644
--- a/net/quic/quic_chromium_client_stream_test.cc
+++ b/net/quic/quic_chromium_client_stream_test.cc
@@ -18,6 +18,7 @@
 #include "net/quic/quic_chromium_client_session.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_with_task_environment.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h"
 #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
 #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
@@ -69,6 +70,8 @@
                              quic::StreamSendingState state,
                              quic::TransmissionType type,
                              absl::optional<quic::EncryptionLevel> level));
+  MOCK_METHOD2(WriteControlFrame,
+               bool(const quic::QuicFrame&, quic::TransmissionType));
   MOCK_METHOD4(SendRstStream,
                void(quic::QuicStreamId stream_id,
                     quic::QuicRstStreamErrorCode error,
@@ -185,6 +188,9 @@
     session_.ActivateStream(base::WrapUnique(stream_));
     handle_ = stream_->CreateHandle();
     helper_.AdvanceTime(quic::QuicTime::Delta::FromSeconds(1));
+    session_.connection()->SetEncrypter(
+        quic::ENCRYPTION_FORWARD_SECURE,
+        std::make_unique<quic::NullEncrypter>(quic::Perspective::IS_CLIENT));
   }
 
   void InitializeHeaders() {
@@ -973,10 +979,6 @@
                         version_.transport_version, 0),
                     quic::QUIC_BAD_APPLICATION_PAYLOAD));
 
-  EXPECT_CALL(
-      *static_cast<quic::test::MockQuicConnection*>(session_.connection()),
-      SendControlFrame(_));
-
   ProcessHeaders(headers);
   EXPECT_FALSE(handle_->IsOpen());
   EXPECT_EQ(quic::QUIC_BAD_APPLICATION_PAYLOAD, handle_->stream_error());
@@ -992,10 +994,6 @@
                         version_.transport_version, 0),
                     quic::QUIC_BAD_APPLICATION_PAYLOAD));
 
-  EXPECT_CALL(
-      *static_cast<quic::test::MockQuicConnection*>(session_.connection()),
-      SendControlFrame(_));
-
   ProcessHeaders(informational_headers);
   EXPECT_FALSE(handle_->IsOpen());
   EXPECT_EQ(quic::QUIC_BAD_APPLICATION_PAYLOAD, handle_->stream_error());
diff --git a/net/reporting/reporting_header_parser.cc b/net/reporting/reporting_header_parser.cc
index 10f6cef..d26f179 100644
--- a/net/reporting/reporting_header_parser.cc
+++ b/net/reporting/reporting_header_parser.cc
@@ -194,18 +194,15 @@
 // Processes a single endpoint tuple received in a Reporting-Endpoints header.
 //
 // |group_key| is the key for the endpoint group this endpoint belongs.
-// |value| is the parsed parameterized member representing the endpoint url.
+// |endpoint_url_string| is the endpoint url as received in the header.
 //
 // |endpoint_info_out| is the endpoint info parsed out of the value.
-bool ProcessEndpointStructuredHeader(
-    ReportingDelegate* delegate,
-    const ReportingEndpointGroupKey& group_key,
-    const structured_headers::ParameterizedMember& value,
-    ReportingEndpoint::EndpointInfo& endpoint_info_out) {
-  if (value.member_is_inner_list || !value.member.front().item.is_string())
+bool ProcessEndpoint(ReportingDelegate* delegate,
+                     const ReportingEndpointGroupKey& group_key,
+                     const std::string& endpoint_url_string,
+                     ReportingEndpoint::EndpointInfo& endpoint_info_out) {
+  if (endpoint_url_string.empty())
     return false;
-  const std::string& endpoint_url_string =
-      value.member.front().item.GetString();
 
   GURL endpoint_url;
   if (!ProcessEndpointURLString(endpoint_url_string, group_key.origin,
@@ -227,15 +224,16 @@
 // the endpoint group we create here is just a wrapper for that endpoint. The
 // endpoint name will be stored in the group name here as individual endpoint
 // doesn't have names.
-bool ProcessEndpointGroupStructuredHeader(
+bool ProcessDocumentEndpoint(
     ReportingDelegate* delegate,
     ReportingCache* cache,
     const NetworkIsolationKey& network_isolation_key,
     const url::Origin& origin,
-    const structured_headers::DictionaryMember& value,
+    const std::string& endpoint_name,
+    const std::string& endpoint_url_string,
     ReportingEndpointGroup& parsed_endpoint_group_out) {
   ReportingEndpointGroupKey group_key(network_isolation_key, origin,
-                                      value.first);
+                                      endpoint_name);
   parsed_endpoint_group_out.group_key = group_key;
 
   // Default to a fixed number of days as Reporting-Endpoints doesn't have the
@@ -246,8 +244,8 @@
 
   ReportingEndpoint::EndpointInfo parsed_endpoint;
 
-  if (!ProcessEndpointStructuredHeader(delegate, group_key, value.second,
-                                       parsed_endpoint)) {
+  if (!ProcessEndpoint(delegate, group_key, endpoint_url_string,
+                       parsed_endpoint)) {
     // Remove the group if it does not have a proper endpoint.
     cache->RemoveEndpointGroup(group_key);
     return false;
@@ -258,6 +256,26 @@
 
 }  // namespace
 
+absl::optional<base::flat_map<std::string, std::string>>
+ParseReportingEndpoints(const std::string& header) {
+  absl::optional<structured_headers::Dictionary> header_dict =
+      structured_headers::ParseDictionary(header);
+  if (!header_dict) {
+    return absl::nullopt;
+  }
+  base::flat_map<std::string, std::string> parsed_header;
+  for (const structured_headers::DictionaryMember& entry : *header_dict) {
+    if (entry.second.member_is_inner_list ||
+        !entry.second.member.front().item.is_string()) {
+      return absl::nullopt;
+    }
+    const std::string& endpoint_url_string =
+        entry.second.member.front().item.GetString();
+    parsed_header[entry.first] = endpoint_url_string;
+  }
+  return parsed_header;
+}
+
 // static
 void ReportingHeaderParser::RecordReportingHeaderType(
     ReportingHeaderType header_type) {
@@ -311,11 +329,11 @@
 }
 
 // static
-void ReportingHeaderParser::ParseReportingEndpointsHeader(
+void ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
     ReportingContext* context,
     const NetworkIsolationKey& network_isolation_key,
     const url::Origin& origin,
-    std::unique_ptr<structured_headers::Dictionary> value) {
+    base::flat_map<std::string, std::string> header) {
   DCHECK(base::FeatureList::IsEnabled(net::features::kDocumentReporting));
   DCHECK(GURL::SchemeIsCryptographic(origin.scheme()));
 
@@ -324,12 +342,11 @@
 
   std::vector<ReportingEndpointGroup> parsed_header;
 
-  for (const structured_headers::DictionaryMember& member : *value) {
-    ReportingEndpointGroup parsed_endpoint_group;
-    if (ProcessEndpointGroupStructuredHeader(delegate, cache,
-                                             network_isolation_key, origin,
-                                             member, parsed_endpoint_group)) {
-      parsed_header.push_back(std::move(parsed_endpoint_group));
+  for (const auto& member : header) {
+    ReportingEndpointGroup parsed_endpoint;
+    if (ProcessDocumentEndpoint(delegate, cache, network_isolation_key, origin,
+                                member.first, member.second, parsed_endpoint)) {
+      parsed_header.push_back(std::move(parsed_endpoint));
     }
   }
 
diff --git a/net/reporting/reporting_header_parser.h b/net/reporting/reporting_header_parser.h
index a332f45..6a1b955e 100644
--- a/net/reporting/reporting_header_parser.h
+++ b/net/reporting/reporting_header_parser.h
@@ -7,9 +7,11 @@
 
 #include <memory>
 
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "net/base/net_export.h"
 #include "net/http/structured_headers.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -22,6 +24,13 @@
 class NetworkIsolationKey;
 class ReportingContext;
 
+// Tries to parse a Reporting-Endpoints header. Returns base::nullopt if parsing
+// failed and the header should be ignored; otherwise returns a (possibly
+// empty) mapping of endpoint names to URLs.
+NET_EXPORT
+absl::optional<base::flat_map<std::string, std::string>>
+ParseReportingEndpoints(const std::string& header);
+
 class NET_EXPORT ReportingHeaderParser {
  public:
   // These values are persisted to logs. Entries should not be renumbered and
@@ -38,11 +47,11 @@
       const GURL& url,
       std::unique_ptr<base::Value> value);
 
-  static void ParseReportingEndpointsHeader(
+  static void ProcessParsedReportingEndpointsHeader(
       ReportingContext* context,
       const NetworkIsolationKey& network_isolation_key,
       const url::Origin& origin,
-      std::unique_ptr<structured_headers::Dictionary> value);
+      base::flat_map<std::string, std::string> parsed_header);
 
   static void RecordReportingHeaderType(ReportingHeaderType header_type);
 
diff --git a/net/reporting/reporting_header_parser_unittest.cc b/net/reporting/reporting_header_parser_unittest.cc
index 6991ddac..af7eaf7 100644
--- a/net/reporting/reporting_header_parser_unittest.cc
+++ b/net/reporting/reporting_header_parser_unittest.cc
@@ -1773,14 +1773,12 @@
   void ParseHeader(const NetworkIsolationKey& network_isolation_key,
                    const url::Origin& origin,
                    const std::string& header_string) {
-    absl::optional<Dictionary> header_dict =
-        structured_headers::ParseDictionary(header_string);
+    absl::optional<base::flat_map<std::string, std::string>> header_map =
+        ParseReportingEndpoints(header_string);
 
-    if (header_dict) {
-      std::unique_ptr<Dictionary> header_value =
-          std::make_unique<Dictionary>(std::move(*header_dict));
-      ReportingHeaderParser::ParseReportingEndpointsHeader(
-          context(), network_isolation_key, origin, std::move(header_value));
+    if (header_map) {
+      ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
+          context(), network_isolation_key, origin, *header_map);
     }
   }
 };
diff --git a/net/reporting/reporting_service.cc b/net/reporting/reporting_service.cc
index 3e237a8..d59d469 100644
--- a/net/reporting/reporting_service.cc
+++ b/net/reporting/reporting_service.cc
@@ -32,7 +32,6 @@
 
 constexpr int kMaxJsonSize = 16 * 1024;
 constexpr int kMaxJsonDepth = 5;
-constexpr int kMaxSHSize = 16 * 1024;
 
 // If constructed with a PersistentReportingStore, the first call to any of
 // QueueReport(), ProcessHeader(), RemoveBrowsingData(), or
@@ -56,6 +55,16 @@
       context_->cache()->Flush();
   }
 
+  void SetDocumentReportingEndpoints(
+      const url::Origin& origin,
+      const net::NetworkIsolationKey& network_isolation_key,
+      const base::flat_map<std::string, std::string>& endpoints) override {
+    DoOrBacklogTask(base::BindOnce(
+        &ReportingServiceImpl::DoSetDocumentReportingEndpoints,
+        base::Unretained(this), FixupNetworkIsolationKey(network_isolation_key),
+        origin, endpoints));
+  }
+
   void QueueReport(const GURL& url,
                    const NetworkIsolationKey& network_isolation_key,
                    const std::string& user_agent,
@@ -104,31 +113,6 @@
         std::move(header_value)));
   }
 
-  void ProcessReportingEndpointsHeader(
-      const url::Origin& origin,
-      const NetworkIsolationKey& network_isolation_key,
-      const std::string& header_string) override {
-    if (header_string.size() == 0 || header_string.size() > kMaxSHSize)
-      return;
-
-    absl::optional<structured_headers::Dictionary> header_dict =
-        structured_headers::ParseDictionary(header_string);
-    if (!header_dict) {
-      DVLOG(1) << "Error processing Reporting-Endpoints header string: "
-               << header_string;
-      return;
-    }
-    std::unique_ptr<structured_headers::Dictionary> header_value =
-        std::make_unique<structured_headers::Dictionary>(
-            std::move(*header_dict));
-
-    DVLOG(1) << "Received Reporting-Endpoints header policy for " << origin;
-    DoOrBacklogTask(base::BindOnce(
-        &ReportingServiceImpl::DoProcessReportingEndpointsHeader,
-        base::Unretained(this), FixupNetworkIsolationKey(network_isolation_key),
-        origin, std::move(header_value)));
-  }
-
   void RemoveBrowsingData(uint64_t data_type_mask,
                           const base::RepeatingCallback<bool(const GURL&)>&
                               origin_filter) override {
@@ -201,12 +185,12 @@
         context_.get(), network_isolation_key, url, std::move(header_value));
   }
 
-  void DoProcessReportingEndpointsHeader(
+  void DoSetDocumentReportingEndpoints(
       const NetworkIsolationKey& network_isolation_key,
       const url::Origin& origin,
-      std::unique_ptr<structured_headers::Dictionary> header_value) {
+      base::flat_map<std::string, std::string> header_value) {
     DCHECK(initialized_);
-    ReportingHeaderParser::ParseReportingEndpointsHeader(
+    ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
         context_.get(), network_isolation_key, origin, std::move(header_value));
   }
 
diff --git a/net/reporting/reporting_service.h b/net/reporting/reporting_service.h
index ab2859a8..07d0c68 100644
--- a/net/reporting/reporting_service.h
+++ b/net/reporting/reporting_service.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "net/base/net_export.h"
 #include "net/reporting/reporting_cache.h"
@@ -71,13 +72,13 @@
       const NetworkIsolationKey& network_isolation_key,
       const std::string& header_value) = 0;
 
-  // Processes a Reporting-Endpoints header. |url| is the URL that originated
-  // the header; |header_value| is the normalized value of the
-  // Reporting-Endpoints header.
-  virtual void ProcessReportingEndpointsHeader(
+  // Configures reporting endpoints set by the Reporting-Endpoints header, once
+  // the associated document has been committed.
+  // |endpoints| is a mapping of endpoint names to URLs.
+  virtual void SetDocumentReportingEndpoints(
       const url::Origin& origin,
       const NetworkIsolationKey& network_isolation_key,
-      const std::string& header_value) = 0;
+      const base::flat_map<std::string, std::string>& endpoints) = 0;
 
   // Removes browsing data from the Reporting system. See
   // ReportingBrowsingDataRemover for more details.
diff --git a/net/reporting/reporting_service_unittest.cc b/net/reporting/reporting_service_unittest.cc
index 83874936..3ce13d2d 100644
--- a/net/reporting/reporting_service_unittest.cc
+++ b/net/reporting/reporting_service_unittest.cc
@@ -188,8 +188,10 @@
 TEST_P(ReportingServiceTest, ProcessReportingEndpointsHeader) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
-  service()->ProcessReportingEndpointsHeader(
-      kOrigin_, kNik_, kGroup_ + "=\"" + kEndpoint_.spec() + "\"");
+  auto parsed_header =
+      ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\"");
+  ASSERT_TRUE(parsed_header.has_value());
+  service()->SetDocumentReportingEndpoints(kOrigin_, kNik_, *parsed_header);
   FinishLoading(true /* load_success */);
 
   EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
@@ -200,8 +202,9 @@
 TEST_P(ReportingServiceTest, ProcessReportingEndpointsHeaderPathAbsolute) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
-  service()->ProcessReportingEndpointsHeader(kOrigin_, kNik_,
-                                             kGroup_ + "=\"/path-absolute\"");
+  auto parsed_header = ParseReportingEndpoints(kGroup_ + "=\"/path-absolute\"");
+  ASSERT_TRUE(parsed_header.has_value());
+  service()->SetDocumentReportingEndpoints(kOrigin_, kNik_, *parsed_header);
   FinishLoading(true /* load_success */);
 
   EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
diff --git a/net/reporting/reporting_test_util.cc b/net/reporting/reporting_test_util.cc
index 47c3d83..5a36a2f 100644
--- a/net/reporting/reporting_test_util.cc
+++ b/net/reporting/reporting_test_util.cc
@@ -334,13 +334,6 @@
   NOTREACHED();
 }
 
-void TestReportingService::ProcessReportingEndpointsHeader(
-    const url::Origin& origin,
-    const NetworkIsolationKey& network_isolation_key,
-    const std::string& header_value) {
-  NOTREACHED();
-}
-
 void TestReportingService::RemoveBrowsingData(
     uint64_t data_type_mask,
     const base::RepeatingCallback<bool(const GURL&)>& origin_filter) {
diff --git a/net/reporting/reporting_test_util.h b/net/reporting/reporting_test_util.h
index 629e7651..e4648d3 100644
--- a/net/reporting/reporting_test_util.h
+++ b/net/reporting/reporting_test_util.h
@@ -313,6 +313,11 @@
 
   ~TestReportingService() override;
 
+  void SetDocumentReportingEndpoints(
+      const url::Origin& origin,
+      const net::NetworkIsolationKey& network_isolation_key,
+      const base::flat_map<std::string, std::string>& endpoints) override {}
+
   void QueueReport(const GURL& url,
                    const NetworkIsolationKey& network_isolation_key,
                    const std::string& user_agent,
@@ -325,11 +330,6 @@
                              const NetworkIsolationKey& network_isolation_key,
                              const std::string& header_value) override;
 
-  void ProcessReportingEndpointsHeader(
-      const url::Origin& origin,
-      const NetworkIsolationKey& network_isolation_key,
-      const std::string& header_value) override;
-
   void RemoveBrowsingData(
       uint64_t data_type_mask,
       const base::RepeatingCallback<bool(const GURL&)>& origin_filter) override;
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index cced331..4a41d3a3 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -2185,7 +2185,26 @@
   }
 }
 
-TEST_F(URLRequestTest, SameSiteCookies) {
+// Tests for SameSite cookies. The test param indicates whether the same-site
+// calculation considers redirect chains.
+class URLRequestSameSiteCookiesTest
+    : public URLRequestTest,
+      public ::testing::WithParamInterface<bool> {
+ public:
+  URLRequestSameSiteCookiesTest() {
+    if (DoesCookieSameSiteConsiderRedirectChain()) {
+      feature_list_.InitAndEnableFeature(
+          features::kCookieSameSiteConsidersRedirectChain);
+    }
+  }
+
+  bool DoesCookieSameSiteConsiderRedirectChain() { return GetParam(); }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_P(URLRequestSameSiteCookiesTest, SameSiteCookies) {
   HttpTestServer test_server;
   ASSERT_TRUE(test_server.Start());
 
@@ -2390,7 +2409,7 @@
   }
 }
 
-TEST_F(URLRequestTest, SameSiteCookies_Redirect) {
+TEST_P(URLRequestSameSiteCookiesTest, SameSiteCookies_Redirect) {
   EmbeddedTestServer http_server;
   RegisterDefaultHandlers(&http_server);
   EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
@@ -2505,10 +2524,14 @@
     EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
   }
 
+  // If redirect chains are considered:
   // Verify that the Strict cookie may or may not be sent for a cross-scheme
   // (same-registrable-domain) redirected top level navigation, depending on the
   // status of Schemeful Same-Site. The Lax cookie is sent regardless, because
   // this is a top-level navigation.
+  //
+  // If redirect chains are not considered:
+  // Verify that both cookies are sent, because this is a top-level navigation.
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndDisableFeature(features::kSchemefulSameSite);
@@ -2524,7 +2547,7 @@
     req->set_first_party_url_policy(
         RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT);
     req->set_site_for_cookies(kHttpSiteForCookies);
-    req->set_initiator(kHttpOrigin);
+    req->set_initiator(kOrigin);
     req->Start();
     d.RunUntilComplete();
 
@@ -2548,18 +2571,20 @@
     req->set_first_party_url_policy(
         RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT);
     req->set_site_for_cookies(kHttpSiteForCookies);
-    req->set_initiator(kHttpOrigin);
+    req->set_initiator(kOrigin);
     req->Start();
     d.RunUntilComplete();
 
     EXPECT_EQ(2u, req->url_chain().size());
-    EXPECT_EQ(std::string::npos,
-              d.data_received().find("StrictSameSiteCookie=1"));
+    EXPECT_EQ(
+        DoesCookieSameSiteConsiderRedirectChain(),
+        std::string::npos == d.data_received().find("StrictSameSiteCookie=1"));
     EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
   }
 
-  // Verify that the Strict cookie is not sent for a cross-site redirected top
-  // level navigation...
+  // Verify that (depending on whether redirect chains are considered), the
+  // Strict cookie is (not) sent for a cross-site redirected top level
+  // navigation...
   {
     TestDelegate d;
     GURL url = https_server.GetURL(
@@ -2579,8 +2604,9 @@
     d.RunUntilComplete();
 
     EXPECT_EQ(2u, req->url_chain().size());
-    EXPECT_EQ(std::string::npos,
-              d.data_received().find("StrictSameSiteCookie=1"));
+    EXPECT_EQ(
+        DoesCookieSameSiteConsiderRedirectChain(),
+        std::string::npos == d.data_received().find("StrictSameSiteCookie=1"));
     EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
   }
   // ... even if the initial URL is same-site.
@@ -2605,13 +2631,15 @@
     d.RunUntilComplete();
 
     EXPECT_EQ(3u, req->url_chain().size());
-    EXPECT_EQ(std::string::npos,
-              d.data_received().find("StrictSameSiteCookie=1"));
+    EXPECT_EQ(
+        DoesCookieSameSiteConsiderRedirectChain(),
+        std::string::npos == d.data_received().find("StrictSameSiteCookie=1"));
     EXPECT_NE(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
   }
 
-  // Verify that neither SameSite cookie is sent for a cross-site redirected
-  // subresource request...
+  // Verify that (depending on whether redirect chains are considered), neither
+  // (or both) SameSite cookie is sent for a cross-site redirected subresource
+  // request...
   {
     TestDelegate d;
     GURL url = https_server.GetURL(
@@ -2629,9 +2657,12 @@
     d.RunUntilComplete();
 
     EXPECT_EQ(2u, req->url_chain().size());
-    EXPECT_EQ(std::string::npos,
-              d.data_received().find("StrictSameSiteCookie=1"));
-    EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
+    EXPECT_EQ(
+        DoesCookieSameSiteConsiderRedirectChain(),
+        std::string::npos == d.data_received().find("StrictSameSiteCookie=1"));
+    EXPECT_EQ(
+        DoesCookieSameSiteConsiderRedirectChain(),
+        std::string::npos == d.data_received().find("LaxSameSiteCookie=1"));
   }
   // ... even if the initial URL is same-site.
   {
@@ -2653,13 +2684,16 @@
     d.RunUntilComplete();
 
     EXPECT_EQ(3u, req->url_chain().size());
-    EXPECT_EQ(std::string::npos,
-              d.data_received().find("StrictSameSiteCookie=1"));
-    EXPECT_EQ(std::string::npos, d.data_received().find("LaxSameSiteCookie=1"));
+    EXPECT_EQ(
+        DoesCookieSameSiteConsiderRedirectChain(),
+        std::string::npos == d.data_received().find("StrictSameSiteCookie=1"));
+    EXPECT_EQ(
+        DoesCookieSameSiteConsiderRedirectChain(),
+        std::string::npos == d.data_received().find("LaxSameSiteCookie=1"));
   }
 }
 
-TEST_F(URLRequestTest, SettingSameSiteCookies) {
+TEST_P(URLRequestSameSiteCookiesTest, SettingSameSiteCookies) {
   HttpTestServer test_server;
   ASSERT_TRUE(test_server.Start());
 
@@ -2855,7 +2889,7 @@
 
 // Tests special chrome:// scheme that is supposed to always attach SameSite
 // cookies if the requested site is secure.
-TEST_F(URLRequestTest, SameSiteCookiesSpecialScheme) {
+TEST_P(URLRequestSameSiteCookiesTest, SameSiteCookiesSpecialScheme) {
   url::ScopedSchemeRegistryForTests scoped_registry;
   url::AddStandardScheme("chrome", url::SchemeType::SCHEME_WITH_HOST);
 
@@ -2947,7 +2981,7 @@
   }
 }
 
-TEST_F(URLRequestTest, SettingSameSiteCookies_Redirect) {
+TEST_P(URLRequestSameSiteCookiesTest, SettingSameSiteCookies_Redirect) {
   EmbeddedTestServer http_server;
   RegisterDefaultHandlers(&http_server);
   EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS);
@@ -3115,9 +3149,9 @@
     EXPECT_EQ(expected_set_cookie_count, network_delegate.set_cookie_count());
   }
 
-  // Verify that SameSite cookies *cannot* be set for a cross-site redirected
-  // subresource request, even if the site-for-cookies and initiator are
-  // same-site, ...
+  // Verify that (depending on whether redirect chains are considered) SameSite
+  // cookies can/cannot be set for a cross-site redirected subresource request,
+  // even if the site-for-cookies and initiator are same-site, ...
   {
     TestDelegate d;
     GURL set_cookie_url = https_server.GetURL(
@@ -3132,7 +3166,7 @@
     req->set_site_for_cookies(kSiteForCookies);
     req->set_initiator(kOrigin);
 
-    expected_cookies += 0;
+    expected_cookies += DoesCookieSameSiteConsiderRedirectChain() ? 0 : 2;
     expected_set_cookie_count += 2;
 
     req->Start();
@@ -3158,7 +3192,7 @@
     req->set_site_for_cookies(kSiteForCookies);
     req->set_initiator(kOrigin);
 
-    expected_cookies += 0;
+    expected_cookies += DoesCookieSameSiteConsiderRedirectChain() ? 0 : 2;
     expected_set_cookie_count += 2;
 
     req->Start();
@@ -3170,7 +3204,7 @@
 
   // Verify that SameSite cookies may or may not be set for a cross-scheme
   // (same-registrable-domain) redirected subresource request, depending on the
-  // status of Schemeful Same-Site.
+  // status of Schemeful Same-Site and whether redirect chains are considered.
   {
     base::test::ScopedFeatureList feature_list;
     feature_list.InitAndDisableFeature(features::kSchemefulSameSite);
@@ -3184,8 +3218,10 @@
     req->set_isolation_info(IsolationInfo::Create(
         IsolationInfo::RequestType::kOther, kHttpOrigin, kHttpOrigin,
         kHttpSiteForCookies, {} /* party_context */));
+    req->set_first_party_url_policy(
+        RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT);
     req->set_site_for_cookies(kHttpSiteForCookies);
-    req->set_initiator(kHttpOrigin);
+    req->set_initiator(kOrigin);
 
     expected_cookies += 2;
     expected_set_cookie_count += 2;
@@ -3209,10 +3245,12 @@
     req->set_isolation_info(IsolationInfo::Create(
         IsolationInfo::RequestType::kOther, kHttpOrigin, kHttpOrigin,
         kHttpSiteForCookies, {} /* party_context */));
+    req->set_first_party_url_policy(
+        RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT);
     req->set_site_for_cookies(kHttpSiteForCookies);
-    req->set_initiator(kHttpOrigin);
+    req->set_initiator(kOrigin);
 
-    expected_cookies += 0;
+    expected_cookies += DoesCookieSameSiteConsiderRedirectChain() ? 0 : 2;
     expected_set_cookie_count += 2;
 
     req->Start();
@@ -3223,6 +3261,10 @@
   }
 }
 
+INSTANTIATE_TEST_SUITE_P(/* no label */,
+                         URLRequestSameSiteCookiesTest,
+                         ::testing::Bool());
+
 // Tests that __Secure- cookies can't be set on non-secure origins.
 TEST_F(URLRequestTest, SecureCookiePrefixOnNonsecureOrigin) {
   EmbeddedTestServer http_server;
diff --git a/remoting/client/software_video_renderer.cc b/remoting/client/software_video_renderer.cc
index 77391b89..1b6a25aa 100644
--- a/remoting/client/software_video_renderer.cc
+++ b/remoting/client/software_video_renderer.cc
@@ -160,7 +160,7 @@
                      std::move(frame)),
       base::BindOnce(&SoftwareVideoRenderer::RenderFrame,
                      weak_factory_.GetWeakPtr(), std::move(frame_stats),
-                     base::AdaptCallbackForRepeating(done_runner.Release())));
+                     done_runner.Release()));
 }
 
 void SoftwareVideoRenderer::RenderFrame(
diff --git a/remoting/protocol/webrtc_transport.cc b/remoting/protocol/webrtc_transport.cc
index 03b04522..a92906a 100644
--- a/remoting/protocol/webrtc_transport.cc
+++ b/remoting/protocol/webrtc_transport.cc
@@ -561,7 +561,12 @@
     const std::string& name) {
   webrtc::DataChannelInit config;
   config.reliable = true;
-  auto data_channel = peer_connection()->CreateDataChannel(name, &config);
+  auto result = peer_connection()->CreateDataChannelOrError(name, &config);
+  if (!result.ok()) {
+    LOG(ERROR) << "CreateDataChannel() failed: " << result.error().message();
+    return nullptr;
+  }
+  auto data_channel = result.MoveValue();
   if (name == kControlChannelName) {
     DCHECK(!control_data_channel_);
     control_data_channel_ = data_channel;
diff --git a/services/media_session/audio_focus_manager.cc b/services/media_session/audio_focus_manager.cc
index 7fe7f43..efd7d274 100644
--- a/services/media_session/audio_focus_manager.cc
+++ b/services/media_session/audio_focus_manager.cc
@@ -84,6 +84,10 @@
     observer_->OnFocusLost(std::move(session));
   }
 
+  void OnRequestIdReleased(const base::UnguessableToken& request_id) {
+    observer_->OnRequestIdReleased(request_id);
+  }
+
  private:
   const base::UnguessableToken identity_;
   mojo::Remote<mojom::AudioFocusObserver> observer_;
@@ -264,6 +268,18 @@
   std::move(callback).Run(std::move(requests));
 }
 
+void AudioFocusManager::RequestIdReleased(
+    const base::UnguessableToken& request_id) {
+  for (const auto& observer : observers_)
+    observer->OnRequestIdReleased(request_id);
+
+  const base::UnguessableToken& source_id = GetBindingIdentity();
+  for (auto& holder : source_observers_) {
+    if (holder->identity() == source_id)
+      holder->OnRequestIdReleased(request_id);
+  }
+}
+
 void AudioFocusManager::CreateActiveMediaController(
     mojo::PendingReceiver<mojom::MediaController> receiver) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
diff --git a/services/media_session/audio_focus_manager.h b/services/media_session/audio_focus_manager.h
index 2ff9f053c..bba3381c 100644
--- a/services/media_session/audio_focus_manager.h
+++ b/services/media_session/audio_focus_manager.h
@@ -74,6 +74,7 @@
       mojo::PendingRemote<mojom::AudioFocusObserver> observer) override;
   void GetSourceFocusRequests(const base::UnguessableToken& source_id,
                               GetFocusRequestsCallback callback) override;
+  void RequestIdReleased(const base::UnguessableToken& request_id) override;
 
   // mojom::AudioFocusManagerDebug.
   void GetDebugInfoForRequest(const RequestId& request_id,
diff --git a/services/media_session/public/cpp/test/audio_focus_test_util.h b/services/media_session/public/cpp/test/audio_focus_test_util.h
index 62d1c95..f5392a5 100644
--- a/services/media_session/public/cpp/test/audio_focus_test_util.h
+++ b/services/media_session/public/cpp/test/audio_focus_test_util.h
@@ -27,6 +27,7 @@
       media_session::mojom::AudioFocusRequestStatePtr session) override;
   void OnFocusLost(
       media_session::mojom::AudioFocusRequestStatePtr session) override;
+  void OnRequestIdReleased(const base::UnguessableToken& request_id) override {}
 
   void WaitForGainedEvent();
   void WaitForLostEvent();
diff --git a/services/media_session/public/mojom/audio_focus.mojom b/services/media_session/public/mojom/audio_focus.mojom
index b10038c..ec8bb45 100644
--- a/services/media_session/public/mojom/audio_focus.mojom
+++ b/services/media_session/public/mojom/audio_focus.mojom
@@ -7,7 +7,7 @@
 import "mojo/public/mojom/base/unguessable_token.mojom";
 import "services/media_session/public/mojom/media_session.mojom";
 
-// Next MinVersion: 8
+// Next MinVersion: 9
 
 // These are the different modes the AudioFocusManager can enforce audio focus.
 [Stable, Extensible]
@@ -73,6 +73,12 @@
 
   // The given |session| lost audio focus.
   OnFocusLost@1(AudioFocusRequestState state);
+
+  // The given |request_id| will no longer be used. This is the |request_id|
+  // attribute of the AudioFocusRequestState struct given in |OnFocusGained()|
+  // and |OnFocusLost()|.
+  [MinVersion=8] OnRequestIdReleased@2(
+      mojo_base.mojom.UnguessableToken request_id);
 };
 
 // Controls audio focus for an associated request.
@@ -154,6 +160,12 @@
   [MinVersion=7] GetSourceFocusRequests@8(
       mojo_base.mojom.UnguessableToken source_id)
           => (array<AudioFocusRequestState> requests);
+
+  // Informs the AudioFocusManager that the given request ID will no longer be
+  // used. This is the |request_id| provided by the client in
+  // |RequestGroupedAudioFocus()|.
+  [MinVersion=8] RequestIdReleased@9(
+      mojo_base.mojom.UnguessableToken request_id);
 };
 
 // Provides debug information about audio focus requests.
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index fa95b174..2928935f 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -883,6 +883,18 @@
   std::move(callback).Run();
 }
 
+void NetworkContext::SetDocumentReportingEndpoints(
+    const url::Origin& origin,
+    const net::NetworkIsolationKey& network_isolation_key,
+    const base::flat_map<std::string, std::string>& endpoints) {
+  net::ReportingService* reporting_service =
+      url_request_context()->reporting_service();
+  if (reporting_service) {
+    reporting_service->SetDocumentReportingEndpoints(
+        origin, network_isolation_key, endpoints);
+  }
+}
+
 void NetworkContext::QueueReport(
     const std::string& type,
     const std::string& group,
@@ -970,6 +982,13 @@
   NOTREACHED();
 }
 
+void NetworkContext::SetDocumentReportingEndpoints(
+    const url::Origin& origin,
+    const net::NetworkIsolationKey& network_isolation_key,
+    const base::flat_map<std::string, std::string>& endpoints) {
+  NOTREACHED();
+}
+
 void NetworkContext::QueueReport(
     const std::string& type,
     const std::string& group,
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 89451c6..a0fbfe1 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -389,6 +389,10 @@
       override;
   void CreateMdnsResponder(
       mojo::PendingReceiver<mojom::MdnsResponder> responder_receiver) override;
+  void SetDocumentReportingEndpoints(
+      const url::Origin& origin,
+      const net::NetworkIsolationKey& network_isolation_key,
+      const base::flat_map<std::string, std::string>& endpoints) override;
   void QueueReport(const std::string& type,
                    const std::string& group,
                    const GURL& url,
diff --git a/services/network/public/cpp/parsed_headers.cc b/services/network/public/cpp/parsed_headers.cc
index 5db6782..5cf224a 100644
--- a/services/network/public/cpp/parsed_headers.cc
+++ b/services/network/public/cpp/parsed_headers.cc
@@ -4,7 +4,10 @@
 
 #include "services/network/public/cpp/parsed_headers.h"
 
+#include "build/build_config.h"
+#include "net/base/features.h"
 #include "net/http/http_response_headers.h"
+#include "net/reporting/reporting_header_parser.h"
 #include "services/network/public/cpp/bfcache_opt_in_parser.h"
 #include "services/network/public/cpp/client_hints.h"
 #include "services/network/public/cpp/content_security_policy/content_security_policy.h"
@@ -73,6 +76,13 @@
         ParseBFCacheOptInUnload(bfcache_opt_in);
   }
 
+#if BUILDFLAG(ENABLE_REPORTING)
+  std::string reporting_endpoints;
+  if (headers->GetNormalizedHeader("Reporting-Endpoints", &reporting_endpoints))
+    parsed_headers->reporting_endpoints =
+        net::ParseReportingEndpoints(reporting_endpoints);
+#endif
+
   return parsed_headers;
 }
 
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 8b74f2f..1769373 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -885,6 +885,19 @@
   // Returns a JSON value containing data for displaying on a debugging page.
   GetDomainReliabilityJSON() => (mojo_base.mojom.Value data);
 
+  // Configures per-resource reporting endpoints set with the
+  // Reporting-Endpoints header.
+  // |network_isolation_key| is used when creating the endpoints in the
+  // ReportingCache, and will need to match the key sent when reports are queued
+  // in order for these reporting endpoints to be considered for delivery.
+  // |endpoints| is a mapping of endpoint name to URL (URLs here are represented
+  // as strings, and will be rejected if they fail to parse or are not secure).
+  //
+  // Spec: https://w3c.github.io/reporting/#header
+  SetDocumentReportingEndpoints(
+      url.mojom.Origin origin, NetworkIsolationKey network_isolation_key,
+      map<string,string> endpoints);
+
   // Queues a report via the Reporting API. |type| describes the type of report
   // (as well as what data will contained in |body|). |group| specifies the
   // endpoint group that the report will be delivered to. |url| indicates the
diff --git a/services/network/public/mojom/parsed_headers.mojom b/services/network/public/mojom/parsed_headers.mojom
index 5880187..ba5a8dc 100644
--- a/services/network/public/mojom/parsed_headers.mojom
+++ b/services/network/public/mojom/parsed_headers.mojom
@@ -77,4 +77,9 @@
   // Parsed BFCache-Opt-In value. Currently "unload" is the only possible value,
   // so record if that's present.
   bool bfcache_opt_in_unload = false;
+
+  // Parsed mapping of endpoint names to URLs from the Reporting-Endpoints
+  // header. Will be null if that header was not present in the response.
+  // URLs are represented here as strings, and have not been validated.
+  map<string,string>? reporting_endpoints;
 };
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index a63f7675..a0b7e16 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -104,6 +104,10 @@
                            ClearTrustTokenDataCallback callback) override {}
   void GetDomainReliabilityJSON(
       GetDomainReliabilityJSONCallback callback) override {}
+  void SetDocumentReportingEndpoints(
+      const url::Origin& origin,
+      const net::NetworkIsolationKey& network_isolation_key,
+      const base::flat_map<std::string, std::string>& endpoints) override {}
   void QueueReport(const std::string& type,
                    const std::string& group,
                    const GURL& url,
diff --git a/services/network/udp_socket_unittest.cc b/services/network/udp_socket_unittest.cc
index 54640ee..51a8a7a2 100644
--- a/services/network/udp_socket_unittest.cc
+++ b/services/network/udp_socket_unittest.cc
@@ -667,10 +667,11 @@
   EXPECT_EQ(std::vector<uint8_t>(), result.data.value());
 }
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
 // Some Android devices do not support multicast socket.
 // The ones supporting multicast need WifiManager.MulticastLock to enable it.
 // https://developer.android.com/reference/android/net/wifi/WifiManager.MulticastLock.html
+// TODO(crbug.com/1215667): Fails on iOS running on Mac 11 machines.
 #define MAYBE_JoinMulticastGroup DISABLED_JoinMulticastGroup
 #else
 #define MAYBE_JoinMulticastGroup JoinMulticastGroup
diff --git a/sql/README.md b/sql/README.md
index e281fa9..e4443ede 100644
--- a/sql/README.md
+++ b/sql/README.md
@@ -111,6 +111,17 @@
 that is a primary key and has an `INTEGER` type is considered an alias for
 rowid.
 
+Each SQLite index
+[is stored in a B-tree](https://sqlite.org/fileformat2.html#representation_of_sql_indices).
+Each index entry is stored as a B-tree node whose key is made up of the record's
+index key column values, followed by the record's primary key column values.
+
+`WITHOUT ROWID` table indexes can include primary key columns without additional
+storage costs. This is because indexes for `WITHOUT ROWID` tables enjoy
+[a space optimization](https://sqlite.org/fileformat2.html#representation_of_sql_indices)
+where columns in both the primary key and the index key are not stored twice in
+B-tree nodes.
+
 
 ### Query processing
 
@@ -207,8 +218,8 @@
      // clang-format on
 ```
 
-* SQLite keywords should use ALL CAPS. This makes SQL query literals easier to
-  distinguish and search for.
+* [SQLite keywords](https://sqlite.org/lang_keywords.html) should use ALL CAPS.
+  This makes SQL query literals easier to distinguish and search for.
 
 * Identifiers, such as table and row names, should use snake_case.
 
@@ -263,6 +274,11 @@
 
 ### Schema style
 
+Identifiers (table / index / column names and aliases) must not be
+[current SQLite keywords](https://sqlite.org/lang_keywords.html). Identifiers
+may not start with the `sqlite_` prefix, to avoid conflicting with the name of a
+[SQLite internal schema object](https://www.sqlite.org/fileformat2.html#storage_of_the_sql_database_schema).
+
 Column types should only be one of the the SQLite storage types (`INTEGER`,
 `REAL`, `TEXT`, `BLOB`), so readers can avoid reasoning about SQLite's type
 affinity.
diff --git a/storage/browser/file_system/file_system_context.cc b/storage/browser/file_system/file_system_context.cc
index d78eea5..c28e98c 100644
--- a/storage/browser/file_system/file_system_context.cc
+++ b/storage/browser/file_system/file_system_context.cc
@@ -575,10 +575,10 @@
   // top of an external filesystem). Hence cracking needs to be iterated.
   for (;;) {
     FileSystemURL cracked = current;
-    for (size_t i = 0; i < url_crackers_.size(); ++i) {
-      if (!url_crackers_[i]->HandlesFileSystemMountType(current.type()))
+    for (MountPoints* url_cracker : url_crackers_) {
+      if (!url_cracker->HandlesFileSystemMountType(current.type()))
         continue;
-      cracked = url_crackers_[i]->CrackFileSystemURL(current);
+      cracked = url_cracker->CrackFileSystemURL(current);
       if (cracked.is_valid())
         break;
     }
diff --git a/storage/browser/file_system/file_system_quota_client.cc b/storage/browser/file_system/file_system_quota_client.cc
index 85f4505c3..e74b046 100644
--- a/storage/browser/file_system/file_system_quota_client.cc
+++ b/storage/browser/file_system/file_system_quota_client.cc
@@ -4,34 +4,30 @@
 
 #include "storage/browser/file_system/file_system_quota_client.h"
 
-#include <algorithm>
-#include <memory>
+#include <string>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/check.h"
-#include "base/files/file_util.h"
+#include "base/files/file.h"
 #include "base/location.h"
+#include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
-#include "base/single_thread_task_runner.h"
-#include "base/task_runner_util.h"
+#include "storage/browser/file_system/file_system_backend.h"
 #include "storage/browser/file_system/file_system_context.h"
-#include "storage/browser/file_system/file_system_usage_cache.h"
-#include "storage/browser/file_system/sandbox_file_system_backend.h"
+#include "storage/common/file_system/file_system_types.h"
 #include "storage/common/file_system/file_system_util.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
-#include "url/gurl.h"
 #include "url/origin.h"
 
-using blink::mojom::StorageType;
-
 namespace storage {
 
 namespace {
 
 std::vector<url::Origin> GetOriginsForTypeOnFileTaskRunner(
     FileSystemContext* context,
-    StorageType storage_type) {
+    blink::mojom::StorageType storage_type) {
   FileSystemType type =
       FileSystemQuotaClient::QuotaStorageTypeToFileSystemType(storage_type);
   DCHECK(type != kFileSystemTypeUnknown);
@@ -44,7 +40,7 @@
 
 std::vector<url::Origin> GetOriginsForHostOnFileTaskRunner(
     FileSystemContext* context,
-    StorageType storage_type,
+    blink::mojom::StorageType storage_type,
     const std::string& host) {
   FileSystemType type =
       FileSystemQuotaClient::QuotaStorageTypeToFileSystemType(storage_type);
@@ -84,13 +80,19 @@
 
 FileSystemQuotaClient::FileSystemQuotaClient(
     FileSystemContext* file_system_context)
-    : file_system_context_(file_system_context) {}
+    : file_system_context_(file_system_context) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
 
-FileSystemQuotaClient::~FileSystemQuotaClient() = default;
+FileSystemQuotaClient::~FileSystemQuotaClient() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
 
-void FileSystemQuotaClient::GetOriginUsage(const url::Origin& origin,
-                                           StorageType storage_type,
-                                           GetOriginUsageCallback callback) {
+void FileSystemQuotaClient::GetOriginUsage(
+    const url::Origin& origin,
+    blink::mojom::StorageType storage_type,
+    GetOriginUsageCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
 
   FileSystemType type =
@@ -103,8 +105,8 @@
     return;
   }
 
-  base::PostTaskAndReplyWithResult(
-      file_task_runner(), FROM_HERE,
+  file_task_runner()->PostTaskAndReplyWithResult(
+      FROM_HERE,
       // It is safe to pass Unretained(quota_util) since context owns it.
       base::BindOnce(&FileSystemQuotaUtil::GetOriginUsageOnFileTaskRunner,
                      base::Unretained(quota_util),
@@ -113,8 +115,9 @@
 }
 
 void FileSystemQuotaClient::GetOriginsForType(
-    StorageType storage_type,
+    blink::mojom::StorageType storage_type,
     GetOriginsForTypeCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
 
   file_task_runner()->PostTaskAndReplyWithResult(
@@ -125,9 +128,10 @@
 }
 
 void FileSystemQuotaClient::GetOriginsForHost(
-    StorageType storage_type,
+    blink::mojom::StorageType storage_type,
     const std::string& host,
     GetOriginsForHostCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!callback.is_null());
 
   file_task_runner()->PostTaskAndReplyWithResult(
@@ -140,21 +144,27 @@
 
 void FileSystemQuotaClient::DeleteOriginData(
     const url::Origin& origin,
-    StorageType type,
+    blink::mojom::StorageType type,
     DeleteOriginDataCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!callback.is_null());
+
   FileSystemType fs_type = QuotaStorageTypeToFileSystemType(type);
   DCHECK(fs_type != kFileSystemTypeUnknown);
 
-  base::PostTaskAndReplyWithResult(
-      file_task_runner(), FROM_HERE,
+  file_task_runner()->PostTaskAndReplyWithResult(
+      FROM_HERE,
       base::BindOnce(&DeleteOriginOnFileTaskRunner,
                      base::RetainedRef(file_system_context_), origin, fs_type),
       std::move(callback));
 }
 
 void FileSystemQuotaClient::PerformStorageCleanup(
-    StorageType type,
+    blink::mojom::StorageType type,
     PerformStorageCleanupCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!callback.is_null());
+
   FileSystemType fs_type = QuotaStorageTypeToFileSystemType(type);
   DCHECK(fs_type != kFileSystemTypeUnknown);
   file_task_runner()->PostTaskAndReply(
@@ -182,6 +192,7 @@
 }
 
 base::SequencedTaskRunner* FileSystemQuotaClient::file_task_runner() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return file_system_context_->default_file_task_runner();
 }
 
diff --git a/storage/browser/file_system/file_system_quota_client.h b/storage/browser/file_system/file_system_quota_client.h
index bddbe642..b6f22d6d 100644
--- a/storage/browser/file_system/file_system_quota_client.h
+++ b/storage/browser/file_system/file_system_quota_client.h
@@ -5,25 +5,24 @@
 #ifndef STORAGE_BROWSER_FILE_SYSTEM_FILE_SYSTEM_QUOTA_CLIENT_H_
 #define STORAGE_BROWSER_FILE_SYSTEM_FILE_SYSTEM_QUOTA_CLIENT_H_
 
-#include <set>
 #include <string>
-#include <utility>
 
-#include "base/compiler_specific.h"
 #include "base/component_export.h"
-#include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "storage/browser/file_system/file_system_quota_util.h"
+#include "base/sequence_checker.h"
+#include "base/thread_annotations.h"
 #include "storage/browser/quota/quota_client.h"
-#include "storage/browser/quota/quota_client_type.h"
 #include "storage/common/file_system/file_system_types.h"
 #include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
-#include "url/origin.h"
 
 namespace base {
 class SequencedTaskRunner;
 }
 
+namespace url {
+class Origin;
+}
+
 namespace storage {
 
 class FileSystemContext;
@@ -35,6 +34,9 @@
  public:
   explicit FileSystemQuotaClient(FileSystemContext* file_system_context);
 
+  FileSystemQuotaClient(const FileSystemQuotaClient&) = delete;
+  FileSystemQuotaClient& operator=(const FileSystemQuotaClient&) = delete;
+
   // QuotaClient methods.
   void OnQuotaManagerDestroyed() override {}
   void GetOriginUsage(const url::Origin& origin,
@@ -64,9 +66,10 @@
 
   base::SequencedTaskRunner* file_task_runner() const;
 
-  scoped_refptr<FileSystemContext> file_system_context_;
+  SEQUENCE_CHECKER(sequence_checker_);
 
-  DISALLOW_COPY_AND_ASSIGN(FileSystemQuotaClient);
+  const scoped_refptr<FileSystemContext> file_system_context_
+      GUARDED_BY_CONTEXT(sequence_checker_);
 };
 
 }  // namespace storage
diff --git a/styleguide/java/java.md b/styleguide/java/java.md
index 67b6b57..2c6ca80 100644
--- a/styleguide/java/java.md
+++ b/styleguide/java/java.md
@@ -132,6 +132,36 @@
   * Always prefer `androidx.annotation.Nullable`.
   * It uses `@Retention(SOURCE)` rather than `@Retention(RUNTIME)`.
 
+### IntDef Instead of Enum
+
+Java enums generate far more bytecode than integer constants. When integers are
+sufficient, prefer using an [@IntDef annotation], which will have usage checked
+by [Android lint].
+
+Values can be declared outside or inside the `@interface`. We recommend the
+latter, with constants nested within it as follows:
+
+```java
+@IntDef({ContactsPickerAction.CANCEL, ContactsPickerAction.CONTACTS_SELECTED,
+        ContactsPickerAction.SELECT_ALL, ContactsPickerAction.UNDO_SELECT_ALL})
+@Retention(RetentionPolicy.SOURCE)
+public @interface ContactsPickerAction {
+    int CANCEL = 0;
+    int CONTACTS_SELECTED = 1;
+    int SELECT_ALL = 2;
+    int UNDO_SELECT_ALL = 3;
+    int NUM_ENTRIES = 4;
+}
+// ...
+void onContactsPickerUserAction(@ContactsPickerAction int action, ...);
+```
+
+Values of `Integer` type are also supported, which allows using a sentinel
+`null` if needed.
+
+[@IntDef annotation]: https://developer.android.com/studio/write/annotations#enum-annotations
+[Android lint]: https://chromium.googlesource.com/chromium/src/+/HEAD/build/android/docs/lint.md
+
 ## Tools
 
 ### Automatically formatting edited files
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 8c6b2cd..4f6cb436 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -4911,6 +4911,23 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "lacros_chrome_browsertests_run_in_series",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
         "test": "lacros_chrome_unittests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_unittests/"
       },
@@ -5785,6 +5802,23 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "lacros_chrome_browsertests_run_in_series",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
         "test": "lacros_chrome_unittests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_unittests/"
       },
diff --git a/testing/buildbot/chromium.clang.json b/testing/buildbot/chromium.clang.json
index 8e0fca4d..f1f7c970 100644
--- a/testing/buildbot/chromium.clang.json
+++ b/testing/buildbot/chromium.clang.json
@@ -43503,7 +43503,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -43553,7 +43553,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -43603,7 +43603,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -43653,7 +43653,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -43703,7 +43703,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -43753,7 +43753,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -43803,7 +43803,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -43853,7 +43853,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -43903,7 +43903,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -43953,7 +43953,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -44003,7 +44003,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -44053,7 +44053,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -44103,7 +44103,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -44153,7 +44153,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -44203,7 +44203,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -44253,7 +44253,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -44303,7 +44303,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -44353,7 +44353,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -44424,7 +44424,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44469,7 +44469,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44514,7 +44514,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44559,7 +44559,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44604,7 +44604,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44649,7 +44649,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44694,7 +44694,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44739,7 +44739,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44784,7 +44784,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44829,7 +44829,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44874,7 +44874,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44919,7 +44919,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -44964,7 +44964,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -45009,7 +45009,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -45054,7 +45054,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -45099,7 +45099,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -45144,7 +45144,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
@@ -45189,7 +45189,7 @@
             {
               "cpu": "x86-64",
               "device": "iPhone8,1",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "expiration": 21600,
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index a5ee086..4662287 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1765,6 +1765,23 @@
       }
     ]
   },
+  "Win x64 Builder (reclient)(cross)": {
+    "additional_compile_targets": [
+      "pdf_fuzzers"
+    ],
+    "scripts": [
+      {
+        "name": "check_network_annotations",
+        "script": "check_network_annotations.py",
+        "swarming": {}
+      },
+      {
+        "name": "webkit_lint",
+        "script": "blink_lint_expectations.py",
+        "swarming": {}
+      }
+    ]
+  },
   "android-backuprefptr-arm-fyi-rel": {
     "gtest_tests": [
       {
@@ -18353,7 +18370,8 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 6
         },
         "test": "web_engine_integration_tests",
         "test_id_prefix": "ninja://fuchsia/engine:web_engine_integration_tests/"
@@ -22869,7 +22887,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22919,7 +22937,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22969,7 +22987,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23019,7 +23037,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23069,7 +23087,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23119,7 +23137,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23169,7 +23187,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23219,7 +23237,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23269,7 +23287,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23319,7 +23337,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23369,7 +23387,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23419,7 +23437,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23469,7 +23487,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23519,7 +23537,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23569,7 +23587,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23619,7 +23637,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23669,7 +23687,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23719,7 +23737,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23769,7 +23787,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23819,7 +23837,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23869,7 +23887,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23919,7 +23937,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -23969,7 +23987,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24019,7 +24037,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24069,7 +24087,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24119,7 +24137,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24169,7 +24187,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24219,7 +24237,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24269,7 +24287,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24319,7 +24337,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24369,7 +24387,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24419,7 +24437,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24469,7 +24487,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24519,7 +24537,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24569,7 +24587,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24619,7 +24637,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24669,7 +24687,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24719,7 +24737,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24769,7 +24787,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24819,7 +24837,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24869,7 +24887,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24919,7 +24937,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -24969,7 +24987,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25019,7 +25037,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25069,7 +25087,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25119,7 +25137,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25176,7 +25194,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25229,7 +25247,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25282,7 +25300,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25335,7 +25353,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25388,7 +25406,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25441,7 +25459,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25494,7 +25512,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25547,7 +25565,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25600,7 +25618,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25653,7 +25671,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25706,7 +25724,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25759,7 +25777,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25812,7 +25830,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25865,7 +25883,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25918,7 +25936,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -25971,7 +25989,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26024,7 +26042,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26077,7 +26095,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26130,7 +26148,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26183,7 +26201,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26236,7 +26254,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26289,7 +26307,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26342,7 +26360,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26395,7 +26413,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26448,7 +26466,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26501,7 +26519,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26554,7 +26572,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26607,7 +26625,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26660,7 +26678,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26713,7 +26731,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26766,7 +26784,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26819,7 +26837,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26872,7 +26890,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26925,7 +26943,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -26979,7 +26997,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27033,7 +27051,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27087,7 +27105,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27141,7 +27159,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27195,7 +27213,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27249,7 +27267,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27303,7 +27321,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27358,7 +27376,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27413,7 +27431,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27468,7 +27486,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27523,7 +27541,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27578,7 +27596,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27633,7 +27651,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27688,7 +27706,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27743,7 +27761,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27798,7 +27816,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27853,7 +27871,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27908,7 +27926,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -27963,7 +27981,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28017,7 +28035,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28071,7 +28089,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28125,7 +28143,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28179,7 +28197,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28233,7 +28251,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28287,7 +28305,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28341,7 +28359,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28395,7 +28413,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28449,7 +28467,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28503,7 +28521,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28557,7 +28575,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28611,7 +28629,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28666,7 +28684,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28721,7 +28739,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28776,7 +28794,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28831,7 +28849,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28886,7 +28904,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28940,7 +28958,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -28993,7 +29011,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29046,7 +29064,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29099,7 +29117,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29152,7 +29170,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29205,7 +29223,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29258,7 +29276,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29311,7 +29329,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29365,7 +29383,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29419,7 +29437,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29473,7 +29491,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29527,7 +29545,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29581,7 +29599,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29635,7 +29653,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29688,7 +29706,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29741,7 +29759,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29794,7 +29812,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29847,7 +29865,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29900,7 +29918,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -29953,7 +29971,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30007,7 +30025,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30061,7 +30079,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30115,7 +30133,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30169,7 +30187,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30223,7 +30241,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30277,7 +30295,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30330,7 +30348,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30383,7 +30401,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30436,7 +30454,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30489,7 +30507,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30542,7 +30560,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30595,7 +30613,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30648,7 +30666,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30701,7 +30719,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30754,7 +30772,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30807,7 +30825,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30861,7 +30879,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30915,7 +30933,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -30969,7 +30987,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31023,7 +31041,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31077,7 +31095,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31131,7 +31149,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31184,7 +31202,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31237,7 +31255,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31290,7 +31308,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31343,7 +31361,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31396,7 +31414,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31449,7 +31467,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31502,7 +31520,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31555,7 +31573,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31608,7 +31626,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31661,7 +31679,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31714,7 +31732,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31767,7 +31785,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31820,7 +31838,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31873,7 +31891,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31926,7 +31944,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -31979,7 +31997,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32032,7 +32050,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32085,7 +32103,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32138,7 +32156,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32191,7 +32209,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32244,7 +32262,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32297,7 +32315,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32350,7 +32368,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32403,7 +32421,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32456,7 +32474,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32509,7 +32527,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32562,7 +32580,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32615,7 +32633,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32668,7 +32686,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32721,7 +32739,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32774,7 +32792,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32827,7 +32845,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32880,7 +32898,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32933,7 +32951,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -32986,7 +33004,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33039,7 +33057,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33092,7 +33110,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33145,7 +33163,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33198,7 +33216,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33251,7 +33269,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33304,7 +33322,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33357,7 +33375,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33410,7 +33428,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33463,7 +33481,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33516,7 +33534,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33569,7 +33587,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33622,7 +33640,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33675,7 +33693,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33729,7 +33747,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33779,7 +33797,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33833,7 +33851,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33883,7 +33901,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33933,7 +33951,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -33983,7 +34001,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34033,7 +34051,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34083,7 +34101,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34133,7 +34151,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34183,7 +34201,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34234,7 +34252,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34285,7 +34303,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34337,7 +34355,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34389,7 +34407,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34440,7 +34458,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34491,7 +34509,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34542,7 +34560,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34593,7 +34611,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34643,7 +34661,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34693,7 +34711,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34743,7 +34761,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34794,7 +34812,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34844,7 +34862,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34894,7 +34912,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34945,7 +34963,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -34995,7 +35013,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35045,7 +35063,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35095,7 +35113,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35145,7 +35163,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35195,7 +35213,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35245,7 +35263,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35295,7 +35313,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35345,7 +35363,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35395,7 +35413,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35451,7 +35469,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35503,7 +35521,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35555,7 +35573,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35607,7 +35625,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35659,7 +35677,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35711,7 +35729,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35763,7 +35781,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35815,7 +35833,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35867,7 +35885,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35919,7 +35937,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -35971,7 +35989,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36023,7 +36041,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36075,7 +36093,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36127,7 +36145,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36179,7 +36197,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36231,7 +36249,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36284,7 +36302,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36337,7 +36355,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36390,7 +36408,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36444,7 +36462,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36498,7 +36516,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36552,7 +36570,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36606,7 +36624,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36659,7 +36677,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36712,7 +36730,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36765,7 +36783,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36818,7 +36836,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36872,7 +36890,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36925,7 +36943,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -36977,7 +36995,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37030,7 +37048,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37083,7 +37101,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37135,7 +37153,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37187,7 +37205,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37239,7 +37257,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37291,7 +37309,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37343,7 +37361,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37395,7 +37413,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37448,7 +37466,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37501,7 +37519,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37553,7 +37571,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37605,7 +37623,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37657,7 +37675,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37709,7 +37727,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37762,7 +37780,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37815,7 +37833,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37867,7 +37885,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37919,7 +37937,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -37971,7 +37989,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38023,7 +38041,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38075,7 +38093,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38127,7 +38145,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38179,7 +38197,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38231,7 +38249,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38283,7 +38301,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38335,7 +38353,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38387,7 +38405,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38439,7 +38457,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38491,7 +38509,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38543,7 +38561,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38595,7 +38613,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38647,7 +38665,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38699,7 +38717,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -38751,7 +38769,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -64410,6 +64428,19 @@
           "can_use_on_swarming_builders": true,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "lacros_chrome_browsertests_run_in_series",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/"
+      },
+      {
+        "isolate_profile_data": true,
+        "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": "lacros_chrome_unittests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_unittests/"
       },
@@ -65746,6 +65777,24 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "lacros_chrome_browsertests_run_in_series",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/"
+      },
+      {
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-18.04",
+              "ssd": "0"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
         "test": "lacros_chrome_unittests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_unittests/"
       },
@@ -66444,6 +66493,29 @@
         },
         "test": "lacros_chrome_browsertests",
         "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/"
+      },
+      {
+        "args": [
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_M92/chrome"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 92.0.4499.0",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_M92",
+              "revision": "version:92.0.4499.0"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests_run_in_series",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests_run_in_series/"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index da91432..cfe6974 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -12726,7 +12726,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -12777,7 +12777,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -12828,7 +12828,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -12879,7 +12879,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -12930,7 +12930,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -12981,7 +12981,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13032,7 +13032,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13083,7 +13083,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13134,7 +13134,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13185,7 +13185,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13236,7 +13236,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13287,7 +13287,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13338,7 +13338,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13389,7 +13389,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13440,7 +13440,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13491,7 +13491,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13542,7 +13542,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13593,7 +13593,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13644,7 +13644,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13695,7 +13695,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13746,7 +13746,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13797,7 +13797,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13848,7 +13848,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13899,7 +13899,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -13950,7 +13950,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14001,7 +14001,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14052,7 +14052,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14103,7 +14103,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14154,7 +14154,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14205,7 +14205,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14256,7 +14256,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14307,7 +14307,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14358,7 +14358,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14409,7 +14409,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14461,7 +14461,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14514,7 +14514,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14567,7 +14567,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14619,7 +14619,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14671,7 +14671,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14724,7 +14724,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14776,7 +14776,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14827,7 +14827,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14878,7 +14878,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14929,7 +14929,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -14980,7 +14980,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15031,7 +15031,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15082,7 +15082,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15133,7 +15133,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15184,7 +15184,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15235,7 +15235,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15286,7 +15286,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15337,7 +15337,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15388,7 +15388,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15439,7 +15439,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15490,7 +15490,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15541,7 +15541,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15592,7 +15592,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15643,7 +15643,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15694,7 +15694,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15745,7 +15745,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15796,7 +15796,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15847,7 +15847,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15898,7 +15898,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -15949,7 +15949,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16001,7 +16001,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16053,7 +16053,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16104,7 +16104,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16155,7 +16155,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16206,7 +16206,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16257,7 +16257,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16308,7 +16308,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16359,7 +16359,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16410,7 +16410,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16461,7 +16461,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16512,7 +16512,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16563,7 +16563,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16614,7 +16614,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16665,7 +16665,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16716,7 +16716,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16767,7 +16767,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16818,7 +16818,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16869,7 +16869,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16920,7 +16920,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -16971,7 +16971,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17022,7 +17022,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17073,7 +17073,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17124,7 +17124,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17175,7 +17175,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17226,7 +17226,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17277,7 +17277,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17328,7 +17328,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17379,7 +17379,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17430,7 +17430,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17481,7 +17481,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17532,7 +17532,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17583,7 +17583,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17634,7 +17634,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17685,7 +17685,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17736,7 +17736,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17787,7 +17787,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17838,7 +17838,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17889,7 +17889,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17940,7 +17940,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -17991,7 +17991,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18042,7 +18042,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18093,7 +18093,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18144,7 +18144,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18195,7 +18195,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18246,7 +18246,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18297,7 +18297,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18348,7 +18348,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18399,7 +18399,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18450,7 +18450,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18501,7 +18501,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18560,7 +18560,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18612,7 +18612,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18664,7 +18664,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18716,7 +18716,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18768,7 +18768,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18820,7 +18820,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18872,7 +18872,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18925,7 +18925,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -18978,7 +18978,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19031,7 +19031,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19084,7 +19084,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19137,7 +19137,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19190,7 +19190,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19243,7 +19243,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19296,7 +19296,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19349,7 +19349,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19402,7 +19402,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19454,7 +19454,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19506,7 +19506,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19558,7 +19558,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19610,7 +19610,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19662,7 +19662,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19714,7 +19714,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19766,7 +19766,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19818,7 +19818,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19870,7 +19870,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19922,7 +19922,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -19975,7 +19975,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20028,7 +20028,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20081,7 +20081,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20134,7 +20134,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20186,7 +20186,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20238,7 +20238,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20290,7 +20290,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20342,7 +20342,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20394,7 +20394,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20446,7 +20446,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20498,7 +20498,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20550,7 +20550,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20602,7 +20602,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20654,7 +20654,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20706,7 +20706,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20758,7 +20758,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20810,7 +20810,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20862,7 +20862,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20914,7 +20914,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -20971,7 +20971,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21021,7 +21021,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21071,7 +21071,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21121,7 +21121,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21171,7 +21171,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21222,7 +21222,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21273,7 +21273,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21324,7 +21324,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21376,7 +21376,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21428,7 +21428,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21480,7 +21480,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21532,7 +21532,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21583,7 +21583,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21634,7 +21634,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21685,7 +21685,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21736,7 +21736,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21788,7 +21788,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21840,7 +21840,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21891,7 +21891,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21941,7 +21941,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -21991,7 +21991,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22041,7 +22041,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22092,7 +22092,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22143,7 +22143,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22193,7 +22193,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22244,7 +22244,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22295,7 +22295,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22345,7 +22345,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22395,7 +22395,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22445,7 +22445,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
@@ -22495,7 +22495,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Mac-10.15"
+              "os": "Mac-10.15|Mac-10.16|Mac-11"
             }
           ],
           "named_caches": [
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index ec50775..d7706c76 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1128,6 +1128,10 @@
     "label": "//chrome/test:lacros_chrome_browsertests",
     "type": "windowed_test_launcher",
   },
+  "lacros_chrome_browsertests_run_in_series": {
+    "label": "//chrome/test:lacros_chrome_browsertests_run_in_series",
+    "type": "windowed_test_launcher",
+  },
   "lacros_chrome_unittests": {
     "label": "//chrome/test:lacros_chrome_unittests",
     "type": "console_test_launcher",
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index d1eb3a0..70be22c 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -687,6 +687,14 @@
       },
     },
   },
+  'mac_10.15_or_mac_11': {
+    'swarming': {
+      'dimensions': {
+        'cpu': 'x86-64',
+        'os': 'Mac-10.15|Mac-10.16|Mac-11',
+      },
+    },
+  },
   'mac_11_beta_x64': {
     'swarming': {
       'dimensions': {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index ffc017f..7da90af 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -3142,6 +3142,15 @@
       },
     },
   },
+  'web_engine_integration_tests': {
+    'modifications': {
+      'fuchsia-code-coverage': {
+        'swarming': {
+          'shards': 6,
+        },
+      },
+    },
+  },
   'webdriver_tests_suite': {
     'remove_from': [
       'Linux Tests',  # https://crbug.com/929689, https://crbug.com/936557
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 557d8188..db069fb 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4331,6 +4331,7 @@
 
     'linux_lacros_chrome_browsertests_version_skew': {
       'lacros_chrome_browsertests': { },
+      'lacros_chrome_browsertests_run_in_series': { },
     },
 
     'linux_lacros_cq_gtests': {
@@ -4348,6 +4349,7 @@
         },
       },
       'lacros_chrome_browsertests': {},
+      'lacros_chrome_browsertests_run_in_series': {},
       'lacros_chrome_unittests': {},
       'sync_integration_tests': {},
       'ui_base_unittests': {},
@@ -4356,6 +4358,7 @@
 
     'linux_lacros_specific_gtests': {
       'lacros_chrome_browsertests': {},
+      'lacros_chrome_browsertests_run_in_series': {},
       'lacros_chrome_unittests': {},
     },
 
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index f7a88da..4b2d14f7a 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2041,7 +2041,7 @@
         ],
         'mixins': [
           'enable_resultdb',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_12d4e',
@@ -2075,7 +2075,7 @@
           'enable_resultdb',
           'ios_restart_device',
           'limited_capacity_bot',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_12d4e',
@@ -2707,6 +2707,16 @@
           'scripts': 'chromium_win_scripts',
         },
       },
+      'Win x64 Builder (reclient)(cross)': {
+        # Copied from
+        # https://source.chromium.org/chromium/chromium/src/+/7b147a6777cb32d6a12e1716c61a0ed50dc1229a:testing/buildbot/waterfalls.pyl;l=6023-6030
+        'additional_compile_targets': [
+          'pdf_fuzzers'
+        ],
+        'test_suites': {
+          'scripts': 'chromium_win_scripts',
+        },
+      },
       'android-backuprefptr-arm-fyi-rel': {
         'test_suites': {
           'gtest_tests': 'chromium_android_gtests',
@@ -2866,7 +2876,7 @@
       'ios-asan': {
         'mixins': [
           'enable_resultdb',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_12d4e',
@@ -2881,7 +2891,7 @@
           'enable_resultdb',
           'ios_output_disabled_tests',
           'isolate_profile_data',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_12d4e',
@@ -2894,7 +2904,7 @@
       'ios-simulator-cronet': {
         'mixins': [
           'enable_resultdb',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_12d4e',
@@ -2907,7 +2917,7 @@
       'ios-simulator-multi-window': {
         'mixins': [
           'enable_resultdb',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_12d4e',
@@ -2921,7 +2931,7 @@
         'mixins': [
           'enable_resultdb',
           'ios_custom_webkit',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_11e608c',
@@ -5109,7 +5119,7 @@
         'mixins': [
           'enable_resultdb',
           'isolate_profile_data',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_12d4e',
@@ -5126,7 +5136,7 @@
         'mixins': [
           'enable_resultdb',
           'isolate_profile_data',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_12d4e',
@@ -5142,7 +5152,7 @@
         ],
         'mixins': [
           'enable_resultdb',
-          'mac_10.15',
+          'mac_10.15_or_mac_11',
           'mac_toolchain',
           'out_dir_arg',
           'xcode_12d4e',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index cc93da9..12f0e76 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2280,7 +2280,9 @@
                 {
                     "name": "Enabled",
                     "enable_features": [
-                        "CopyLinkToText"
+                        "CopyLinkToText",
+                        "PreemptiveLinkToTextGeneration",
+                        "SharedHighlightingV2"
                     ]
                 }
             ]
@@ -4136,6 +4138,25 @@
             ]
         }
     ],
+    "IOSStartSurface": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "ShrinkLogo",
+                    "params": {
+                        "ReturnToStartSurfaceInactiveDurationInSeconds": "0",
+                        "shrink_logo": "true"
+                    },
+                    "enable_features": [
+                        "StartSurface"
+                    ]
+                }
+            ]
+        }
+    ],
     "IOSUMABackgroundSessions": [
         {
             "platforms": [
@@ -6323,6 +6344,25 @@
             ]
         }
     ],
+    "ProcessHostOnUI": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ProcessHostOnUI"
+                    ]
+                }
+            ]
+        }
+    ],
     "ProtoDBSharedMigration": [
         {
             "platforms": [
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 293b5754..6826455 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -813,11 +813,14 @@
   // MediaLog from any thread, but it must be destroyed on |owner_task_runner|.
   // MediaLog owners should destroy the MediaLog if the ExecutionContext is
   // destroyed, since |inspector_context| may no longer be valid at that point.
+  // |is_on_worker| is used to avoid logging to the chrome://media-internal
+  // page, which can only be logged to from the window main thread.
   // Note: |inspector_context| is only used on |owner_task_runner|, so
   // destroying the MediaLog on |owner_task_runner| should avoid races.
   virtual std::unique_ptr<media::MediaLog> GetMediaLog(
       MediaInspectorContext* inspector_context,
-      scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner) {
+      scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner,
+      bool is_on_worker) {
     return nullptr;
   }
 
diff --git a/third_party/blink/renderer/core/css/parser/css_supports_parser.cc b/third_party/blink/renderer/core/css/parser/css_supports_parser.cc
index 10f1c17f..1b669f3 100644
--- a/third_party/blink/renderer/core/css/parser/css_supports_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_supports_parser.cc
@@ -13,16 +13,6 @@
 
 namespace {
 
-// The result kUnknown must be converted to 'false' if passed to a context
-// which requires a boolean value.
-// TODO(crbug.com/1052274): This is supposed to happen at the top-level,
-// but currently happens on ConsumeGeneralEnclosed's result.
-CSSSupportsParser::Result EvalUnknown(CSSSupportsParser::Result result) {
-  return result == CSSSupportsParser::Result::kUnknown
-             ? CSSSupportsParser::Result::kUnsupported
-             : result;
-}
-
 // https://drafts.csswg.org/css-syntax/#typedef-any-value
 bool IsNextTokenAllowedForAnyValue(CSSParserTokenRange& range) {
   switch (range.Peek().GetType()) {
@@ -168,13 +158,7 @@
   }
 
   // <general-enclosed>
-  //
-  // TODO(crbug.com/1052274): Support kUnknown beyond this point.
-  //
-  // The result kUnknown is supposed to be evaluated at the top level, but
-  // we have already shipped the behavior of evaluating it here, and Firefox
-  // does the same thing.
-  return EvalUnknown(ConsumeGeneralEnclosed(first_token, stream));
+  return ConsumeGeneralEnclosed(first_token, stream);
 }
 
 // <supports-feature> = <supports-selector-fn> | <supports-decl>
@@ -225,7 +209,7 @@
       return Result::kParseFailure;
 
     stream.ConsumeWhitespace();
-    return Result::kUnknown;
+    return Result::kUnsupported;
   }
   return Result::kParseFailure;
 }
diff --git a/third_party/blink/renderer/core/css/parser/css_supports_parser.h b/third_party/blink/renderer/core/css/parser/css_supports_parser.h
index 478cacb2..a9f324d 100644
--- a/third_party/blink/renderer/core/css/parser/css_supports_parser.h
+++ b/third_party/blink/renderer/core/css/parser/css_supports_parser.h
@@ -24,13 +24,6 @@
     // don't support the feature.
     kUnsupported,
     kSupported,
-    // kUnknown is a special value used for productions that only match
-    // <general-enclosed> [1]. See note regarding Kleene 3-valued logic [2]
-    // for explanation of how this is different from kUnsupported.
-    //
-    // [1] https://drafts.csswg.org/css-conditional-3/#at-supports
-    // [2] https://drafts.csswg.org/mediaqueries-4/#evaluating
-    kUnknown,
     // This is used to signal parse failure in the @supports syntax itself.
     // This means that for a production like:
     //
@@ -114,8 +107,6 @@
   using Result = CSSSupportsParser::Result;
   if (a == Result::kParseFailure || b == Result::kParseFailure)
     return Result::kParseFailure;
-  if (a == Result::kUnknown && b == Result::kUnknown)
-    return Result::kUnknown;
   if (a != Result::kSupported || b != Result::kSupported)
     return Result::kUnsupported;
   return Result::kSupported;
@@ -128,8 +119,6 @@
     return Result::kParseFailure;
   if (a == Result::kSupported || b == Result::kSupported)
     return Result::kSupported;
-  if (a == Result::kUnknown || b == Result::kUnknown)
-    return Result::kUnknown;
   return Result::kUnsupported;
 }
 
diff --git a/third_party/blink/renderer/core/css/parser/css_supports_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_supports_parser_test.cc
index 438a118..ec9025d 100644
--- a/third_party/blink/renderer/core/css/parser/css_supports_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_supports_parser_test.cc
@@ -106,7 +106,6 @@
   EXPECT_EQ(Result::kSupported, !Result::kUnsupported);
   EXPECT_EQ(Result::kUnsupported, !Result::kSupported);
   EXPECT_EQ(Result::kParseFailure, !Result::kParseFailure);
-  EXPECT_EQ(Result::kUnknown, !Result::kUnknown);
 }
 
 TEST_F(CSSSupportsParserTest, ResultAnd) {
@@ -117,10 +116,6 @@
 
   EXPECT_EQ(Result::kParseFailure, Result::kSupported & Result::kParseFailure);
   EXPECT_EQ(Result::kParseFailure, Result::kParseFailure & Result::kSupported);
-
-  EXPECT_EQ(Result::kUnknown, Result::kUnknown & Result::kUnknown);
-  EXPECT_EQ(Result::kUnsupported, Result::kSupported & Result::kUnknown);
-  EXPECT_EQ(Result::kUnsupported, Result::kUnknown & Result::kSupported);
 }
 
 TEST_F(CSSSupportsParserTest, ResultOr) {
@@ -131,10 +126,6 @@
 
   EXPECT_EQ(Result::kParseFailure, Result::kSupported | Result::kParseFailure);
   EXPECT_EQ(Result::kParseFailure, Result::kParseFailure | Result::kSupported);
-
-  EXPECT_EQ(Result::kUnknown, Result::kUnknown | Result::kUnknown);
-  EXPECT_EQ(Result::kSupported, Result::kSupported | Result::kUnknown);
-  EXPECT_EQ(Result::kSupported, Result::kUnknown | Result::kSupported);
 }
 
 TEST_F(CSSSupportsParserTest, ConsumeSupportsCondition) {
@@ -318,12 +309,12 @@
 }
 
 TEST_F(CSSSupportsParserTest, ConsumeGeneralEnclosed) {
-  EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("(asdf)"));
-  EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("( asdf )"));
-  EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("(3)"));
-  EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("max(1, 2)"));
-  EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("asdf(1, 2)"));
-  EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("asdf(1, 2)\t"));
+  EXPECT_EQ(Result::kUnsupported, ConsumeGeneralEnclosed("(asdf)"));
+  EXPECT_EQ(Result::kUnsupported, ConsumeGeneralEnclosed("( asdf )"));
+  EXPECT_EQ(Result::kUnsupported, ConsumeGeneralEnclosed("(3)"));
+  EXPECT_EQ(Result::kUnsupported, ConsumeGeneralEnclosed("max(1, 2)"));
+  EXPECT_EQ(Result::kUnsupported, ConsumeGeneralEnclosed("asdf(1, 2)"));
+  EXPECT_EQ(Result::kUnsupported, ConsumeGeneralEnclosed("asdf(1, 2)\t"));
 
   EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("("));
   EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("()"));
@@ -335,8 +326,8 @@
   EXPECT_EQ(Result::kParseFailure, ConsumeGeneralEnclosed("(url(as'df))"));
 
   // Valid <any-value>
-  EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("(as;df)"));
-  EXPECT_EQ(Result::kUnknown, ConsumeGeneralEnclosed("(as ! df)"));
+  EXPECT_EQ(Result::kUnsupported, ConsumeGeneralEnclosed("(as;df)"));
+  EXPECT_EQ(Result::kUnsupported, ConsumeGeneralEnclosed("(as ! df)"));
 }
 
 TEST_F(CSSSupportsParserTest, AtSupportsCondition) {
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 580d4d20..b91abc2e 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -657,6 +657,7 @@
 
   style.SetTextShadow(ComputedStyleInitialValues::InitialTextShadow());
   style.SetBoxShadow(ComputedStyleInitialValues::InitialBoxShadow());
+  style.SetColorScheme({"light", "dark"});
   style.SetAccentColor(ComputedStyleInitialValues::InitialAccentColor());
   if (!style.HasUrlBackgroundImage())
     style.ClearBackgroundImage();
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory.cc b/third_party/blink/renderer/core/events/pointer_event_factory.cc
index ac9a6f2..7f72990 100644
--- a/third_party/blink/renderer/core/events/pointer_event_factory.cc
+++ b/third_party/blink/renderer/core/events/pointer_event_factory.cc
@@ -217,6 +217,7 @@
   return result;
 }
 
+const PointerId PointerEventFactory::kReservedNonPointerId = -1;
 const PointerId PointerEventFactory::kInvalidId = 0;
 
 // Mouse id is 1 to behave the same as MS Edge for compatibility reasons.
diff --git a/third_party/blink/renderer/core/events/pointer_event_factory.h b/third_party/blink/renderer/core/events/pointer_event_factory.h
index 82d3abec..252b24d 100644
--- a/third_party/blink/renderer/core/events/pointer_event_factory.h
+++ b/third_party/blink/renderer/core/events/pointer_event_factory.h
@@ -85,6 +85,7 @@
 
   static const PointerId kMouseId;
   static const PointerId kInvalidId;
+  static const PointerId kReservedNonPointerId;
 
   // Removes pointer_id from the map.
   void RemoveLastPosition(const PointerId pointer_id);
diff --git a/third_party/blink/renderer/core/events/simulated_event_util.cc b/third_party/blink/renderer/core/events/simulated_event_util.cc
index 939380b..86d9a67 100644
--- a/third_party/blink/renderer/core/events/simulated_event_util.cc
+++ b/third_party/blink/renderer/core/events/simulated_event_util.cc
@@ -124,9 +124,6 @@
   // any event attributes not initialized in the |PointerEventInit| below get
   // their default values, all of which are appropriate for a simulated
   // |PointerEvent|.
-  //
-  // TODO(mustaq): Set |pointerId| to -1 after we have a spec change to fix the
-  // issue https://github.com/w3c/pointerevents/issues/343.
   PointerEventInit* initializer = PointerEventInit::Create();
   PopulateSimulatedMouseEventInit(event_type, node, underlying_event,
                                   initializer, creation_scope);
@@ -160,6 +157,8 @@
       initializer->setPointerId(PointerEventFactory::kMouseId);
       initializer->setPointerType(pointer_type_names::kMouse);
       initializer->setIsPrimary(true);
+    } else {
+      initializer->setPointerId(PointerEventFactory::kReservedNonPointerId);
     }
     created_event = MakeGarbageCollected<PointerEvent>(
         event_type, initializer, timestamp, synthetic_type);
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
index 8e83b077..e93cdc1a 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
@@ -17,6 +17,14 @@
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
+namespace {
+
+inline base::HistogramBase::Sample ToSample(int64_t value) {
+  return base::saturated_cast<base::HistogramBase::Sample>(value);
+}
+
+}  // namespace
+
 namespace blink {
 
 LocalFrameUkmAggregator::ScopedUkmHierarchicalTimer::ScopedUkmHierarchicalTimer(
@@ -40,13 +48,14 @@
 LocalFrameUkmAggregator::ScopedUkmHierarchicalTimer::
     ~ScopedUkmHierarchicalTimer() {
   if (aggregator_ && base::TimeTicks::IsHighResolution()) {
-    aggregator_->RecordSample(metric_index_, start_time_, clock_->NowTicks());
+    aggregator_->RecordTimerSample(metric_index_, start_time_,
+                                   clock_->NowTicks());
   }
 }
 
 void LocalFrameUkmAggregator::AbsoluteMetricRecord::reset() {
-  interval_duration = base::TimeDelta();
-  main_frame_duration = base::TimeDelta();
+  interval_count = 0;
+  main_frame_count = 0;
 }
 
 LocalFrameUkmAggregator::LocalFrameUkmAggregator(int64_t source_id,
@@ -57,9 +66,9 @@
       event_name_("Blink.UpdateTime") {
   // All of these are assumed to have one entry per sub-metric.
   DCHECK_EQ(base::size(absolute_metric_records_), metrics_data().size());
-  DCHECK_EQ(base::size(current_sample_.sub_metrics_durations),
+  DCHECK_EQ(base::size(current_sample_.sub_metrics_counts),
             metrics_data().size());
-  DCHECK_EQ(base::size(current_sample_.sub_main_frame_durations),
+  DCHECK_EQ(base::size(current_sample_.sub_main_frame_counts),
             metrics_data().size());
 
   // Record average and worst case for the primary metric.
@@ -90,7 +99,7 @@
     // They have an associated UMA too that we own and allocate here.
     auto& absolute_record = absolute_metric_records_[metric_index];
     absolute_record.reset();
-    absolute_record.pre_fcp_aggregate = base::TimeDelta();
+    absolute_record.pre_fcp_aggregate = 0;
     if (metric_data.has_uma) {
       StringBuilder uma_name;
       uma_name.Append(uma_preamble);
@@ -144,37 +153,37 @@
   // metrics and would result in double counting.
   std::unique_ptr<cc::BeginMainFrameMetrics> metrics_data =
       std::make_unique<cc::BeginMainFrameMetrics>();
-  metrics_data->handle_input_events =
+  metrics_data->handle_input_events = base::TimeDelta::FromMicroseconds(
       absolute_metric_records_[static_cast<unsigned>(
                                    MetricId::kHandleInputEvents)]
-          .main_frame_duration;
-  metrics_data->animate =
+          .main_frame_count);
+  metrics_data->animate = base::TimeDelta::FromMicroseconds(
       absolute_metric_records_[static_cast<unsigned>(MetricId::kAnimate)]
-          .main_frame_duration;
-  metrics_data->style_update =
+          .main_frame_count);
+  metrics_data->style_update = base::TimeDelta::FromMicroseconds(
       absolute_metric_records_[static_cast<unsigned>(MetricId::kStyle)]
-          .main_frame_duration;
-  metrics_data->layout_update =
+          .main_frame_count);
+  metrics_data->layout_update = base::TimeDelta::FromMicroseconds(
       absolute_metric_records_[static_cast<unsigned>(MetricId::kLayout)]
-          .main_frame_duration;
-  metrics_data->prepaint =
+          .main_frame_count);
+  metrics_data->prepaint = base::TimeDelta::FromMicroseconds(
       absolute_metric_records_[static_cast<unsigned>(MetricId::kPrePaint)]
-          .main_frame_duration;
-  metrics_data->compositing_assignments =
+          .main_frame_count);
+  metrics_data->compositing_assignments = base::TimeDelta::FromMicroseconds(
       absolute_metric_records_[static_cast<unsigned>(
                                    MetricId::kCompositingAssignments)]
-          .main_frame_duration;
-  metrics_data->compositing_inputs =
+          .main_frame_count);
+  metrics_data->compositing_inputs = base::TimeDelta::FromMicroseconds(
       absolute_metric_records_[static_cast<unsigned>(
                                    MetricId::kCompositingInputs)]
-          .main_frame_duration;
-  metrics_data->paint =
+          .main_frame_count);
+  metrics_data->paint = base::TimeDelta::FromMicroseconds(
       absolute_metric_records_[static_cast<unsigned>(MetricId::kPaint)]
-          .main_frame_duration;
-  metrics_data->composite_commit =
+          .main_frame_count);
+  metrics_data->composite_commit = base::TimeDelta::FromMicroseconds(
       absolute_metric_records_[static_cast<unsigned>(
                                    MetricId::kCompositingCommit)]
-          .main_frame_duration;
+          .main_frame_count);
   metrics_data->should_measure_smoothness =
       (fcp_state_ >= kThisFrameReachedFCP);
   return metrics_data;
@@ -197,34 +206,37 @@
   fcp_state_ = kThisFrameReachedFCP;
 }
 
-void LocalFrameUkmAggregator::RecordSample(size_t metric_index,
-                                           base::TimeTicks start,
-                                           base::TimeTicks end) {
+void LocalFrameUkmAggregator::RecordTimerSample(size_t metric_index,
+                                                base::TimeTicks start,
+                                                base::TimeTicks end) {
+  RecordCountSample(metric_index, (end - start).InMicroseconds());
+}
+
+void LocalFrameUkmAggregator::RecordCountSample(size_t metric_index,
+                                                int64_t count) {
   // Always use RecordForcedLayoutSample for the kForcedStyleAndLayout
   // metric id.
   DCHECK_NE(metric_index, static_cast<size_t>(kForcedStyleAndLayout));
 
-  base::TimeDelta duration = end - start;
   bool is_pre_fcp = (fcp_state_ != kHavePassedFCP);
 
   // Accumulate for UKM and record the UMA
   DCHECK_LT(metric_index, base::size(absolute_metric_records_));
   auto& record = absolute_metric_records_[metric_index];
-  record.interval_duration += duration;
+  record.interval_count += count;
   if (in_main_frame_update_)
-    record.main_frame_duration += duration;
+    record.main_frame_count += count;
   if (is_pre_fcp)
-    record.pre_fcp_aggregate += duration;
+    record.pre_fcp_aggregate += count;
   // Record the UMA
   // ForcedStyleAndLayout happen so frequently on some pages that we overflow
   // the signed 32 counter for number of events in a 30 minute period. So
   // randomly record with probability 1/100.
   if (record.pre_fcp_uma_counter) {
-    if (is_pre_fcp) {
-      record.pre_fcp_uma_counter->CountMicroseconds(duration);
-    } else {
-      record.post_fcp_uma_counter->CountMicroseconds(duration);
-    }
+    if (is_pre_fcp)
+      record.pre_fcp_uma_counter->Count(ToSample(count));
+    else
+      record.post_fcp_uma_counter->Count(ToSample(count));
   }
 }
 
@@ -232,7 +244,7 @@
     DocumentUpdateReason reason,
     base::TimeTicks start,
     base::TimeTicks end) {
-  base::TimeDelta duration = end - start;
+  int64_t count = (end - start).InMicroseconds();
   bool is_pre_fcp = (fcp_state_ != kHavePassedFCP);
 
   // Accumulate for UKM always, but only record the UMA for a subset of cases to
@@ -248,17 +260,17 @@
 
   auto& record =
       absolute_metric_records_[static_cast<size_t>(kForcedStyleAndLayout)];
-  record.interval_duration += duration;
+  record.interval_count += count;
   if (in_main_frame_update_)
-    record.main_frame_duration += duration;
+    record.main_frame_count += count;
   if (is_pre_fcp)
-    record.pre_fcp_aggregate += duration;
+    record.pre_fcp_aggregate += count;
 
   if (should_report_uma_this_frame) {
     if (is_pre_fcp)
-      record.pre_fcp_uma_counter->CountMicroseconds(duration);
+      record.pre_fcp_uma_counter->Count(ToSample(count));
     else
-      record.post_fcp_uma_counter->CountMicroseconds(duration);
+      record.post_fcp_uma_counter->Count(ToSample(count));
   }
 
   // Record a variety of DocumentUpdateReasons as distinct metrics
@@ -322,16 +334,16 @@
   if (sub_metric != kCount) {
     auto& sub_record =
         absolute_metric_records_[static_cast<size_t>(sub_metric)];
-    sub_record.interval_duration += duration;
+    sub_record.interval_count += count;
     if (in_main_frame_update_)
-      sub_record.main_frame_duration += duration;
+      sub_record.main_frame_count += count;
     if (is_pre_fcp)
-      sub_record.pre_fcp_aggregate += duration;
+      sub_record.pre_fcp_aggregate += count;
     if (should_report_uma_this_frame) {
       if (is_pre_fcp)
-        sub_record.pre_fcp_uma_counter->CountMicroseconds(duration);
+        sub_record.pre_fcp_uma_counter->Count(ToSample(count));
       else
-        sub_record.post_fcp_uma_counter->CountMicroseconds(duration);
+        sub_record.post_fcp_uma_counter->Count(ToSample(count));
     }
   }
 }
@@ -348,10 +360,10 @@
   // all time to the compositor commit so as to not imply that wait time was
   // consumed.
   if (started == base::TimeTicks()) {
-    RecordSample(kImplCompositorCommit, requested, completed);
+    RecordTimerSample(kImplCompositorCommit, requested, completed);
   } else {
-    RecordSample(kWaitForCommit, requested, started);
-    RecordSample(kImplCompositorCommit, started, completed);
+    RecordTimerSample(kWaitForCommit, requested, started);
+    RecordTimerSample(kImplCompositorCommit, started, completed);
   }
 }
 
@@ -359,14 +371,14 @@
     base::TimeTicks start,
     base::TimeTicks end,
     cc::ActiveFrameSequenceTrackers trackers) {
-  const base::TimeDelta duration = end - start;
+  const int64_t count = (end - start).InMicroseconds();
   const bool have_valid_metrics =
       // Any of the early outs in LocalFrameView::UpdateLifecyclePhases() will
       // mean we are not in a main frame update. Recording is triggered higher
       // in the stack, so we cannot know to avoid calling this method.
       in_main_frame_update_ &&
       // In tests it's possible to reach here with zero duration.
-      (duration > base::TimeDelta());
+      (count > 0);
 
   in_main_frame_update_ = false;
   if (!have_valid_metrics) {
@@ -381,14 +393,14 @@
 
   // Record UMA
   if (report_as_pre_fcp)
-    primary_metric_.pre_fcp_uma_counter->CountMicroseconds(duration);
+    primary_metric_.pre_fcp_uma_counter->Count(ToSample(count));
   else
-    primary_metric_.post_fcp_uma_counter->CountMicroseconds(duration);
+    primary_metric_.post_fcp_uma_counter->Count(ToSample(count));
 
   // Record primary time information
-  primary_metric_.interval_duration = duration;
+  primary_metric_.interval_count = count;
   if (report_as_pre_fcp)
-    primary_metric_.pre_fcp_aggregate += duration;
+    primary_metric_.pre_fcp_aggregate += count;
 
   UpdateEventTimeAndUpdateSampleIfNeeded(trackers);
 
@@ -430,31 +442,31 @@
 
 void LocalFrameUkmAggregator::UpdateSample(
     cc::ActiveFrameSequenceTrackers trackers) {
-  current_sample_.primary_metric_duration = primary_metric_.interval_duration;
+  current_sample_.primary_metric_count = primary_metric_.interval_count;
   for (size_t i = 0; i < metrics_data().size(); ++i) {
-    current_sample_.sub_metrics_durations[i] =
-        absolute_metric_records_[i].interval_duration;
-    current_sample_.sub_main_frame_durations[i] =
-        absolute_metric_records_[i].main_frame_duration;
+    current_sample_.sub_metrics_counts[i] =
+        absolute_metric_records_[i].interval_count;
+    current_sample_.sub_main_frame_counts[i] =
+        absolute_metric_records_[i].main_frame_count;
   }
   current_sample_.trackers = trackers;
 }
 
 void LocalFrameUkmAggregator::ReportPreFCPEvent() {
-#define CASE_FOR_ID(name)                                                  \
-  case k##name:                                                            \
-    builder.Set##name(absolute_record.pre_fcp_aggregate.InMicroseconds()); \
+#define CASE_FOR_ID(name)                                           \
+  case k##name:                                                     \
+    builder.Set##name(ToSample(absolute_record.pre_fcp_aggregate)); \
     break
 
   ukm::builders::Blink_PageLoad builder(source_id_);
-  builder.SetMainFrame(primary_metric_.pre_fcp_aggregate.InMicroseconds());
-  primary_metric_.uma_aggregate_counter->CountMicroseconds(
-      primary_metric_.pre_fcp_aggregate);
+  builder.SetMainFrame(primary_metric_.pre_fcp_aggregate);
+  primary_metric_.uma_aggregate_counter->Count(
+      ToSample(primary_metric_.pre_fcp_aggregate));
   for (size_t i = 0; i < metrics_data().size(); ++i) {
     auto& absolute_record = absolute_metric_records_[i];
     if (absolute_record.uma_aggregate_counter) {
-      absolute_record.uma_aggregate_counter->CountMicroseconds(
-          absolute_record.pre_fcp_aggregate);
+      absolute_record.uma_aggregate_counter->Count(
+          ToSample(absolute_record.pre_fcp_aggregate));
     }
 
     switch (static_cast<MetricId>(i)) {
@@ -499,18 +511,15 @@
   if (!frames_since_last_report_)
     return;
 
-#define CASE_FOR_ID(name, index)                                               \
-  case k##name:                                                                \
-    builder                                                                    \
-        .Set##name(                                                            \
-            current_sample_.sub_metrics_durations[index].InMicroseconds())     \
-        .Set##name##BeginMainFrame(                                            \
-            current_sample_.sub_main_frame_durations[index].InMicroseconds()); \
+#define CASE_FOR_ID(name, index)                                 \
+  case k##name:                                                  \
+    builder.Set##name(current_sample_.sub_metrics_counts[index]) \
+        .Set##name##BeginMainFrame(                              \
+            current_sample_.sub_main_frame_counts[index]);       \
     break
 
   ukm::builders::Blink_UpdateTime builder(source_id_);
-  builder.SetMainFrame(
-      current_sample_.primary_metric_duration.InMicroseconds());
+  builder.SetMainFrame(current_sample_.primary_metric_count);
   builder.SetMainFrameIsBeforeFCP(fcp_state_ != kHavePassedFCP);
   builder.SetMainFrameReasons(current_sample_.trackers);
   for (size_t i = 0; i < metrics_data().size(); ++i) {
@@ -561,13 +570,13 @@
 }
 
 bool LocalFrameUkmAggregator::AllMetricsAreZero() {
-  if (!primary_metric_.interval_duration.is_zero())
+  if (primary_metric_.interval_count != 0)
     return false;
   for (auto& record : absolute_metric_records_) {
-    if (!record.interval_duration.is_zero()) {
+    if (record.interval_count != 0) {
       return false;
     }
-    if (!record.main_frame_duration.is_zero()) {
+    if (record.main_frame_count != 0) {
       return false;
     }
   }
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
index 569e6a7..c470b02 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
@@ -242,9 +242,12 @@
   // Record a sample for a sub-metric. This should only be used when
   // a ScopedUkmHierarchicalTimer cannot be used (such as when the timed
   // interval does not fall inside a single calling function).
-  void RecordSample(size_t metric_index,
-                    base::TimeTicks start,
-                    base::TimeTicks end);
+  void RecordTimerSample(size_t metric_index,
+                         base::TimeTicks start,
+                         base::TimeTicks end);
+
+  // Record a sample for a count-based sub-metric.
+  void RecordCountSample(size_t metric_index, int64_t count);
 
   // Record a ForcedLayout sample. The reason will determine which, if any,
   // additional metrics are reported in order to diagnose the cause of
@@ -287,22 +290,22 @@
 
     // Accumulated at each sample, then reset with a call to
     // RecordEndOfFrameMetrics.
-    base::TimeDelta interval_duration;
+    int64_t interval_count = 0;
 
     // Accumulated at each sample when within a BeginMainFrame,
     // reset with a call to RecordEndOfFrameMetrics.
-    base::TimeDelta main_frame_duration;
+    int64_t main_frame_count = 0;
 
     // Accumulated at each sample up to the time of First Contentful Paint.
-    base::TimeDelta pre_fcp_aggregate;
+    int64_t pre_fcp_aggregate = 0;
 
     void reset();
   };
 
   struct SampleToRecord {
-    base::TimeDelta primary_metric_duration;
-    std::array<base::TimeDelta, kCount> sub_metrics_durations;
-    std::array<base::TimeDelta, kCount> sub_main_frame_durations;
+    int64_t primary_metric_count;
+    std::array<int64_t, kCount> sub_metrics_counts;
+    std::array<int64_t, kCount> sub_main_frame_counts;
     cc::ActiveFrameSequenceTrackers trackers;
   };
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
index b02feae..8886107 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
@@ -204,9 +204,8 @@
   }
 
   bool SampleMatchesIteration(int64_t iteration_count) {
-    return aggregator()
-               .current_sample_.sub_metrics_durations[0]
-               .InMilliseconds() == iteration_count;
+    return aggregator().current_sample_.sub_metrics_counts[0] / 1000 ==
+           iteration_count;
   }
 
  private:
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index b224b2b..c9a7622 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2332,7 +2332,7 @@
   // in preparation for a hit test.
   if (reason == DocumentUpdateReason::kHitTest) {
     LocalFrameUkmAggregator& aggregator = EnsureUkmAggregator();
-    aggregator.RecordSample(
+    aggregator.RecordTimerSample(
         static_cast<size_t>(LocalFrameUkmAggregator::kHitTestDocumentUpdate),
         lifecycle_data_.start_time, base::TimeTicks::Now());
   }
@@ -2841,6 +2841,12 @@
 
   auto* layout_view = GetLayoutView();
   DCHECK(layout_view);
+
+  if (RuntimeEnabledFeatures::CullRectUpdateEnabled() &&
+      !ShouldThrottleRendering()) {
+    CullRectUpdater(*layout_view->Layer()).Update();
+  }
+
   paint_frame_count_++;
   ForAllNonThrottledLocalFrameViews(
       [](LocalFrameView& frame_view) {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index fab478e9..6814fdb 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -2081,9 +2081,13 @@
 void WebFrameWidgetImpl::RecordDispatchRafAlignedInputTime(
     base::TimeTicks raf_aligned_input_start_time) {
   if (LocalRootImpl()) {
-    LocalRootImpl()->GetFrame()->View()->EnsureUkmAggregator().RecordSample(
-        LocalFrameUkmAggregator::kHandleInputEvents,
-        raf_aligned_input_start_time, base::TimeTicks::Now());
+    LocalRootImpl()
+        ->GetFrame()
+        ->View()
+        ->EnsureUkmAggregator()
+        .RecordTimerSample(LocalFrameUkmAggregator::kHandleInputEvents,
+                           raf_aligned_input_start_time,
+                           base::TimeTicks::Now());
   }
 }
 
@@ -2171,9 +2175,13 @@
 void WebFrameWidgetImpl::EndUpdateLayers() {
   if (LocalRootImpl()) {
     DCHECK(update_layers_start_time_);
-    LocalRootImpl()->GetFrame()->View()->EnsureUkmAggregator().RecordSample(
-        LocalFrameUkmAggregator::kUpdateLayers,
-        update_layers_start_time_.value(), base::TimeTicks::Now());
+    LocalRootImpl()
+        ->GetFrame()
+        ->View()
+        ->EnsureUkmAggregator()
+        .RecordTimerSample(LocalFrameUkmAggregator::kUpdateLayers,
+                           update_layers_start_time_.value(),
+                           base::TimeTicks::Now());
     probe::LayerTreeDidChange(LocalRootImpl()->GetFrame());
   }
   update_layers_start_time_.reset();
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
index c541017..3fe144a3 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.cc
@@ -1081,7 +1081,7 @@
   LayoutUnit intrinsic_block_size = BorderScrollbarPadding().BlockSum();
 
   if (algorithm_.FlexLines().IsEmpty() && Node().HasLineIfEmpty()) {
-    intrinsic_block_size += Node().GetLayoutBox()->LogicalHeightForEmptyLine();
+    intrinsic_block_size += Node().EmptyLineBlockSize();
   } else {
     intrinsic_block_size += algorithm_.IntrinsicContentBlockSize();
   }
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index 22f40ba..06379cd7 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -190,6 +190,14 @@
       grid_geometry.row_geometry.FinalGutterSize() +
       BorderScrollbarPadding().block_end;
 
+  // TODO(layout-dev): This isn't great but matches legacy. Ideally this would
+  // only apply when we have only flexible track(s).
+  if (grid_items.IsEmpty() && Node().HasLineIfEmpty()) {
+    intrinsic_block_size =
+        std::max(intrinsic_block_size, BorderScrollbarPadding().BlockSum() +
+                                           Node().EmptyLineBlockSize());
+  }
+
   // TODO(layout-dev): ClampIntrinsicBlockSize might not be correct for grid.
   // Specifically do we need to run the sizing algorithm assuming no children
   // for size containment.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index 1c50430..d30a8cf1 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -762,8 +762,7 @@
   if (container_builder_.HasSeenAllChildren() &&
       HasLineEvenIfEmpty(Node().GetLayoutBox())) {
     intrinsic_block_size_ +=
-        std::max(intrinsic_block_size_,
-                 Node().GetLayoutBox()->LogicalHeightForEmptyLine());
+        std::max(intrinsic_block_size_, Node().EmptyLineBlockSize());
     if (container_builder_.IsInitialColumnBalancingPass()) {
       container_builder_.PropagateTallestUnbreakableBlockSize(
           intrinsic_block_size_);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 1f4715c..ebe2899 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -415,8 +415,7 @@
 
     // We may have to update the margins on box_; we reuse the layout result
     // even if a percentage margin may have changed.
-    if (UNLIKELY(Style().MayHaveMargin() && !constraint_space.IsTableCell()))
-      box_->SetMargin(ComputePhysicalMargins(constraint_space, Style()));
+    UpdateMarginPaddingInfoIfNeeded(constraint_space);
 
     UpdateShapeOutsideInfoIfNeeded(
         *layout_result, constraint_space.PercentageResolutionInlineSize());
@@ -1110,21 +1109,7 @@
   // TODO(mstensho): This should always be done by the parent algorithm, since
   // we may have auto margins, which only the parent is able to resolve. Remove
   // the following line when all layout modes do this properly.
-  if (UNLIKELY(box_->IsTableCell())) {
-    // Table-cell margins compute to zero.
-    box_->SetMargin(NGPhysicalBoxStrut());
-  } else {
-    box_->SetMargin(ComputePhysicalMargins(constraint_space, Style()));
-  }
-
-  // We copy back the %-size so that |LayoutBoxModelObject::ComputedCSSPadding|
-  // is able to return the correct value. This isn't ideal, but eventually
-  // we'll answer these queries from the fragment.
-  const auto* containing_block = box_->ContainingBlock();
-  if (UNLIKELY(containing_block && containing_block->IsLayoutNGGrid())) {
-    box_->SetOverrideContainingBlockContentLogicalWidth(
-        constraint_space.PercentageResolutionInlineSizeForParentWritingMode());
-  }
+  UpdateMarginPaddingInfoIfNeeded(constraint_space);
 
   auto* block_flow = DynamicTo<LayoutBlockFlow>(box_);
   LayoutMultiColumnFlowThread* flow_thread = GetFlowThread(block_flow);
@@ -1830,7 +1815,28 @@
   return box_->InlineBlockBaseline(line_direction);
 }
 
-// Floats can optionally have a shape area, specifed by "shape-outside". The
+void NGBlockNode::UpdateMarginPaddingInfoIfNeeded(
+    const NGConstraintSpace& space) const {
+  // Table-cells don't have margins, and aren't grid-items.
+  if (space.IsTableCell())
+    return;
+
+  if (Style().MayHaveMargin())
+    box_->SetMargin(ComputePhysicalMargins(space, Style()));
+
+  if (Style().MayHaveMargin() || Style().MayHavePadding()) {
+    // Copy back the %-size so that |LayoutBoxModelObject::ComputedCSSPadding|
+    // is able to return the correct value. This isn't ideal, but eventually
+    // we'll answer these queries from the fragment.
+    const auto* containing_block = box_->ContainingBlock();
+    if (UNLIKELY(containing_block && containing_block->IsLayoutNGGrid())) {
+      box_->SetOverrideContainingBlockContentLogicalWidth(
+          space.PercentageResolutionInlineSizeForParentWritingMode());
+    }
+  }
+}
+
+// Floats can optionally have a shape area, specified by "shape-outside". The
 // current shape machinery requires setting the size of the float after layout
 // in the parents writing mode.
 void NGBlockNode::UpdateShapeOutsideInfoIfNeeded(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
index 3cdc13d5..6711e82e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
@@ -203,6 +203,9 @@
       return block->HasLineIfEmpty();
     return false;
   }
+  LayoutUnit EmptyLineBlockSize() const {
+    return box_->LogicalHeightForEmptyLine();
+  }
 
   // After we run the layout algorithm, this function copies back the fragment
   // position to the layout box.
@@ -257,6 +260,8 @@
   LayoutUnit AtomicInlineBaselineFromLegacyLayout(
       const NGConstraintSpace&) const;
 
+  void UpdateMarginPaddingInfoIfNeeded(const NGConstraintSpace&) const;
+
   void UpdateShapeOutsideInfoIfNeeded(
       const NGLayoutResult&,
       LayoutUnit percentage_resolution_inline_size) const;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index f99ab56..a18e07ab 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -903,7 +903,7 @@
   NGBoxStrut scrollbars_before =
       ComputeScrollbarsForNonAnonymous(node_info.node);
   scoped_refptr<const NGLayoutResult> layout_result =
-      Layout(oof_node_to_layout, fragmentainer_constraint_space);
+      Layout(oof_node_to_layout, offset_info, fragmentainer_constraint_space);
   NGBoxStrut scrollbars_after =
       ComputeScrollbarsForNonAnonymous(node_info.node);
 
@@ -934,9 +934,8 @@
       // so recompute the OffsetInfo.
       offset_info = CalculateOffset(node_info, only_layout,
                                     /* is_first_run */ false);
-
-      layout_result =
-          Layout(oof_node_to_layout, fragmentainer_constraint_space);
+      layout_result = Layout(oof_node_to_layout, offset_info,
+                             fragmentainer_constraint_space);
 
       scrollbars_after = ComputeScrollbarsForNonAnonymous(node_info.node);
       DCHECK(!freeze_horizontal || !freeze_vertical ||
@@ -1037,9 +1036,9 @@
 
 scoped_refptr<const NGLayoutResult> NGOutOfFlowLayoutPart::Layout(
     const NodeToLayout& oof_node_to_layout,
+    const OffsetInfo& offset_info,
     const NGConstraintSpace* fragmentainer_constraint_space) {
   const NodeInfo& node_info = oof_node_to_layout.node_info;
-  const OffsetInfo& offset_info = oof_node_to_layout.offset_info;
   const WritingDirectionMode candidate_writing_direction =
       node_info.node.Style().GetWritingDirection();
   LogicalSize container_content_size_in_candidate_writing_mode =
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 7aebd00..c332a3c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -222,6 +222,7 @@
 
   scoped_refptr<const NGLayoutResult> Layout(
       const NodeToLayout& oof_node_to_layout,
+      const OffsetInfo& offset_info,
       const NGConstraintSpace* fragmentainer_constraint_space);
 
   bool IsContainingBlockForCandidate(const NGLogicalOutOfFlowPositionedNode&);
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 43ba55c7..6f87db4 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -2168,9 +2168,6 @@
   //
   // Use response_.AddressSpace() instead of frame_->DomWindow()->AddressSpace()
   // since the latter isn't populated in unit tests.
-  //
-  // TODO(crbug.com/1171214): move RecordAddressSpaceFeature() call from
-  // StartLoadingInternal to here.
   if (frame_->IsMainFrame()) {
     auto address_space = response_.AddressSpace();
     if ((address_space == network::mojom::blink::IPAddressSpace::kPrivate ||
diff --git a/third_party/blink/renderer/core/paint/cull_rect_updater.cc b/third_party/blink/renderer/core/paint/cull_rect_updater.cc
index eff7798..dc63743 100644
--- a/third_party/blink/renderer/core/paint/cull_rect_updater.cc
+++ b/third_party/blink/renderer/core/paint/cull_rect_updater.cc
@@ -288,7 +288,7 @@
       !root_layer.NeedsCullRectUpdate() &&
       !root_layer.DescendantNeedsCullRectUpdate() &&
       cull_rect == root_layer.GetLayoutObject().FirstFragment().GetCullRect()) {
-    // The cull rects calculated during PrePaint are good.
+    // The current cull rects are good.
     return;
   }
 
diff --git a/third_party/blink/renderer/core/paint/paint_controller_paint_test.h b/third_party/blink/renderer/core/paint/paint_controller_paint_test.h
index 07713df9..2602df7 100644
--- a/third_party/blink/renderer/core/paint/paint_controller_paint_test.h
+++ b/third_party/blink/renderer/core/paint/paint_controller_paint_test.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/paint/cull_rect_updater.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
@@ -49,6 +50,10 @@
   void UpdateAllLifecyclePhasesExceptPaint() {
     GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(
         DocumentUpdateReason::kTest);
+    // Run CullRectUpdater to ease testing of cull rects and repaint flags of
+    // PaintLayers on cull rect change.
+    if (RuntimeEnabledFeatures::CullRectUpdateEnabled())
+      CullRectUpdater(*GetLayoutView().Layer()).Update();
   }
 
   void PaintContents(const IntRect& interest_rect) {
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index 08626f3..97e3958 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -28,7 +28,6 @@
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
-#include "third_party/blink/renderer/core/paint/cull_rect_updater.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_property_tree_printer.h"
 #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
@@ -206,11 +205,6 @@
   Walk(root_frame_view, context);
   paint_invalidator_.ProcessPendingDelayedPaintInvalidations();
 
-  if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
-    if (auto* layout_view = root_frame_view.GetLayoutView())
-      CullRectUpdater(*layout_view->Layer()).Update();
-  }
-
 #if DCHECK_IS_ON()
   if (needs_tree_builder_context_update) {
     if (VLOG_IS_ON(2) && root_frame_view.GetLayoutView()) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index c8803095..b2d1e4a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -624,7 +624,8 @@
   DCHECK(!GetLayoutObject());
 
   if (DisplayLockUtilities::NearestLockedExclusiveAncestor(*GetNode())) {
-    if (DisplayLockUtilities::ShouldIgnoreNodeDueToDisplayLock(
+    if (IsAriaHidden() ||
+        DisplayLockUtilities::ShouldIgnoreNodeDueToDisplayLock(
             *GetNode(), DisplayLockActivationReason::kAccessibility)) {
       if (ignored_reasons)
         ignored_reasons->push_back(IgnoredReason(kAXNotRendered));
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index ce35c04..a8e4445b 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -2930,6 +2930,7 @@
   if (!node)
     return false;
 
+  // content-visibility:hidden or content-visibility: auto.
   if (DisplayLockUtilities::NearestLockedExclusiveAncestor(*node)) {
     // Ensure contents of head, style and script are never exposed.
     // Note: an AXObject is created for <title> to gather the document's name.
@@ -2941,6 +2942,8 @@
     DCHECK(!Traversal<HTMLScriptElement>::FirstAncestorOrSelf(*node)) << node;
 
     // content-visibility: hidden subtrees are always hidden.
+    // content-visibility: auto subtrees are treated as visible, as we must
+    // make a guess since computed style is not available.
     return DisplayLockUtilities::ShouldIgnoreNodeDueToDisplayLock(
         *node, DisplayLockActivationReason::kAccessibility);
   }
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc
index 435855b..d6afb38 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc
@@ -5,8 +5,11 @@
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.h"
 
 #include "third_party/blink/public/strings/grit/blink_strings.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_scroll_into_view_options.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_union_boolean_scrollintoviewoptions.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
+#include "third_party/blink/renderer/core/dom/frame_request_callback_collection.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
 #include "third_party/blink/renderer/core/html/forms/html_label_element.h"
@@ -45,6 +48,28 @@
 
 }  // anonymous namespace
 
+class MediaControlPlaybackSpeedListElement::RequestAnimationFrameCallback final
+    : public FrameCallback {
+ public:
+  explicit RequestAnimationFrameCallback(
+      MediaControlPlaybackSpeedListElement* list)
+      : list_(list) {}
+
+  RequestAnimationFrameCallback(const RequestAnimationFrameCallback&) = delete;
+  RequestAnimationFrameCallback& operator=(
+      const RequestAnimationFrameCallback&) = delete;
+
+  void Invoke(double) override { list_->CenterCheckedItem(); }
+
+  void Trace(Visitor* visitor) const override {
+    visitor->Trace(list_);
+    FrameCallback::Trace(visitor);
+  }
+
+ private:
+  Member<MediaControlPlaybackSpeedListElement> list_;
+};
+
 MediaControlPlaybackSpeedListElement::MediaControlPlaybackSpeedListElement(
     MediaControlsImpl& media_controls)
     : MediaControlPopupMenuElement(media_controls) {
@@ -111,6 +136,7 @@
   if (playback_rate == MediaElement().playbackRate()) {
     playback_speed_item_input->setChecked(true);
     playback_speed_item->setAttribute(html_names::kAriaCheckedAttr, "true");
+    checked_item_ = playback_speed_item;
   }
   // Allows to focus the list entry instead of the button.
   playback_speed_item->setTabIndex(0);
@@ -154,6 +180,8 @@
 
   ParserAppendChild(CreatePlaybackSpeedHeaderItem());
 
+  checked_item_ = nullptr;
+
   // Construct a menu for playback speeds.
   for (unsigned i = 0; i < base::size(kPlaybackSpeeds); i++) {
     auto& playback_speed = kPlaybackSpeeds[i];
@@ -168,6 +196,24 @@
                                       "menuitemcheckbox");
     ParserAppendChild(playback_speed_item);
   }
+  RequestAnimationFrameCallback* callback =
+      MakeGarbageCollected<RequestAnimationFrameCallback>(this);
+  GetDocument().RequestAnimationFrame(callback);
+}
+
+void MediaControlPlaybackSpeedListElement::CenterCheckedItem() {
+  if (!checked_item_)
+    return;
+  ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
+  options->setBlock("center");
+  auto* arg =
+      MakeGarbageCollected<V8UnionBooleanOrScrollIntoViewOptions>(options);
+  checked_item_->scrollIntoView(arg);
+}
+
+void MediaControlPlaybackSpeedListElement::Trace(Visitor* visitor) const {
+  visitor->Trace(checked_item_);
+  MediaControlPopupMenuElement::Trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.h
index ff76bcf..a7eaf46 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.h
@@ -22,7 +22,11 @@
 
   void SetIsWanted(bool) final;
 
+  void Trace(Visitor*) const override;
+
  private:
+  class RequestAnimationFrameCallback;
+
   void DefaultEventHandler(Event&) override;
 
   void RefreshPlaybackSpeedListMenu();
@@ -33,6 +37,11 @@
 
   // Creates the header element of the playback speed list.
   Element* CreatePlaybackSpeedHeaderItem();
+
+  // Centers vertically the checked item in the playback speed list.
+  void CenterCheckedItem();
+
+  Member<Element> checked_item_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_logger.cc b/third_party/blink/renderer/modules/webcodecs/codec_logger.cc
index 94a44c0d..257f84f 100644
--- a/third_party/blink/renderer/modules/webcodecs/codec_logger.cc
+++ b/third_party/blink/renderer/modules/webcodecs/codec_logger.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/inspector/inspector_media_context_impl.h"
+#include "third_party/blink/renderer/platform/wtf/wtf.h"
 
 namespace blink {
 
@@ -25,7 +26,8 @@
   // collected before |parent_media_log_| is destroyed.
   if (!context->IsContextDestroyed()) {
     parent_media_log_ = Platform::Current()->GetMediaLog(
-        MediaInspectorContextImpl::From(*context), task_runner);
+        MediaInspectorContextImpl::From(*context), task_runner,
+        /*is_on_worker=*/!IsMainThread());
   }
 
   // NullMediaLog silently and safely does nothing.
@@ -37,10 +39,6 @@
   media_log_ = parent_media_log_->Clone();
 }
 
-CodecLogger::CodecLogger()
-    : parent_media_log_(std::make_unique<media::NullMediaLog>()),
-      media_log_(parent_media_log_->Clone()) {}
-
 DOMException* CodecLogger::MakeException(std::string error_msg,
                                          media::Status status) {
   media_log_->NotifyError(status);
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_logger.h b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
index 80bb6f6..0329c6e 100644
--- a/third_party/blink/renderer/modules/webcodecs/codec_logger.h
+++ b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
@@ -30,9 +30,6 @@
 // call Neuter() if the ExecutionContext passed to the constructor is destroyed.
 class MODULES_EXPORT CodecLogger final {
  public:
-  // Creates a CodecLogger backed by a NullMediaLog, which does nothing.
-  CodecLogger();
-
   // Attempts to create CodecLogger backed by a BatchingMediaLog. Falls back to
   // a NullMediaLog on failure.
   CodecLogger(ExecutionContext*,
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
index bfdef322..e4ca1009 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
@@ -75,14 +75,8 @@
   ExecutionContext* context = GetExecutionContext();
   DCHECK(context);
 
-  // TODO(crbug.com/1151005): Use a real MediaLog in worker contexts too.
-  if (IsMainThread()) {
-    logger_ = std::make_unique<CodecLogger>(
-        context, context->GetTaskRunner(TaskType::kInternalMedia));
-  } else {
-    // This will create a logger backed by a NullMediaLog, which does nothing.
-    logger_ = std::make_unique<CodecLogger>();
-  }
+  logger_ = std::make_unique<CodecLogger>(
+      context, context->GetTaskRunner(TaskType::kInternalMedia));
 
   logger_->log()->SetProperty<media::MediaLogProperty::kFrameUrl>(
       context->Url().GetString().Ascii());
diff --git a/third_party/blink/renderer/modules/webcodecs/encoder_base.cc b/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
index f159c05..5bc5fc8f 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
+++ b/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
@@ -43,14 +43,8 @@
     : ExecutionContextLifecycleObserver(ExecutionContext::From(script_state)),
       state_(V8CodecState::Enum::kUnconfigured),
       script_state_(script_state) {
-  // TODO(crbug.com/1151005): Use a real MediaLog in worker contexts too.
-  if (IsMainThread()) {
-    logger_ = std::make_unique<CodecLogger>(
-        GetExecutionContext(), Thread::MainThread()->GetTaskRunner());
-  } else {
-    // This will create a logger backed by a NullMediaLog, which does nothing.
-    logger_ = std::make_unique<CodecLogger>();
-  }
+  logger_ = std::make_unique<CodecLogger>(GetExecutionContext(),
+                                          Thread::Current()->GetTaskRunner());
 
   media::MediaLog* log = logger_->log();
 
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_core.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_core.cc
index 9ede7c13..9a9836f0 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_core.cc
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_core.cc
@@ -135,12 +135,18 @@
 
 std::unique_ptr<ImageDecoderCore::ImageDecodeResult> ImageDecoderCore::Decode(
     uint32_t frame_index,
-    bool complete_frames_only) {
+    bool complete_frames_only,
+    const base::AtomicFlag* abort_flag) {
   DCHECK(decoder_);
 
   auto result = std::make_unique<ImageDecodeResult>();
   result->frame_index = frame_index;
 
+  if (abort_flag->IsSet()) {
+    result->status = Status::kAborted;
+    return result;
+  }
+
   if (decoder_->Failed()) {
     result->status = Status::kDecodeError;
     return result;
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_core.h b/third_party/blink/renderer/modules/webcodecs/image_decoder_core.h
index 0d38ad4..fdca0b1 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_core.h
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_core.h
@@ -56,6 +56,7 @@
     kNoImage,      // No new images or no image could currently be decoded.
     kDecodeError,  // ImageDecoder::Failed() became true.
     kIndexError,   // |frame_index| out of range when |data_complete_| is true.
+    kAborted,      // Decode was aborted by caller.
   };
 
   struct ImageDecodeResult {
@@ -76,9 +77,11 @@
   };
 
   // Decodes the frame at |frame_index|, returning partial frames relative to
-  // the last Decode() request if |complete_frames_only| is false.
+  // the last Decode() request if |complete_frames_only| is false. |abort_flag|
+  // may be used to cancel decoding; it must outlive this Decode() call.
   std::unique_ptr<ImageDecodeResult> Decode(uint32_t frame_index,
-                                            bool complete_frames_only);
+                                            bool complete_frames_only,
+                                            const base::AtomicFlag* abort_flag);
 
   // Calls ImageDecoder::SetData() after appending |data| to |stream_buffer_|.
   // May not be called after |data_complete| becomes true.
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc
index 8c04c19..1eee849a 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.cc
@@ -83,7 +83,13 @@
     bool complete_frames_only)
     : resolver(resolver),
       frame_index(frame_index),
-      complete_frames_only(complete_frames_only) {}
+      complete_frames_only(complete_frames_only),
+      abort_flag(std::make_unique<base::AtomicFlag>()) {}
+
+ImageDecoderExternal::DecodeRequest::~DecodeRequest() {
+  // This must have already been released to the decoder thread manually.
+  DCHECK(!abort_flag);
+}
 
 void ImageDecoderExternal::DecodeRequest::Trace(Visitor* visitor) const {
   visitor->Trace(resolver);
@@ -142,7 +148,7 @@
     animation_option_ = AnimationOptionFromIsAnimated(*prefer_animation_);
   }
 
-  auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
+  decode_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
       {base::TaskPriority::USER_VISIBLE,
        base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
 
@@ -156,8 +162,9 @@
     }
 
     decoder_ = std::make_unique<WTF::SequenceBound<ImageDecoderCore>>(
-        task_runner, mime_type_, /*data=*/nullptr, /*data_complete=*/false,
-        alpha_option, color_behavior, desired_size, animation_option_);
+        decode_task_runner_, mime_type_, /*data=*/nullptr,
+        /*data_complete=*/false, alpha_option, color_behavior, desired_size,
+        animation_option_);
 
     consumer_ = MakeGarbageCollected<ReadableStreamBytesConsumer>(
         script_state, init->data().GetAsReadableStream());
@@ -199,8 +206,9 @@
   data_complete_ = true;
   completed_property_->ResolveWithUndefined();
   decoder_ = std::make_unique<WTF::SequenceBound<ImageDecoderCore>>(
-      task_runner, mime_type_, std::move(segment_reader), data_complete_,
-      alpha_option, color_behavior, desired_size, animation_option_);
+      decode_task_runner_, mime_type_, std::move(segment_reader),
+      data_complete_, alpha_option, color_behavior, desired_size,
+      animation_option_);
 
   DecodeMetadata();
 }
@@ -300,12 +308,18 @@
   num_submitted_decodes_ = 0u;
   decode_weak_factory_.InvalidateWeakPtrs();
 
-  // Move all state to local variables since promise resolution is reentrant.
+  // Move all state to local variables since promise resolution is re-entrant.
   HeapVector<Member<DecodeRequest>> local_pending_decodes;
   local_pending_decodes.swap(pending_decodes_);
 
-  for (auto& request : local_pending_decodes)
+  for (auto& request : local_pending_decodes) {
     request->resolver->Reject(exception);
+    request->abort_flag->Set();
+
+    // Since the AtomicFlag may still be referenced by the decoder sequence, we
+    // need to delete it on that sequence.
+    decode_task_runner_->DeleteSoon(FROM_HERE, std::move(request->abort_flag));
+  }
 }
 
 void ImageDecoderExternal::close() {
@@ -454,7 +468,8 @@
 
     ++num_submitted_decodes_;
     decoder_->AsyncCall(&ImageDecoderCore::Decode)
-        .WithArgs(request->frame_index, request->complete_frames_only)
+        .WithArgs(request->frame_index, request->complete_frames_only,
+                  WTF::CrossThreadUnretained(request->abort_flag.get()))
         .Then(CrossThreadBindOnce(&ImageDecoderExternal::OnDecodeReady,
                                   decode_weak_factory_.GetWeakPtr()));
   }
@@ -472,6 +487,7 @@
 
   // Note: Promise resolution may invoke calls into this class.
   for (auto& request : completed_decodes) {
+    DCHECK(!request->abort_flag->IsSet());
     if (request->exception) {
       request->resolver->Reject(request->exception);
     } else if (request->range_error_message) {
@@ -481,6 +497,10 @@
     } else {
       request->resolver->Resolve(request->result);
     }
+
+    // Since the AtomicFlag may still be referenced by the decoder sequence, we
+    // need to delete it on that sequence.
+    decode_task_runner_->DeleteSoon(FROM_HERE, std::move(request->abort_flag));
   }
 }
 
@@ -501,6 +521,11 @@
   }
 
   request->pending = false;
+
+  // Abort always invalidates WeakPtrs, so OnDecodeReady() should never receive
+  // the kAborted status.
+  DCHECK_NE(result->status, ImageDecoderCore::Status::kAborted);
+
   if (result->status == ImageDecoderCore::Status::kIndexError) {
     request->range_error_message =
         ExceptionMessages::IndexOutsideRange<uint32_t>(
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.h b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.h
index 185d4c0..c47c6c1 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_external.h
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_external.h
@@ -126,6 +126,7 @@
   int pending_metadata_requests_ = 0;
 
   // The workhorse which actually does the decoding. Bound to another sequence.
+  scoped_refptr<base::SequencedTaskRunner> decode_task_runner_;
   std::unique_ptr<WTF::SequenceBound<ImageDecoderCore>> decoder_;
 
   // List of tracks in this image. Filled in during OnMetadata().
@@ -139,6 +140,7 @@
     DecodeRequest(ScriptPromiseResolver* resolver,
                   uint32_t frame_index,
                   bool complete_frames_only);
+    ~DecodeRequest();
     void Trace(Visitor*) const;
     bool IsFinal() const;
 
@@ -148,6 +150,7 @@
     bool pending = false;
     absl::optional<size_t> bytes_read_index;
     Member<ImageDecodeResult> result;
+    std::unique_ptr<base::AtomicFlag> abort_flag;
 
     absl::optional<String> range_error_message;
     Member<DOMException> exception;
diff --git a/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc b/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc
index fc3a87b..d752b66 100644
--- a/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc
+++ b/third_party/blink/renderer/modules/webcodecs/image_decoder_external_test.cc
@@ -293,6 +293,38 @@
   }
 }
 
+TEST_F(ImageDecoderTest, DecodeAborted) {
+  V8TestingScope v8_scope;
+  constexpr char kImageType[] = "image/avif";
+  EXPECT_TRUE(IsTypeSupported(&v8_scope, kImageType));
+
+  // Use an expensive-to-decode image to try and ensure work exists to abort.
+  auto* decoder = CreateDecoder(
+      &v8_scope,
+      "images/resources/avif/red-at-12-oclock-with-color-profile-12bpc.avif",
+      kImageType);
+
+  ASSERT_TRUE(decoder);
+  ASSERT_FALSE(v8_scope.GetExceptionState().HadException());
+
+  {
+    auto promise = decoder->tracks().ready(v8_scope.GetScriptState());
+    ScriptPromiseTester tester(v8_scope.GetScriptState(), promise);
+    tester.WaitUntilSettled();
+    ASSERT_TRUE(tester.IsFulfilled());
+  }
+
+  // Setup a scenario where there should be work to abort. Since blink tests use
+  // real threads with the base::TaskEnvironment, we can't actually be sure that
+  // work hasn't completed by the time reset() is called.
+  for (int i = 0; i < 10; ++i)
+    decoder->decode();
+  decoder->reset();
+
+  // There's no way to verify work was aborted, so just ensure nothing explodes.
+  base::RunLoop().RunUntilIdle();
+}
+
 TEST_F(ImageDecoderTest, DecoderReset) {
   V8TestingScope v8_scope;
   constexpr char kImageType[] = "image/gif";
diff --git a/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc b/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc
index 410c38a..3317e8a 100644
--- a/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc
+++ b/third_party/blink/renderer/platform/audio/pffft/fft_frame_pffft.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/platform/audio/hrtf_panner.h"
 #include "third_party/blink/renderer/platform/audio/vector_math.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
 #include "third_party/pffft/src/pffft.h"
 namespace blink {
 
@@ -43,19 +44,46 @@
   // we're confident the first call is from the main thread.
   static bool first_call = true;
 
-  if (first_call) {
-    // Make sure we construct the fft_setups vector below on the main thread.
-    // Once constructed, we can access it from any thread.
-    DCHECK(IsMainThread());
-    first_call = false;
-  }
-
   // A HashMap to hold all of the possible FFT setups we need.  The setups are
   // initialized lazily.  The key is the fft size, and the value is the setup
   // data.
   typedef HashMap<unsigned, std::unique_ptr<FFTSetup>> FFTHashMap_t;
 
-  DEFINE_STATIC_LOCAL(FFTHashMap_t, fft_setups, ());
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(FFTHashMap_t, fft_setups, ());
+
+  if (first_call) {
+    DEFINE_STATIC_LOCAL(Mutex, setup_lock, ());
+
+    // Make sure we construct the fft_setups vector below on the main thread.
+    // Once constructed, we can access it from any thread.
+    DCHECK(IsMainThread());
+    first_call = false;
+
+    MutexLocker locker(setup_lock);
+
+    // Initialize the hash map with all the possible keys (FFT sizes), with a
+    // value of nullptr because we want to initialize the setup data lazily. The
+    // set of valid FFT sizes for PFFFT are of the form 2^k*3^m*5*n where k >=
+    // 5, m >= 0, n >= 0.  We only go up to a max size of 32768, because we need
+    // at least an FFT size of 32768 for the convolver node.
+
+    // TODO(crbug.com/988121):  Sync this with kMaxFFTPow2Size.
+    const int kMaxConvolverFFTSize = 32768;
+
+    for (int n = 1; n <= kMaxConvolverFFTSize; n *= 5) {
+      for (int m = 1; m <= kMaxConvolverFFTSize / n; m *= 3) {
+        for (int k = 32; k <= kMaxConvolverFFTSize / (n * m); k *= 2) {
+          int size = k * m * n;
+          if (size <= kMaxConvolverFFTSize && !fft_setups.Contains(size)) {
+            fft_setups.insert(size, nullptr);
+          }
+        }
+      }
+    }
+
+    // There should be 87 entries when we're done.
+    DCHECK_EQ(fft_setups.size(), 87u);
+  }
 
   return fft_setups;
 }
@@ -63,13 +91,19 @@
 void FFTFrame::InitializeFFTSetupForSize(wtf_size_t fft_size) {
   auto& setup = FFTSetups();
 
-  if (!setup.Contains(fft_size)) {
+  DCHECK(setup.Contains(fft_size));
+
+  if (setup.find(fft_size)->value == nullptr) {
+    DEFINE_STATIC_LOCAL(Mutex, setup_lock, ());
+
     // Make sure allocation of a new setup only occurs on the main thread so we
     // don't have a race condition with multiple threads trying to write to the
     // same element of the vector.
     DCHECK(IsMainThread());
 
-    setup.insert(fft_size, std::make_unique<FFTSetup>(fft_size));
+    auto fft_data = std::make_unique<FFTSetup>(fft_size);
+    MutexLocker locker(setup_lock);
+    setup.find(fft_size)->value = std::move(fft_data);
   }
 }
 
@@ -77,6 +111,7 @@
   auto& setup = FFTSetups();
 
   DCHECK(setup.Contains(fft_size));
+  DCHECK(setup.find(fft_size)->value);
 
   return setup.find(fft_size)->value->GetSetup();
 }
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc
index 0a90f61..f9d9e41 100644
--- a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc
@@ -48,6 +48,81 @@
   info_.have_animation = false;
 }
 
+bool JXLImageDecoder::ReadBytes(size_t remaining,
+                                size_t* offset,
+                                WTF::Vector<uint8_t>* segment,
+                                FastSharedBufferReader* reader,
+                                const uint8_t** jxl_data,
+                                size_t* jxl_size) {
+  *offset -= remaining;
+  if (*offset + remaining >= reader->size()) {
+    segment->clear();
+    if (IsAllDataReceived()) {
+      DVLOG(1) << "need more input but all data received";
+      SetFailed();
+      return false;
+    }
+    // Return because we need more input from the reader, to continue
+    // decoding in the next call.
+    return false;
+  }
+  const char* buffer = nullptr;
+  size_t read = reader->GetSomeData(buffer, *offset);
+
+  if (read > remaining) {
+    // Sufficient data present in the segment from the
+    // FastSharedBufferReader, no need to copy to segment_.
+    *jxl_data = reinterpret_cast<const uint8_t*>(buffer);
+    *jxl_size = read;
+    *offset += read;
+    segment->clear();
+  } else {
+    if (segment->size() == remaining) {
+      // Keep reading from the end of the segment_ we already are
+      // appending to. The above read is ignored, and start reading after the
+      // end of the data we already have.
+      *offset += remaining;
+      read = 0;
+    } else {
+      // segment_->size() could be greater than or smaller than remaining.
+      // Typically, it'll be smaller than. If it is greater than, then we could
+      // do something similar as in the segment->size() == remaining case but
+      // remove the non-remaining bytes from the beginning of the segment_
+      // vector. This would avoid re-reading, however the case where
+      // segment->size() > remaining is rare since normally if the JXL decoder
+      // returns a positive value for remaining, it will be consistent, making
+      // the sizes match exactly, so this more complex case is not implemented.
+      // Clear the segment, the bytes from the GetSomeData above will be
+      // appended and then we continue reading from the position after the
+      // above GetSomeData read.
+      segment->clear();
+    }
+
+    for (;;) {
+      if (read) {
+        *offset += read;
+        segment->Append(buffer, read);
+      }
+      if (segment->size() > remaining) {
+        *jxl_data = segment->data();
+        *jxl_size = segment->size();
+        // Have enough data, break and continue JXL decoding, rather than
+        // copy more input than needed into segment_.
+        break;
+      }
+      read = reader->GetSomeData(buffer, *offset);
+      if (read == 0) {
+        // We tested above that *offset + remaining >= reader.size() so
+        // should be able to read all data.
+        DVLOG(1) << "couldn't read all available data";
+        SetFailed();
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 void JXLImageDecoder::DecodeImpl(size_t index, bool only_size) {
   if (Failed())
     return;
@@ -104,28 +179,22 @@
         return;
       }
       case JXL_DEC_NEED_MORE_INPUT: {
+        // The decoder returns how many bytes it has not yet processed, and
+        // must be included in the next JxlDecoderSetInput call.
         const size_t remaining = JxlDecoderReleaseInput(dec_.get());
-        offset_ -= remaining;
-        if (offset_ >= reader.size()) {
-          if (IsAllDataReceived()) {
-            DVLOG(1) << "need more input but all data received";
-            SetFailed();
-            return;
-          }
-          // Return because we need more input from the reader, to continue
-          // decoding in the next call.
+        const uint8_t* jxl_data = nullptr;
+        size_t jxl_size = 0;
+        if (!ReadBytes(remaining, &offset_, &segment_, &reader, &jxl_data,
+                       &jxl_size)) {
           return;
         }
-        const char* buffer = nullptr;
-        size_t read = reader.GetSomeData(buffer, offset_);
+
         if (JXL_DEC_SUCCESS !=
-            JxlDecoderSetInput(
-                dec_.get(), reinterpret_cast<const uint8_t*>(buffer), read)) {
+            JxlDecoderSetInput(dec_.get(), jxl_data, jxl_size)) {
           DVLOG(1) << "JxlDecoderSetInput failed";
           SetFailed();
           return;
         }
-        offset_ += read;
         break;
       }
       case JXL_DEC_BASIC_INFO: {
@@ -357,6 +426,7 @@
       case JXL_DEC_SUCCESS: {
         dec_ = nullptr;
         finished_ = true;
+        segment_.clear();
         return;
       }
       default: {
@@ -454,26 +524,25 @@
         return frame_buffer_cache_.size();
       }
       case JXL_DEC_NEED_MORE_INPUT: {
-        frame_count_offset_ -= JxlDecoderReleaseInput(frame_count_dec_.get());
-        if (frame_count_offset_ >= reader.size()) {
-          if (IsAllDataReceived()) {
-            DVLOG(1) << "need more input but all data received";
-            SetFailed();
+        // The decoder returns how many bytes it has not yet processed, and
+        // must be included in the next JxlDecoderSetInput call.
+        const size_t remaining = JxlDecoderReleaseInput(frame_count_dec_.get());
+        const uint8_t* jxl_data = nullptr;
+        size_t jxl_size = 0;
+        if (!ReadBytes(remaining, &frame_count_offset_, &frame_count_segment_,
+                       &reader, &jxl_data, &jxl_size)) {
+          if (Failed()) {
             return frame_buffer_cache_.size();
           }
           return frame_durations_.size();
         }
-        const char* buffer = nullptr;
-        size_t read = reader.GetSomeData(buffer, frame_count_offset_);
+
         if (JXL_DEC_SUCCESS !=
-            JxlDecoderSetInput(frame_count_dec_.get(),
-                               reinterpret_cast<const uint8_t*>(buffer),
-                               read)) {
+            JxlDecoderSetInput(frame_count_dec_.get(), jxl_data, jxl_size)) {
           DVLOG(1) << "JxlDecoderSetInput failed";
           SetFailed();
           return frame_buffer_cache_.size();
         }
-        frame_count_offset_ += read;
         break;
       }
       case JXL_DEC_FRAME: {
@@ -497,6 +566,7 @@
         // anymore: we can free the memory.
         frame_count_dec_ = nullptr;
         DCHECK(has_full_frame_count_);
+        frame_count_segment_.clear();
         return frame_durations_.size();
       }
       default: {
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
index 792ece1..3c687c6 100644
--- a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
@@ -78,6 +78,18 @@
   int RepetitionCount() const override;
   bool CanReusePreviousFrameBuffer(size_t) const override { return false; }
 
+  // Reads bytes from the segment reader, after releasing input from the JXL
+  // decoder, which required `remaining` previous bytes to still be available.
+  // Starts reading from *offset - remaining, and ensures more than remaining
+  // bytes are read, if possible. Returns false if not enough bytes are
+  // available or if Failed() was set.
+  bool ReadBytes(size_t remaining,
+                 size_t* offset,
+                 WTF::Vector<uint8_t>* segment,
+                 FastSharedBufferReader* reader,
+                 const uint8_t** jxl_data,
+                 size_t* jxl_size);
+
   JxlDecoderPtr dec_ = nullptr;
   size_t offset_ = 0;
 
@@ -101,6 +113,11 @@
   bool has_full_frame_count_ = false;
   size_t size_at_last_frame_count_ = 0;
   WTF::Vector<float> frame_durations_;
+  // Multiple concatenated segments from the FastSharedBufferReader, these are
+  // only used when a single segment did not contain enough data for the JXL
+  // parser.
+  WTF::Vector<uint8_t> segment_;
+  WTF::Vector<uint8_t> frame_count_segment_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc
index 9d2c7e7..76404d8 100644
--- a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc
@@ -180,6 +180,64 @@
   }
 }
 
+// SegmentReader implementation for testing, which always returns segments
+// of size 1. This allows to test whether the decoder handles streaming
+// correctly in the most fine-grained case.
+class PerByteSegmentReader : public SegmentReader {
+ public:
+  PerByteSegmentReader(SharedBuffer& buffer) : buffer_(buffer) {}
+  size_t size() const override { return buffer_.size(); }
+  size_t GetSomeData(const char*& data, size_t position) const override {
+    if (position >= buffer_.size()) {
+      return 0;
+    }
+    data = buffer_.Data() + position;
+    return 1;
+  }
+  sk_sp<SkData> GetAsSkData() const override { return nullptr; }
+
+ private:
+  SharedBuffer& buffer_;
+};
+
+// Tests whether the decoder successfully parses the file without errors or
+// infinite loop in the worst case of the reader returning 1-byte segments.
+void TestSegmented(const char* jxl_file, IntSize expected_size) {
+  auto decoder = std::make_unique<JXLImageDecoder>(
+      ImageDecoder::kAlphaNotPremultiplied, ImageDecoder::kDefaultBitDepth,
+      ColorBehavior::Tag(), ImageDecoder::kNoDecodedImageByteLimit);
+  scoped_refptr<SharedBuffer> data = ReadFile(jxl_file);
+  EXPECT_FALSE(data->IsEmpty());
+
+  scoped_refptr<SegmentReader> reader =
+      base::AdoptRef(new PerByteSegmentReader(*data.get()));
+  decoder->SetData(reader, true);
+
+  ImageFrame* frame;
+  for (;;) {
+    frame = decoder->DecodeFrameBufferAtIndex(0);
+    if (decoder->Failed())
+      break;
+    if (frame)
+      break;
+  }
+
+  EXPECT_TRUE(decoder->IsSizeAvailable());
+  EXPECT_LE(1u, decoder->FrameCount());
+  EXPECT_TRUE(!!frame);
+  EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+  EXPECT_FALSE(decoder->Failed());
+  EXPECT_EQ(expected_size, decoder->Size());
+}
+
+TEST(JXLTests, SegmentedTest) {
+  TestSegmented("/images/resources/jxl/alpha-lossless.jxl", IntSize(2, 10));
+  TestSegmented("/images/resources/jxl/3x3_srgb_lossy.jxl", IntSize(3, 3));
+  TestSegmented("/images/resources/jxl/pq_gradient_icc_lossy.jxl",
+                IntSize(16, 16));
+  TestSegmented("/images/resources/jxl/animated.jxl", IntSize(16, 16));
+}
+
 TEST(JXLTests, SizeTest) {
   TestSize("/images/resources/jxl/alpha-lossless.jxl", IntSize(2, 10));
 }
diff --git a/third_party/blink/renderer/platform/network/http_parsers.cc b/third_party/blink/renderer/platform/network/http_parsers.cc
index 602de86..8f83be6 100644
--- a/third_party/blink/renderer/platform/network/http_parsers.cc
+++ b/third_party/blink/renderer/platform/network/http_parsers.cc
@@ -252,7 +252,10 @@
           ? absl::make_optional(ConvertToBlink(in->critical_ch.value()))
           : absl::nullopt,
       in->xfo, ConvertToBlink(in->link_headers),
-      ConvertToBlink(in->timing_allow_origin), in->bfcache_opt_in_unload);
+      ConvertToBlink(in->timing_allow_origin), in->bfcache_opt_in_unload,
+      in->reporting_endpoints.has_value()
+          ? absl::make_optional(ConvertToBlink(in->reporting_endpoints.value()))
+          : absl::nullopt);
 }
 
 }  // namespace mojom
diff --git a/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.cc b/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.cc
index 01ad6416..c4c987d 100644
--- a/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.cc
+++ b/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.cc
@@ -58,7 +58,7 @@
 std::unique_ptr<ElasticOverscrollController>
 ElasticOverscrollController::Create(cc::ScrollElasticityHelper* helper) {
 #if defined(OS_WIN)
-  return base::FeatureList::IsEnabled(features::kElasticOverscrollWin)
+  return base::FeatureList::IsEnabled(features::kElasticOverscroll)
              ? std::make_unique<ElasticOverscrollControllerBezier>(helper)
              : nullptr;
 #endif
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
index 2d10d5fc..2cd58bf4 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
@@ -1014,7 +1014,10 @@
         if self.patchset:
             command.append('--patchset=' + str(self.patchset))
         command += tests_to_rebaseline
-        self.host.executive.run_command(command)
+        rebaseline_output = self.host.executive.run_command(command)
+        _log.debug(
+            "Output of rebaseline-cl:\n%s\n--end of rebaseline-cl output --" %
+            rebaseline_output)
         return tests_to_rebaseline, test_results
 
     def get_tests_to_rebaseline(self, test_results):
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer b/third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer
index b3171a1..3a7cb85e 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer
+++ b/third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer
@@ -31,6 +31,7 @@
 crbug.com/1175758 [ Linux ] media/video-controls-hide-on-move-outside-controls.html [ Failure Pass ]
 crbug.com/1175758 [ Linux ] virtual/android/fullscreen/model/fully-exit-fullscreen-single.html [ Failure Pass ]
 [ Linux ] transforms/shadows.html [ Skip ]
+crbug.com/1215248 [ Linux ] images/color-profile-mask-image-svg.html [ Failure ]
 
 # Flaky on Windows.
 crbug.com/1026375 [ Win ] media/video-currentTime-delay.html [ Timeout Pass ]
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index e2257cc..ce21327c 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -188,4 +188,5 @@
 
 # Sheriff 2021-06-02
 crbug.com/1215390 [ Linux ] external/wpt/pointerevents/pointerevent_pointerId_scope.html [ Pass Failure ]
-crbug.com/1215530 [ Linux ] external/wpt/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html [ Pass Timeout ]
+crbug.com/1215632 [ Linux ] external/wpt/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html [ Pass Timeout ]
+crbug.com/1215632 [ Linux ] external/wpt/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index b3a9767..a7bc0b13 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3923,7 +3923,6 @@
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-004.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html [ Failure ]
 crbug.com/1045599 virtual/layout-ng-grid/external/wpt/css/css-grid/layout-algorithm/grid-intrinsic-size-with-orthogonal-items.html [ Failure ]
-crbug.com/1045599 virtual/layout-ng-grid/fast/css-grid-layout/percent-padding-margin-resolution-grid-item-update.html [ Failure ]
 
 ### Tests *expected* to be failing with LayoutNGGrid enabled:
 crbug.com/1108097 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-004.html [ Failure ]
@@ -5533,8 +5532,6 @@
 
 # SwANGLE issues
 crbug.com/1204234 css3/blending/background-blend-mode-single-accelerated-element.html [ Failure ]
-crbug.com/1204234 external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html [ Failure ]
-crbug.com/1204234 external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https.html [ Failure ]
 
 # Upcoming DevTools change
 crbug.com/1006759 http/tests/devtools/profiler/cpu-profiler-save-load.js [ Pass Failure Timeout ]
@@ -6972,3 +6969,12 @@
 crbug.com/1210687 [ Win ] virtual/threaded/http/tests/devtools/tracing/timeline-misc/timeline-event-dispatch.js [ Pass Timeout ]
 crbug.com/1210687 [ Win ] virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/stale-revalidation-test.js [ Pass Timeout ]
 crbug.com/1210687 [ Win ] virtual/shared_array_buffer_on_desktop/http/tests/devtools/a11y-axe-core/sources/scope-pane-a11y-test.js [ Pass Timeout ]
+
+# Sheriff 2021-06-02
+crbug.com/1215575 [ Mac11.0 ] fast/peerconnection/RTCPeerConnection-applyConstraints-remoteVideoTrack.html [ Pass Timeout ]
+crbug.com/1215575 [ Mac11.0 ] fast/peerconnection/RTCPeerConnection-createDTMFSender.html [ Pass Failure ]
+crbug.com/1215575 [ Mac11.0 ] fast/peerconnection/RTCPeerConnection-datachannel.html [ Pass Timeout ]
+crbug.com/1215575 [ Mac11.0 ] fast/peerconnection/RTCPeerConnection-lifetime.html [ Pass Timeout ]
+crbug.com/1215575 [ Mac11.0 ] fast/peerconnection/RTCPeerConnection-sdes-gcm.html [ Pass Timeout ]
+crbug.com/1215581 [ Mac11.0 ] svg/filters/feImage-preserveAspectRatio-all.svg [ Pass Failure ]
+crbug.com/1215584 [ Mac11.0 ] inspector-protocol/layout-fonts/lang-fallback.js [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/crashtests/position-absolute-crash-014.html b/third_party/blink/web_tests/external/wpt/css/css-position/crashtests/position-absolute-crash-014.html
new file mode 100644
index 0000000..a169a7b9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/crashtests/position-absolute-crash-014.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1021676">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<style>
+#htmlvar00005 {
+  filter: drop-shadow(-1px -1px 1px yellow);
+  float: left;
+  line-height: 71vw;
+}
+
+#htmlvar00006 {
+  overflow-x: scroll;
+  position: absolute;
+}
+
+.class8 {
+  font: 52px sans-serif;
+}
+</style>
+<ol id="htmlvar00005">
+  <li id="htmlvar00006">
+    <details class="class8"></details>
+  </li>
+</ol>
diff --git a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-51.html b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-51.html
new file mode 100644
index 0000000..0257714
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-51.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Forced colors mode - color-scheme.</title>
+<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#forced-colors-properties">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  #dark {
+    color-scheme: dark;
+  }
+  #light {
+    color-scheme: light;
+    forced-color-adjust: none;
+  }
+</style>
+<div id="dark"></div>
+<div id="light"></div>
+
+<script>
+  test(function(){
+    assert_equals(getComputedStyle(dark).colorScheme, "light dark");
+    assert_equals(getComputedStyle(light).colorScheme, "light");
+  }, "Color-scheme computes to 'light dark' in forced colors mode, unless forced-color-adjust is none.");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/uievents/interface/click-event.htm b/third_party/blink/web_tests/external/wpt/uievents/interface/click-event.htm
index ff78ae95..b45dc29 100644
--- a/third_party/blink/web_tests/external/wpt/uievents/interface/click-event.htm
+++ b/third_party/blink/web_tests/external/wpt/uievents/interface/click-event.htm
@@ -15,9 +15,9 @@
     clicktarget.addEventListener('click', t.step_func(function (e) {
       assert_equals(e.constructor, window.PointerEvent, "Click is a PointerEvent");
       assert_true(e instanceof window.PointerEvent, "Click is an instance of PointerEvent");
-      // Since this click is not generated by a pointing device, pointerId and
-      // pointerType must have default values (0 and empty string)
-      assert_equals(e.pointerId, 0, "Click's pointerId has the default value of 0");
+      // Since this click is not generated by a pointing device, pointerId must have
+      // the reserved value -1, and pointerType must have the default empty string
+      assert_equals(e.pointerId, -1, "Click's pointerId has the default value of -1");
       assert_equals(e.pointerType, "", "Click's pointerType has the default value of empty string");
       assert_equals(e.screenX, 0, "Click's screenX coordinate should not be set.");
       assert_equals(e.screenY, 0, "Click's screenY coordinate should not be set.");
diff --git a/third_party/blink/web_tests/external/wpt/uievents/interface/keyboard-click-event.html b/third_party/blink/web_tests/external/wpt/uievents/interface/keyboard-click-event.html
index d70b8cc..2b5e06d 100644
--- a/third_party/blink/web_tests/external/wpt/uievents/interface/keyboard-click-event.html
+++ b/third_party/blink/web_tests/external/wpt/uievents/interface/keyboard-click-event.html
@@ -24,7 +24,7 @@
     currentTest.step(()=>{
       if(e instanceof PointerEvent){
         // We want the test to run on all browsers even if click is not a PointerEvent.
-        assert_equals(e.pointerId, 0, "Click's pointerId has default value");
+        assert_equals(e.pointerId, -1, "Click's pointerId has reserved value");
         assert_equals(e.pointerType, "", "Click's pointerType has default value");
       }
     });
diff --git a/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html
index b46f448..a68a0f2b5 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html
+++ b/third_party/blink/web_tests/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html
@@ -9,72 +9,79 @@
     let testName = "Test that getReflectionCubeMap returns or throws appropriately without a reflection map.";
 
     let testFunction = (session, controller, t, sessionObjects) => new Promise((resolve) => {
+      let halfFloatExt = sessionObjects.gl.getExtension('OES_texture_half_float');
+      // The preferredReflectionFormat used below is set to "rgba16f" by default.
+      // This means half float textures must be supported in order to run this test
+      if (!halfFloatExt) {
+        resolve(session.end());
+      } else {
         let debug = xr_debug.bind(this, 'testFunction');
         let lightProbe1 = null;
         let binding1 = new XRWebGLBinding(session, sessionObjects.gl);
 
-      // Request a default lightProbe
-      session.requestLightProbe({reflectionFormat: session.preferredReflectionFormat }).then((probe) => {
-        // Stash and end session.
-        lightProbe1 = probe;
+        // Request a default lightProbe
+        session.requestLightProbe({reflectionFormat: session.preferredReflectionFormat }).then((probe) => {
+          // Stash and end session.
+          lightProbe1 = probe;
 
-        debug("Querying first pair");
-        t.step(() => {
-          assert_equals(
-            binding1.getReflectionCubeMap(lightProbe1),
-            null,
-            "Active binding and light probe shouldn't throw when requesting cube map");
-        });
+          debug("Querying first pair");
+          t.step(() => {
+            assert_equals(
+              binding1.getReflectionCubeMap(lightProbe1),
+              null,
+              "Active binding and light probe shouldn't throw when requesting cube map");
+          });
 
-        return session.end();
-      }).then(() => {
-        // Need to request a new session.
-        navigator.xr.test.simulateUserActivation( () => {
-          navigator.xr.requestSession('immersive-ar', { 'requiredFeatures': ['light-estimation'] })
-          .then((newSession) => {
-            let newBinding = new XRWebGLBinding(newSession, sessionObjects.gl);
-            newSession.requestLightProbe({ reflectionFormat: newSession.preferredReflectionFormat }).then((newProbe) => {
-              t.step(() => {
-                debug("Querying second pair");
-                assert_equals(
-                  newBinding.getReflectionCubeMap(newProbe),
-                  null,
-                  "Newly created binding and light probe shouldn't throw");
-
-                debug("Querying old pair");
-                assert_throws_dom(
-                  "InvalidStateError",
-                  () => binding1.getReflectionCubeMap(lightProbe1),
-                  "Binding created with an ended session should throw InvalidStateError");
-                debug("Querying mismatched pair");
-                assert_throws_dom(
-                  "InvalidStateError",
-                  () => newBinding.getReflectionCubeMap(lightProbe1),
-                  "Querying binding with a probe with a different backing session should throw InvalidStateError");
-              });
-              debug("losing context");
-
-              // Trigger a context loss and verify that we are unable to get the reflectionCubeMap.
-              let lose_context_ext = sessionObjects.gl.getExtension('WEBGL_lose_context');
-
-              sessionObjects.gl.canvas.addEventListener('webglcontextlost', (ev) => {
-                ev.preventDefault();
-
+          return session.end();
+        }).then(() => {
+          // Need to request a new session.
+          navigator.xr.test.simulateUserActivation( () => {
+            navigator.xr.requestSession('immersive-ar', { 'requiredFeatures': ['light-estimation'] })
+            .then((newSession) => {
+              let newBinding = new XRWebGLBinding(newSession, sessionObjects.gl);
+              newSession.requestLightProbe({ reflectionFormat: newSession.preferredReflectionFormat }).then((newProbe) => {
                 t.step(() => {
+                  debug("Querying second pair");
+                  assert_equals(
+                    newBinding.getReflectionCubeMap(newProbe),
+                    null,
+                    "Newly created binding and light probe shouldn't throw");
+
+                  debug("Querying old pair");
                   assert_throws_dom(
                     "InvalidStateError",
-                    () => newBinding.getReflectionCubeMap(newProbe),
-                    "Querying for reflection cube map on a binding with context loss should throw InvalidStateError");
+                    () => binding1.getReflectionCubeMap(lightProbe1),
+                    "Binding created with an ended session should throw InvalidStateError");
+                  debug("Querying mismatched pair");
+                  assert_throws_dom(
+                    "InvalidStateError",
+                    () => newBinding.getReflectionCubeMap(lightProbe1),
+                    "Querying binding with a probe with a different backing session should throw InvalidStateError");
+                });
+                debug("losing context");
+
+                // Trigger a context loss and verify that we are unable to get the reflectionCubeMap.
+                let lose_context_ext = sessionObjects.gl.getExtension('WEBGL_lose_context');
+
+                sessionObjects.gl.canvas.addEventListener('webglcontextlost', (ev) => {
+                  ev.preventDefault();
+
+                  t.step(() => {
+                    assert_throws_dom(
+                      "InvalidStateError",
+                      () => newBinding.getReflectionCubeMap(newProbe),
+                      "Querying for reflection cube map on a binding with context loss should throw InvalidStateError");
+                  });
+
+                  resolve(newSession.end());
                 });
 
-                resolve(newSession.end());
-              });
-
-              lose_context_ext.loseContext();
-            }); // Request second light probe
-          }); // Request second session
-        }); // SimulateUserActivation
-      }); // .then on session end
+                lose_context_ext.loseContext();
+              }); // Request second light probe
+            }); // Request second session
+          }); // SimulateUserActivation
+        }); // .then on session end
+      } // halfFloatExt
     }); // testFunction
 
     xr_session_promise_test(
diff --git a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https.html b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https.html
index 7eadd005..17f991f 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https.html
+++ b/third_party/blink/web_tests/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https.html
@@ -183,7 +183,7 @@
 
     // check that the main color is used correctly (green)
     pixels[0] = pixels[1] = pixels[2] = pixels[3] = 30;
-    gl.readPixels(xrViewport.x + xrViewport.width / 2, xrViewport.y + xrViewport.height/4, 1, 1, gl.RGB, gl.UNSIGNED_BYTE, pixels);
+    gl.readPixels(xrViewport.x + xrViewport.width / 2, xrViewport.y + xrViewport.height/4, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
     if (pixels[0] == 0x0 && pixels[1] == 0xFF && pixels[2] == 0x0) { // green?
       // PASSED.
     } else if (pixels[0] == 0xFF && pixels[1] == 0xFF && pixels[2] == 0xFF) { // white?
@@ -194,7 +194,7 @@
 
     // check if stencil worked, i.e. white pixels in the center
     pixels[0] = pixels[1] = pixels[2] = pixels[3] = 20;
-    gl.readPixels(xrViewport.x + xrViewport.width / 2, xrViewport.y + xrViewport.height/2, 1, 1, gl.RGB, gl.UNSIGNED_BYTE, pixels);
+    gl.readPixels(xrViewport.x + xrViewport.width / 2, xrViewport.y + xrViewport.height/2, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
     if (pixels[0] == 0xFF && pixels[1] == 0xFF && pixels[2] == 0xFF) { // white?
       // PASSED.
     } else if (pixels[0] == 0x0 && pixels[1] == 0xFF && pixels[2] == 0x0) { // green?
diff --git a/third_party/blink/web_tests/media/media-controls.js b/third_party/blink/web_tests/media/media-controls.js
index 1e1865f0..2c2605c9 100644
--- a/third_party/blink/web_tests/media/media-controls.js
+++ b/third_party/blink/web_tests/media/media-controls.js
@@ -489,8 +489,16 @@
           return playbackSpeedItem;
   }
 }
+
 function clickPlaybackSpeedButton(video, callback) {
-  openOverflowAndClickButton(video, playbackSpeedOverflowItem(video), callback);
+  openOverflowAndClickButton(video, playbackSpeedOverflowItem(video), function() {
+    var playbackSpeed = playbackSpeedListItemAtPlaybackRate(video, video.playbackRate);
+    var playbackSpeedsList = playbackSpeedMenu(video);
+    assert_between_inclusive(playbackSpeedsList.scrollTop,
+        playbackSpeed.offsetTop + playbackSpeed.offsetHeight - playbackSpeedsList.offsetHeight,
+        playbackSpeed.offsetTop);
+    callback();
+  });
 }
 
 function clickPlaybackSpeedAtPlaybackRate(video, playbackRate, callback) {
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index cd4bf1c..022f4be 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -137,7 +137,7 @@
       'linux-angle-builder': 'angle_specific_release_trybot',
       'linux-angle-chromium-builder': 'gpu_tests_release_trybot',
       'mac-angle-builder': 'angle_specific_release_trybot',
-      'mac-angle-chromium-builder': 'gpu_tests_release_trybot_deterministic_mac',
+      'mac-angle-chromium-builder': 'gpu_tests_release_trybot',
       'win-angle-chromium-x64-builder': 'gpu_tests_release_trybot',
       'win-angle-chromium-x86-builder': 'gpu_tests_release_trybot_x86',
       'win-angle-x64-builder': 'angle_specific_release_trybot',
@@ -291,8 +291,8 @@
       'linux-lacros-builder-fyi-rel': 'lacros_on_linux_release_bot',
       'linux-lacros-tester-fyi-rel': 'lacros_on_linux_release_bot',
       'Mac Builder Next': 'gpu_tests_release_bot_minimal_symbols',
-      'Mac deterministic': 'release_bot_mac_strip_minimal_symbols_deterministic',
-      'Mac deterministic (dbg)': 'debug_bot_deterministic',
+      'Mac deterministic': 'release_bot_mac_strip_minimal_symbols',
+      'Mac deterministic (dbg)': 'debug_bot',
 
       'Site Isolation Android': 'android_release_bot_minimal_symbols_arm64',
       'TSAN Debug (reclient)': 'tsan_disable_nacl_debug_bot_reclient',
@@ -308,6 +308,7 @@
       'VR Linux (reclient)': 'vr_release_bot_reclient',
       'Win 10 Fast Ring': 'release_trybot',
       'Win x64 Builder (reclient)': 'gpu_tests_release_bot_minimal_symbols_reclient',
+      'Win x64 Builder (reclient)(cross)': 'gpu_tests_release_bot_minimal_symbols_reclient_win_cross',
       'android-code-coverage': 'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild_java_coverage',
       'android-code-coverage-native': 'gpu_tests_android_release_bot_no_symbols_arm64_fastbuild_native_coverage',
       'android-backuprefptr-arm-fyi-rel': 'release_trybot_backuprefptr_arm',
@@ -432,7 +433,7 @@
 
     'chromium.gpu': {
       # These all use the 'trybot' mixins to ensure that dcheck is on.
-      'GPU Mac Builder': 'gpu_tests_release_trybot_deterministic_mac',
+      'GPU Mac Builder': 'gpu_tests_release_trybot',
       'GPU Mac Builder (dbg)': 'gpu_tests_debug_bot',
       'GPU Linux Builder': 'gpu_tests_release_trybot',
       'GPU Linux Builder (dbg)': 'gpu_tests_debug_bot',
@@ -594,7 +595,7 @@
       'linux-swangle-tot-angle-x64': 'angle_deqp_release_trybot',
       'linux-swangle-tot-swiftshader-x64': 'angle_deqp_release_trybot',
       'linux-swangle-x64': 'angle_deqp_release_trybot',
-      'mac-swangle-chromium-x64': 'gpu_tests_release_trybot_deterministic_mac',
+      'mac-swangle-chromium-x64': 'gpu_tests_release_trybot',
       'win-swangle-chromium-x86': 'gpu_tests_release_trybot_x86_resource_allowlisting',
       'win-swangle-tot-angle-x64': 'angle_deqp_release_trybot',
       'win-swangle-tot-angle-x86': 'angle_deqp_release_trybot_x86',
@@ -866,7 +867,7 @@
       'linux-angle-chromium-try': 'gpu_tests_release_trybot',
       'linux_angle_deqp_rel_ng': 'angle_deqp_release_trybot',
       'linux-angle-try': 'angle_specific_release_trybot',
-      'mac-angle-chromium-try': 'gpu_tests_release_trybot_deterministic_mac',
+      'mac-angle-chromium-try': 'gpu_tests_release_trybot',
       'mac-angle-try': 'angle_specific_release_trybot',
       'win-angle-chromium-x64-try': 'gpu_tests_release_trybot',
       'win-angle-chromium-x86-try': 'gpu_tests_release_trybot_x86',
@@ -1090,9 +1091,9 @@
       'ios-simulator-noncq': 'ios_simulator_debug_static_bot_xctest',
       'ios-simulator-multi-window': 'ios_simulator_debug_static_bot_xctest',
       'ios-simulator-rts': 'ios_simulator_code_coverage_partial_instrumentation_xctest',
-      'mac-osxbeta-rel': 'gpu_tests_release_trybot_deterministic_mac',
-      'mac_chromium_10.11_rel_ng': 'gpu_tests_release_trybot_deterministic_mac',
-      'mac_chromium_10.12_rel_ng': 'gpu_tests_release_trybot_deterministic_mac',
+      'mac-osxbeta-rel': 'gpu_tests_release_trybot',
+      'mac_chromium_10.11_rel_ng': 'gpu_tests_release_trybot',
+      'mac_chromium_10.12_rel_ng': 'gpu_tests_release_trybot',
       'mac_chromium_10.13_rel_ng': 'release_trybot',
       'mac_chromium_10.14_rel_ng': 'release_trybot',
       'mac_chromium_10.15_rel_ng': 'release_trybot_no_nacl',
@@ -1100,14 +1101,14 @@
       'mac_chromium_archive_rel_ng': 'release_bot_mac_strip_minimal_symbols',
       'mac_chromium_asan_rel_ng': 'asan_dcheck_disable_nacl_release_bot',
       'mac_chromium_compile_dbg_ng': 'gpu_tests_debug_bot',
-      'mac_chromium_compile_rel_ng': 'gpu_tests_release_trybot_deterministic_mac',
+      'mac_chromium_compile_rel_ng': 'gpu_tests_release_trybot',
       'mac_chromium_dbg_ng': 'gpu_tests_debug_bot',
       'mac_optional_gpu_tests_rel': 'gpu_fyi_tests_release_trybot',
       'mac_upload_clang': 'release_bot',
       'mac_upload_clang_arm': 'release_bot',
-      'mac-inverse-fieldtrials-fyi-rel': 'gpu_tests_release_trybot_deterministic_mac_invert_fieldtrials',
-      'mac-rel': 'gpu_tests_release_trybot_deterministic_mac',
-      'mac-rel-rts': 'gpu_tests_release_trybot_deterministic_mac',
+      'mac-inverse-fieldtrials-fyi-rel': 'gpu_tests_release_trybot_invert_fieldtrials',
+      'mac-rel': 'gpu_tests_release_trybot',
+      'mac-rel-rts': 'gpu_tests_release_trybot',
       'mac-arm64-rel': 'mac_arm64_release_trybot',
     },
 
@@ -1128,7 +1129,7 @@
       'linux-swangle-try-tot-angle-x64': 'angle_deqp_release_trybot',
       'linux-swangle-try-tot-swiftshader-x64': 'angle_deqp_release_trybot',
       'linux-swangle-try-x64': 'angle_deqp_release_trybot',
-      'mac-swangle-chromium-try-x64': 'gpu_tests_release_trybot_deterministic_mac',
+      'mac-swangle-chromium-try-x64': 'gpu_tests_release_trybot',
       'win-swangle-chromium-try-x86': 'gpu_tests_release_trybot_x86_resource_allowlisting',
       'win-swangle-try-tot-angle-x64': 'angle_deqp_release_trybot',
       'win-swangle-try-tot-angle-x86': 'angle_deqp_release_trybot_x86',
@@ -1995,10 +1996,6 @@
       'debug_bot_blink',
     ],
 
-    'debug_bot_deterministic': [
-      'debug_bot', 'mac_deterministic_build',
-    ],
-
     'debug_bot_local_build': [
       'debug_bot_local_build',
     ],
@@ -2174,6 +2171,10 @@
       'gpu_tests', 'release_bot_reclient', 'minimal_symbols',
     ],
 
+    'gpu_tests_release_bot_minimal_symbols_reclient_win_cross': [
+      'gpu_tests', 'release_bot_reclient_win_cross', 'minimal_symbols',
+    ],
+
     'gpu_tests_release_bot_minimal_symbols_code_coverage': [
       'gpu_tests', 'release_bot', 'minimal_symbols', 'use_clang_coverage',
       'partial_code_coverage_instrumentation',
@@ -2199,6 +2200,10 @@
       'gpu_tests', 'release_trybot',
     ],
 
+    'gpu_tests_release_trybot_invert_fieldtrials': [
+      'gpu_tests', 'release_trybot', 'invert_fieldtrials',
+    ],
+
     'gpu_tests_release_trybot_mbi_mode_per_render_process_host': [
       'gpu_tests', 'release_trybot', 'mbi_mode_per_render_process_host'
     ],
@@ -2207,16 +2212,6 @@
       'gpu_tests', 'release_trybot', 'mbi_mode_per_site_instance'
     ],
 
-    # TODO(https://crbug.com/330262): Once deterministic builds are default on
-    # macOS, remove this special case of gpu_tests_release_trybot.
-    'gpu_tests_release_trybot_deterministic_mac': [
-      'gpu_tests', 'release_trybot', 'mac_deterministic_build',
-    ],
-
-    'gpu_tests_release_trybot_deterministic_mac_invert_fieldtrials': [
-      'gpu_tests', 'release_trybot', 'mac_deterministic_build', 'invert_fieldtrials',
-    ],
-
     'gpu_tests_release_trybot_resource_allowlisting': [
       'gpu_tests', 'release_trybot', 'resource_allowlisting',
     ],
@@ -2640,10 +2635,6 @@
       'release_bot', 'mac_strip', 'minimal_symbols', 'arm64',
     ],
 
-    'release_bot_mac_strip_minimal_symbols_deterministic': [
-      'release_bot', 'mac_strip', 'minimal_symbols', 'mac_deterministic_build'
-    ],
-
     'release_bot_minimal_symbols': [
       'release_bot', 'minimal_symbols',
     ],
@@ -3300,10 +3291,6 @@
       'gn_args': 'is_lsan=true',
     },
 
-    'mac_deterministic_build': {
-      'gn_args': 'mac_deterministic_build=true',
-    },
-
     'mac_strip': {
       'gn_args': 'enable_stripping=true',
     },
@@ -3448,6 +3435,11 @@
       'gn_args': 'use_rbe=true',
     },
 
+    # experiment windows cross. crbug.com/1213717
+    'reclient_win_cross': {
+      'gn_args': 'use_rbe=true rbe_cfg_dir="../../buildtools/reclient_cfgs/win-cross-experiments"',
+    },
+
     'release': {
       'gn_args': 'is_debug=false',
     },
@@ -3466,6 +3458,10 @@
       'mixins': ['release', 'static', 'reclient'],
     },
 
+    'release_bot_reclient_win_cross': {
+      'mixins': ['release', 'static', 'reclient_win_cross'],
+    },
+
     'release_java': {
       'gn_args': 'is_java_debug=false',
     },
diff --git a/tools/mb/mb_config_expectations/chromium.angle.json b/tools/mb/mb_config_expectations/chromium.angle.json
index 9d01346..02830bf 100644
--- a/tools/mb/mb_config_expectations/chromium.angle.json
+++ b/tools/mb/mb_config_expectations/chromium.angle.json
@@ -153,7 +153,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json
index 5d68f2d..1875778 100644
--- a/tools/mb/mb_config_expectations/chromium.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -242,7 +242,6 @@
       "enable_stripping": true,
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "symbol_level": 1,
       "use_goma": true
     }
@@ -251,7 +250,6 @@
     "gn_args": {
       "is_component_build": true,
       "is_debug": true,
-      "mac_deterministic_build": true,
       "symbol_level": 1,
       "use_goma": true
     }
@@ -387,6 +385,17 @@
       "use_rbe": true
     }
   },
+  "Win x64 Builder (reclient)(cross)": {
+    "gn_args": {
+      "ffmpeg_branding": "Chrome",
+      "is_component_build": false,
+      "is_debug": false,
+      "proprietary_codecs": true,
+      "rbe_cfg_dir": "../../buildtools/reclient_cfgs/win-cross-experiments",
+      "symbol_level": 1,
+      "use_rbe": true
+    }
+  },
   "android-backuprefptr-arm-fyi-rel": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/mb/mb_config_expectations/chromium.gpu.json b/tools/mb/mb_config_expectations/chromium.gpu.json
index a1809c5..e675d8f 100644
--- a/tools/mb/mb_config_expectations/chromium.gpu.json
+++ b/tools/mb/mb_config_expectations/chromium.gpu.json
@@ -42,7 +42,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
diff --git a/tools/mb/mb_config_expectations/chromium.swangle.json b/tools/mb/mb_config_expectations/chromium.swangle.json
index 21bc3b62..917dd5a 100644
--- a/tools/mb/mb_config_expectations/chromium.swangle.json
+++ b/tools/mb/mb_config_expectations/chromium.swangle.json
@@ -46,7 +46,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.angle.json b/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
index ad2e467..d5986ff0 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
@@ -194,7 +194,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
index e6b90cc..21e0377 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.mac.json
@@ -421,7 +421,6 @@
       "invert_fieldtrials": true,
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
@@ -433,7 +432,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
@@ -445,7 +443,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
@@ -457,7 +454,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
@@ -469,7 +465,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
@@ -481,7 +476,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
@@ -559,7 +553,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.swangle.json b/tools/mb/mb_config_expectations/tryserver.chromium.swangle.json
index 3f97205..ee0e3232 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.swangle.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.swangle.json
@@ -46,7 +46,6 @@
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
-      "mac_deterministic_build": true,
       "proprietary_codecs": true,
       "symbol_level": 1,
       "use_goma": true
diff --git a/tools/metrics/histograms/PRESUBMIT.py b/tools/metrics/histograms/PRESUBMIT.py
index 790a1faf..92461f63 100644
--- a/tools/metrics/histograms/PRESUBMIT.py
+++ b/tools/metrics/histograms/PRESUBMIT.py
@@ -7,6 +7,8 @@
 for more details on the presubmit API built into depot_tools.
 """
 
+USE_PYTHON3 = True
+
 
 def GetPrettyPrintErrors(input_api, output_api, cwd, rel_path, results):
   """Runs pretty-print command for specified file."""
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index bf3dd08..e8f0bc8 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -46282,6 +46282,7 @@
   <int value="-968675274" label="Rar2Fs:enabled"/>
   <int value="-968010468" label="SharedArrayBuffer:disabled"/>
   <int value="-967825290" label="WebViewConnectionlessSafeBrowsing:disabled"/>
+  <int value="-966661415" label="ElasticOverscroll:disabled"/>
   <int value="-966290456" label="WebAuthenticationCtap2:enabled"/>
   <int value="-965842218" label="MultiDeviceApi:disabled"/>
   <int value="-964676765" label="enable-accelerated-mjpeg-decode"/>
@@ -47688,7 +47689,6 @@
   <int value="296215399" label="WindowsMixedReality:disabled"/>
   <int value="296296761" label="MediaFoundationVideoCapture:disabled"/>
   <int value="297095559" label="ShowBluetoothDebugLogToggle:disabled"/>
-  <int value="297346310" label="ElasticOverscrollWin:enabled"/>
   <int value="297750703" label="DesktopPWAsAppIconShortcutsMenu:enabled"/>
   <int value="299792415" label="OmniboxSuggestionTransparencyOptions:enabled"/>
   <int value="300095239" label="FCMInvalidations:enabled"/>
@@ -47959,6 +47959,7 @@
   <int value="513728926" label="OsSettingsPolymer3:enabled"/>
   <int value="514406112" label="WebUIOmniboxPopup:disabled"/>
   <int value="514569020" label="RemoteCopyImageNotification:enabled"/>
+  <int value="515981953" label="ElasticOverscroll:enabled"/>
   <int value="516143995" label="EnableAriaElementReflection:enabled"/>
   <int value="516603570" label="QuickAnswersRichUi:disabled"/>
   <int value="517429103" label="AutofillImportDynamicForms:enabled"/>
@@ -49355,7 +49356,6 @@
   <int value="1714520147" label="MBIMode:disabled"/>
   <int value="1714922056" label="GlobalMediaControls:disabled"/>
   <int value="1715338237" label="ContextualSearchSecondTap:disabled"/>
-  <int value="1715382788" label="ElasticOverscrollWin:disabled"/>
   <int value="1716104463" label="enable-fullscreen-app-list"/>
   <int value="1717788959" label="SlowDCTimerInterruptsWin:disabled"/>
   <int value="1717987538" label="NTPTilesLowerResolutionFavicons:enabled"/>
@@ -54353,7 +54353,9 @@
 <enum name="NearbyConnectionsUtilityProcessShutdownReason">
   <int value="0" label="Normal"/>
   <int value="1" label="Crash"/>
-  <int value="2" label="Mojo pipe disconnection"/>
+  <int value="2" label="Mojo pipe disconnection (deprecated)"/>
+  <int value="3" label="Utility process disconnected Decoder mojo pipe"/>
+  <int value="4" label="Utility process disconnected Connections mojo pipe"/>
 </enum>
 
 <enum name="NearbyShareAttachmentType">
@@ -62942,6 +62944,24 @@
   <int value="1" label="Started"/>
 </enum>
 
+<enum name="PlatformCellularConnectResult">
+  <summary>
+    The return status as a result of a call to connect to a cellular device at
+    the shill layer.
+  </summary>
+  <int value="0" label="Success"/>
+  <int value="1" label="Unknown"/>
+  <int value="2" label="Wrong State"/>
+  <int value="3" label="Operation Failed"/>
+  <int value="4" label="Already Connected"/>
+  <int value="5" label="Not Registered"/>
+  <int value="6" label="Not On Home Network"/>
+  <int value="7" label="Incorrect Pin"/>
+  <int value="8" label="Pin Required"/>
+  <int value="9" label="Pin Blocked"/>
+  <int value="10" label="Invalid APN"/>
+</enum>
+
 <enum name="PlatformFileError">
   <summary>Errors from base::File::Error</summary>
   <int value="0" label="OK"/>
@@ -76676,6 +76696,24 @@
   <int value="5" label="Unknown response code"/>
 </enum>
 
+<enum name="SurveyDownloadResponseCodes2">
+<!-- This enum allocations are not without meaning. Thus, these allocations
+     should never be changed, and new possible return values should be appended
+     as new items with new values.
+  -->
+
+  <summary>
+    Results when download survey response is available. Used by
+    Android.Survey.SurveyDownloadResponseCodes2.
+  </summary>
+  <int value="0" label="Success"/>
+  <int value="1" label="Backend timeout"/>
+  <int value="2" label="Failed to fetch survey"/>
+  <int value="3" label="No available survey"/>
+  <int value="4" label="Trigger Id not set"/>
+  <int value="5" label="Unsupported Cronet engine"/>
+</enum>
+
 <enum name="SurveyFilteringResult">
   <int value="0" label="Survey info bar already displayed"/>
   <int value="1" label="Chrome Home enabled for less than one week [removed]"/>
diff --git a/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
index d28864b..9ab9d251 100644
--- a/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
@@ -11,6 +11,7 @@
 dewittj@chromium.org
 dschinazi@chromium.org
 drubery@chromium.org
+dullweber@chromium.org
 eirage@chromium.org
 ellyjones@chromium.org
 ender@chromium.org
diff --git a/tools/metrics/histograms/histograms_xml/android/histograms.xml b/tools/metrics/histograms/histograms_xml/android/histograms.xml
index b2692cd..e9ebfc8b 100644
--- a/tools/metrics/histograms/histograms_xml/android/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/android/histograms.xml
@@ -2490,13 +2490,27 @@
 <histogram name="Android.Survey.DownloadResponseCode"
     enum="SurveyDownloadResponseCodes" expires_after="2021-06-01">
   <obsolete>
-    Deprecated as of 05/2021.
+    Deprecated as of 05/2021 with the old library. Replaced by
+    Android.Survey.DownloadResponseCode2.
   </obsolete>
   <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>The response code of the completed survey download request.</summary>
 </histogram>
 
+<histogram name="Android.Survey.DownloadResponseCode2"
+    enum="SurveyDownloadResponseCodes2" expires_after="2022-06-01">
+  <owner>skym@chromium.org</owner>
+  <owner>wenyufu@chromium.org</owner>
+  <owner>clank-app-team@google.com</owner>
+  <summary>
+    The result of the completed survey download request. A survey download is
+    initiated on ChromeTabbedActivity cold start iff the user passes a series of
+    eligibility checks. Recorded when a valid survey response is received.
+    Android only.
+  </summary>
+</histogram>
+
 <histogram name="Android.Survey.InfoBarClosingState"
     enum="InfoBarClosingStates" expires_after="2022-06-01">
   <owner>twellington@chromium.org</owner>
@@ -2518,6 +2532,18 @@
   </summary>
 </histogram>
 
+<histogram name="Android.Survey.SurveyCompleted" enum="BooleanCompleted"
+    expires_after="2022-06-01">
+  <owner>skym@chromium.org</owner>
+  <owner>wenyufu@chromium.org</owner>
+  <owner>clank-app-team@google.com</owner>
+  <summary>
+    Whether a user completed the survey after it was presented. This histogram
+    is agnostic to the content of the survey, which is configured server side,
+    and will be recorded for all surveys shown in Chrome. Android only.
+  </summary>
+</histogram>
+
 <histogram name="Android.Survey.SurveyFilteringResults"
     enum="SurveyFilteringResult" expires_after="2022-06-01">
   <owner>twellington@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/history/OWNERS b/tools/metrics/histograms/histograms_xml/history/OWNERS
new file mode 100644
index 0000000..6be499c8
--- /dev/null
+++ b/tools/metrics/histograms/histograms_xml/history/OWNERS
@@ -0,0 +1,7 @@
+per-file OWNERS=file://tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
+
+# Prefer sending CLs to the owners listed below.
+# Use chromium-metrics-reviews@google.com as a backup.
+
+# For History.ClearBrowsingData.* histograms
+dullweber@chromium.org
diff --git a/tools/metrics/histograms/histograms_xml/nearby/histograms.xml b/tools/metrics/histograms/histograms_xml/nearby/histograms.xml
index ebdc106f..8fa7d4a5 100644
--- a/tools/metrics/histograms/histograms_xml/nearby/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/nearby/histograms.xml
@@ -233,9 +233,11 @@
   <owner>hansberry@chromium.org</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
-    Records which Mojo dependency of the Sharing utility process disconnected.
-    This is a break down of the &quot;Mojo pipe disconnection&quot; bucket of
-    Nearby.Connections.UtilityProcessShutdownReason.
+    Records which Mojo dependency of the Sharing utility process disconnected
+    from the browser process. This will not necessarily line up with the counts
+    in the Mojo disconnect bucket of
+    Nearby.Connections.UtilityProcessShutdownReason which tracks disconnects on
+    the utility process side.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/network/histograms.xml b/tools/metrics/histograms/histograms_xml/network/histograms.xml
index 884d3af..dfe69c73 100644
--- a/tools/metrics/histograms/histograms_xml/network/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/network/histograms.xml
@@ -485,6 +485,19 @@
   </summary>
 </histogram>
 
+<histogram name="Network.Shill.Cellular.ConnectResult"
+    enum="PlatformCellularConnectResult" expires_after="2021-12-01">
+  <owner>danielwinkler@google.com</owner>
+  <owner>cros-connectivity@google.com</owner>
+  <summary>
+    Reports the result of Cellular connection attempts through the platform
+    layers. This captures any success or failure as a result of a Connect call
+    through ModemManager, or any causes of early failure in shill that prevents
+    a connect attempt altogether. Refer to go/cros-cellular-apn-metrics for
+    details.
+  </summary>
+</histogram>
+
 <histogram name="Network.Shill.Cellular.DevicePresenceStatus"
     enum="BooleanPresent" expires_after="2021-12-01">
   <owner>ejcaruso@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/notifications/histograms.xml b/tools/metrics/histograms/histograms_xml/notifications/histograms.xml
index 0d024f9..3d65e7e 100644
--- a/tools/metrics/histograms/histograms_xml/notifications/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/notifications/histograms.xml
@@ -630,7 +630,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.BackgroundTask.Event"
-    enum="NotificationSchedulerBackgroundTaskEvent" expires_after="2021-07-01">
+    enum="NotificationSchedulerBackgroundTaskEvent" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -640,7 +640,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.BackgroundTask.NotificationShown"
-    units="notifications" expires_after="2021-07-01">
+    units="notifications" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -649,7 +649,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.BackgroundTask.Start" units="hours"
-    expires_after="2021-09-05">
+    expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -659,7 +659,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.IconDb.InitResult"
-    enum="BooleanSuccess" expires_after="2021-09-05">
+    enum="BooleanSuccess" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -669,7 +669,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.IconDb.OperationResult"
-    enum="BooleanSuccess" expires_after="2021-07-01">
+    enum="BooleanSuccess" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -679,7 +679,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.IconDb.RecordCount" units="records"
-    expires_after="2021-07-01">
+    expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -689,7 +689,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.IhnrActionButtonEvent"
-    enum="NotificationSchedulerActionButtonEvent" expires_after="2021-07-01">
+    enum="NotificationSchedulerActionButtonEvent" expires_after="2021-12-31">
 <!-- Name completed by histogram_suffixes name="NotificationSchedulerClientType" -->
 
   <owner>xingliu@chromium.org</owner>
@@ -701,7 +701,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.Impression.Count" units="records"
-    expires_after="2021-11-07">
+    expires_after="2021-12-31">
 <!-- Name completed by histogram_suffixes name="NotificationSchedulerClientType" -->
 
   <owner>xingliu@chromium.org</owner>
@@ -713,7 +713,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.Impression.Event"
-    enum="NotificationSchedulerImpressionEvent" expires_after="2021-07-01">
+    enum="NotificationSchedulerImpressionEvent" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -723,7 +723,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.ImpressionDb.InitResult"
-    enum="BooleanSuccess" expires_after="2021-07-01">
+    enum="BooleanSuccess" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -733,7 +733,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.ImpressionDb.OperationResult"
-    enum="BooleanSuccess" expires_after="2021-07-01">
+    enum="BooleanSuccess" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -743,7 +743,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.ImpressionDb.RecordCount"
-    units="records" expires_after="2021-07-01">
+    units="records" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -753,7 +753,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.NotificationDb.InitResult"
-    enum="BooleanSuccess" expires_after="2021-11-07">
+    enum="BooleanSuccess" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -763,7 +763,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.NotificationDb.OperationResult"
-    enum="BooleanSuccess" expires_after="2021-07-01">
+    enum="BooleanSuccess" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -773,7 +773,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.NotificationDb.RecordCount"
-    units="records" expires_after="2021-07-01">
+    units="records" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -784,7 +784,7 @@
 
 <histogram name="Notifications.Scheduler.NotificationLifeCycleEvent"
     enum="NotificationSchedulerNotificationLifeCycleEvent"
-    expires_after="2021-11-07">
+    expires_after="2021-12-31">
 <!-- Name completed by histogram_suffixes name="NotificationSchedulerClientType" -->
 
   <owner>xingliu@chromium.org</owner>
@@ -796,7 +796,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.PngIconConverter.DecodeResult"
-    enum="BooleanSuccess" expires_after="2021-07-01">
+    enum="BooleanSuccess" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -808,7 +808,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.PngIconConverter.EncodeResult"
-    enum="BooleanSuccess" expires_after="2021-07-01">
+    enum="BooleanSuccess" expires_after="2021-12-31">
   <owner>xingliu@chromium.org</owner>
   <owner>hesen@chromium.org</owner>
   <summary>
@@ -820,7 +820,7 @@
 </histogram>
 
 <histogram name="Notifications.Scheduler.UserAction"
-    enum="NotificationSchedulerUserActionType" expires_after="2021-11-07">
+    enum="NotificationSchedulerUserActionType" expires_after="2021-12-31">
 <!-- Name completed by histogram_suffixes name="NotificationSchedulerClientType" -->
 
   <owner>xingliu@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/OWNERS b/tools/metrics/histograms/histograms_xml/others/OWNERS
index 14dfe8d..7cba7e77 100644
--- a/tools/metrics/histograms/histograms_xml/others/OWNERS
+++ b/tools/metrics/histograms/histograms_xml/others/OWNERS
@@ -18,3 +18,5 @@
 sebsg@chromium.org
 # For Compositor*, Gpu.*, Graphics* and Viz.* histograms:
 jonross@chromium.org
+# For Privacy* and WebsiteSettings.* histograms:
+dullweber@chromium.org
diff --git a/tools/metrics/histograms/histograms_xml/print/histograms.xml b/tools/metrics/histograms/histograms_xml/print/histograms.xml
index 2f04736e..cb806d3 100644
--- a/tools/metrics/histograms/histograms_xml/print/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/print/histograms.xml
@@ -178,7 +178,7 @@
 </histogram>
 
 <histogram name="PrintPreview.PrintDocumentType"
-    enum="PrintPreviewPrintDocumentTypeBuckets" expires_after="2021-07-11">
+    enum="PrintPreviewPrintDocumentTypeBuckets" expires_after="2022-02-11">
   <owner>rbpotter@chromium.org</owner>
   <owner>awscreen@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/subresource/histograms.xml b/tools/metrics/histograms/histograms_xml/subresource/histograms.xml
index 45d7c0b..95d80c2d 100644
--- a/tools/metrics/histograms/histograms_xml/subresource/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/subresource/histograms.xml
@@ -258,7 +258,7 @@
 
 <histogram
     name="SubresourceFilter.DocumentLoad.SubframeFilteringDelay.Disallowed2"
-    units="microseconds" expires_after="M92">
+    units="microseconds" expires_after="2022-06-01">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -273,7 +273,7 @@
 
 <histogram
     name="SubresourceFilter.DocumentLoad.SubframeFilteringDelay.WouldDisallow"
-    units="microseconds" expires_after="M92">
+    units="microseconds" expires_after="2022-06-01">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -363,7 +363,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.IndexRuleset.Verify2.WallDuration"
-    units="ms" expires_after="M92">
+    units="ms" expires_after="2022-06-01">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -375,7 +375,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.IndexRuleset.WallDuration" units="ms"
-    expires_after="M92">
+    expires_after="2022-06-01">
   <owner>jkarlin@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -514,7 +514,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.NumSubresourceLoads.Evaluated"
-    units="resource loads" expires_after="M92">
+    units="resource loads" expires_after="2022-06-01">
   <owner>jkarlin@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -538,7 +538,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.NumSubresourceLoads.Total"
-    units="resource loads" expires_after="M92">
+    units="resource loads" expires_after="2022-06-01">
   <owner>jkarlin@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -549,7 +549,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.PageLoad.SafeBrowsingDelay" units="ms"
-    expires_after="M92">
+    expires_after="2022-06-01">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
@@ -674,7 +674,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.WriteRuleset.Result"
-    enum="SubresourceFilterWriteRulesetResult" expires_after="M92">
+    enum="SubresourceFilterWriteRulesetResult" expires_after="2022-06-01">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index b3f0d36..27482ca6 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -10,7 +10,7 @@
         },
         "linux": {
             "hash": "5b5e6db44c3cd71a42adcad20800c9a4e35b3fd8",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/34eb6a14728a870046c2637dbfb909d02936b84c/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/8545284860afe328993751b80a9cf856872463df/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/typescript/definitions/activity_log_private.d.ts b/tools/typescript/definitions/activity_log_private.d.ts
index f4e944b..90b60c7 100644
--- a/tools/typescript/definitions/activity_log_private.d.ts
+++ b/tools/typescript/definitions/activity_log_private.d.ts
@@ -5,87 +5,84 @@
 /** @fileoverview Definitions for chrome.activityLogPrivate API. */
 // TODO(crbug.com/1203307): Auto-generate this file.
 
-declare namespace chrome {
-  export namespace activityLogPrivate {
+import {ChromeEvent} from './chrome_event.js';
 
-    export enum ExtensionActivityType {
-      API_CALL = 'api_call',
-      API_EVENT = 'api_event',
-      CONTENT_SCRIPT = 'content_script',
-      DOM_ACCESS = 'dom_access',
-      DOM_EVENT = 'dom_event',
-      WEB_REQUEST = 'web_request',
+declare global {
+  export namespace chrome {
+    export namespace activityLogPrivate {
+
+      export enum ExtensionActivityType {
+        API_CALL = 'api_call',
+        API_EVENT = 'api_event',
+        CONTENT_SCRIPT = 'content_script',
+        DOM_ACCESS = 'dom_access',
+        DOM_EVENT = 'dom_event',
+        WEB_REQUEST = 'web_request',
+      }
+
+      export enum ExtensionActivityFilter {
+        API_CALL = 'api_call',
+        API_EVENT = 'api_event',
+        CONTENT_SCRIPT = 'content_script',
+        DOM_ACCESS = 'dom_access',
+        DOM_EVENT = 'dom_event',
+        WEB_REQUEST = 'web_request',
+        ANY = 'any',
+      }
+
+      export enum ExtensionActivityDomVerb {
+        GETTER = 'getter',
+        SETTER = 'setter',
+        METHOD = 'method',
+        INSERTED = 'inserted',
+        XHR = 'xhr',
+        WEBREQUEST = 'webrequest',
+        MODIFIED = 'modified',
+      }
+
+      export type ExtensionActivity = {
+        activityId?: string,
+        extensionId?: string, activityType: ExtensionActivityType,
+        time?: number,
+        apiCall?: string,
+        args?: string,
+        count?: number,
+        pageUrl?: string,
+        pageTitle?: string,
+        argUrl?: string,
+        other?: {
+          prerender?: boolean,
+          domVerb?: ExtensionActivityDomVerb,
+          webRequest?: string,
+          extra?: string,
+        },
+      };
+
+      export type Filter = {
+        extensionId?: string, activityType: ExtensionActivityFilter,
+        apiCall?: string,
+        pageUrl?: string,
+        argUrl?: string,
+        daysAgo?: number
+      };
+
+      export type ActivityResultSet = {
+        activities: chrome.activityLogPrivate.ExtensionActivity[],
+      };
+
+      type VoidCallback = () => void;
+
+      export function getExtensionActivities(
+          filter: Filter, callback: (result: ActivityResultSet) => void): void;
+      export function deleteActivities(
+          activityIds: string[], callback?: VoidCallback): void;
+      export function deleteActivitiesByExtension(
+          extensionId: string, callback?: VoidCallback): void;
+      export function deleteDatabase(): void;
+      export function deleteUrls(urls: string[]): void;
+
+      export const onExtensionActivity:
+          ChromeEvent<(activity: ExtensionActivity) => void>;
     }
-
-    export enum ExtensionActivityFilter {
-      API_CALL = 'api_call',
-      API_EVENT = 'api_event',
-      CONTENT_SCRIPT = 'content_script',
-      DOM_ACCESS = 'dom_access',
-      DOM_EVENT = 'dom_event',
-      WEB_REQUEST = 'web_request',
-      ANY = 'any',
-    }
-
-    export enum ExtensionActivityDomVerb {
-      GETTER = 'getter',
-      SETTER = 'setter',
-      METHOD = 'method',
-      INSERTED = 'inserted',
-      XHR = 'xhr',
-      WEBREQUEST = 'webrequest',
-      MODIFIED = 'modified',
-    }
-
-    export type ExtensionActivity = {
-      activityId?: string,
-      extensionId?: string, activityType: ExtensionActivityType,
-      time?: number,
-      apiCall?: string,
-      args?: string,
-      count?: number,
-      pageUrl?: string,
-      pageTitle?: string,
-      argUrl?: string,
-      other?: {
-        prerender?: boolean,
-        domVerb?: ExtensionActivityDomVerb,
-        webRequest?: string,
-        extra?: string,
-      },
-    };
-
-    export type Filter = {
-      extensionId?: string, activityType: ExtensionActivityFilter,
-      apiCall?: string,
-      pageUrl?: string,
-      argUrl?: string,
-      daysAgo?: number
-    };
-
-    export type ActivityResultSet = {
-      activities: chrome.activityLogPrivate.ExtensionActivity[],
-    };
-
-    type VoidCallback = () => void;
-
-    export function getExtensionActivities(
-        filter: Filter, callback: (result: ActivityResultSet) => void): void;
-    export function deleteActivities(
-        activityIds: string[], callback?: VoidCallback): void;
-    export function deleteActivitiesByExtension(
-        extensionId: string, callback?: VoidCallback): void;
-    export function deleteDatabase(): void;
-    export function deleteUrls(urls: string[]): void;
-
-    // This is a workaround for the fact that importing other .d.ts files does
-    // not work. ChromeEvent is also used elsewhere so should be in its own
-    // file.
-    export interface ChromeEvent<ListenerType> {
-      addListener(listener: ListenerType): void;
-      removeListener(listener: ListenerType): void;
-    }
-
-    export const onExtensionActivity: ChromeEvent<(activity: ExtensionActivity) => void>;
   }
 }
diff --git a/tools/typescript/definitions/bookmark_manager_private.d.ts b/tools/typescript/definitions/bookmark_manager_private.d.ts
index c9fd4a35..e832a92 100644
--- a/tools/typescript/definitions/bookmark_manager_private.d.ts
+++ b/tools/typescript/definitions/bookmark_manager_private.d.ts
@@ -5,41 +5,53 @@
 /** @fileoverview Definitions for chrome.bookmarkManagerPrivate API. */
 // TODO(crbug.com/1203307): Auto-generate this file.
 
-declare namespace chrome {
-  export namespace bookmarkManagerPrivate {
-    export interface BookmarkNodeDataElement {
-      id?: string;
-      parentId?: string;
-      title: string;
-      url?: string;
-      children: BookmarkNodeDataElement[];
+import {ChromeEvent} from './chrome_event.js';
+
+declare global {
+  export namespace chrome {
+    export namespace bookmarkManagerPrivate {
+      export interface BookmarkNodeDataElement {
+        id?: string;
+        parentId?: string;
+        title: string;
+        url?: string;
+        children: BookmarkNodeDataElement[];
+      }
+
+      export interface BookmarkNodeData {
+        sameProfile: boolean;
+        elements: BookmarkNodeDataElement[];
+      }
+
+      export function copy(idList: string[], callback: () => void): void;
+      export function cut(idList: string[], callback?: () => void): void;
+      export function paste(
+          parentId: string, selectedIdList?: string[],
+          callback?: () => void): void;
+      export function canPaste(
+          parentId: string, callback: (p1: boolean) => void): void;
+      export function sortChildren(parentId: string): void;
+      export function startDrag(
+          idList: string[], dragNodeIndex: number, isFromTouch: boolean,
+          x: number, y: number): void;
+      export function drop(
+          parentId: string, index?: number, callback?: () => void): void;
+      export function getSubtree(
+          id: string, foldersOnly: boolean,
+          callback: (p1: chrome.bookmarks.BookmarkTreeNode[]) => void): void;
+      export function removeTrees(idList: string[], callback?: () => void):
+          void;
+      export function undo(): void;
+      export function redo(): void;
+
+      type DragData = {
+        elements: chrome.bookmarks.BookmarkTreeNode[]|null;
+        sameProfile: boolean;
+      };
+
+      export const onDragEnter: ChromeEvent<(p1: DragData) => void>;
+      export const onDragLeave: ChromeEvent<() => void>;
+      export const onDrop: ChromeEvent<() => void>;
     }
-
-    export interface BookmarkNodeData {
-      sameProfile: boolean,
-      elements: BookmarkNodeDataElement[];
-    }
-
-    export function copy(idList: string[], callback: () => void): void;
-    export function cut(idList: string[], callback?: () => void): void;
-    export function paste(parentId: string, selectedIdList?: string[],
-                          callback?: () => void): void;
-    export function canPaste(
-        parentId: string, callback: (boolean) => void): void;
-    export function sortChildren(parentId: string): void;
-    export function startDrag(idList: string[], dragNodeIndex: number,
-                              isFromTouch: boolean, x: number, y: number): void;
-    export function drop(parentId: string, index?: number,
-                         callback?: () => void): void;
-    export function getSubtree(
-        id: string, foldersOnly: boolean,
-        callback: (p1: chrome.bookmarks.BookmarkTreeNode[]) => void): void;
-    export function removeTrees(idList: string[], callback?: () => void): void;
-    export function undo(): void;
-    export function redo(): void;
-
-    export const onDragEnter: chrome.bookmarks.ChromeEvent<() => void>;
-    export const onDragLeave: chrome.bookmarks.ChromeEvent<() => void>;
-    export const onDrop: chrome.bookmarks.ChromeEvent<() => void>;
   }
 }
diff --git a/tools/typescript/definitions/bookmarks.d.ts b/tools/typescript/definitions/bookmarks.d.ts
index 9a963d3..b775b239 100644
--- a/tools/typescript/definitions/bookmarks.d.ts
+++ b/tools/typescript/definitions/bookmarks.d.ts
@@ -5,123 +5,120 @@
 /** @fileoverview Definitions for chrome.bookmarks API. */
 // TODO(crbug.com/1203307): Auto-generate this file.
 
-declare namespace chrome {
-  export namespace bookmarks {
-    export enum BookmarkTreeNodeUnmodifiable {
-      MANAGED = 'managed',
+import {ChromeEvent} from './chrome_event.js';
+
+declare global {
+  export namespace chrome {
+    export namespace bookmarks {
+      export enum BookmarkTreeNodeUnmodifiable {
+        MANAGED = 'managed',
+      }
+
+      export interface BookmarkTreeNode {
+        id: string;
+        parentId?: string;
+        index?: number;
+        url?: string;
+        title: string;
+        dateAdded?: number;
+        dateGroupModified?: number;
+        unmodifiable?: BookmarkTreeNodeUnmodifiable;
+        children?: BookmarkTreeNode[];
+      }
+
+      export interface CreateDetails {
+        parentId?: string|null;
+        index?: number;
+        title?: string;
+        url?: string;
+      }
+
+      export const MAX_WRITE_OPERATIONS_PER_HOUR: number;
+      export const MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE: number;
+
+      export function get(
+          idOrIdList: string|string[],
+          callback: (p1: BookmarkTreeNode[]) => void): void;
+
+      export function getChildren(
+          id: string, callback: (p1: BookmarkTreeNode[]) => void): void;
+
+      export function getRecent(
+          numberOfItems: number,
+          callback: (p1: BookmarkTreeNode[]) => void): void;
+
+      export function getTree(callback: (p1: BookmarkTreeNode[]) => void): void;
+
+      export function getSubTree(
+          id: string, callback: (p1: BookmarkTreeNode[]) => void): void;
+
+      export function search(
+          query: string|{
+            query: string | undefined,
+            url: string|undefined,
+            title: string|undefined
+          },
+          callback: (p1: BookmarkTreeNode[]) => void): void;
+
+      export function create(
+          bookmark: CreateDetails,
+          callback?: (p1: BookmarkTreeNode) => void): void;
+
+      export function move(
+          id: string,
+          destination: {parentId: string|undefined, index: number|undefined},
+          callback?: (p1: BookmarkTreeNode) => void): void;
+
+      export function update(
+          id: string, changes: {title?: string, url?: string},
+          callback?: (p1: BookmarkTreeNode) => void): void;
+
+      export function remove(id: string, callback?: () => void): void;
+      export function removeTree(id: string, callback?: () => void): void;
+      function _import(callback?: () => void): void;
+      function _export(callback?: () => void): void;
+      export { _import as import }
+      export { _export as export }
+
+      export const onCreated:
+          ChromeEvent<(id: string, bookmark: BookmarkTreeNode) => void>;
+
+      export interface ChangeInfo {
+        title: string;
+        url: string;
+      }
+
+      export const onChanged:
+          ChromeEvent<(id: string, changeInfo: ChangeInfo) => void>;
+
+      export interface ReorderInfo {
+        childIds: string[],
+      }
+
+      export const onChildrenReordered:
+          ChromeEvent<(id: string, reorderInfo: ReorderInfo) => void>;
+
+      export interface RemoveInfo {
+        index: number;
+        node: BookmarkTreeNode;
+        parentId: string;
+      }
+
+      export const onRemoved:
+          ChromeEvent<(id: string, removeInfo: RemoveInfo) => void>;
+
+      export interface MoveInfo {
+        index: number;
+        oldIndex: number;
+        oldParentId: string;
+        parentId: string;
+      }
+
+      export const onMoved:
+          ChromeEvent<(id: string, moveInfo: MoveInfo) => void>;
+
+      export const onImportEnded: ChromeEvent<() => void>;
+      export const onImportBegan: ChromeEvent<() => void>;
     }
-
-    export interface BookmarkTreeNode {
-      id: string;
-      parentId?: string;
-      index?: number;
-      url?: string;
-      title: string;
-      dateAdded?: number;
-      dateGroupModified?: number;
-      unmodifiable?: BookmarkTreeNodeUnmodifiable;
-      children?: BookmarkTreeNode[];
-    }
-
-    export interface CreateDetails {
-      parentId?: string;
-      index?: number;
-      title?: string;
-      url?: string;
-    }
-
-    export const MAX_WRITE_OPERATIONS_PER_HOUR: number;
-    export const MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE: number;
-
-    export function get(
-        idOrIdList: string|string[],
-        callback: (p1: BookmarkTreeNode[]) => void): void;
-
-    export function getChildren(
-        id: string, callback: (p1: BookmarkTreeNode[]) => void): void;
-
-    export function getRecent(
-        numberOfItems: number,
-        callback: (p1: BookmarkTreeNode[]) => void): void;
-
-    export function getTree(callback: (p1: BookmarkTreeNode[]) => void): void;
-
-    export function getSubTree(
-        id: string, callback: (p1: BookmarkTreeNode[]) => void): void;
-
-    export function search(
-        query: string|{
-          query: string | undefined,
-          url: string|undefined,
-          title: string|undefined
-        },
-        callback: (p1: BookmarkTreeNode[]) => void): void;
-
-    export function create(
-        bookmark: CreateDetails,
-        callback?: (p1: BookmarkTreeNode) => void): void;
-
-    export function move(
-        id: string,
-        destination: {parentId: string|undefined, index: number|undefined},
-        callback?: (p1: BookmarkTreeNode) => void): void;
-
-    export function update(
-        id: string, changes: {title?: string, url?: string},
-        callback?: (p1: BookmarkTreeNode) => void): void;
-
-    export function remove(id: string, callback?: () => void): void;
-    export function removeTree(id: string, callback?: () => void): void;
-    function _import(callback?: () => void): void;
-    function _export(callback?: () => void): void;
-    export {_import as import}
-    export {_export as export}
-
-    // This is a workaround for the fact that importing other .d.ts files does
-    // not work. ChromeEvent is also used elsewhere so should be in its own
-    // file.
-    export interface ChromeEvent<ListenerType> {
-      addListener(listener: ListenerType): void;
-      removeListener(listener: ListenerType): void;
-    }
-
-    export const onCreated:
-        ChromeEvent<(id: string, bookmark: BookmarkTreeNode) => void>;
-
-    export interface ChangeInfo {
-      title: string,
-      url: string,
-    }
-
-    export const onChanged:
-        ChromeEvent<(id: string, changeInfo: ChangeInfo) => void>;
-
-    export interface ReorderInfo {
-      childIds: string[],
-    }
-
-    export const onChildrenReordered:
-        ChromeEvent<(id: string, reorderInfo: ReorderInfo) => void>;
-
-    export interface RemoveInfo {
-      index: number,
-      node: BookmarkTreeNode,
-      parentId: string,
-    }
-
-    export const onRemoved:
-        ChromeEvent<(id: string, removeInfo: RemoveInfo) => void>;
-
-    export interface MoveInfo {
-      index: number,
-      oldIndex: number,
-      oldParentId: string,
-      parentId: string,
-    }
-
-    export const onMoved: ChromeEvent<(id: string, moveInfo: MoveInfo) => void>;
-
-    export const onImportEnded: ChromeEvent<() => void>;
-    export const onImportBegan: ChromeEvent<() => void>;
   }
 }
diff --git a/tools/typescript/definitions/chrome_event.d.ts b/tools/typescript/definitions/chrome_event.d.ts
new file mode 100644
index 0000000..0ce7cf1
--- /dev/null
+++ b/tools/typescript/definitions/chrome_event.d.ts
@@ -0,0 +1,8 @@
+// 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.
+
+export interface ChromeEvent<ListenerType> {
+  addListener(listener: ListenerType): void;
+  removeListener(listener: ListenerType): void;
+}
diff --git a/tools/typescript/definitions/developer_private.d.ts b/tools/typescript/definitions/developer_private.d.ts
index 2edd0c1..5a1930ad 100644
--- a/tools/typescript/definitions/developer_private.d.ts
+++ b/tools/typescript/definitions/developer_private.d.ts
@@ -5,433 +5,430 @@
 /** @fileoverview Definitions for chrome.developerPrivate API */
 // TODO(crbug.com/1203307): Auto-generate this file.
 
-declare namespace chrome {
-  export namespace developerPrivate {
+import {ChromeEvent} from './chrome_event.js';
 
-    export enum ItemType {
-      HOSTED_APP = 'hosted_app',
-      PACKAGED_APP = 'packaged_app',
-      LEGACY_PACKAGED_APP = 'legacy_packaged_app',
-      EXTENSION = 'extension',
-      THEME = 'theme',
+declare global {
+  export namespace chrome {
+    export namespace developerPrivate {
+
+      export enum ItemType {
+        HOSTED_APP = 'hosted_app',
+        PACKAGED_APP = 'packaged_app',
+        LEGACY_PACKAGED_APP = 'legacy_packaged_app',
+        EXTENSION = 'extension',
+        THEME = 'theme',
+      }
+
+      export type ItemInspectView = {
+        path: string,
+        render_process_id: number,
+        render_view_id: number,
+        incognito: boolean,
+        generatedBackgroundPage: boolean,
+      };
+
+      export type InstallWarning = {
+        message: string,
+      };
+
+      export enum ExtensionType {
+        HOSTED_APP = 'HOSTED_APP',
+        PLATFORM_APP = 'PLATFORM_APP',
+        LEGACY_PACKAGED_APP = 'LEGACY_PACKAGED_APP',
+        EXTENSION = 'EXTENSION',
+        THEME = 'THEME',
+        SHARED_MODULE = 'SHARED_MODULE',
+      }
+
+      export enum Location {
+        FROM_STORE = 'FROM_STORE',
+        UNPACKED = 'UNPACKED',
+        THIRD_PARTY = 'THIRD_PARTY',
+        UNKNOWN = 'UNKNOWN',
+      }
+
+      export enum ViewType {
+        APP_WINDOW = 'APP_WINDOW',
+        BACKGROUND_CONTENTS = 'BACKGROUND_CONTENTS',
+        COMPONENT = 'COMPONENT',
+        EXTENSION_BACKGROUND_PAGE = 'EXTENSION_BACKGROUND_PAGE',
+        EXTENSION_DIALOG = 'EXTENSION_DIALOG',
+        EXTENSION_GUEST = 'EXTENSION_GUEST',
+        EXTENSION_POPUP = 'EXTENSION_POPUP',
+        EXTENSION_SERVICE_WORKER_BACKGROUND =
+            'EXTENSION_SERVICE_WORKER_BACKGROUND',
+        TAB_CONTENTS = 'TAB_CONTENTS',
+      }
+
+      export enum ErrorType {
+        MANIFEST = 'MANIFEST',
+        RUNTIME = 'RUNTIME',
+      }
+
+      export enum ErrorLevel {
+        LOG = 'LOG',
+        WARN = 'WARN',
+        ERROR = 'ERROR',
+      }
+
+      export enum ExtensionState {
+        ENABLED = 'ENABLED',
+        DISABLED = 'DISABLED',
+        TERMINATED = 'TERMINATED',
+        BLACKLISTED = 'BLACKLISTED',
+      }
+
+      export enum ComandScope {
+        GLOBAL = 'GLOBAL',
+        CHROME = 'CHROME',
+      }
+
+      export type GetExtensionsInfoOptions = {
+        includeDisabled?: boolean,
+        includeTerminated?: boolean,
+      };
+
+      export enum CommandScope {
+        GLOBAL = 'GLOBAL',
+        CHROME = 'CHROME',
+      }
+
+      export type AccessModifier = {
+        isEnabled: boolean,
+        isActive: boolean,
+      };
+
+      export type StackFrame = {
+        lineNumber: number,
+        columnNumber: number,
+        url: string,
+        functionName: string,
+      };
+
+      export type ManifestError = {
+        type: ErrorType,
+        extensionId: string,
+        fromIncognito: boolean,
+        source: string,
+        message: string,
+        id: number,
+        manifestKey: string,
+        manifestSpecific?: string,
+      };
+
+      export type RuntimeError = {
+        type: ErrorType,
+        extensionId: string,
+        fromIncognito: boolean,
+        source: string,
+        message: string,
+        id: number,
+        severity: ErrorLevel,
+        contextUrl: string,
+        occurrences: number,
+        renderViewId: number,
+        renderProcessId: number,
+        canInspect: boolean,
+        stackTrace: StackFrame[],
+      };
+
+      export type DisableReasons = {
+        suspiciousInstall: boolean,
+        corruptInstall: boolean,
+        updateRequired: boolean,
+        blockedByPolicy: boolean,
+        reloading: boolean,
+        custodianApprovalRequired: boolean,
+        parentDisabledPermissions: boolean,
+      };
+
+      export type OptionsPage = {
+        openInTab: boolean,
+        url: string,
+      };
+
+      export type HomePage = {
+        url: string,
+        specified: boolean,
+      };
+
+      export type ExtensionView = {
+        url: string,
+        renderProcessId: number,
+        renderViewId: number,
+        incognito: boolean,
+        isIframe: boolean,
+        type: ViewType,
+      };
+
+      export enum HostAccess {
+        ON_CLICK = 'ON_CLICK',
+        ON_SPECIFIC_SITES = 'ON_SPECIFIC_SITES',
+        ON_ALL_SITES = 'ON_ALL_SITES',
+      }
+
+      export type ControlledInfo = {
+        text: string,
+      };
+
+      export type Command = {
+        description: string,
+        keybinding: string,
+        name: string,
+        isActive: boolean,
+        scope: CommandScope,
+        isExtensionAction: boolean,
+      };
+
+      export type DependentExtension = {
+        id: string,
+        name: string,
+      };
+
+      export type Permission = {
+        message: string,
+        submessages: string[],
+      };
+
+      export type SiteControl = {
+        host: string,
+        granted: boolean,
+      };
+
+      export type RuntimeHostPermissions = {
+        hasAllHosts: boolean,
+        hostAccess: HostAccess,
+        hosts: chrome.developerPrivate.SiteControl[],
+      };
+
+      export type Permissions = {
+        simplePermissions: chrome.developerPrivate.Permission[],
+        runtimeHostPermissions?: RuntimeHostPermissions,
+      };
+
+      export type ExtensionInfo = {
+        blacklistText?: string,
+        commands: Command[],
+        controlledInfo?: ControlledInfo,
+        dependentExtensions: DependentExtension[],
+        description: string,
+        disableReasons: DisableReasons,
+        errorCollection: AccessModifier,
+        fileAccess: AccessModifier,
+        homePage: HomePage,
+        iconUrl: string,
+        id: string,
+        incognitoAccess: AccessModifier,
+        installWarnings: string[],
+        launchUrl?: string, location: Location,
+        locationText?: string,
+        manifestErrors: ManifestError[],
+        manifestHomePageUrl: string,
+        mustRemainInstalled: boolean,
+        name: string,
+        offlineEnabled: boolean,
+        optionsPage?: OptionsPage,
+        path?: string,
+        permissions: Permissions,
+        prettifiedPath?: string,
+        runtimeErrors: RuntimeError[],
+        runtimeWarnings: string[],
+        state: ExtensionState,
+        type: ExtensionType,
+        updateUrl: string,
+        userMayModify: boolean,
+        version: string,
+        views: ExtensionView[],
+        webStoreUrl: string,
+        showSafeBrowsingAllowlistWarning: boolean,
+      };
+
+      export type ProfileInfo = {
+        canLoadUnpacked: boolean,
+        inDeveloperMode: boolean,
+        isDeveloperModeControlledByPolicy: boolean,
+        isIncognitoAvailable: boolean,
+        isSupervised: boolean,
+      };
+
+      export type ExtensionConfigurationUpdate = {
+        extensionId: string,
+        fileAccess?: boolean,
+        incognitoAccess?: boolean,
+        errorCollection?: boolean,
+        hostAccess?: HostAccess,
+      };
+
+      export type ProfileConfigurationUpdate = {
+        inDeveloperMode: boolean,
+      };
+
+      export type ExtensionCommandUpdate = {
+        extensionId: string,
+        commandName: string,
+        scope?: CommandScope,
+        keybinding?: string,
+      };
+
+      export type ReloadOptions = {
+        failQuietly?: boolean,
+        populateErrorForUnpacked?: boolean,
+      };
+
+      export type LoadUnpackedOptions = {
+        failQuietly?: boolean,
+        populateError?: boolean,
+        retryGuid?: string,
+        useDraggedPath?: boolean,
+      };
+
+      export enum PackStatus {
+        SUCCESS = 'SUCCESS',
+        ERROR = 'ERROR',
+        WARNING = 'WARNING',
+      }
+
+      export enum FileType {
+        LOAD = 'LOAD',
+        PEM = 'PEM',
+      }
+
+      export enum SelectType {
+        FILE = 'FILE',
+        FOLDER = 'FOLDER',
+      }
+
+      export enum EventType {
+        INSTALLED = 'INSTALLED',
+        UNINSTALLED = 'UNINSTALLED',
+        LOADED = 'LOADED',
+        UNLOADED = 'UNLOADED',
+        VIEW_REGISTERED = 'VIEW_REGISTERED',
+        VIEW_UNREGISTERED = 'VIEW_UNREGISTERED',
+        ERROR_ADDED = 'ERROR_ADDED',
+        ERRORS_REMOVED = 'ERRORS_REMOVED',
+        PREFS_CHANGED = 'PREFS_CHANGED',
+        WARNINGS_CHANGED = 'WARNINGS_CHANGED',
+        COMMAND_ADDED = 'COMMAND_ADDED',
+        COMMAND_REMOVED = 'COMMAND_REMOVED',
+        PERMISSIONS_CHANGED = 'PERMISSIONS_CHANGED',
+        SERVICE_WORKER_STARTED = 'SERVICE_WORKER_STARTED',
+        SERVICE_WORKER_STOPPED = 'SERVICE_WORKER_STOPPED',
+      }
+
+      export type PackDirectoryResponse = {
+        message: string,
+        item_path: string,
+        pem_path: string,
+        override_flags: number,
+        status: PackStatus,
+      };
+
+      export type EventData = {
+        event_type: EventType,
+        item_id: string,
+        extensionInfo?: ExtensionInfo,
+      };
+
+      export type ErrorFileSource = {
+        beforeHighlight: string,
+        highlight: string,
+        afterHighlight: string,
+      };
+
+      export type LoadError = {
+        error: string,
+        path: string,
+        source?: ErrorFileSource, retryGuid: string,
+      };
+
+      export type RequestFileSourceProperties = {
+        extensionId: string,
+        pathSuffix: string,
+        message: string,
+        manifestKey?: string,
+        manifestSpecific?: string,
+        lineNumber?: number,
+      };
+
+      export type RequestFileSourceResponse = {
+        highlight: string,
+        beforeHighlight: string,
+        afterHighlight: string,
+        title: string,
+        message: string
+      };
+
+      export type OpenDevToolsProperties = {
+        extensionId?: string, renderViewId: number, renderProcessId: number,
+        isServiceWorker?: boolean,
+        incognito?: boolean,
+        url?: string,
+        lineNumber?: number,
+        columnNumber?: number
+      };
+
+      export type DeleteExtensionErrorsProperties = {
+        extensionId: string,
+        errorIds?: number[],
+        type?: ErrorType,
+      };
+
+      type VoidCallback = () => void;
+      type StringCallback = (s: string) => void;
+
+      export function addHostPermission(
+          extensionId: string, host: string, callback: VoidCallback): void;
+      export function autoUpdate(callback: VoidCallback): void;
+      export function choosePath(
+          selectType: SelectType, fileType: FileType,
+          callback: StringCallback): void;
+      export function deleteExtensionErrors(
+          properties: DeleteExtensionErrorsProperties,
+          callback?: VoidCallback): void;
+      export function getExtensionsInfo(
+          options: GetExtensionsInfoOptions,
+          callback: (info: ExtensionInfo) => void): void;
+      export function getExtensionSize(id: string, callback: StringCallback):
+          void;
+      export function getProfileConfiguration(
+          callback: (info: ProfileInfo) => void): void;
+      export function installDroppedFile(callback?: VoidCallback): void;
+      export function loadUnpacked(
+          options: LoadUnpackedOptions,
+          callback: (error?: LoadError) => void): void;
+      export function notifyDragInstallInProgress(): void;
+      export function openDevTools(
+          properties: OpenDevToolsProperties, callback?: VoidCallback): void;
+      export function packDirectory(
+          path: string, privateKeyPath: string, flags?: number,
+          callback?: (response: PackDirectoryResponse) => void): void;
+      export function reload(
+          extensionId: string, options?: ReloadOptions,
+          callback?: (error?: LoadError) => void): void;
+      export function removeHostPermission(
+          extensionId: string, host: string, callback: VoidCallback): void;
+      export function repairExtension(
+          extensionId: string, callback?: VoidCallback): void;
+      export function requestFileSource(
+          properties: RequestFileSourceProperties,
+          callback: (response: RequestFileSourceResponse) => void): void;
+      export function setShortcutHandlingSuspended(
+          isSuspended: boolean, callback?: VoidCallback): void;
+      export function showOptions(extensionId: string, callback?: VoidCallback):
+          void;
+      export function showPath(extensionId: string, callback?: VoidCallback):
+          void;
+      export function updateExtensionCommand(
+          update: ExtensionCommandUpdate, callback?: VoidCallback): void;
+      export function updateExtensionConfiguration(
+          update: ExtensionConfigurationUpdate, callback?: VoidCallback): void;
+      export function updateProfileConfiguration(
+          update: ProfileConfigurationUpdate, callback?: VoidCallback): void;
+
+      export const onItemStateChanged: ChromeEvent<(data: EventData) => void>;
+      export const onProfileStateChanged:
+          ChromeEvent<(info: ProfileInfo) => void>;
     }
-
-    export type ItemInspectView = {
-      path: string,
-      render_process_id: number,
-      render_view_id: number,
-      incognito: boolean,
-      generatedBackgroundPage: boolean,
-    };
-
-    export type InstallWarning = {
-      message: string,
-    };
-
-    export enum ExtensionType {
-      HOSTED_APP = 'HOSTED_APP',
-      PLATFORM_APP = 'PLATFORM_APP',
-      LEGACY_PACKAGED_APP = 'LEGACY_PACKAGED_APP',
-      EXTENSION = 'EXTENSION',
-      THEME = 'THEME',
-      SHARED_MODULE = 'SHARED_MODULE',
-    }
-
-    export enum Location {
-      FROM_STORE = 'FROM_STORE',
-      UNPACKED = 'UNPACKED',
-      THIRD_PARTY = 'THIRD_PARTY',
-      UNKNOWN = 'UNKNOWN',
-    }
-
-    export enum ViewType {
-      APP_WINDOW = 'APP_WINDOW',
-      BACKGROUND_CONTENTS = 'BACKGROUND_CONTENTS',
-      COMPONENT = 'COMPONENT',
-      EXTENSION_BACKGROUND_PAGE = 'EXTENSION_BACKGROUND_PAGE',
-      EXTENSION_DIALOG = 'EXTENSION_DIALOG',
-      EXTENSION_GUEST = 'EXTENSION_GUEST',
-      EXTENSION_POPUP = 'EXTENSION_POPUP',
-      EXTENSION_SERVICE_WORKER_BACKGROUND =
-          'EXTENSION_SERVICE_WORKER_BACKGROUND',
-      TAB_CONTENTS = 'TAB_CONTENTS',
-    }
-
-    export enum ErrorType {
-      MANIFEST = 'MANIFEST',
-      RUNTIME = 'RUNTIME',
-    }
-
-    export enum ErrorLevel {
-      LOG = 'LOG',
-      WARN = 'WARN',
-      ERROR = 'ERROR',
-    }
-
-    export enum ExtensionState {
-      ENABLED = 'ENABLED',
-      DISABLED = 'DISABLED',
-      TERMINATED = 'TERMINATED',
-      BLACKLISTED = 'BLACKLISTED',
-    }
-
-    export enum ComandScope {
-      GLOBAL = 'GLOBAL',
-      CHROME = 'CHROME',
-    }
-
-    export type GetExtensionsInfoOptions = {
-      includeDisabled?: boolean,
-      includeTerminated?: boolean,
-    };
-
-    export enum CommandScope {
-      GLOBAL = 'GLOBAL',
-      CHROME = 'CHROME',
-    }
-
-    export type AccessModifier = {
-      isEnabled: boolean,
-      isActive: boolean,
-    };
-
-    export type StackFrame = {
-      lineNumber: number,
-      columnNumber: number,
-      url: string,
-      functionName: string,
-    };
-
-    export type ManifestError = {
-      type: ErrorType,
-      extensionId: string,
-      fromIncognito: boolean,
-      source: string,
-      message: string,
-      id: number,
-      manifestKey: string,
-      manifestSpecific?: string,
-    };
-
-    export type RuntimeError = {
-      type: ErrorType,
-      extensionId: string,
-      fromIncognito: boolean,
-      source: string,
-      message: string,
-      id: number,
-      severity: ErrorLevel,
-      contextUrl: string,
-      occurrences: number,
-      renderViewId: number,
-      renderProcessId: number,
-      canInspect: boolean,
-      stackTrace: StackFrame[],
-    };
-
-    export type DisableReasons = {
-      suspiciousInstall: boolean,
-      corruptInstall: boolean,
-      updateRequired: boolean,
-      blockedByPolicy: boolean,
-      reloading: boolean,
-      custodianApprovalRequired: boolean,
-      parentDisabledPermissions: boolean,
-    };
-
-    export type OptionsPage = {
-      openInTab: boolean,
-      url: string,
-    };
-
-    export type HomePage = {
-       url: string,
-       specified: boolean,
-    };
-
-    export type ExtensionView = {
-      url: string,
-      renderProcessId: number,
-      renderViewId: number,
-      incognito: boolean,
-      isIframe: boolean,
-      type: ViewType,
-    };
-
-    export enum HostAccess {
-      ON_CLICK = 'ON_CLICK',
-      ON_SPECIFIC_SITES = 'ON_SPECIFIC_SITES',
-      ON_ALL_SITES = 'ON_ALL_SITES',
-    }
-
-    export type ControlledInfo = {
-      text: string,
-    };
-
-    export type Command = {
-      description: string,
-      keybinding: string,
-      name: string,
-      isActive: boolean,
-      scope: CommandScope,
-      isExtensionAction: boolean,
-    };
-
-    export type DependentExtension = {
-      id: string,
-      name: string,
-    };
-
-    export type Permission = {
-      message: string,
-      submessages: string[],
-    };
-
-    export type SiteControl = {
-      host: string,
-      granted: boolean,
-    };
-
-    export type RuntimeHostPermissions = {
-       hasAllHosts: boolean,
-       hostAccess: HostAccess,
-       hosts: chrome.developerPrivate.SiteControl[],
-    };
-
-    export type Permissions = {
-      simplePermissions: chrome.developerPrivate.Permission[],
-      runtimeHostPermissions?: RuntimeHostPermissions,
-    };
-
-    export type ExtensionInfo = {
-      blacklistText?: string,
-      commands: Command[],
-      controlledInfo?: ControlledInfo,
-      dependentExtensions: DependentExtension[],
-      description: string,
-      disableReasons: DisableReasons,
-      errorCollection: AccessModifier,
-      fileAccess: AccessModifier,
-      homePage: HomePage,
-      iconUrl: string,
-      id: string,
-      incognitoAccess: AccessModifier,
-      installWarnings: string[],
-      launchUrl?: string, location: Location,
-      locationText?: string,
-      manifestErrors: ManifestError[],
-      manifestHomePageUrl: string,
-      mustRemainInstalled: boolean,
-      name: string,
-      offlineEnabled: boolean,
-      optionsPage?: OptionsPage,
-      path?: string,
-      permissions: Permissions,
-      prettifiedPath?: string,
-      runtimeErrors: RuntimeError[],
-      runtimeWarnings: string[],
-      state: ExtensionState,
-      type: ExtensionType,
-      updateUrl: string,
-      userMayModify: boolean,
-      version: string,
-      views: ExtensionView[],
-      webStoreUrl: string,
-      showSafeBrowsingAllowlistWarning: boolean,
-    };
-
-    export type ProfileInfo = {
-      canLoadUnpacked: boolean,
-      inDeveloperMode: boolean,
-      isDeveloperModeControlledByPolicy: boolean,
-      isIncognitoAvailable: boolean,
-      isSupervised: boolean,
-    };
-
-    export type ExtensionConfigurationUpdate = {
-      extensionId: string,
-      fileAccess?: boolean,
-      incognitoAccess?: boolean,
-      errorCollection?: boolean,
-      hostAccess?: HostAccess,
-    };
-
-    export type ProfileConfigurationUpdate = {
-      inDeveloperMode: boolean,
-    };
-
-    export type ExtensionCommandUpdate = {
-      extensionId: string,
-      commandName: string,
-      scope?: CommandScope,
-      keybinding?: string,
-    };
-
-    export type ReloadOptions = {
-      failQuietly?: boolean,
-      populateErrorForUnpacked?: boolean,
-    };
-
-    export type LoadUnpackedOptions = {
-      failQuietly?: boolean,
-      populateError?: boolean,
-      retryGuid?: string,
-      useDraggedPath?: boolean,
-    };
-
-    export enum PackStatus {
-      SUCCESS = 'SUCCESS',
-      ERROR = 'ERROR',
-      WARNING = 'WARNING',
-    }
-
-    export enum FileType {
-      LOAD = 'LOAD',
-      PEM = 'PEM',
-    }
-
-    export enum SelectType {
-      FILE = 'FILE',
-      FOLDER = 'FOLDER',
-    }
-
-    export enum EventType {
-      INSTALLED = 'INSTALLED',
-      UNINSTALLED = 'UNINSTALLED',
-      LOADED = 'LOADED',
-      UNLOADED = 'UNLOADED',
-      VIEW_REGISTERED = 'VIEW_REGISTERED',
-      VIEW_UNREGISTERED = 'VIEW_UNREGISTERED',
-      ERROR_ADDED = 'ERROR_ADDED',
-      ERRORS_REMOVED = 'ERRORS_REMOVED',
-      PREFS_CHANGED = 'PREFS_CHANGED',
-      WARNINGS_CHANGED = 'WARNINGS_CHANGED',
-      COMMAND_ADDED = 'COMMAND_ADDED',
-      COMMAND_REMOVED = 'COMMAND_REMOVED',
-      PERMISSIONS_CHANGED = 'PERMISSIONS_CHANGED',
-      SERVICE_WORKER_STARTED = 'SERVICE_WORKER_STARTED',
-      SERVICE_WORKER_STOPPED = 'SERVICE_WORKER_STOPPED',
-    }
-
-    export type PackDirectoryResponse = {
-      message: string,
-      item_path: string,
-      pem_path: string,
-      override_flags: number,
-      status: PackStatus,
-    };
-
-    export type EventData = {
-      event_type: EventType,
-      item_id: string,
-      extensionInfo?: ExtensionInfo,
-    };
-
-    export type ErrorFileSource = {
-      beforeHighlight: string,
-      highlight: string,
-      afterHighlight: string,
-    };
-
-    export type LoadError = {
-      error: string,
-      path: string,
-      source?: ErrorFileSource, retryGuid: string,
-    };
-
-    export type RequestFileSourceProperties = {
-      extensionId: string,
-      pathSuffix: string,
-      message: string,
-      manifestKey?: string,
-      manifestSpecific?: string,
-      lineNumber?: number,
-    };
-
-    export type RequestFileSourceResponse = {
-      highlight: string,
-      beforeHighlight: string,
-      afterHighlight: string,
-      title: string,
-      message: string
-    };
-
-    export type OpenDevToolsProperties = {
-      extensionId?: string, renderViewId: number, renderProcessId: number,
-      isServiceWorker?: boolean,
-      incognito?: boolean,
-      url?: string,
-      lineNumber?: number,
-      columnNumber?: number
-    };
-
-    export type DeleteExtensionErrorsProperties = {
-      extensionId: string,
-      errorIds?: number[],
-      type?: ErrorType,
-    };
-
-    type VoidCallback = () => void;
-    type StringCallback = (s: string) => void;
-
-    export function addHostPermission(
-        extensionId: string, host: string, callback: VoidCallback): void;
-    export function autoUpdate(callback: VoidCallback): void;
-    export function choosePath(
-        selectType: SelectType, fileType: FileType,
-        callback: StringCallback): void;
-    export function deleteExtensionErrors(
-        properties: DeleteExtensionErrorsProperties,
-        callback?: VoidCallback): void;
-    export function getExtensionsInfo(
-        options: GetExtensionsInfoOptions,
-        callback: (info: ExtensionInfo) => void): void;
-    export function getExtensionSize(id: string, callback: StringCallback):
-        void;
-    export function getProfileConfiguration(
-        callback: (info: ProfileInfo) => void): void;
-    export function installDroppedFile(callback?: VoidCallback): void;
-    export function loadUnpacked(
-        options: LoadUnpackedOptions,
-        callback: (error?: LoadError) => void): void;
-    export function notifyDragInstallInProgress(): void;
-    export function openDevTools(
-        properties: OpenDevToolsProperties, callback?: VoidCallback): void;
-    export function packDirectory(
-        path: string, privateKeyPath: string, flags?: number,
-        callback?: (response: PackDirectoryResponse) => void): void;
-    export function reload(
-        extensionId: string, options?: ReloadOptions,
-        callback?: (error?: LoadError) => void): void;
-    export function removeHostPermission(
-        extensionId: string, host: string, callback: VoidCallback): void;
-    export function repairExtension(
-        extensionId: string, callback?: VoidCallback): void;
-    export function requestFileSource(
-        properties: RequestFileSourceProperties,
-        callback: (response: RequestFileSourceResponse) => void): void;
-    export function setShortcutHandlingSuspended(
-        isSuspended: boolean, callback?: VoidCallback): void;
-    export function showOptions(extensionId: string, callback?: VoidCallback):
-        void;
-    export function showPath(extensionId: string, callback?: VoidCallback):
-        void;
-    export function updateExtensionCommand(
-        update: ExtensionCommandUpdate, callback?: VoidCallback): void;
-    export function updateExtensionConfiguration(
-        update: ExtensionConfigurationUpdate, callback?: VoidCallback): void;
-    export function updateProfileConfiguration(
-        update: ProfileConfigurationUpdate, callback?: VoidCallback): void;
-
-    // This is a workaround for the fact that importing other .d.ts files does
-    // not work. ChromeEvent is also used elsewhere so should be in its own
-    // file.
-    export interface ChromeEvent<ListenerType> {
-      addListener(listener: ListenerType): void;
-      removeListener(listener: ListenerType): void;
-    }
-
-    export const onItemStateChanged: ChromeEvent<(data: EventData) => void>;
-    export const onProfileStateChanged: ChromeEvent<(info: ProfileInfo) => void>;
   }
 }
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index ad99efb..7591e3a 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -163,7 +163,7 @@
         create_node_count(0),
         node_exists(!!node),
         parent_node_id((node && node->parent())
-                           ? absl::optional<AXNodeID>{node->parent()->id()}
+                           ? absl::make_optional<AXNodeID>(node->parent()->id())
                            : absl::nullopt),
         last_known_data(node ? &node->data() : nullptr) {}
 
@@ -541,10 +541,14 @@
   // We need to keep this around in order to correctly fire post-update events.
   std::map<AXNodeID, AXNodeData> old_node_id_to_data;
 
-  // Optional copy of the old tree data, only populated when the tree
-  // data has changed.
+  // Optional copy of the old tree data, only populated when the tree data will
+  // need to be updated.
   absl::optional<AXTreeData> old_tree_data;
 
+  // Optional copy of the updated tree data, used when calculating what changes
+  // will occur during an update before the update applies changes.
+  absl::optional<AXTreeData> new_tree_data;
+
   // Keep track of the pending tree update to help create useful error messages.
   // TODO(crbug.com/1156601) Revert this once we have the crash data we need
   // (crrev.com/c/2892259).
@@ -710,14 +714,14 @@
   }
 }
 
-void AXTree::UpdateData(const AXTreeData& new_data) {
+void AXTree::UpdateDataForTesting(const AXTreeData& new_data) {
   if (data_ == new_data)
     return;
 
-  AXTreeData old_data = data_;
-  data_ = new_data;
-  for (AXTreeObserver& observer : observers_)
-    observer.OnTreeDataChanged(this, old_data, new_data);
+  AXTreeUpdate update;
+  update.has_tree_data = true;
+  update.tree_data = new_data;
+  Unserialize(update);
 }
 
 gfx::RectF AXTree::RelativeToTreeBoundsInternal(const AXNode* node,
@@ -990,6 +994,12 @@
   // false whenever this function exits.
   base::AutoReset<bool> update_state_resetter(&tree_update_in_progress_, true);
 
+  // Update the tree data. Do not call `UpdateDataForTesting` since this method
+  // should be used only for testing, but importantly, we want to defer the
+  // `OnTreeDataChanged` event until after the tree has finished updating.
+  if (update_state.new_tree_data)
+    data_ = update.tree_data;
+
   // Handle |node_id_to_clear| before applying ordinary node updates.
   // We distinguish between updating the root, e.g. changing its children or
   // some of its attributes, or replacing the root completely. If the root is
@@ -1040,13 +1050,6 @@
 
   DCHECK_EQ(!GetFromId(update.root_id), update_state.root_will_be_created);
 
-  // Update the tree data, do not call |UpdateData| since we want to defer
-  // the |OnTreeDataChanged| event until after the tree has finished updating.
-  if (update.has_tree_data && data_ != update.tree_data) {
-    update_state.old_tree_data = data_;
-    data_ = update.tree_data;
-  }
-
   // Update all of the nodes in the update.
   for (size_t i = 0; i < update.nodes.size(); ++i) {
     const bool is_new_root = update_state.root_will_be_created &&
@@ -1148,10 +1151,15 @@
   // Tree is no longer updating.
   SetTreeUpdateInProgressState(false);
 
-  // Now that the tree is stable and its nodes have been updated, notify if
-  // the tree data changed. We must do this after updating nodes in case the
-  // root has been replaced, so observers have the most up-to-date information.
   if (update_state.old_tree_data) {
+    DCHECK(update.has_tree_data)
+        << "If `UpdateState::old_tree_data` exists, then there must be a "
+           "request to update the tree data.";
+
+    // Now that the tree is stable and its nodes have been updated, notify if
+    // the tree data changed. We must do this after updating nodes in case the
+    // root has been replaced, so observers have the most up-to-date
+    // information.
     for (AXTreeObserver& observer : observers_)
       observer.OnTreeDataChanged(this, *update_state.old_tree_data, data_);
   }
@@ -1266,9 +1274,17 @@
   update_state->pending_update_status =
       AXTreePendingStructureStatus::kComputing;
 
+  // The ID of the current root  is temporarily stored in `update_state`,
+  // but reset after all pending updates have been computed in order to
+  // avoid stale data hanging around.
   base::AutoReset<absl::optional<AXNodeID>> pending_root_id_resetter(
       &update_state->pending_root_id,
-      root_ ? absl::optional<AXNodeID>{root_->id()} : absl::nullopt);
+      root_ ? absl::make_optional<AXNodeID>(root_->id()) : absl::nullopt);
+
+  if (update.has_tree_data && data_ != update.tree_data) {
+    update_state->old_tree_data = data_;
+    update_state->new_tree_data = update.tree_data;
+  }
 
   // We distinguish between updating the root, e.g. changing its children or
   // some of its attributes, or replacing the root completely. If the root is
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index 1a836fbd..dcbeb5ca 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -105,7 +105,9 @@
   // should not be trusted any longer.
   virtual bool Unserialize(const AXTreeUpdate& update);
 
-  virtual void UpdateData(const AXTreeData& data);
+  // Used by tests to update the tree data without changing any of the nodes in
+  // the tree, notifying all tree observers in the process.
+  virtual void UpdateDataForTesting(const AXTreeData& data);
 
   // Convert any rectangle from the local coordinate space of one node in
   // the tree, to bounds in the coordinate space of the tree.
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.cc b/ui/accessibility/platform/test_ax_node_wrapper.cc
index cbe632fb..aab1b98c 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -444,7 +444,7 @@
   new_tree_data.sel_focus_object_id = focus_node_id;
   new_tree_data.sel_focus_offset = focus_offset;
 
-  tree_->UpdateData(new_tree_data);
+  tree_->UpdateDataForTesting(new_tree_data);
 }
 
 bool TestAXNodeWrapper::IsTable() const {
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index b183e79..88ddc1c 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -142,9 +142,12 @@
 #endif
 };
 
+#if defined(OS_WIN) || defined(OS_ANDROID)
+const base::Feature kElasticOverscroll = {"ElasticOverscroll",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+#endif  // defined(OS_WIN) || defined(OS_ANDROID)
+
 #if defined(OS_WIN)
-const base::Feature kElasticOverscrollWin = {"ElasticOverscrollWin",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables InputPane API for controlling on screen keyboard.
 const base::Feature kInputPaneOnScreenKeyboard = {
@@ -177,20 +180,6 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
-#if defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) || \
-    defined(OS_CHROMEOS)
-// Enables stylus appearing as touch when in contact with digitizer.
-const base::Feature kDirectManipulationStylus = {
-    "DirectManipulationStylus",
-#if defined(OS_WIN)
-    base::FEATURE_ENABLED_BY_DEFAULT
-#else
-    base::FEATURE_DISABLED_BY_DEFAULT
-#endif
-};
-#endif  // defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) ||
-        // defined(OS_CHROMEOS)
-
 // Enables forced colors mode for web content.
 const base::Feature kForcedColors{"ForcedColors",
                                   base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index 7e2ebc5..48fc88f 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -44,12 +44,15 @@
 
 COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsUiGpuRasterizationEnabled();
 
+#if defined(OS_WIN) || defined(OS_ANDROID)
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+extern const base::Feature kElasticOverscroll;
+#endif  // defined(OS_WIN) || defined(OS_ANDROID)
+
 #if defined(OS_WIN)
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kCalculateNativeWinOcclusion;
 COMPONENT_EXPORT(UI_BASE_FEATURES)
-extern const base::Feature kElasticOverscrollWin;
-COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kInputPaneOnScreenKeyboard;
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 extern const base::Feature kPointerEventsForTouch;
@@ -71,13 +74,6 @@
 bool IsImprovedKeyboardShortcutsEnabled();
 #endif  // defined(OS_CHROMEOS)
 
-#if defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) || \
-    defined(OS_CHROMEOS)
-COMPONENT_EXPORT(UI_BASE_FEATURES)
-extern const base::Feature kDirectManipulationStylus;
-#endif  // defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) ||
-        // defined(OS_CHROMEOS)
-
 // Used to enable forced colors mode for web content.
 COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kForcedColors;
 COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsForcedColorsEnabled();
diff --git a/ui/base/ui_features.gni b/ui/base/ui_features.gni
index c664f73..7865a56 100644
--- a/ui/base/ui_features.gni
+++ b/ui/base/ui_features.gni
@@ -31,4 +31,4 @@
 # AXPlatformNode to implement a native C++ API, instead it bridges to a Java API.
 has_platform_accessibility_support = has_native_accessibility || is_android
 
-enable_hidpi = is_mac || is_win || is_linux || is_chromeos || is_ios
+enable_hidpi = !is_android
diff --git a/ui/events/ozone/evdev/event_device_info.cc b/ui/events/ozone/evdev/event_device_info.cc
index 067929d..6347457 100644
--- a/ui/events/ozone/evdev/event_device_info.cc
+++ b/ui/events/ozone/evdev/event_device_info.cc
@@ -70,6 +70,11 @@
     {0x413c, 0x81d5},  // Dell Active Pen PN579X
 };
 
+// Note: this is not SteelSeries's actual VID; the Stratus Duo just reports it
+// incorrectly over Bluetooth.
+const uint16_t kSteelSeriesStratusDuoBluetoothVendorId = 0x0111;
+const uint16_t kSteelSeriesStratusDuoBluetoothProductId = 0x1431;
+
 bool GetEventBits(int fd,
                   const base::FilePath& path,
                   unsigned int type,
@@ -536,6 +541,13 @@
 }
 
 bool EventDeviceInfo::HasMouse() const {
+  // The SteelSeries Stratus Duo claims to be a mouse over Bluetooth, preventing
+  // it from being set up as a gamepad correctly, so check for its vendor and
+  // product ID. (b/189491809)
+  if (input_id_.vendor == kSteelSeriesStratusDuoBluetoothVendorId &&
+      input_id_.product == kSteelSeriesStratusDuoBluetoothProductId) {
+    return false;
+  }
   return HasRelXY() && !HasProp(INPUT_PROP_POINTING_STICK);
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index fbbf408..21de756 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -551,7 +551,7 @@
     "//ui/webui/resources/js/cr/ui:dialogs.m",
     "//ui/webui/resources/js/cr/ui:menu.m",
     "//ui/webui/resources/js/cr/ui:menu_item.m",
-    "//ui/webui/resources/js/cr/ui:splitter.m",
+    "//ui/webui/resources/js/cr/ui:splitter",
   ]
   visibility +=
       [ "//ui/file_manager/file_manager/externs:command_handler_deps.m" ]
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
index 46fb47810..79f3888 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_manager_ui.js
@@ -17,7 +17,7 @@
 // #import {ImportCrostiniImageDialog} from './import_crostini_image_dialog.m.js';
 // #import {DialogFooter} from './dialog_footer.m.js';
 // #import {InstallLinuxPackageDialog} from './install_linux_package_dialog.m.js';
-// #import {Splitter} from 'chrome://resources/js/cr/ui/splitter.m.js';
+// #import {Splitter} from 'chrome://resources/js/cr/ui/splitter.js';
 // #import {FilesMenuItem} from './files_menu.m.js';
 // #import {decorate, define as crUiDefine} from 'chrome://resources/js/cr/ui.m.js';
 // #import {MenuItem} from 'chrome://resources/js/cr/ui/menu_item.m.js';
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/table/BUILD.gn
index 0ebcc5c..b0de3a2c 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/BUILD.gn
@@ -117,7 +117,7 @@
   deps = [
     "//ui/webui/resources/js:cr.m",
     "//ui/webui/resources/js/cr:event_target.m",
-    "//ui/webui/resources/js/cr/ui:splitter.m",
+    "//ui/webui/resources/js/cr/ui:splitter",
   ]
 
   extra_deps = [ ":modulize" ]
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js b/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js
index 52d0ac4..8fdc3e4 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js
@@ -11,7 +11,7 @@
  */
 
 // clang-format off
-// #import {Splitter} from 'chrome://resources/js/cr/ui/splitter.m.js';
+// #import {Splitter} from 'chrome://resources/js/cr/ui/splitter.js';
 // #import {Table} from './table.m.js';
 // #import {getPropertyDescriptor, dispatchSimpleEvent} from 'chrome://resources/js/cr.m.js';
 // clang-format on
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 4f6e487b..0713478 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -2923,7 +2923,6 @@
       'GLES2/gl2extchromium.h'
     ], [
       "GL_ANGLE_robust_resource_initialization",
-      "GL_ANGLE_texture_rectangle",
       "GL_ARB_texture_swizzle",
       "GL_EXT_texture_swizzle",
       "GL_EXT_texture_format_BGRA8888",
diff --git a/ui/gl/gl_bindings_autogen_gl.cc b/ui/gl/gl_bindings_autogen_gl.cc
index b3c6dc2..fe40cf0c 100644
--- a/ui/gl/gl_bindings_autogen_gl.cc
+++ b/ui/gl/gl_bindings_autogen_gl.cc
@@ -307,8 +307,6 @@
       gfx::HasExtension(extensions, "GL_ANGLE_semaphore_fuchsia");
   ext.b_GL_ANGLE_texture_external_update =
       gfx::HasExtension(extensions, "GL_ANGLE_texture_external_update");
-  ext.b_GL_ANGLE_texture_rectangle =
-      gfx::HasExtension(extensions, "GL_ANGLE_texture_rectangle");
   ext.b_GL_ANGLE_translated_shader_source =
       gfx::HasExtension(extensions, "GL_ANGLE_translated_shader_source");
   ext.b_GL_APPLE_fence = gfx::HasExtension(extensions, "GL_APPLE_fence");
diff --git a/ui/gl/gl_bindings_autogen_gl.h b/ui/gl/gl_bindings_autogen_gl.h
index ec4f2ce..cf41327ad5 100644
--- a/ui/gl/gl_bindings_autogen_gl.h
+++ b/ui/gl/gl_bindings_autogen_gl.h
@@ -1965,7 +1965,6 @@
   bool b_GL_ANGLE_robust_resource_initialization;
   bool b_GL_ANGLE_semaphore_fuchsia;
   bool b_GL_ANGLE_texture_external_update;
-  bool b_GL_ANGLE_texture_rectangle;
   bool b_GL_ANGLE_translated_shader_source;
   bool b_GL_APPLE_fence;
   bool b_GL_APPLE_sync;
diff --git a/ui/gl/gl_stub_autogen_gl.h b/ui/gl/gl_stub_autogen_gl.h
index 72a2fdf..84c2e018 100644
--- a/ui/gl/gl_stub_autogen_gl.h
+++ b/ui/gl/gl_stub_autogen_gl.h
@@ -8,8 +8,8 @@
 //    clang-format -i -style=chromium filename
 // DO NOT EDIT!
 
-#ifndef UI_GL_GL_STUB_AUTOGEN_H_
-#define UI_GL_GL_STUB_AUTOGEN_H_
+#ifndef UI_GL_GL_STUB_AUTOGEN_GL_H_
+#define UI_GL_GL_STUB_AUTOGEN_GL_H_
 
 void glActiveShaderProgramFn(GLuint pipeline, GLuint program) override {}
 void glActiveTextureFn(GLenum texture) override {}
@@ -1664,4 +1664,4 @@
                              GLsizei n,
                              const GLint* box) override {}
 
-#endif  //  UI_GL_GL_STUB_AUTOGEN_H_
\ No newline at end of file
+#endif  // UI_GL_GL_STUB_AUTOGEN_GL_H_
diff --git a/ui/gl/gl_utils.cc b/ui/gl/gl_utils.cc
index 24a9f02..70fa53a 100644
--- a/ui/gl/gl_utils.cc
+++ b/ui/gl/gl_utils.cc
@@ -33,10 +33,6 @@
 #include "ui/gl/gl_implementation.h"                     // nogncheck
 #endif
 
-#if defined(OS_MAC)
-#include "ui/gl/gl_bindings.h"
-#endif
-
 namespace gl {
 
 // Used by chrome://gpucrash and gpu_benchmarking_extension's
@@ -190,24 +186,4 @@
 }
 #endif  // defined(USE_X11) || BUILDFLAG(OZONE_PLATFORM_X11)
 
-#if defined(OS_MAC)
-
-ScopedEnableTextureRectangleInShaderCompiler::
-    ScopedEnableTextureRectangleInShaderCompiler(gl::GLApi* gl_api) {
-  if (gl_api && !gl_api->glIsEnabledFn(GL_TEXTURE_RECTANGLE_ANGLE)) {
-    gl_api->glEnableFn(GL_TEXTURE_RECTANGLE_ANGLE);
-    gl_api_ = gl_api;
-  } else {
-    gl_api_ = nullptr;  // Signal to the destructor that this is a no-op.
-  }
-}
-
-ScopedEnableTextureRectangleInShaderCompiler::
-    ~ScopedEnableTextureRectangleInShaderCompiler() {
-  if (gl_api_)
-    gl_api_->glDisableFn(GL_TEXTURE_RECTANGLE_ANGLE);
-}
-
-#endif  // defined(OS_MAC)
-
 }  // namespace gl
diff --git a/ui/gl/gl_utils.h b/ui/gl/gl_utils.h
index b4fdf6d..0ac70a4a 100644
--- a/ui/gl/gl_utils.h
+++ b/ui/gl/gl_utils.h
@@ -31,8 +31,6 @@
 #endif
 
 namespace gl {
-class GLApi;
-
 GL_EXPORT void Crash();
 GL_EXPORT void Hang();
 
@@ -69,31 +67,6 @@
                                       gfx::GpuExtraInfo& info);
 #endif
 
-// Temporarily allows compilation of shaders that use the
-// ARB_texture_rectangle/ANGLE_texture_rectangle extension. We don't want to
-// expose the extension to WebGL user shaders but we still need to use it for
-// parts of the implementation on macOS. Note that the extension is always
-// enabled on macOS and this only controls shader compilation.
-class GL_EXPORT ScopedEnableTextureRectangleInShaderCompiler {
- public:
-  ScopedEnableTextureRectangleInShaderCompiler(
-      const ScopedEnableTextureRectangleInShaderCompiler&) = delete;
-  ScopedEnableTextureRectangleInShaderCompiler& operator=(
-      const ScopedEnableTextureRectangleInShaderCompiler&) = delete;
-
-  // This class is a no-op except on macOS.
-#if !defined(OS_MAC)
-  explicit ScopedEnableTextureRectangleInShaderCompiler(gl::GLApi* gl_api) {}
-
-#else
-  explicit ScopedEnableTextureRectangleInShaderCompiler(gl::GLApi* gl_api);
-  ~ScopedEnableTextureRectangleInShaderCompiler();
-
- private:
-  gl::GLApi* gl_api_;
-#endif
-};
-
 }  // namespace gl
 
 #endif  // UI_GL_GL_UTILS_H_
diff --git a/ui/gl/yuv_to_rgb_converter.cc b/ui/gl/yuv_to_rgb_converter.cc
index ac354da..e6be831 100644
--- a/ui/gl/yuv_to_rgb_converter.cc
+++ b/ui/gl/yuv_to_rgb_converter.cc
@@ -9,7 +9,6 @@
 #include "base/strings/stringprintf.h"
 #include "ui/gfx/color_transform.h"
 #include "ui/gl/gl_helper.h"
-#include "ui/gl/gl_utils.h"
 #include "ui/gl/gl_version_info.h"
 #include "ui/gl/scoped_binders.h"
 
@@ -159,23 +158,17 @@
 
   glGenFramebuffersEXT(1, &framebuffer_);
 
-  {
-    ScopedEnableTextureRectangleInShaderCompiler enable(
-        g_current_gl_driver->ext.b_GL_ANGLE_texture_rectangle
-            ? g_current_gl_context
-            : nullptr);
-    vertex_buffer_ = GLHelper::SetupQuadVertexBuffer();
-    vertex_shader_ = GLHelper::LoadShader(
-        GL_VERTEX_SHADER,
-        base::StringPrintf("%s\n%s", vertex_header, kVertexShader).c_str());
-    fragment_shader_ = GLHelper::LoadShader(
-        GL_FRAGMENT_SHADER,
-        base::StringPrintf("%s\n%s\n%s", fragment_header,
-                           do_color_conversion.c_str(),
-                           (is_rect ? kFragmentShaderRect : kFragmentShader2D))
-            .c_str());
-    program_ = GLHelper::SetupProgram(vertex_shader_, fragment_shader_);
-  }
+  vertex_buffer_ = GLHelper::SetupQuadVertexBuffer();
+  vertex_shader_ = GLHelper::LoadShader(
+      GL_VERTEX_SHADER,
+      base::StringPrintf("%s\n%s", vertex_header, kVertexShader).c_str());
+  fragment_shader_ = GLHelper::LoadShader(
+      GL_FRAGMENT_SHADER,
+      base::StringPrintf("%s\n%s\n%s", fragment_header,
+                         do_color_conversion.c_str(),
+                         (is_rect ? kFragmentShaderRect : kFragmentShader2D))
+          .c_str());
+  program_ = GLHelper::SetupProgram(vertex_shader_, fragment_shader_);
 
   ScopedUseProgram use_program(program_);
   size_location_ = glGetUniformLocation(program_, "a_texScale");
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 32b38281..89e1df1 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -1153,6 +1153,9 @@
       <message name="IDS_BROWSER_SHARING_OMNIBOX_SENDING_LABEL" desc="The label to be shown next to the omnibox icon when the message is being sent to the other device.">
         Sending...
       </message>
+      <message name="IDS_BROWSER_SHARING_OMNIBOX_SENT_LABEL" translateable="false" desc="The label to be shown next to the omnibox icon when the message is returned from the other device.">
+        Sent
+      </message>
       <message name="IDS_BROWSER_SHARING_DIALOG_DEVICE_SUBTITLE_LAST_ACTIVE_DAYS" desc="The label to be shown below the name of a valid device indicating the last active time of device,">
         {DAYS, plural, =0 {Active today} =1 {Active 1 day ago} other {Active # days ago}}
       </message>
diff --git a/ui/views/controls/focus_ring.cc b/ui/views/controls/focus_ring.cc
index 677bd52..699c15f 100644
--- a/ui/views/controls/focus_ring.cc
+++ b/ui/views/controls/focus_ring.cc
@@ -28,7 +28,6 @@
 namespace {
 
 DEFINE_UI_CLASS_PROPERTY_KEY(int, kFocusRingBackgroundColorId, -1)
-DEFINE_UI_CLASS_PROPERTY_KEY(int, kFocusRingFallbackColorId, -1)
 
 bool IsPathUsable(const SkPath& path) {
   return !path.isEmpty() && (path.isRect(nullptr) || path.isOval(nullptr) ||
@@ -40,50 +39,36 @@
                : ui::NativeTheme::kColorId_AlertSeverityHigh;
 }
 
-View* GetViewForSubtreeColors(View* view) {
+int GetBackgroundColorId(View* view) {
   int color_id_property = view->GetProperty(kFocusRingBackgroundColorId);
   if (color_id_property != -1)
-    return view;
+    return color_id_property;
   if (!view->parent())
-    return nullptr;
-  return GetViewForSubtreeColors(view->parent());
+    return -1;
+  return GetBackgroundColorId(view->parent());
+}
+
+SkColor GetBackgroundColor(View* view) {
+  int color_id = GetBackgroundColorId(view);
+  return color_id == -1 ? view->GetNativeTheme()->GetSystemColor(
+                              ui::NativeTheme::kColorId_WindowBackground)
+                        : view->GetThemeProvider()->GetColor(color_id);
 }
 
 SkColor GetColor(View* focus_ring, bool valid) {
-  const ui::NativeTheme* const native_theme = focus_ring->GetNativeTheme();
   const SkColor default_color =
-      native_theme->GetSystemColor(ColorIdForValidity(valid));
+      focus_ring->GetNativeTheme()->GetSystemColor(ColorIdForValidity(valid));
 
   if (!valid)
     return default_color;
 
-  View* const fallback_color_view = GetViewForSubtreeColors(focus_ring);
-
-  const SkColor background_color =
-      fallback_color_view
-          ? focus_ring->GetThemeProvider()->GetColor(
-                fallback_color_view->GetProperty(kFocusRingBackgroundColorId))
-          : focus_ring->GetNativeTheme()->GetSystemColor(
-                ui::NativeTheme::kColorId_WindowBackground);
-
-  const SkColor focus_ring_color =
-      color_utils::PickGoogleColor(default_color, background_color,
-                                   color_utils::kMinimumVisibleContrastRatio);
-
-  // If the Google color is contrasty enough, use it.
-  if (color_utils::GetContrastRatio(focus_ring_color, background_color) >
-      color_utils::kMinimumVisibleContrastRatio) {
-    return focus_ring_color;
-  }
-
-  return fallback_color_view
-             ? focus_ring->GetThemeProvider()->GetColor(
-                   fallback_color_view->GetProperty(kFocusRingFallbackColorId))
-             : focus_ring_color;
+  return color_utils::PickGoogleColor(
+      default_color, GetBackgroundColor(focus_ring),
+      color_utils::kMinimumVisibleContrastRatio);
 }
 
 double GetCornerRadius() {
-  double thickness = PlatformStyle::kFocusHaloThickness / 2.f;
+  const double thickness = PlatformStyle::kFocusHaloThickness / 2.f;
   return FocusableBorder::kCornerRadiusDp + thickness;
 }
 
@@ -120,11 +105,9 @@
   return parent->AddChildView(std::move(ring));
 }
 
-void FocusRing::SetColorContextForSubtree(View* view,
-                                          int background_color_id,
-                                          int fallback_color_id) {
+void FocusRing::SetBackgroundColorIdForSubtree(View* view,
+                                               int background_color_id) {
   view->SetProperty(kFocusRingBackgroundColorId, background_color_id);
-  view->SetProperty(kFocusRingFallbackColorId, fallback_color_id);
 }
 
 FocusRing::~FocusRing() = default;
diff --git a/ui/views/controls/focus_ring.h b/ui/views/controls/focus_ring.h
index b576d27..3c50c76 100644
--- a/ui/views/controls/focus_ring.h
+++ b/ui/views/controls/focus_ring.h
@@ -38,16 +38,19 @@
   static FocusRing* Install(View* parent);
 
   // Configures `view` so that FocusRings under it are aware of the background
-  // they are painted against. If the default color can't be made to contrast
-  // against `background_color_id` then `fallback_color_id` will be used.
+  // they are painted against. Unless the color of the FocusRing has been
+  // explicitly set, a color will be chosen that contrasts well against
+  // `background_color_id`.
   // Warning: The FocusRing ThemeProvider is queried for these IDs, do not use
   // NativeTheme color IDs here.
   // WARNING: This is temporary shenanigans to solve an accessibility problem.
   // DO NOT COPY this pattern or its implementation to other places in its
   // current state.
-  static void SetColorContextForSubtree(View* view,
-                                        int background_color_id,
-                                        int fallback_color_id);
+  // TODO(pbos): This seems not directly related to the FocusRing anymore,
+  // perhaps we could inform the view of what background color it's being
+  // painted onto orthogonally to how FocusRing uses it.
+  static void SetBackgroundColorIdForSubtree(View* view,
+                                             int background_color_id);
 
   ~FocusRing() override;
 
diff --git a/ui/views/controls/webview/web_dialog_view.cc b/ui/views/controls/webview/web_dialog_view.cc
index cd3f3c8..606f82c 100644
--- a/ui/views/controls/webview/web_dialog_view.cc
+++ b/ui/views/controls/webview/web_dialog_view.cc
@@ -110,6 +110,15 @@
 ////////////////////////////////////////////////////////////////////////////////
 // WebDialogView, views::View implementation:
 
+void WebDialogView::AddedToWidget() {
+  gfx::RoundedCornersF corner_radii(
+      delegate_ && delegate_->GetWebDialogFrameKind() ==
+                       WebDialogDelegate::FrameKind::kDialog
+          ? GetCornerRadius()
+          : 0);
+  web_view_->holder()->SetCornerRadii(corner_radii);
+}
+
 gfx::Size WebDialogView::CalculatePreferredSize() const {
   gfx::Size out;
   if (delegate_)
diff --git a/ui/views/controls/webview/web_dialog_view.h b/ui/views/controls/webview/web_dialog_view.h
index 96896f5..978155ad 100644
--- a/ui/views/controls/webview/web_dialog_view.h
+++ b/ui/views/controls/webview/web_dialog_view.h
@@ -18,8 +18,8 @@
 #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/controls/webview/webview_export.h"
-#include "ui/views/widget/widget_delegate.h"
 #include "ui/views/window/client_view.h"
+#include "ui/views/window/dialog_delegate.h"
 #include "ui/web_dialogs/web_dialog_delegate.h"
 #include "ui/web_dialogs/web_dialog_web_contents_delegate.h"
 
@@ -74,7 +74,7 @@
 class WEBVIEW_EXPORT WebDialogView : public ClientView,
                                      public ui::WebDialogWebContentsDelegate,
                                      public ui::WebDialogDelegate,
-                                     public WidgetDelegate {
+                                     public DialogDelegate {
  public:
   METADATA_HEADER(WebDialogView);
 
@@ -91,6 +91,7 @@
   content::WebContents* web_contents();
 
   // ClientView:
+  void AddedToWidget() override;
   gfx::Size CalculatePreferredSize() const override;
   gfx::Size GetMinimumSize() const override;
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
diff --git a/ui/views/controls/webview/webview.h b/ui/views/controls/webview/webview.h
index 1bb2399..71883873 100644
--- a/ui/views/controls/webview/webview.h
+++ b/ui/views/controls/webview/webview.h
@@ -144,11 +144,6 @@
                               content::RenderFrameHost* new_host) override;
   void DidToggleFullscreenModeForTab(bool entered_fullscreen,
                                      bool will_cause_resize) override;
-  // Workaround for MSVC++ linker bug/feature that requires
-  // instantiation of the inline IPC::Listener methods in all translation units.
-  void OnChannelConnected(int32_t peer_id) override {}
-  void OnChannelError() override {}
-  void OnBadMessageReceived(const IPC::Message& message) override {}
   void OnWebContentsFocused(
       content::RenderWidgetHost* render_widget_host) override;
   void AXTreeIDForMainFrameHasChanged() override;
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index aabbdffa0..a221915d 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -415,9 +415,7 @@
       is_first_nccalc_(true),
       menu_depth_(0),
       id_generator_(0),
-      pen_processor_(
-          &id_generator_,
-          base::FeatureList::IsEnabled(::features::kDirectManipulationStylus)),
+      pen_processor_(&id_generator_, true),
       touch_down_contexts_(0),
       last_mouse_hwheel_time_(0),
       dwm_transition_desired_(false),
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index 7d8d1149..e3b8dcc 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -420,6 +420,8 @@
   if (GetModalType() == ui::MODAL_TYPE_WINDOW)
     return 2;
 #endif
+  if (params_.corner_radius)
+    return *params_.corner_radius;
   return LayoutProvider::Get()->GetCornerRadiusMetric(views::Emphasis::kMedium);
 }
 
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index d0de3f3..47bf36d 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -45,6 +45,8 @@
     ~Params();
     absl::optional<int> default_button = absl::nullopt;
     bool round_corners = true;
+    absl::optional<int> corner_radius = absl::nullopt;
+
     bool draggable = false;
 
     // Whether to use the Views-styled frame (if true) or a platform-native
@@ -203,6 +205,12 @@
   void DialogModelChanged();
 
   void set_use_round_corners(bool round) { params_.round_corners = round; }
+  void set_corner_radius(int corner_radius) {
+    params_.corner_radius = corner_radius;
+  }
+  const absl::optional<int> corner_radius() const {
+    return params_.corner_radius;
+  }
   void set_draggable(bool draggable) { params_.draggable = draggable; }
   bool draggable() const { return params_.draggable; }
   void set_use_custom_frame(bool use) { params_.custom_frame = use; }
diff --git a/ui/webui/resources/css/text_defaults.css b/ui/webui/resources/css/text_defaults.css
index 33942fb..4724fdf 100644
--- a/ui/webui/resources/css/text_defaults.css
+++ b/ui/webui/resources/css/text_defaults.css
@@ -15,11 +15,6 @@
  * to an HTML string.
  * Otherwise its placeholders won't be expanded. */
 
-html {
-  /* TODO(dbeam): remove this soon. Prefer dir= in HTML. */
-  direction: $i18n{textDirection};
-}
-
 body {
   font-family: $i18nRaw{fontFamily};
   font-size: $i18n{fontSize};
diff --git a/ui/webui/resources/css/text_defaults_md.css b/ui/webui/resources/css/text_defaults_md.css
index a862419..d85fc32 100644
--- a/ui/webui/resources/css/text_defaults_md.css
+++ b/ui/webui/resources/css/text_defaults_md.css
@@ -17,11 +17,6 @@
 
 @import url(chrome://resources/css/roboto.css);
 
-html {
-  /* TODO(dbeam): remove this soon. Prefer dir= in HTML. */
-  direction: $i18n{textDirection};
-}
-
 body {
   font-family: Roboto, $i18nRaw{fontFamily};
   font-size: 81.25%;
diff --git a/ui/webui/resources/html/BUILD.gn b/ui/webui/resources/html/BUILD.gn
index 0ede812..e2fe30ec 100644
--- a/ui/webui/resources/html/BUILD.gn
+++ b/ui/webui/resources/html/BUILD.gn
@@ -32,7 +32,6 @@
     "cr/ui/menu.html",
     "cr/ui/menu_item.html",
     "cr/ui/position_util.html",
-    "cr/ui/splitter.html",
     "cr/ui/store_client.html",
     "cr/ui/store.html",
     "event_tracker.html",
diff --git a/ui/webui/resources/html/cr/ui/splitter.html b/ui/webui/resources/html/cr/ui/splitter.html
deleted file mode 100644
index 4397d22..0000000
--- a/ui/webui/resources/html/cr/ui/splitter.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<link rel="import" href="../../../html/cr/ui.html">
-<script src="../../../js/cr/ui/splitter.js"></script>
diff --git a/ui/webui/resources/js/cr/ui/BUILD.gn b/ui/webui/resources/js/cr/ui/BUILD.gn
index 7cc6657d..bb7d672b 100644
--- a/ui/webui/resources/js/cr/ui/BUILD.gn
+++ b/ui/webui/resources/js/cr/ui/BUILD.gn
@@ -38,6 +38,7 @@
   in_files = [
     "drag_wrapper.js",
     "focus_outline_manager.js",
+    "splitter.js",
   ]
 
   if (is_chromeos_ash) {
@@ -62,7 +63,6 @@
       "menu_item.js",
       "menu.js",
       "position_util.js",
-      "splitter.js",
       "store_client.js",
       "store.js",
       "tabs.js",
@@ -86,7 +86,6 @@
     "list.m.js",
     "list_selection_controller.m.js",
     "list_selection_model.m.js",
-    "splitter.m.js",
     "store_client.m.js",
     "store.m.js",
     "tabs.m.js",
@@ -145,7 +144,6 @@
     ":menu_button",
     ":menu_item",
     ":position_util",
-    ":splitter",
     ":store",
     ":store_client",
     ":tabs",
@@ -302,13 +300,6 @@
   deps = [ "../..:cr" ]
 }
 
-js_library("splitter") {
-  deps = [
-    "..:ui",
-    "../..:cr",
-  ]
-}
-
 js_library("store") {
   deps = [ "../..:cr" ]
 }
@@ -359,7 +350,6 @@
     "menu_button.js",
     "menu_item.js",
     "position_util.js",
-    "splitter.js",
     "store.js",
     "store_client.js",
     "tabs.js",
@@ -392,7 +382,7 @@
     ":menu_button.m",
     ":menu_item.m",
     ":position_util.m",
-    ":splitter.m",
+    ":splitter",
     ":store.m",
     ":store_client.m",
     ":tabs.m",
@@ -606,13 +596,11 @@
   extra_deps = [ ":modulize" ]
 }
 
-js_library("splitter.m") {
-  sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/splitter.m.js" ]
+js_library("splitter") {
   deps = [
     "..:ui.m",
     "../..:cr.m",
   ]
-  extra_deps = [ ":modulize" ]
 }
 
 js_library("store.m") {
diff --git a/ui/webui/resources/js/cr/ui/splitter.js b/ui/webui/resources/js/cr/ui/splitter.js
index 7048e87c..1c7f07d8 100644
--- a/ui/webui/resources/js/cr/ui/splitter.js
+++ b/ui/webui/resources/js/cr/ui/splitter.js
@@ -18,233 +18,226 @@
  *
  */
 
-// #import {define as crUiDefine} from '../ui.m.js';
-// #import {dispatchSimpleEvent} from '../../cr.m.js';
+import {dispatchSimpleEvent} from '../../cr.m.js';
+import {define as crUiDefine} from '../ui.m.js';
 
-cr.define('cr.ui', function() {
+/**
+ * Creates a new splitter element.
+ * @param {Object=} opt_propertyBag Optional properties.
+ * @constructor
+ * @extends {HTMLDivElement}
+ */
+export const Splitter = crUiDefine('div');
+
+Splitter.prototype = {
+  __proto__: HTMLDivElement.prototype,
+
   /**
-   * Creates a new splitter element.
-   * @param {Object=} opt_propertyBag Optional properties.
-   * @constructor
-   * @extends {HTMLDivElement}
+   * Initializes the element.
    */
-  /* #export */ const Splitter = cr.ui.define('div');
+  decorate() {
+    this.addEventListener('mousedown', this.handleMouseDown_.bind(this), true);
+    this.addEventListener(
+        'touchstart', this.handleTouchStart_.bind(this), true);
+    this.resizeNextElement_ = false;
+  },
 
-  Splitter.prototype = {
-    __proto__: HTMLDivElement.prototype,
+  /**
+   * @param {boolean} resizeNext True if resize the next element.
+   *     By default, splitter resizes previous (left) element.
+   */
+  set resizeNextElement(resizeNext) {
+    this.resizeNextElement_ = resizeNext;
+  },
 
-    /**
-     * Initializes the element.
-     */
-    decorate() {
-      this.addEventListener(
-          'mousedown', this.handleMouseDown_.bind(this), true);
-      this.addEventListener(
-          'touchstart', this.handleTouchStart_.bind(this), true);
-      this.resizeNextElement_ = false;
-    },
-
-    /**
-     * @param {boolean} resizeNext True if resize the next element.
-     *     By default, splitter resizes previous (left) element.
-     */
-    set resizeNextElement(resizeNext) {
-      this.resizeNextElement_ = resizeNext;
-    },
-
-    /**
-     * Starts the dragging of the splitter. Adds listeners for mouse or touch
-     * events and calls splitter drag start handler.
-     * @param {number} clientX X position of the mouse or touch event that
-     *                         started the drag.
-     * @param {boolean} isTouchEvent True if the drag started by touch event.
-     */
-    startDrag(clientX, isTouchEvent) {
-      if (this.handlers_) {
-        console.log('Concurent drags');
-        this.endDrag_();
-      }
-      if (isTouchEvent) {
-        const endDragBound = this.endDrag_.bind(this);
-        this.handlers_ = {
-          'touchmove': this.handleTouchMove_.bind(this),
-          'touchend': endDragBound,
-          'touchcancel': endDragBound,
-
-          // Another touch start (we somehow missed touchend or touchcancel).
-          'touchstart': endDragBound,
-        };
-      } else {
-        this.handlers_ = {
-          'mousemove': this.handleMouseMove_.bind(this),
-          'mouseup': this.handleMouseUp_.bind(this),
-        };
-      }
-
-      const doc = this.ownerDocument;
-
-      // Use capturing events on the document to get events when the mouse
-      // leaves the document.
-      for (const eventType in this.handlers_) {
-        doc.addEventListener(eventType, this.handlers_[eventType], true);
-      }
-
-      this.startX_ = clientX;
-      this.handleSplitterDragStart();
-    },
-
-    /**
-     * Ends the dragging of the splitter. Removes listeners set in startDrag
-     * and calls splitter drag end handler.
-     * @private
-     */
-    endDrag_() {
-      const doc = this.ownerDocument;
-      for (const eventType in this.handlers_) {
-        doc.removeEventListener(eventType, this.handlers_[eventType], true);
-      }
-      this.handlers_ = null;
-      this.handleSplitterDragEnd();
-    },
-
-    /**
-     * @return {Element}
-     * @private
-     */
-    getResizeTarget_() {
-      return this.resizeNextElement_ ? this.nextElementSibling :
-                                       this.previousElementSibling;
-    },
-
-    /**
-     * Calculate width to resize target element.
-     * @param {number} deltaX horizontal drag amount
-     * @return {number}
-     * @private
-     */
-    calcDeltaX_(deltaX) {
-      return this.resizeNextElement_ ? -deltaX : deltaX;
-    },
-
-    /**
-     * Handles the mousedown event which starts the dragging of the splitter.
-     * @param {!Event} e The mouse event.
-     * @private
-     */
-    handleMouseDown_(e) {
-      e = /** @type {!MouseEvent} */ (e);
-      if (e.button) {
-        return;
-      }
-      this.startDrag(e.clientX, false);
-      // Default action is to start selection and to move focus.
-      e.preventDefault();
-    },
-
-    /**
-     * Handles the touchstart event which starts the dragging of the splitter.
-     * @param {!Event} e The touch event.
-     * @private
-     */
-    handleTouchStart_(e) {
-      e = /** @type {!TouchEvent} */ (e);
-      if (e.touches.length === 1) {
-        this.startDrag(e.touches[0].clientX, true);
-        e.preventDefault();
-      }
-    },
-
-    /**
-     * Handles the mousemove event which moves the splitter as the user moves
-     * the mouse.
-     * @param {!MouseEvent} e The mouse event.
-     * @private
-     */
-    handleMouseMove_(e) {
-      this.handleMove_(e.clientX);
-    },
-
-    /**
-     * Handles the touch move event.
-     * @param {!TouchEvent} e The touch event.
-     */
-    handleTouchMove_(e) {
-      if (e.touches.length === 1) {
-        this.handleMove_(e.touches[0].clientX);
-      }
-    },
-
-    /**
-     * Common part of handling mousemove and touchmove. Calls splitter drag
-     * move handler.
-     * @param {number} clientX X position of the mouse or touch event.
-     * @private
-     */
-    handleMove_(clientX) {
-      const rtl =
-          this.ownerDocument.defaultView.getComputedStyle(this).direction ===
-          'rtl';
-      const dirMultiplier = rtl ? -1 : 1;
-      const deltaX = dirMultiplier * (clientX - this.startX_);
-      this.handleSplitterDragMove(deltaX);
-    },
-
-    /**
-     * Handles the mouse up event which ends the dragging of the splitter.
-     * @param {!MouseEvent} e The mouse event.
-     * @private
-     */
-    handleMouseUp_(e) {
+  /**
+   * Starts the dragging of the splitter. Adds listeners for mouse or touch
+   * events and calls splitter drag start handler.
+   * @param {number} clientX X position of the mouse or touch event that
+   *                         started the drag.
+   * @param {boolean} isTouchEvent True if the drag started by touch event.
+   */
+  startDrag(clientX, isTouchEvent) {
+    if (this.handlers_) {
+      console.log('Concurent drags');
       this.endDrag_();
-    },
+    }
+    if (isTouchEvent) {
+      const endDragBound = this.endDrag_.bind(this);
+      this.handlers_ = {
+        'touchmove': this.handleTouchMove_.bind(this),
+        'touchend': endDragBound,
+        'touchcancel': endDragBound,
 
-    /**
-     * Handles start of the splitter dragging. Saves current width of the
-     * element being resized.
-     */
-    handleSplitterDragStart() {
-      // Use the computed width style as the base so that we can ignore what
-      // box sizing the element has. Add the difference between offset and
-      // client widths to account for any scrollbars.
-      const targetElement = this.getResizeTarget_();
-      const doc = targetElement.ownerDocument;
-      this.startWidth_ =
-          parseFloat(doc.defaultView.getComputedStyle(targetElement).width) +
-          targetElement.offsetWidth - targetElement.clientWidth;
+        // Another touch start (we somehow missed touchend or touchcancel).
+        'touchstart': endDragBound,
+      };
+    } else {
+      this.handlers_ = {
+        'mousemove': this.handleMouseMove_.bind(this),
+        'mouseup': this.handleMouseUp_.bind(this),
+      };
+    }
 
-      this.classList.add('splitter-active');
-    },
+    const doc = this.ownerDocument;
 
-    /**
-     * Handles splitter moves. Updates width of the element being resized.
-     * @param {number} deltaX The change of splitter horizontal position.
-     */
-    handleSplitterDragMove(deltaX) {
-      const targetElement = this.getResizeTarget_();
-      const newWidth = this.startWidth_ + this.calcDeltaX_(deltaX);
-      targetElement.style.width = newWidth + 'px';
-      cr.dispatchSimpleEvent(this, 'dragmove');
-    },
+    // Use capturing events on the document to get events when the mouse
+    // leaves the document.
+    for (const eventType in this.handlers_) {
+      doc.addEventListener(eventType, this.handlers_[eventType], true);
+    }
 
-    /**
-     * Handles end of the splitter dragging. This fires a 'resize' event if the
-     * size changed.
-     */
-    handleSplitterDragEnd() {
-      // Check if the size changed.
-      const targetElement = this.getResizeTarget_();
-      const doc = targetElement.ownerDocument;
-      const computedWidth =
-          parseFloat(doc.defaultView.getComputedStyle(targetElement).width);
-      if (this.startWidth_ !== computedWidth) {
-        cr.dispatchSimpleEvent(this, 'resize');
-      }
+    this.startX_ = clientX;
+    this.handleSplitterDragStart();
+  },
 
-      this.classList.remove('splitter-active');
-    },
-  };
+  /**
+   * Ends the dragging of the splitter. Removes listeners set in startDrag
+   * and calls splitter drag end handler.
+   * @private
+   */
+  endDrag_() {
+    const doc = this.ownerDocument;
+    for (const eventType in this.handlers_) {
+      doc.removeEventListener(eventType, this.handlers_[eventType], true);
+    }
+    this.handlers_ = null;
+    this.handleSplitterDragEnd();
+  },
 
-  // #cr_define_end
-  console.warn('crbug/1173575, non-JS module files deprecated.');
-  return {Splitter: Splitter};
-});
+  /**
+   * @return {Element}
+   * @private
+   */
+  getResizeTarget_() {
+    return this.resizeNextElement_ ? this.nextElementSibling :
+                                     this.previousElementSibling;
+  },
+
+  /**
+   * Calculate width to resize target element.
+   * @param {number} deltaX horizontal drag amount
+   * @return {number}
+   * @private
+   */
+  calcDeltaX_(deltaX) {
+    return this.resizeNextElement_ ? -deltaX : deltaX;
+  },
+
+  /**
+   * Handles the mousedown event which starts the dragging of the splitter.
+   * @param {!Event} e The mouse event.
+   * @private
+   */
+  handleMouseDown_(e) {
+    e = /** @type {!MouseEvent} */ (e);
+    if (e.button) {
+      return;
+    }
+    this.startDrag(e.clientX, false);
+    // Default action is to start selection and to move focus.
+    e.preventDefault();
+  },
+
+  /**
+   * Handles the touchstart event which starts the dragging of the splitter.
+   * @param {!Event} e The touch event.
+   * @private
+   */
+  handleTouchStart_(e) {
+    e = /** @type {!TouchEvent} */ (e);
+    if (e.touches.length === 1) {
+      this.startDrag(e.touches[0].clientX, true);
+      e.preventDefault();
+    }
+  },
+
+  /**
+   * Handles the mousemove event which moves the splitter as the user moves
+   * the mouse.
+   * @param {!MouseEvent} e The mouse event.
+   * @private
+   */
+  handleMouseMove_(e) {
+    this.handleMove_(e.clientX);
+  },
+
+  /**
+   * Handles the touch move event.
+   * @param {!TouchEvent} e The touch event.
+   */
+  handleTouchMove_(e) {
+    if (e.touches.length === 1) {
+      this.handleMove_(e.touches[0].clientX);
+    }
+  },
+
+  /**
+   * Common part of handling mousemove and touchmove. Calls splitter drag
+   * move handler.
+   * @param {number} clientX X position of the mouse or touch event.
+   * @private
+   */
+  handleMove_(clientX) {
+    const rtl =
+        this.ownerDocument.defaultView.getComputedStyle(this).direction ===
+        'rtl';
+    const dirMultiplier = rtl ? -1 : 1;
+    const deltaX = dirMultiplier * (clientX - this.startX_);
+    this.handleSplitterDragMove(deltaX);
+  },
+
+  /**
+   * Handles the mouse up event which ends the dragging of the splitter.
+   * @param {!MouseEvent} e The mouse event.
+   * @private
+   */
+  handleMouseUp_(e) {
+    this.endDrag_();
+  },
+
+  /**
+   * Handles start of the splitter dragging. Saves current width of the
+   * element being resized.
+   */
+  handleSplitterDragStart() {
+    // Use the computed width style as the base so that we can ignore what
+    // box sizing the element has. Add the difference between offset and
+    // client widths to account for any scrollbars.
+    const targetElement = this.getResizeTarget_();
+    const doc = targetElement.ownerDocument;
+    this.startWidth_ =
+        parseFloat(doc.defaultView.getComputedStyle(targetElement).width) +
+        targetElement.offsetWidth - targetElement.clientWidth;
+
+    this.classList.add('splitter-active');
+  },
+
+  /**
+   * Handles splitter moves. Updates width of the element being resized.
+   * @param {number} deltaX The change of splitter horizontal position.
+   */
+  handleSplitterDragMove(deltaX) {
+    const targetElement = this.getResizeTarget_();
+    const newWidth = this.startWidth_ + this.calcDeltaX_(deltaX);
+    targetElement.style.width = newWidth + 'px';
+    dispatchSimpleEvent(this, 'dragmove');
+  },
+
+  /**
+   * Handles end of the splitter dragging. This fires a 'resize' event if the
+   * size changed.
+   */
+  handleSplitterDragEnd() {
+    // Check if the size changed.
+    const targetElement = this.getResizeTarget_();
+    const doc = targetElement.ownerDocument;
+    const computedWidth =
+        parseFloat(doc.defaultView.getComputedStyle(targetElement).width);
+    if (this.startWidth_ !== computedWidth) {
+      dispatchSimpleEvent(this, 'resize');
+    }
+
+    this.classList.remove('splitter-active');
+  },
+};
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
index 7e22d97..e65abe977 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
@@ -78,7 +78,7 @@
         // Normal profiles have restrictions on the name.
         if (!isIncognito && !name.matches("^\\w+$")) {
             throw new IllegalArgumentException(
-                    "Non-incongito profiles names can only contain words: " + name);
+                    "Non-incognito profiles names can only contain words: " + name);
         }
         mIsIncognito = isIncognito;
         mName = name;
diff --git a/weblayer/public/java/org/chromium/weblayer/WebLayer.java b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
index 1f22436..b281ba8 100644
--- a/weblayer/public/java/org/chromium/weblayer/WebLayer.java
+++ b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
@@ -514,7 +514,7 @@
     /**
      * Creates a new WebLayer Fragment using the incognito profile with the specified name.
      *
-     * @param profileName The name of the incongito profile, null is mapped to an empty string.
+     * @param profileName The name of the incognito profile, null is mapped to an empty string.
      * @param persistenceId If non-null and not empty uniquely identifies the Browser for saving
      * state.
      *
diff --git a/weblayer/shell/android/shell_apk/AndroidManifest.xml b/weblayer/shell/android/shell_apk/AndroidManifest.xml
index a7734e0..8b47b41 100644
--- a/weblayer/shell/android/shell_apk/AndroidManifest.xml
+++ b/weblayer/shell/android/shell_apk/AndroidManifest.xml
@@ -7,6 +7,7 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="org.chromium.weblayer.shell">
 
     <application android:label="WebLayer shell"
@@ -63,6 +64,14 @@
       <meta-data
           android:name="org.chromium.weblayer.ENABLE_LOGGING_OF_JS_CONSOLE_MESSAGES" android:value="true"/>
 
+      <!-- Disables at startup init of Emoji2. See http://crbug.com/1205141 -->
+      <provider
+          android:authorities="org.chromium.weblayer.shell.androidx-startup"
+          android:name="androidx.startup.InitializationProvider"
+          android:exported="false"
+          tools:node="remove">
+      </provider>
+
       {% if weblayer_package is defined %}
             <meta-data android:name="org.chromium.weblayer.WebLayerPackage"
                        android:value="{{ weblayer_package }}"/>