diff --git a/DEPS b/DEPS
index d5c638e..021ed6d 100644
--- a/DEPS
+++ b/DEPS
@@ -178,11 +178,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'b34241557068489923895ba81bd4c25168fb097a',
+  'skia_revision': '25dc7ca1f314bfe250f32093d8c71b4d21fcee86',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'c3455c4a11aba8f4fee319038b2cda6639797153',
+  'v8_revision': '0cc5899dfff453c41ee5370c5edd3be9d166cfe4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -190,7 +190,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '3b82fdcf1c836f5c7df7c9c76f0c8ccd168d0871',
+  'angle_revision': 'a5750efe1eedf64d7a57a58998f15d440f28cd97',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -198,7 +198,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '2acdf79f9eef6c65ea4e2f546454c4c562d45588',
+  'pdfium_revision': '3e36f68831431bf497babc74075cd69af5fd9823',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -249,7 +249,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': '8072a0c8b9aa908118a8267a97bdc02d7e138b13',
+  'devtools_frontend_revision': '6f60dca88247f77e4511c801a3908ce3f119e72f',
   # 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.
@@ -301,7 +301,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': 'f4df7916cacb0f8a87abf99454aafc12c1bdae20',
+  'dawn_revision': '60bb88d23c0538909f9d1186adb585b56c83392f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -524,7 +524,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '459066d00ee638e32b6c46ed05fa7242ed524ae3',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '5f23d99dfef0ba8417538eeb8b116e3a68f424c0',
       'condition': 'checkout_ios',
   },
 
@@ -871,7 +871,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8280ae5e0a3853fb30b77c603e8c75bbe3ad403a',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '30ef5cb43761b8536b071a26ca59fca17e6a7de6',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1220,7 +1220,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '79825c95b6dd67ddb24115d1fcd955ea17a18ea7',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '974f915feb5c5577aab82ac92ba3dcff380e4b10',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1453,7 +1453,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1190f49aeaa1116caf8fd650204052f57cf0ad2d',
+    Var('webrtc_git') + '/src.git' + '@' + '95d40ee0dfabe4e0a7926e359a385a35da4d6fb1',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1525,7 +1525,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b6e0a07f363e9c1e477e873c704972e6a0f7ca14',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@48c4843491b2d270104d5b93248150464acd66c4',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/SplitApkWorkaround.java b/android_webview/glue/java/src/com/android/webview/chromium/SplitApkWorkaround.java
index 76ef7791..a194baf 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/SplitApkWorkaround.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/SplitApkWorkaround.java
@@ -4,6 +4,8 @@
 
 package com.android.webview.chromium;
 
+import android.annotation.SuppressLint;
+
 import dalvik.system.BaseDexClassLoader;
 
 import org.chromium.base.Log;
@@ -28,6 +30,7 @@
      * This function runs in the WebView zygote, which cannot make any binder calls to the framework
      * and is a very restricted environment.
      */
+    @SuppressLint("DiscouragedPrivateApi")
     @SuppressWarnings("unchecked")
     public static void apply() {
         try {
diff --git a/ash/assistant/ui/assistant_web_container_view.cc b/ash/assistant/ui/assistant_web_container_view.cc
index 220f415..99cb18d 100644
--- a/ash/assistant/ui/assistant_web_container_view.cc
+++ b/ash/assistant/ui/assistant_web_container_view.cc
@@ -19,6 +19,7 @@
 #include "ui/display/screen.h"
 #include "ui/views/background.h"
 #include "ui/views/layout/fill_layout.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 #include "ui/views/window/caption_button_layout_constants.h"
 
 namespace ash {
@@ -42,10 +43,6 @@
 
 AssistantWebContainerView::~AssistantWebContainerView() = default;
 
-const char* AssistantWebContainerView::GetClassName() const {
-  return "AssistantWebContainerView";
-}
-
 gfx::Size AssistantWebContainerView::CalculatePreferredSize() const {
   const int non_client_frame_view_height =
       views::GetCaptionButtonLayoutSize(
@@ -79,11 +76,11 @@
   // We should only respond to the |DidStopLoading| event the first time, to add
   // the view for contents to our view hierarchy and perform other one-time view
   // initializations.
-  if (contents_view_->parent())
+  if (!contents_view_)
     return;
 
   contents_view_->SetPreferredSize(GetPreferredSize());
-  AddChildView(contents_view_.get());
+  contents_view_ptr_ = AddChildView(std::move(contents_view_));
   SetFocusBehavior(FocusBehavior::ALWAYS);
 }
 
@@ -105,7 +102,7 @@
   }
 
   // Otherwise we'll allow our WebContents to navigate freely.
-  contents_view_->Navigate(url);
+  ContentsView()->Navigate(url);
 }
 
 void AssistantWebContainerView::DidChangeCanGoBack(bool can_go_back) {
@@ -115,7 +112,7 @@
 }
 
 bool AssistantWebContainerView::GoBack() {
-  return contents_view_ && contents_view_->GoBack();
+  return ContentsView() && ContentsView()->GoBack();
 }
 
 void AssistantWebContainerView::OpenUrl(const GURL& url) {
@@ -127,17 +124,16 @@
 
   contents_view_ = AssistantWebViewFactory::Get()->Create(contents_params);
 
-  // We retain ownership of |contents_view_| as it is only added to the view
-  // hierarchy once loading stops and we want to ensure that it is cleaned up in
-  // the rare chance that that never occurs.
-  contents_view_->set_owned_by_client();
-
   // We observe |contents_view_| so that we can handle events from the
   // underlying WebContents.
-  contents_view_->AddObserver(this);
+  ContentsView()->AddObserver(this);
 
   // Navigate to the specified |url|.
-  contents_view_->Navigate(url);
+  ContentsView()->Navigate(url);
+}
+
+AssistantWebView* AssistantWebContainerView::ContentsView() {
+  return contents_view_ptr_ ? contents_view_ptr_ : contents_view_.get();
 }
 
 void AssistantWebContainerView::InitLayout() {
@@ -154,15 +150,17 @@
 }
 
 void AssistantWebContainerView::RemoveContents() {
-  if (!contents_view_)
+  if (!contents_view_ptr_)
     return;
 
-  RemoveChildView(contents_view_.get());
-
   SetFocusBehavior(FocusBehavior::NEVER);
 
-  contents_view_->RemoveObserver(this);
-  contents_view_.reset();
+  RemoveChildViewT(contents_view_ptr_)->RemoveObserver(this);
+  contents_view_ptr_ = nullptr;
 }
 
+BEGIN_METADATA(AssistantWebContainerView)
+METADATA_PARENT_CLASS(views::WidgetDelegateView)
+END_METADATA()
+
 }  // namespace ash
diff --git a/ash/assistant/ui/assistant_web_container_view.h b/ash/assistant/ui/assistant_web_container_view.h
index 9400eb8..7d19e7e 100644
--- a/ash/assistant/ui/assistant_web_container_view.h
+++ b/ash/assistant/ui/assistant_web_container_view.h
@@ -8,6 +8,7 @@
 #include "ash/public/cpp/assistant/assistant_web_view.h"
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "ui/views/metadata/metadata_header_macros.h"
 #include "ui/views/widget/widget_delegate.h"
 
 namespace ash {
@@ -19,12 +20,13 @@
     : public views::WidgetDelegateView,
       public AssistantWebView::Observer {
  public:
+  METADATA_HEADER(AssistantWebContainerView);
+
   explicit AssistantWebContainerView(
       AssistantWebViewDelegate* web_container_view_delegate);
   ~AssistantWebContainerView() override;
 
   // views::WidgetDelegateView:
-  const char* GetClassName() const override;
   gfx::Size CalculatePreferredSize() const override;
   void ChildPreferredSizeChanged(views::View* child) override;
 
@@ -44,12 +46,14 @@
   void OpenUrl(const GURL& url);
 
  private:
+  AssistantWebView* ContentsView();
   void InitLayout();
   void RemoveContents();
 
   AssistantWebViewDelegate* const web_container_view_delegate_;
 
   std::unique_ptr<AssistantWebView> contents_view_;
+  AssistantWebView* contents_view_ptr_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(AssistantWebContainerView);
 };
diff --git a/base/android/java/src/org/chromium/base/process_launcher/BindService.java b/base/android/java/src/org/chromium/base/process_launcher/BindService.java
index a430bf27..c2c3dff 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/BindService.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/BindService.java
@@ -4,6 +4,7 @@
 
 package org.chromium.base.process_launcher;
 
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -62,6 +63,7 @@
     }
 
     @TargetApi(Build.VERSION_CODES.N)
+    @SuppressLint("DiscouragedPrivateApi")
     private static boolean bindServiceByReflection(Context context, Intent intent,
             ServiceConnection connection, int flags, Handler handler)
             throws ReflectiveOperationException {
diff --git a/base/files/file_util_win.cc b/base/files/file_util_win.cc
index 9351660..e0ac259 100644
--- a/base/files/file_util_win.cc
+++ b/base/files/file_util_win.cc
@@ -19,7 +19,6 @@
 #include <string>
 
 #include "base/debug/alias.h"
-#include "base/debug/dump_without_crashing.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/memory_mapped_file.h"
@@ -530,48 +529,13 @@
   File file;
 
   // Although it is nearly impossible to get a duplicate name with GUID, we
-  // still use a loop here in case it happens. TODO(grt): remove this loop
-  // because the probability of a name collision is so very, very low.
+  // still use a loop here in case it happens.
   for (int i = 0; i < 100; ++i) {
     temp_name =
         dir.Append(UTF8ToWide(GenerateGUID()) + FILE_PATH_LITERAL(".tmp"));
     file.Initialize(temp_name, kFlags);
     if (file.IsValid())
       break;
-    const auto create_error = ::GetLastError();
-    debug::Alias(&create_error);
-    // PATH_NOT_FOUND means that |dir| does not exist. This likely means that
-    // the caller specified a custom directory and forgot to create it before
-    // trying to put a file in it, so consider it programmer error (though there
-    // is a chance that a user has set TMP to a non-existent dir).
-    // Unfortunately, too many tests crash if we put the following here:
-    // DCHECK_NE(create_error, DWORD{ERROR_PATH_NOT_FOUND});
-    //
-    // So for now tolerate it. Other popular errors are more difficult to
-    // understand. Capture some info to try to diagnose them in case there's
-    // something to be done about them; see https://crbug.com/1075917.
-    if (!i) {
-      static const bool hit = [&temp_name]() {
-        const DWORD attributes = ::GetFileAttributes(temp_name.value().c_str());
-        const auto attr_error = ::GetLastError();
-        size_t num_entries = 0;
-        FileEnumerator enumerator(
-            temp_name.DirName(), /*recursive=*/false,
-            FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
-            FilePath::StringType());
-        while (!enumerator.Next().empty())
-          ++num_entries;
-        wchar_t path_copy[MAX_PATH];
-        wcslcpy(path_copy, temp_name.value().c_str(), size(path_copy));
-        debug::Alias(&attributes);
-        debug::Alias(&attr_error);
-        debug::Alias(&num_entries);
-        debug::Alias(path_copy);
-        debug::DumpWithoutCrashing();
-        return true;
-      }();
-      debug::Alias(&hit);
-    }
   }
 
   if (!file.IsValid()) {
diff --git a/build/android/gyp/lint.py b/build/android/gyp/lint.py
index ec2d729..fb751bd6 100755
--- a/build/android/gyp/lint.py
+++ b/build/android/gyp/lint.py
@@ -55,6 +55,7 @@
              manifest_package,
              resource_sources,
              resource_zips,
+             android_sdk_root,
              testonly_target=False,
              can_fail_build=False,
              include_unexpected=False,
@@ -102,8 +103,19 @@
 
   with build_utils.TempDir() as temp_dir:
     cmd = [
-        _RebasePath(lint_path), '-Werror', '--exitcode', '--showall',
-        '--xml', _RebasePath(result_path),
+        _RebasePath(lint_path),
+        '-Werror',
+        '--exitcode',
+        '--showall',
+        '--xml',
+        _RebasePath(result_path),
+        # An explicit sdk root needs to be specified since we have an extra
+        # intermediate 'lastest' directory under cmdline-tools which prevents
+        # lint from automatically deducing the location of the sdk. The sdk is
+        # required for many checks (e.g. NewApi). Lint also requires absolute
+        # paths.
+        '--sdk-home',
+        os.path.abspath(android_sdk_root),
     ]
     if config_path:
       cmd.extend(['--config', _RebasePath(config_path)])
@@ -306,6 +318,9 @@
 def _ParseArgs(argv):
   parser = argparse.ArgumentParser()
   build_utils.AddDepfileOption(parser)
+  parser.add_argument('--android-sdk-root',
+                      required=True,
+                      help='Lint needs an explicit path to the android sdk.')
   parser.add_argument('--testonly',
                       action='store_true',
                       help='If set, some checks like UnusedResources will be '
@@ -401,6 +416,7 @@
            args.manifest_package,
            resource_sources,
            args.resource_zips,
+           args.android_sdk_root,
            testonly_target=args.testonly,
            can_fail_build=args.can_fail_build,
            include_unexpected=args.include_unexpected_failures,
diff --git a/build/android/lint/suppressions.xml b/build/android/lint/suppressions.xml
index 9e78605..db949c8 100644
--- a/build/android/lint/suppressions.xml
+++ b/build/android/lint/suppressions.xml
@@ -65,11 +65,25 @@
     <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
     <ignore regexp="chrome/android/java/res/layout/video_player.xml"/>
   </issue>
+  <issue id="CustomViewStyleable">
+    <!-- TODO(crbug.com/1077861): Old code, good to fix. -->
+    <ignore regexp="components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/BoundedLinearLayout.java"/>
+  </issue>
   <issue id="DefaultLocale">
     <ignore regexp="clank"/>
     <ignore regexp="com/android/tv"/>
     <ignore regexp="org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.class"/>
     <ignore regexp="third_party/cacheinvalidation/src/java/com/google/ipc/invalidation/external/client/contrib/AndroidListenerState.java"/>
+    <!-- TODO(crbug.com/1081240): Fix -->
+    <ignore regexp="chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java"/>
+    <!-- TODO(crbug.com/1082222): Fix -->
+    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderProcessor.java"/>
+  </issue>
+  <!--TODO(crbug.com/1069186): See if this new check is useful. -->
+  <issue id="DuplicateDefinition" severity="ignore"/>
+  <issue id="DuplicateIds" severity="Fatal">
+    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
+    <ignore regexp="chrome/android/java/res/menu/main_menu.xml"/>
   </issue>
   <!-- TODO(crbug.com/635567): Fix this properly. -->
   <issue id="Deprecated" severity="Error">
@@ -183,6 +197,10 @@
     <ignore regexp="components/browser_ui/strings/android/browser_ui_strings_grd"/>
     <ignore regexp="clank/third_party/chime/chime_systemtray_strings_grd.resources.zip"/>
   </issue>
+  <issue id="IncludeLayoutParam" severity="Error">
+    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
+    <ignore regexp="chrome/android/java/res/layout/start_top_toolbar.xml"/>
+  </issue>
   <issue id="InsecureBaseConfiguration">
     <!-- See https://crbug.com/827265 and comment in the file for context. -->
     <ignore regexp="chrome/android/java/res/xml/network_security_config.xml"/>
@@ -201,6 +219,7 @@
   <issue id="InflateParams" severity="ignore"/>
   <issue id="InlinedApi" severity="ignore"/>
   <issue id="InvalidVectorPath" severity="ignore"/>
+  <issue id="JobSchedulerService" severity="ignore"/>
   <!-- TODO(crbug.com/804453): Remove this after fixing. -->
   <issue id="KeyboardInaccessibleWidget" severity="ignore"/>
   <issue id="LabelFor" severity="Error">
@@ -248,53 +267,23 @@
     <ignore path="AndroidManifest.xml"/>
   </issue>
   <issue id="NewApi">
-    <!-- 1 We support try-with-resources via desugar. -->
-    <ignore regexp="Call requires API level 19.*`java.lang.Throwable#addSuppressed`"/>
-    <!-- 1 We support requireNonNull via desugar. -->
-    <ignore regexp="Call requires API level 19.*`java.util.Objects#requireNonNull`"/>
     <!-- Do not add new suppressions without rationale. -->
-    <!-- 2 AutoCloseable has been available since API 15, just hidden. -->
-    <ignore regexp="Call requires API level 19.*java.lang.AutoCloseable#close"/>
-    <ignore regexp="Class requires API level 19.*java.lang.AutoCloseable"/>
-    <!-- 1 We support default methods via desugar. -->
+    <!-- 2: We support these via desugar. -->
     <ignore regexp="Default method requires API level 24"/>
-    <!-- 1 TaskInfo is refactored at API 29. -->
-    <ignore regexp="Field requires API level 29.*`android.app.TaskInfo"/>
-    <!-- 1 We support static interface methods via desugar. -->
     <ignore regexp="Static interface  method requires API level 24"/>
-    <!-- 1 We support try-with-resources via desugar. -->
-    <ignore regexp="Try-with-resources requires API level 19"/>
-    <!-- 1 This is for testonly target android_support_chromium_java in android_sdk. -->
+    <!-- 1: TaskInfo is refactored at API 29. -->
+    <ignore regexp="Field requires API level .*`android.app.TaskInfo"/>
+    <!-- 1: This is for testonly target android_support_chromium_java in android_sdk. -->
     <ignore regexp="third_party/android_sdk/public/extras/chromium/support/src/org/chromium/android/support/PackageManagerWrapper.java"/>
+    <!-- 1: TODO(crbug.com/1081242): Fix -->
+    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtils.java"/>
+    <!-- 1: TODO(crbug.com/1081243): Fix -->
+    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerVideoPlayer.java"/>
+    <!-- 1: TODO(crbug.com/1081280): Fix -->
+    <ignore regexp="chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/ConditionalTabStripTest.java"/>
+    <!-- 1: TODO(crbug.com/1082222): Fix -->
+    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderView.java"/>
     <!-- Endnote: Please specify number of suppressions when adding more -->
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/address.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/email.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/face.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/ic_checkmark_24dp.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/ic_logo_googleg_20dp.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/ic_pause_circle_outline_white_24dp.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/ic_volume_off_white_24dp.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/ic_volume_on_white_24dp.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/logo_partly_cloudy.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/names.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/values/styles.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/telephone.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/zoom_in.xml"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/drawable/zoom_out.xml"/>
   </issue>
   <!-- This warning just adds a lot of false positives. -->
   <issue id="ObsoleteSdkInt" severity="ignore"/>
@@ -322,11 +311,18 @@
   <issue id="ResourceAsColor" severity="ignore"/>
   <issue id="ResourceType" severity="Error">
     <ignore regexp="/javatests/"/>
+    <!-- TODO(crbug.com/1081236): To fix -->
+    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java"/>
   </issue>
   <!-- TODO(crbug.com/831774): Play Services starts complaining about RestrictedApi. Needs investigation -->
   <issue id="RestrictedApi" severity="ignore"/>
   <issue id="RtlCompat" severity="ignore"/>
   <issue id="RtlEnabled" severity="ignore"/>
+  <issue id="RtlHardcoded" severity="Error">
+    <ignore regexp="remoting/android/internal"/>
+    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
+    <ignore regexp="chrome/android/java/res/layout/sheet_tab_toolbar.xml"/>
+  </issue>
   <issue id="RtlSymmetry" severity="ignore"/>
   <issue id="SetJavaScriptEnabled" severity="ignore"/>
   <issue id="SignatureOrSystemPermissions" severity="ignore"/>
@@ -335,7 +331,7 @@
   </issue>
   <issue id="StaticFieldLeak">
     <!-- Nice to fix, but not necessary or performance critical. -->
-    <ignore regexp="This AsyncTask class should be static or leaks might occur"/>
+    <ignore regexp="This `AsyncTask` class should be static or leaks might occur"/>
   </issue>
   <issue id="StringFormatCount" severity="Error">
     <ignore regexp="chrome/browser/ui/android/strings/ui_strings_grd.resources.zip/values-cs/android_chrome_strings.xml"/>
@@ -473,7 +469,6 @@
     <ignore regexp="chromecast/internal/shell/browser/android/java/res/drawable-hdpi/ic_settings_cast.png"/>
     <!-- 1 string used by Android's policies system, pulled from app directly -->
     <ignore regexp="restriction_values.xml"/>
-    <!-- Endnote: Please specify number of suppressions when adding more -->
     <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
     <ignore regexp="../obj/components/strings/components_strings_grd.resources.zip/values/components_strings.xml"/>
     <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
@@ -506,6 +501,14 @@
     <ignore regexp="The resource `R.plurals.public_notification_text` appears to be unused" />
     <ignore regexp="The resource `R.mipmap.app_shortcut_icon` appears to be unused" />
     <ignore regexp="The resource `R.mipmap.app_single_page_icon` appears to be unused" />
+    <!-- Endnote: Please specify number of suppressions when adding more -->
+  </issue>
+  <issue id="UsableSpace">
+    <!-- TODO(crbug.com/1077861): Old code, good to fix. -->
+    <ignore regexp="chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadDirectoryProvider.java"/>
+    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java"/>
+    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java"/>
+    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageSummaryProvider.java"/>
   </issue>
   <issue id="UseCompoundDrawables">
     <!-- Upscaling 24dp to 48dp doesn't work as expected with a TextView compound drawable. -->
@@ -541,6 +544,7 @@
     <ignore regexp="clank/java/src/com/google/android/apps/chrome/help/FeedbackCategoryChooserActivity.java"/>
     <ignore regexp="clank/java/src/com/google/android/apps/chrome/help/HelpAndFeedbackInternal.java"/>
   </issue>
+  <issue id="WebViewApiAvailability" severity="ignore"/>
   <issue id="WrongCall" severity="ignore"/>
   <issue id="WrongConstant">
     <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/SSLClientCertificateRequest.java"/>
@@ -548,20 +552,7 @@
     <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/instantapps/InstantAppsHandler.java"/>
     <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/widget/prefeditor/EditorDialog.java"/>
     <ignore regexp="third_party/android_data_chart/java/src/org/chromium/third_party/android/datausagechart/ChartDataUsageView.java"/>
-  </issue>
-  <issue id="DuplicateIds" severity="Fatal">
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/menu/main_menu.xml"/>
-  </issue>
-  <!--TODO(crbug.com/1069186): See if this new check is useful. -->
-  <issue id="DuplicateDefinition" severity="ignore"/>
-  <issue id="IncludeLayoutParam" severity="Error">
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/layout/start_top_toolbar.xml"/>
-  </issue>
-  <issue id="RtlHardcoded" severity="Error">
-    <ignore regexp="remoting/android/internal"/>
-    <!--TODO(crbug.com/1044658): This suppression was added blindly, and needs investigated.-->
-    <ignore regexp="chrome/android/java/res/layout/sheet_tab_toolbar.xml"/>
+    <!-- Discussed in crbug.com/1069204, ignoring this class of errors since these are Q+ constants. -->
+    <ignore regexp="Must be one of: LineBreaker.BREAK_STRATEGY_SIMPLE, LineBreaker.BREAK_STRATEGY_HIGH_QUALITY, LineBreaker.BREAK_STRATEGY_BALANCED"/>
   </issue>
 </lint>
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 93be059..9aacad1d 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -924,6 +924,8 @@
         rebase_path(_stamp_path, root_build_dir),
         "--include-unexpected-failures",
         "--min-sdk-version=$_min_sdk_version",
+        "--android-sdk-root",
+        rebase_path(lint_android_sdk_root, root_build_dir),
       ]
 
       if (defined(testonly) && testonly) {
diff --git a/build/config/ios/find_signing_identity.py b/build/config/ios/find_signing_identity.py
index 693054a2..8116e68 100644
--- a/build/config/ios/find_signing_identity.py
+++ b/build/config/ios/find_signing_identity.py
@@ -10,6 +10,27 @@
 import sys
 import re
 
+
+def Redact(value, from_nth_char=5):
+  """Redact value past the N-th character."""
+  return value[:from_nth_char] + '*' * (len(value) - from_nth_char)
+
+
+class Identity(object):
+  """Represents a valid identity."""
+
+  def __init__(self, identifier, name, team):
+    self.identifier = identifier
+    self.name = name
+    self.team = team
+
+  def redacted(self):
+    return Identity(Redact(self.identifier), self.name, Redact(self.team))
+
+  def format(self):
+    return '%s: "%s (%s)"' % (self.identifier, self.name, self.team)
+
+
 def ListIdentities():
   return subprocess.check_output([
     'xcrun',
@@ -21,24 +42,48 @@
   ])
 
 
-def FindValidIdentity(identity_description):
+def FindValidIdentity(pattern):
+  """Find all identities matching the pattern."""
   lines = list(map(str.strip, ListIdentities().splitlines()))
   # Look for something like "2) XYZ "iPhone Developer: Name (ABC)""
-  exp = re.compile('[0-9]+\) ([A-F0-9]+) "([^"]*)"')
+  regex = re.compile('[0-9]+\) ([A-F0-9]+) "([^"(]*) \(([^)"]*)\)"')
+
+  result = []
   for line in lines:
-    res = exp.match(line)
+    res = regex.match(line)
     if res is None:
       continue
-    if identity_description in res.group(2):
-      yield res.group(1)
+    if pattern is None or pattern in res.group(2):
+      result.append(Identity(*res.groups()))
+  return result
+
+
+def Main(args):
+  parser = argparse.ArgumentParser('codesign iOS bundles')
+  parser.add_argument('--matching-pattern',
+                      dest='pattern',
+                      help='Pattern used to select the code signing identity.')
+  parsed = parser.parse_args(args)
+
+  identities = FindValidIdentity(parsed.pattern)
+  if len(identities) == 1:
+    print(identities[0].identifier, end='')
+    return 0
+
+  all_identities = FindValidIdentity(None)
+
+  print('Automatic code signing identity selection was enabled but could not')
+  print('find exactly one codesigning identity matching "%s".' % parsed.pattern)
+  print('')
+  print('Check that the keychain is accessible and that there is exactly one')
+  print('valid codesigning identity matching the pattern. Here is the parsed')
+  print('output of `xcrun security find-identity -v -p codesigning`:')
+  print()
+  for i, identity in enumerate(all_identities):
+    print('  %d) %s' % (i + 1, identity.redacted().format()))
+  print('    %d valid identities found' % (len(all_identities)))
+  return 1
 
 
 if __name__ == '__main__':
-  parser = argparse.ArgumentParser('codesign iOS bundles')
-  parser.add_argument(
-      '--identity-description', required=True,
-      help='Text description used to select the code signing identity.')
-  args = parser.parse_args()
-
-  for identity in FindValidIdentity(args.identity_description):
-    print(identity)
+  sys.exit(Main(sys.argv[1:]))
diff --git a/build/config/ios/ios_sdk.gni b/build/config/ios/ios_sdk.gni
index 3f05770..f3aaf810 100644
--- a/build/config/ios/ios_sdk.gni
+++ b/build/config/ios/ios_sdk.gni
@@ -131,47 +131,16 @@
   }
 }
 
-if (ios_enable_code_signing && !use_ios_simulator) {
-  find_signing_identity_args = [
-    "--identity-description",
-    ios_code_signing_identity_description,
-  ]
-
-  # If an identity is not provided, look for one on the host
+if (!use_ios_simulator && ios_enable_code_signing) {
+  # Automatically select a codesigning identity if no identity is configured.
+  # This only applies to device build as simulator builds are not signed.
   if (ios_code_signing_identity == "") {
-    _ios_identities = exec_script("find_signing_identity.py",
-                                  find_signing_identity_args,
-                                  "list lines")
-    if (_ios_identities == []) {
-      print("Automatic code signing identity selection was enabled but could")
-      print("not find exactly one code signing identity matching")
-      print("$ios_code_signing_identity_description. Check that your keychain")
-      print("is accessible and that there is a valid code signing identity")
-      print("listed by `xcrun security find-identity -v -p codesigning`")
-      print("TIP: Simulator builds don't require code signing...")
-      assert(false)
-    } else {
-      _ios_identities_len = 0
-      foreach(_, _ios_identities) {
-        _ios_identities_len += 1
-      }
-
-      ios_code_signing_identity = _ios_identities[0]
-      if (_ios_identities_len != 1) {
-        print("Warning: Multiple codesigning identities match " +
-              "\"$ios_code_signing_identity_description\"")
-        foreach(_ios_identity, _ios_identities) {
-          _selected = ""
-          if (ios_code_signing_identity == _ios_identity) {
-            _selected = " (selected)"
-          }
-          print("Warning: - $_ios_identity$_selected")
-        }
-        print("Warning: Please use either ios_code_signing_identity or ")
-        print("Warning: ios_code_signing_identity_description variable to ")
-        print("Warning: control which identity is selected.")
-        print()
-      }
-    }
+    ios_code_signing_identity =
+        exec_script("find_signing_identity.py",
+                    [
+                      "--matching-pattern",
+                      ios_code_signing_identity_description,
+                    ],
+                    "string")
   }
 }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 983c840a..10ddeaeb 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200512.3.1
\ No newline at end of file
+0.20200513.1.1
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 6256fe8..10ddeaeb 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200512.2.1
\ No newline at end of file
+0.20200513.1.1
\ No newline at end of file
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index da79b9837..7e03709 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -243,10 +243,6 @@
   // Returns true if there is an active scroll on the viewport.
   virtual bool IsCurrentlyScrollingViewport() const = 0;
 
-  // Whether the layer under |viewport_point| is the currently scrolling layer.
-  virtual bool IsCurrentlyScrollingLayerAt(
-      const gfx::Point& viewport_point) const = 0;
-
   virtual EventListenerProperties GetEventListenerProperties(
       EventListenerClass event_class) const = 0;
 
diff --git a/cc/ipc/cc_param_traits_macros.h b/cc/ipc/cc_param_traits_macros.h
index eed946bd..4b1d723 100644
--- a/cc/ipc/cc_param_traits_macros.h
+++ b/cc/ipc/cc_param_traits_macros.h
@@ -6,12 +6,22 @@
 #define CC_IPC_CC_PARAM_TRAITS_MACROS_H_
 
 #include "base/component_export.h"
+#include "cc/input/overscroll_behavior.h"
 #include "cc/input/touch_action.h"
 #include "ipc/ipc_message_macros.h"
 
 #undef IPC_MESSAGE_EXPORT
 #define IPC_MESSAGE_EXPORT COMPONENT_EXPORT(CC_IPC)
 
+IPC_ENUM_TRAITS_MAX_VALUE(
+    cc::OverscrollBehavior::OverscrollBehaviorType,
+    cc::OverscrollBehavior::OverscrollBehaviorType::kOverscrollBehaviorTypeMax)
+
+IPC_STRUCT_TRAITS_BEGIN(cc::OverscrollBehavior)
+  IPC_STRUCT_TRAITS_MEMBER(x)
+  IPC_STRUCT_TRAITS_MEMBER(y)
+IPC_STRUCT_TRAITS_END()
+
 IPC_ENUM_TRAITS_MAX_VALUE(cc::TouchAction, cc::TouchAction::kMax)
 
 #endif  // CC_IPC_CC_PARAM_TRAITS_MACROS_H_
diff --git a/cc/mojom/BUILD.gn b/cc/mojom/BUILD.gn
index fdafe611..12d7925 100644
--- a/cc/mojom/BUILD.gn
+++ b/cc/mojom/BUILD.gn
@@ -6,7 +6,10 @@
 
 mojom("mojom") {
   generate_java = true
-  sources = [ "touch_action.mojom" ]
+  sources = [
+    "overscroll_behavior.mojom",
+    "touch_action.mojom",
+  ]
 
   public_deps = [ "//mojo/public/mojom/base" ]
 
@@ -21,6 +24,23 @@
     traits_public_deps = [ "//cc/ipc" ]
   }
 
-  cpp_typemaps = [ touch_action_typemap ]
-  blink_cpp_typemaps = [ touch_action_typemap ]
+  overscroll_behavior_typemap = {
+    types = [
+      {
+        mojom = "cc.mojom.OverscrollBehavior"
+        cpp = "::cc::OverscrollBehavior"
+      },
+    ]
+    traits_headers = [ "//cc/ipc/cc_param_traits_macros.h" ]
+    traits_public_deps = [ "//cc/ipc" ]
+  }
+
+  cpp_typemaps = [
+    overscroll_behavior_typemap,
+    touch_action_typemap,
+  ]
+  blink_cpp_typemaps = [
+    overscroll_behavior_typemap,
+    touch_action_typemap,
+  ]
 }
diff --git a/cc/mojom/overscroll_behavior.mojom b/cc/mojom/overscroll_behavior.mojom
new file mode 100644
index 0000000..36352e21
--- /dev/null
+++ b/cc/mojom/overscroll_behavior.mojom
@@ -0,0 +1,8 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module cc.mojom;
+
+[Native]
+enum OverscrollBehavior;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index ffa5c8d6..f929592 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -899,33 +899,6 @@
   return viewport().ShouldScroll(*node);
 }
 
-bool LayerTreeHostImpl::IsCurrentlyScrollingLayerAt(
-    const gfx::Point& viewport_point) const {
-  auto* scrolling_node = CurrentlyScrollingNode();
-  if (!scrolling_node)
-    return false;
-
-  gfx::PointF device_viewport_point = gfx::ScalePoint(
-      gfx::PointF(viewport_point), active_tree_->device_scale_factor());
-
-  LayerImpl* layer_impl =
-      active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
-
-  bool scroll_on_main_thread = false;
-  uint32_t main_thread_scrolling_reasons;
-  auto* test_scroll_node = FindScrollNodeForDeviceViewportPoint(
-      device_viewport_point, layer_impl, &scroll_on_main_thread,
-      &main_thread_scrolling_reasons);
-
-  if (scroll_on_main_thread)
-    return false;
-
-  if (scrolling_node == test_scroll_node)
-    return true;
-
-  return false;
-}
-
 EventListenerProperties LayerTreeHostImpl::GetEventListenerProperties(
     EventListenerClass event_class) const {
   return active_tree_->event_listener_properties(event_class);
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index c2f8100..d19ad11 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -302,8 +302,6 @@
                                base::TimeDelta duration);
   void SetNeedsAnimateInput() override;
   bool IsCurrentlyScrollingViewport() const override;
-  bool IsCurrentlyScrollingLayerAt(
-      const gfx::Point& viewport_point) const override;
   EventListenerProperties GetEventListenerProperties(
       EventListenerClass event_class) const override;
   InputHandler::TouchStartOrMoveEventListenerType
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 910365d..e90184a 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -1188,13 +1188,12 @@
   EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
             status.main_thread_scrolling_reasons);
 
-  EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point()));
+  EXPECT_TRUE(host_impl_->CurrentlyScrollingNode());
   host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
                                        ui::ScrollInputType::kTouchscreen)
                                .get());
-  EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, 10)));
   host_impl_->ScrollEnd();
-  EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point()));
+  EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
   EXPECT_TRUE(did_request_redraw_);
   EXPECT_TRUE(did_request_commit_);
 }
@@ -1641,7 +1640,6 @@
   EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
   EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
             status.main_thread_scrolling_reasons);
-  EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25)));
 
   status = host_impl_->ScrollBegin(
       BeginState(gfx::Point(25, 25), gfx::Vector2d(0, 10),
@@ -1651,7 +1649,6 @@
   EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
   EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
             status.main_thread_scrolling_reasons);
-  EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25)));
 
   // All scroll types outside this region should succeed.
   status = host_impl_->ScrollBegin(
@@ -1663,13 +1660,10 @@
   EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
             status.main_thread_scrolling_reasons);
 
-  EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75)));
   host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
                                        ui::ScrollInputType::kWheel)
                                .get());
-  EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(25, 25)));
   host_impl_->ScrollEnd();
-  EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75)));
 
   status = host_impl_->ScrollBegin(
       BeginState(gfx::Point(75, 75), gfx::Vector2d(0, 10),
@@ -1679,12 +1673,10 @@
   EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
   EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
             status.main_thread_scrolling_reasons);
-  EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75)));
   host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 10),
                                        ui::ScrollInputType::kTouchscreen)
                                .get());
   host_impl_->ScrollEnd();
-  EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(75, 75)));
 }
 
 TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
@@ -1708,7 +1700,6 @@
   EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
             status.main_thread_scrolling_reasons);
 
-  EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(40, 10)));
   host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 1),
                                        ui::ScrollInputType::kWheel)
                                .get());
@@ -11726,14 +11717,12 @@
                                     .get(),
                                 ui::ScrollInputType::kTouchscreen)
                   .thread);
-    EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point()));
 
     // Scroll near the edge of the outer viewport.
     host_impl_->ScrollUpdate(UpdateState(gfx::Point(), scroll_delta,
                                          ui::ScrollInputType::kTouchscreen)
                                  .get());
     inner_expected += scroll_delta;
-    EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point()));
 
     EXPECT_VECTOR_EQ(inner_expected, CurrentScrollOffset(inner_scroll));
     EXPECT_VECTOR_EQ(outer_expected, CurrentScrollOffset(outer_scroll));
@@ -11745,11 +11734,9 @@
                                          gfx::ScaleVector2d(scroll_delta, 2),
                                          ui::ScrollInputType::kTouchscreen)
                                  .get());
-    EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point()));
     outer_expected += scroll_delta;
     inner_expected += scroll_delta;
     host_impl_->ScrollEnd();
-    EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point()));
 
     EXPECT_VECTOR_EQ(inner_expected, CurrentScrollOffset(inner_scroll));
     EXPECT_VECTOR_EQ(outer_expected, CurrentScrollOffset(outer_scroll));
@@ -13343,16 +13330,13 @@
       InputHandler::SCROLL_ON_IMPL_THREAD,
       host_impl_->ScrollBegin(begin_state.get(), ui::ScrollInputType::kWheel)
           .thread);
-  EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y)));
   auto update_state = UpdateState(gfx::Point(0, y), gfx::Vector2d(0, 50),
                                   ui::ScrollInputType::kWheel);
   // Use "precise pixel" granularity to avoid animating.
   update_state->data()->delta_granularity =
       ui::ScrollGranularity::kScrollByPrecisePixel;
   host_impl_->ScrollUpdate(update_state.get());
-  EXPECT_TRUE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point(0, y + 50)));
   host_impl_->ScrollEnd();
-  EXPECT_FALSE(host_impl_->IsCurrentlyScrollingLayerAt(gfx::Point()));
 
   // The instant scroll should have marked the smooth scroll animation as
   // aborted.
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index a015bf70..d308087 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -333,6 +333,7 @@
     "//components/background_task_scheduler:background_task_scheduler_java",
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
     "//components/bookmarks/common/android:bookmarks_java",
+    "//components/browser_ui/android/bottomsheet:java",
     "//components/browser_ui/modaldialog/android:java",
     "//components/browser_ui/notifications/android:java",
     "//components/browser_ui/settings/android:java",
@@ -879,6 +880,7 @@
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
     "//components/background_task_scheduler/internal:background_task_scheduler_javatests",
     "//components/bookmarks/common/android:bookmarks_java",
+    "//components/browser_ui/android/bottomsheet:java",
     "//components/browser_ui/modaldialog/android:java",
     "//components/browser_ui/modaldialog/android:javatests",
     "//components/browser_ui/settings/android:java",
diff --git a/chrome/android/DEPS b/chrome/android/DEPS
index 6d41c59..3920c911 100644
--- a/chrome/android/DEPS
+++ b/chrome/android/DEPS
@@ -9,6 +9,7 @@
   "+chrome/browser/ui/messages/android",
   "+chrome/browser/util/android/java",
   "+chrome/browser/xsurface/android",
+  "+components/browser_ui/android/bottomsheet",
   "+components/browser_ui/modaldialog/android",
   "+components/browser_ui/site_settings/android",
   "+components/browser_ui/util/android",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 4ba6ff5..d47b944c 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1797,7 +1797,6 @@
   "java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java",
   "java/src/org/chromium/chrome/browser/widget/ScrimView.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java",
-  "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContent.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java",
   "java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetSwipeDetector.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 243fd3f..7e20e80 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -40,6 +40,7 @@
     "//chrome/browser/ui/messages/android:java",
     "//chrome/browser/util:java",
     "//components/autofill/android:autofill_java",
+    "//components/browser_ui/android/bottomsheet:java",
     "//components/browser_ui/modaldialog/android:java",
     "//components/browser_ui/widget/android:java",
     "//components/policy/android:policy_java",
@@ -274,6 +275,7 @@
     "//chrome/browser/preferences:java",
     "//chrome/test/android:chrome_java_test_support",
     "//components/autofill_assistant/browser:proto_java",
+    "//components/browser_ui/android/bottomsheet:java",
     "//components/browser_ui/widget/android:java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
index a51393a..0ead609 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java
@@ -34,9 +34,9 @@
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataModel;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.ApplicationViewportInsetSupplier;
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java
index 1078798..ae58e2a3 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java
@@ -13,7 +13,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.autofill_assistant.R;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
 /**
  * The {@link BottomSheetContent} for the Autofill Assistant. It supports notifying the
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java
index 6c8e2bd..1a9e46d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetUtils.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.autofill_assistant;
 
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
 class BottomSheetUtils {
     /** Request {@code controller} to show {@code content} and expand the sheet when it is shown. */
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java
index b5ebcb3..d73181b 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/SizeListenableLinearLayout.java
@@ -10,7 +10,7 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
 /** A LinearLayout that can notify when its size changes. */
 public class SizeListenableLinearLayout extends LinearLayout {
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAccessibilityIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAccessibilityIntegrationTest.java
index b2cfad9..e2e4520 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAccessibilityIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAccessibilityIntegrationTest.java
@@ -39,7 +39,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.autofill_assistant.proto.ActionProto;
 import org.chromium.chrome.browser.autofill_assistant.proto.ChipProto;
@@ -108,7 +107,6 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "crbug.com/1081604")
     public void testBottomSheetHasRestrictedFixedHeight() throws Exception {
         ArrayList<ActionProto> list = new ArrayList<>();
 
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
index 9ec7cbf..13af9ea 100644
--- a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantChromeTabIntegrationTest.java
@@ -16,10 +16,10 @@
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
 
-import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
 
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntil;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewAssertionTrue;
 import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
 
@@ -306,7 +306,6 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "https://crbug.com/1081788")
     public void backButtonTerminatesAutofillAssistant() {
         ChromeTabUtils.loadUrlOnUiThread(
                 mTestRule.getActivity().getActivityTab(), getURL(TEST_PAGE_B));
@@ -333,13 +332,13 @@
         Espresso.pressBack();
         Espresso.pressBack();
         waitUntilViewMatchesCondition(withText(containsString("Sorry")), isCompletelyDisplayed());
-        assertThat(mTestRule.getActivity().getActivityTab().getUrl().getSpec(),
-                is(getURL(TEST_PAGE_A)));
+        waitUntil(()
+                          -> mTestRule.getActivity().getActivityTab().getUrl().getSpec().equals(
+                                  getURL(TEST_PAGE_A)));
     }
 
     @Test
     @MediumTest
-    @DisabledTest(message = "https://crbug.com/1081608")
     public void interactingWithLocationBarHidesAutofillAssistant() throws Exception {
         ArrayList<ActionProto> list = new ArrayList<>();
         list.add((ActionProto) ActionProto.newBuilder()
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
index 7cc8fb1..abccf4ec 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -579,9 +579,13 @@
                 && maxPosFraction <= 1.0f
             : "Max position fraction should be ranging between 0.0 and 1.0";
 
-        int sectionHeaderTop = mSectionHeaderView.getTop();
-        if (sectionHeaderTop < 0) return false;
-        if (sectionHeaderTop > maxPosFraction * mRootView.getHeight()) return false;
+        // Get the top position of the section header view in the recycler view.
+        int[] headerPositions = new int[2];
+        mSectionHeaderView.getLocationOnScreen(headerPositions);
+        int topPosInStream = headerPositions[1] - mRootView.getTop();
+
+        if (topPosInStream < 0) return false;
+        if (topPosInStream > maxPosFraction * mRootView.getHeight()) return false;
 
         return true;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/DisclosureNotification.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/DisclosureNotification.java
index d532ef7..7dfd7ddf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/DisclosureNotification.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/DisclosureNotification.java
@@ -112,6 +112,9 @@
         PendingIntentProvider intent = DisclosureAcceptanceBroadcastReceiver.createPendingIntent(
                 mContext, scope, NOTIFICATION_ID_TWA_DISCLOSURE, packageName);
 
+        // We don't have an icon to display.
+        int icon = 0;
+
         return NotificationBuilderFactory
                 .createChromeNotificationBuilder(
                         preferCompat, channelId, remoteAppPackageName, metadata)
@@ -119,6 +122,8 @@
                 .setContentTitle(title)
                 .setContentText(text)
                 .setContentIntent(intent)
+                .addAction(icon, mResources.getString(R.string.got_it), intent,
+                        NotificationUmaTracker.ActionType.TWA_NOTIFICATION_ACCEPTANCE)
                 .setShowWhen(false)
                 .setAutoCancel(false)
                 .setOngoing(!firstTime)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/DisclosureSnackbar.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/DisclosureSnackbar.java
index 935ce03..d712993 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/DisclosureSnackbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/view/DisclosureSnackbar.java
@@ -4,8 +4,6 @@
 
 package org.chromium.chrome.browser.browserservices.trustedwebactivityui.view;
 
-import static org.chromium.chrome.browser.browserservices.trustedwebactivityui.TrustedWebActivityModel.DISCLOSURE_SCOPE;
-
 import android.content.res.Resources;
 
 import org.chromium.chrome.R;
@@ -58,15 +56,14 @@
         if (mShown) return null;
         mShown = true;
 
-        String scope = mModel.get(DISCLOSURE_SCOPE);
-        String text = mResources.getString(R.string.twa_running_in_chrome_v2, scope);
+        String title = mResources.getString(R.string.twa_running_in_chrome);
 
         int type = Snackbar.TYPE_ACTION;
         int code = Snackbar.UMA_TWA_PRIVACY_DISCLOSURE_V2;
 
-        String action = mResources.getString(R.string.ok);
+        String action = mResources.getString(R.string.got_it);
 
-        return Snackbar.make(text, controller, type, code)
+        return Snackbar.make(title, controller, type, code)
                 .setAction(action, null)
                 .setDuration(DURATION_MS)
                 .setSingleLine(false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
index 5b40cfa..d8635659 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
@@ -24,11 +24,11 @@
 import org.chromium.chrome.browser.tabmodel.TabCreatorManager.TabCreator;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
 import org.chromium.chrome.browser.ui.favicon.FaviconUtils;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.widget.RoundedIconGenerator;
 import org.chromium.components.embedder_support.view.ContentView;
 import org.chromium.components.feature_engagement.EventConstants;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java
index d0c16710..454f32d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabSheetContent.java
@@ -23,8 +23,8 @@
 import org.chromium.chrome.browser.thinwebview.ThinWebView;
 import org.chromium.chrome.browser.thinwebview.ThinWebViewConstraints;
 import org.chromium.chrome.browser.thinwebview.ThinWebViewFactory;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.widget.FadingShadow;
 import org.chromium.components.browser_ui.widget.FadingShadowView;
 import org.chromium.components.embedder_support.delegate.WebContentsDelegateAndroid;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index 1171711d..c3fdcf4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -265,8 +265,7 @@
      */
     private void shareImageWithLastShareComponent() {
         retrieveImage(ContextMenuImageFormat.ORIGINAL, (Uri imageUri) -> {
-            ShareHelper.shareImage(
-                    mWindow, ShareHelper.getLastShareByChromeComponentName(), imageUri);
+            ShareHelper.shareImage(mWindow, ShareHelper.getLastShareComponentName(), imageUri);
         });
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ShareContextMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ShareContextMenuItem.java
index 3971df7..64cfcd1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ShareContextMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ShareContextMenuItem.java
@@ -22,7 +22,6 @@
     @IdRes
     private final int mMenuId;
     private final boolean mIsShareLink;
-    private String mCreatorPackageName;
 
     /**
      * A representation of a Context Menu Item. Each item should have a string and an id associated
@@ -37,13 +36,6 @@
         mIsShareLink = isShareLink;
     }
 
-    /**
-     * Set the package name of the app who requests for share. If Null, it is requested by Chrome.
-     */
-    public void setCreatorPackageName(String creatorPackageName) {
-        mCreatorPackageName = creatorPackageName;
-    }
-
     @Override
     public String getTitle(Context context) {
         return context.getString(mStringId);
@@ -67,6 +59,6 @@
     public Pair<Drawable, CharSequence> getShareInfo() {
         Intent shareIntent = mIsShareLink ? ShareHelper.getShareLinkAppCompatibilityIntent()
                                           : ShareHelper.getShareImageIntent(null);
-        return ShareHelper.getShareableIconAndName(shareIntent, mCreatorPackageName);
+        return ShareHelper.getShareableIconAndName(shareIntent);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
index d473b37..d5fac53 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
@@ -18,12 +18,12 @@
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.gesturenav.NavigationSheetMediator.ItemProperties;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.NavigationHistory;
 import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
index b92119f..857c6cb9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
@@ -133,8 +133,10 @@
         int ANNOUNCEMENT_ACK = 13;
         // Open button on announcement notification.
         int ANNOUNCEMENT_OPEN = 14;
+        // "Got it" button on the TWA "Running in Chrome" notification.
+        int TWA_NOTIFICATION_ACCEPTANCE = 15;
 
-        int NUM_ENTRIES = 15;
+        int NUM_ENTRIES = 16;
     }
 
     private static class LazyHolder {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
index 96ab566..83ac1ec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
@@ -12,10 +12,10 @@
 import org.chromium.chrome.browser.payments.SslValidityChecker;
 import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator.PaymentHandlerUiObserver;
 import org.chromium.chrome.browser.payments.handler.toolbar.PaymentHandlerToolbarCoordinator.PaymentHandlerToolbarObserver;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.content_public.browser.WebContents;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerView.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerView.java
index 1037323..37989b71 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerView.java
@@ -13,7 +13,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.RenderCoordinates;
 import org.chromium.content_public.browser.WebContents;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/minimal/MinimalUIMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/minimal/MinimalUIMediator.java
index d6ac0737..3fcd0785 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/minimal/MinimalUIMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/minimal/MinimalUIMediator.java
@@ -25,11 +25,11 @@
 import org.chromium.chrome.browser.payments.minimal.MinimalUICoordinator.DismissObserver;
 import org.chromium.chrome.browser.payments.minimal.MinimalUICoordinator.ErrorAndCloseObserver;
 import org.chromium.chrome.browser.payments.minimal.MinimalUICoordinator.ReadyObserver;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.payments.PaymentApp;
 import org.chromium.ui.modelutil.PropertyModel;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/minimal/MinimalUIView.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/minimal/MinimalUIView.java
index e1ca4a9c..e2b44ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/minimal/MinimalUIView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/minimal/MinimalUIView.java
@@ -14,7 +14,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
 /** Payment minimal UI. */
 /* package */ class MinimalUIView implements BottomSheetContent {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetContent.java
index e080451..7577a0c5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/DevicePickerBottomSheetContent.java
@@ -21,8 +21,8 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfMetrics.SendTabToSelfShareClickResult;
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
 import org.chromium.components.sync.AndroidSyncSettings;
 import org.chromium.content_public.browser.NavigationEntry;
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 c2704460..e6356ec 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
@@ -12,8 +12,8 @@
 import org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfMetrics.SendTabToSelfShareClickResult;
 import org.chromium.chrome.browser.share.ShareActivity;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.content_public.browser.WebContents;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
index 02955be..049cc06 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
@@ -34,7 +34,6 @@
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AlertDialog;
@@ -82,8 +81,6 @@
     /** The task ID of the activity that triggered the share action. */
     public static final String EXTRA_TASK_ID = "org.chromium.chrome.extra.TASK_ID";
 
-    private static final String PACKAGE_NAME_KEY_SUFFIX = "last_shared_package_name";
-    private static final String CLASS_NAME_KEY_SUFFIX = "last_shared_class_name";
     private static final String EXTRA_SHARE_SCREENSHOT_AS_STREAM = "share_screenshot_as_stream";
 
     /** Force the use of a Chrome-specific intent chooser, not the system chooser. */
@@ -170,7 +167,6 @@
      */
     static class TargetChosenReceiver extends BroadcastReceiver implements IntentCallback {
         private static final String EXTRA_RECEIVER_TOKEN = "receiver_token";
-        private static final String EXTRA_SOURCE_PACKAGE_NAME = "source_package_name";
         private static final Object LOCK = new Object();
 
         private static String sTargetChosenReceiveAction;
@@ -193,8 +189,7 @@
 
         @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
         static void sendChooserIntent(boolean saveLastUsed, WindowAndroid window,
-                Intent sharingIntent, @Nullable TargetChosenCallback callback,
-                @Nullable String sourcePackageName) {
+                Intent sharingIntent, @Nullable TargetChosenCallback callback) {
             final String packageName = ContextUtils.getApplicationContext().getPackageName();
             synchronized (LOCK) {
                 if (sTargetChosenReceiveAction == null) {
@@ -218,7 +213,6 @@
             Intent intent = new Intent(sTargetChosenReceiveAction);
             intent.setPackage(packageName);
             intent.putExtra(EXTRA_RECEIVER_TOKEN, sLastRegisteredReceiver.hashCode());
-            intent.putExtra(EXTRA_SOURCE_PACKAGE_NAME, sourcePackageName);
             Activity activity = window.getActivity().get();
             final PendingIntent pendingIntent = PendingIntent.getBroadcast(activity, 0, intent,
                     PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
@@ -244,13 +238,12 @@
             }
 
             ComponentName target = intent.getParcelableExtra(Intent.EXTRA_CHOSEN_COMPONENT);
-            String sourcePackageName = intent.getStringExtra(EXTRA_SOURCE_PACKAGE_NAME);
             if (mCallback != null) {
                 mCallback.onTargetChosen(target);
                 mCallback = null;
             }
             if (mSaveLastUsed && target != null) {
-                setLastShareComponentName(target, sourcePackageName);
+                setLastShareComponentName(target);
             }
         }
 
@@ -275,13 +268,12 @@
      */
     public static void shareDirectly(ShareParams params) {
         assert params.shareDirectly();
-        ComponentName component = getLastShareComponentName(params.getSourcePackageName());
+        ComponentName component = getLastShareComponentName();
         if (component == null) return;
         assert params.getCallback() == null;
         makeIntentAndShare(params, component);
     }
 
-
     /**
      * Share an image URI with an activity identified by the provided Component Name.
      * @param window The current window.
@@ -293,7 +285,7 @@
         Intent shareIntent = getShareImageIntent(imageUri);
         if (name == null) {
             if (TargetChosenReceiver.isSupported()) {
-                TargetChosenReceiver.sendChooserIntent(true, window, shareIntent, null, null);
+                TargetChosenReceiver.sendChooserIntent(true, window, shareIntent, null);
             } else {
                 Intent chooserIntent = Intent.createChooser(shareIntent,
                         window.getActivity().get().getString(R.string.share_link_chooser_title));
@@ -369,7 +361,7 @@
                     callbackCalled[0] = true;
                 }
                 if (params.saveLastUsed()) {
-                    setLastShareComponentName(component, params.getSourcePackageName());
+                    setLastShareComponentName(component);
                 }
                 makeIntentAndShare(params, component);
                 dialog.dismiss();
@@ -383,9 +375,6 @@
                     callback.onCancel();
                     callbackCalled[0] = true;
                 }
-                if (params.getOnDialogDismissed() != null) {
-                    params.getOnDialogDismissed().run();
-                }
             }
         });
 
@@ -417,8 +406,8 @@
             fireIntent(params.getWindow(), intent, null);
         } else {
             assert TargetChosenReceiver.isSupported();
-            TargetChosenReceiver.sendChooserIntent(params.saveLastUsed(), params.getWindow(),
-                    intent, params.getCallback(), params.getSourcePackageName());
+            TargetChosenReceiver.sendChooserIntent(
+                    params.saveLastUsed(), params.getWindow(), intent, params.getCallback());
         }
     }
 
@@ -429,7 +418,7 @@
      */
     public static void configureDirectShareMenuItem(Context context, MenuItem item) {
         Intent shareIntent = getShareLinkAppCompatibilityIntent();
-        Pair<Drawable, CharSequence> directShare = getShareableIconAndName(shareIntent, null);
+        Pair<Drawable, CharSequence> directShare = getShareableIconAndName(shareIntent);
         Drawable directShareIcon = directShare.first;
         CharSequence directShareTitle = directShare.second;
 
@@ -443,16 +432,13 @@
     /**
      * Get the icon and name of the most recently shared app by certain app.
      * @param shareIntent Intent used to get list of apps support sharing.
-     * @param sourcePackageName The package name of the app who requests for share. If Null, it is
-     *                          requested by Chrome.
      * @return The Image and the String of the recently shared Icon.
      */
-    public static Pair<Drawable, CharSequence> getShareableIconAndName(
-            Intent shareIntent, @Nullable String sourcePackageName) {
+    public static Pair<Drawable, CharSequence> getShareableIconAndName(Intent shareIntent) {
         Drawable directShareIcon = null;
         CharSequence directShareTitle = null;
 
-        final ComponentName component = getLastShareComponentName(sourcePackageName);
+        final ComponentName component = getLastShareComponentName();
         boolean isComponentValid = false;
         if (component != null) {
             shareIntent.setPackage(component.getPackageName());
@@ -492,25 +478,9 @@
      *
      * This method is public since it is used in tests to avoid creating share dialog.
      * @param component The {@link ComponentName} of the app selected for sharing.
-     * @param sourcePackageName The package name of the app who requests share. If Null, it is
-     *                          request by Chrome.
      */
     @VisibleForTesting
-    public static void setLastShareComponentName(
-            ComponentName component, @Nullable String sourcePackageName) {
-        if (sourcePackageName == null) {
-            setLastShareComponentNameForChrome(component);
-            return;
-        }
-
-        SharedPreferences preferences = getExternalAppSharingSharedPreferences();
-        SharedPreferences.Editor editor = preferences.edit();
-        editor.putString(getPackageNameKey(sourcePackageName), component.getPackageName());
-        editor.putString(getClassNameKey(sourcePackageName), component.getClassName());
-        editor.apply();
-    }
-
-    private static void setLastShareComponentNameForChrome(ComponentName component) {
+    public static void setLastShareComponentName(ComponentName component) {
         SharedPreferencesManager preferencesManager = SharedPreferencesManager.getInstance();
         preferencesManager.writeString(
                 ChromePreferenceKeys.SHARING_LAST_SHARED_PACKAGE_NAME, component.getPackageName());
@@ -595,27 +565,10 @@
     }
 
     /**
-     * Gets the {@link ComponentName} of the app that was used to last share by certain app.
-     * @param sourcePackageName The package name of the app who requests for share. If Null, it is
-     *                          requested by Chrome.
+     * Gets the {@link ComponentName} of the app that was used to last share.
      */
     @Nullable
-    public static ComponentName getLastShareComponentName(@Nullable String sourcePackageName) {
-        if (sourcePackageName == null) {
-            return getLastShareByChromeComponentName();
-        }
-
-        SharedPreferences preferences = getExternalAppSharingSharedPreferences();
-        String packageName = preferences.getString(getPackageNameKey(sourcePackageName), null);
-        String className = preferences.getString(getClassNameKey(sourcePackageName), null);
-        return createComponentName(packageName, className);
-    }
-
-    /**
-     * Gets the {@link ComponentName} of the app that was used to last share by Chrome.
-     */
-    @Nullable
-    public static ComponentName getLastShareByChromeComponentName() {
+    public static ComponentName getLastShareComponentName() {
         SharedPreferencesManager preferencesManager = SharedPreferencesManager.getInstance();
         String packageName = preferencesManager.readString(
                 ChromePreferenceKeys.SHARING_LAST_SHARED_PACKAGE_NAME, null);
@@ -634,14 +587,6 @@
                 EXTERNAL_APP_SHARING_PREF_FILE_NAME, Context.MODE_PRIVATE);
     }
 
-    private static String getPackageNameKey(@NonNull String sourcePackageName) {
-        return sourcePackageName + PACKAGE_NAME_KEY_SUFFIX;
-    }
-
-    private static String getClassNameKey(@NonNull String sourcePackageName) {
-        return sourcePackageName + CLASS_NAME_KEY_SUFFIX;
-    }
-
     /**
      * Loads the icon for the provided ResolveInfo.
      * @param info The ResolveInfo to load the icon for.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareParams.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareParams.java
index f82a048..7a18da9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareParams.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareParams.java
@@ -62,18 +62,10 @@
      */
     private TargetChosenCallback mCallback;
 
-    /** The package name of the app who requests for share. If Null, it is requested by Chrome */
-    private final String mSourcePackageName;
-
-    /** The {@link Runnable} called when the share dialog is dismissed. */
-    @Nullable
-    private final Runnable mOnDialogDismissed;
-
     private ShareParams(boolean shareDirectly, boolean saveLastUsed, WindowAndroid window,
             String title, String text, String url, @Nullable String fileContentType,
             @Nullable ArrayList<Uri> fileUris, @Nullable Uri offlineUri,
-            @Nullable Uri screenshotUri, @Nullable TargetChosenCallback callback,
-            @Nullable String sourcePackageName, @Nullable Runnable onDialogDismissed) {
+            @Nullable Uri screenshotUri, @Nullable TargetChosenCallback callback) {
         mShareDirectly = shareDirectly;
         mSaveLastUsed = saveLastUsed;
         mWindow = window;
@@ -85,8 +77,6 @@
         mOfflineUri = offlineUri;
         mScreenshotUri = screenshotUri;
         mCallback = callback;
-        mSourcePackageName = sourcePackageName;
-        mOnDialogDismissed = onDialogDismissed;
     }
 
     /**
@@ -172,21 +162,6 @@
         return mCallback;
     }
 
-    /**
-     * @return The package name of the app who requests for share.
-     */
-    public String getSourcePackageName() {
-        return mSourcePackageName;
-    }
-
-    /**
-     * @return The {@link Runnable} to be called when the share dialog is dismissed.
-     */
-    @Nullable
-    public Runnable getOnDialogDismissed() {
-        return mOnDialogDismissed;
-    }
-
     /** The builder for {@link ShareParams} objects. */
     public static class Builder {
         private boolean mShareDirectly;
@@ -200,9 +175,7 @@
         private Uri mOfflineUri;
         private Uri mScreenshotUri;
         private TargetChosenCallback mCallback;
-        private String mSourcePackageName;
         private boolean mIsExternalUrl;
-        private Runnable mOnDialogDismissed;
 
         public Builder(@NonNull WindowAndroid window, @NonNull String title, @NonNull String url) {
             mWindow = window;
@@ -284,14 +257,6 @@
         }
 
         /**
-         * Set the package name of the app who requests for share.
-         */
-        public Builder setSourcePackageName(String sourcePackageName) {
-            mSourcePackageName = sourcePackageName;
-            return this;
-        }
-
-        /**
          * Set whether the params are created by the url from external app.
          */
         public Builder setIsExternalUrl(boolean isExternalUrl) {
@@ -299,14 +264,6 @@
             return this;
         }
 
-        /**
-         * Set a runnable to be called when the share dialog is dismissed.
-         */
-        public Builder setOnDialogDismissed(Runnable onDialogDismised) {
-            mOnDialogDismissed = onDialogDismised;
-            return this;
-        }
-
         /** @return A fully constructed {@link ShareParams} object. */
         public ShareParams build() {
             if (!TextUtils.isEmpty(mUrl)) {
@@ -321,8 +278,7 @@
                 }
             }
             return new ShareParams(mShareDirectly, mSaveLastUsed, mWindow, mTitle, mText, mUrl,
-                    mFileContentType, mFileUris, mOfflineUri, mScreenshotUri, mCallback,
-                    mSourcePackageName, mOnDialogDismissed);
+                    mFileContentType, mFileUris, mOfflineUri, mScreenshotUri, mCallback);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareSheetBottomSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareSheetBottomSheetContent.java
index 5df3895..1f35d9f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareSheetBottomSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareSheetBottomSheetContent.java
@@ -18,7 +18,7 @@
 
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareSheetPropertyModelBuilder.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareSheetPropertyModelBuilder.java
index 9c890c64..7fed1e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareSheetPropertyModelBuilder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareSheetPropertyModelBuilder.java
@@ -112,8 +112,7 @@
                                     callback.onTargetChosen(component);
                                 }
                                 if (params.saveLastUsed()) {
-                                    ShareHelper.setLastShareComponentName(
-                                            component, params.getSourcePackageName());
+                                    ShareHelper.setLastShareComponentName(component);
                                 }
                                 mBottomSheetController.hideContent(bottomSheet, true);
                                 ShareHelper.makeIntentAndShare(params, component);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
index 4c30e3e..d28b565 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
@@ -9,9 +9,9 @@
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.SelectionPopupController;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.modaldialog.ModalDialogManager;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
index 7359a44..dfa0ced2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -27,9 +27,10 @@
 import org.chromium.base.ObserverList;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent.HeightMode;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.StateChangeReason;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent.HeightMode;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java
index 49fde24..2abc20b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetController.java
@@ -28,6 +28,7 @@
 import org.chromium.chrome.browser.vr.VrModuleProvider;
 import org.chromium.chrome.browser.widget.ScrimView.ScrimObserver;
 import org.chromium.chrome.browser.widget.ScrimView.ScrimParams;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.components.browser_ui.widget.scrim.ScrimProperties;
 import org.chromium.ui.KeyboardVisibilityDelegate;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java
index bb9af31..9275f648 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserver.java
@@ -8,6 +8,7 @@
 
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.StateChangeReason;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
 /**
  * An interface for notifications about the state of the bottom sheet.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/EmptyBottomSheetObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/EmptyBottomSheetObserver.java
index 174d23d..0155588 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/EmptyBottomSheetObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/EmptyBottomSheetObserver.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.widget.bottomsheet;
 
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.StateChangeReason;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
 /**
  * An empty base implementation of the {@link BottomSheetObserver} interface.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ShareIntentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ShareIntentTest.java
index e9d79f54..e9055f7 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ShareIntentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ShareIntentTest.java
@@ -179,8 +179,7 @@
                     mockActivity.getShareDelegateSupplier(), mockActivity.getActivityTabProvider(),
                     null, null);
         });
-        ShareHelper.setLastShareComponentName(
-                new ComponentName("test.package", "test.activity"), null);
+        ShareHelper.setLastShareComponentName(new ComponentName("test.package", "test.activity"));
         // Skips the capture of screenshot and notifies with an empty file.
         ShareDelegateImpl.setScreenshotCaptureSkippedForTesting(true);
 
@@ -202,7 +201,7 @@
 
         mockActivity.waitForFileCheck();
 
-        ShareHelper.setLastShareComponentName(new ComponentName("", ""), null);
+        ShareHelper.setLastShareComponentName(new ComponentName("", ""));
     }
 
     @Before
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java
index 330432b..f3ee556 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/RunningInChromeTest.java
@@ -4,7 +4,6 @@
 
 package org.chromium.chrome.browser.browserservices;
 
-import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
@@ -33,7 +32,6 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeApplication;
@@ -127,33 +125,13 @@
 
     @Test
     @MediumTest
-    @DisabledTest(message = "https://crbug.com/1072424")
-    @Features.DisableFeatures(ChromeFeatureList.TRUSTED_WEB_ACTIVITY_NEW_DISCLOSURE)
-    public void showsRunningInChrome() throws TimeoutException {
-        Intent intent = createTrustedWebActivityIntent(mTestPage);
-        launch(intent);
-
-        clearOtherSnackbars();
-
-        assertTrue(isTrustedWebActivity(mCustomTabActivityTestRule.getActivity()));
-        Espresso.onView(withText(containsString(getV1String())))
-                .check(matches(isDisplayed()));
-    }
-
-    @Test
-    @MediumTest
     public void showsNewRunningInChrome() throws TimeoutException {
         launch(createTrustedWebActivityIntent(mTestPage));
 
         clearOtherSnackbars();
 
         assertTrue(isTrustedWebActivity(mCustomTabActivityTestRule.getActivity()));
-        Espresso.onView(withText(containsString(getV1String())))
-                .check(doesNotExist());
-
-        String site = Origin.createOrThrow(mTestPage).toString();
-
-        Espresso.onView(withText(containsString(getV2String(site))))
+        Espresso.onView(withText(containsString(getString())))
                 .check(matches(isDisplayed()));
     }
 
@@ -235,12 +213,7 @@
         });
     }
 
-    private String getV2String(String scope) {
-        return mCustomTabActivityTestRule.getActivity().getResources()
-                .getString(R.string.twa_running_in_chrome_v2, scope);
-    }
-
-    private String getV1String() {
+    private String getString() {
         return mCustomTabActivityTestRule.getActivity().getResources()
                 .getString(R.string.twa_running_in_chrome);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
index 885c40a..dd7d7d1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
@@ -37,6 +37,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserverTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserverTest.java
index d325c32..9dfee75b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserverTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetObserverTest.java
@@ -23,6 +23,7 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.concurrent.TimeoutException;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetTest.java
index 1cd5aa8..deff232 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetTest.java
@@ -23,10 +23,11 @@
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.chrome.browser.ui.TabObscuringHandler;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent.HeightMode;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent.HeightMode;
 import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.concurrent.ExecutionException;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/TestBottomSheetContent.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/TestBottomSheetContent.java
index 6357b12..98ea7146 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/TestBottomSheetContent.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/TestBottomSheetContent.java
@@ -13,6 +13,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 /** A simple sheet content to test with. This only displays two empty white views. */
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 2f2fa2ad..fc631d7 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
@@ -27,8 +27,8 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileJni;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.content_public.browser.WebContents;
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java
index 45e860d6..5779e9c 100644
--- a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java
+++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/DexOptimizer.java
@@ -169,7 +169,7 @@
      *   client's file space.
      */
     private static class VMRuntime {
-        @SuppressLint("NewApi")
+        @SuppressLint({"DiscouragedPrivateApi", "NewApi"})
         @SuppressWarnings("unchecked")
         public static String getCurrentInstructionSet() throws NoSuchMethodException {
             Method getCurrentInstructionSetMethod;
diff --git a/chrome/android/webapk/libs/runtime_library/javatests/apk_with_webapk_service/BUILD.gn b/chrome/android/webapk/libs/runtime_library/javatests/apk_with_webapk_service/BUILD.gn
index 85f81dd..89d860a1 100644
--- a/chrome/android/webapk/libs/runtime_library/javatests/apk_with_webapk_service/BUILD.gn
+++ b/chrome/android/webapk/libs/runtime_library/javatests/apk_with_webapk_service/BUILD.gn
@@ -6,6 +6,8 @@
 
 # Implements service which uses {@link WebApkServiceImpl} for testing.
 android_apk("apk_with_webapk_service") {
+  testonly = true
+
   # Used as an additional_apk in test scripts.
   never_incremental = true
 
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index 6aba5c3..222e0f67 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -244,7 +244,11 @@
   }
 
   android_apk(target_name) {
-    forward_variables_from(invoker, [ "apk_name" ])
+    forward_variables_from(invoker,
+                           [
+                             "apk_name",
+                             "testonly",
+                           ])
     deps = [ ":$_java_with_services_target_name" ]
 
     android_manifest = _manifest_output
@@ -296,6 +300,7 @@
 }
 
 webapk_tmpl("uiautomator_maps_go_webapk") {
+  testonly = true
   config_file = "manifest/maps_go_manifest_config.json"
   manifest_to_upload_dep = ":generate_old_style_manifest_for_upload"
   apk_name = "JavatestsMapsWebApk"
@@ -319,6 +324,7 @@
 
 # Used by javatests
 webapk_tmpl("javatests_webapk") {
+  testonly = true
   config_file = "manifest/bound_manifest_config.json"
   manifest_to_upload_dep = ":generate_old_style_manifest_for_upload"
   delta_config_file = "manifest/javatest_manifest_config_delta.json"
diff --git a/chrome/android/webapk/shell_apk/current_version/current_version.gni b/chrome/android/webapk/shell_apk/current_version/current_version.gni
index 8e44813e..0cad3b88 100644
--- a/chrome/android/webapk/shell_apk/current_version/current_version.gni
+++ b/chrome/android/webapk/shell_apk/current_version/current_version.gni
@@ -12,4 +12,4 @@
 # //chrome/android/webapk/shell_apk:webapk is changed. This includes
 # Java files, Android resource files and AndroidManifest.xml. Does not affect
 # Chrome.apk
-current_shell_apk_version = 127
+current_shell_apk_version = 128
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java
index 851ea8ea..3567322 100644
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java
@@ -4,6 +4,7 @@
 
 package org.chromium.webapk.shell_apk;
 
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.ComponentName;
@@ -333,6 +334,7 @@
     }
 
     /** Returns the ComponentName for the top activity in {@link taskId}'s task stack. */
+    @SuppressLint("NewApi") // See crbug.com/1081331 for context.
     @TargetApi(Build.VERSION_CODES.M)
     public static ComponentName fetchTopActivityComponent(Context context, int taskId) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
diff --git a/chrome/app/PRESUBMIT.py b/chrome/app/PRESUBMIT.py
index 0fbcc04..541da20 100644
--- a/chrome/app/PRESUBMIT.py
+++ b/chrome/app/PRESUBMIT.py
@@ -9,6 +9,7 @@
 """
 
 import os
+from xml.dom import minidom
 
 def _CheckNoProductNameInGeneratedResources(input_api, output_api):
   """Check that no PRODUCT_NAME placeholders are found in resources files.
@@ -59,11 +60,98 @@
         items=problems)]
   return []
 
+def _CheckCrOsStringsEduLoginInfoTextVersion(input_api, output_api):
+  """Check text version for IDS_EDU_LOGIN_INFO_* strings.
+
+  If any of IDS_EDU_LOGIN_INFO_* strings changed, text version in
+  chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.cc
+  has to be updated.
+  """
+
+  CHROMEOS_STRINGS_PATH = input_api.os_path.join(
+      input_api.change.RepositoryRoot(), "chrome/app/chromeos_strings.grdp")
+  TEXT_VERSION_PATH = input_api.os_path.join(input_api.change.RepositoryRoot(),
+  "chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.cc")
+  UPDATE_TEXT_VERSION_MESSAGE = (
+      "You have changed EDU login parental consent text "
+      "(IDS_EDU_LOGIN_INFO_* strings). Update kConsentScreenTextVersion in "
+      "chrome/browser/chromeos/child_accounts/"
+      "secondary_account_consent_logger.cc to the value of \"%s\"."
+  )
+  UPDATE_INVALIDATION_VERSION_MESSAGE = (
+      "You have changed EDU login parental consent text "
+      "(IDS_EDU_LOGIN_INFO_* strings). If you want to invalidate secondary "
+      "accounts added with previous consent versions, also update "
+      "kSecondaryAccountsInvalidationVersion in "
+      "chrome/browser/chromeos/child_accounts/"
+      "secondary_account_consent_logger.cc to the value of \"%s\"."
+  )
+
+  def _GetInfoStrings(file_contents):
+    """Retrieves IDS_EDU_LOGIN_INFO_* messages from the file contents
+
+    Args:
+      file_contents: string
+
+    Returns:
+      A list of tuples, where each element represents a message.
+      element[0] is the 'name' attribute of the message
+      element[1] is the message contents
+    """
+    return [(message.getAttribute('name'), message.firstChild.nodeValue)
+            for message in (file_contents.getElementsByTagName('grit-part')[0]
+                            .getElementsByTagName('message'))
+            if message.getAttribute('name').startswith('IDS_EDU_LOGIN_INFO_')]
+
+  strings_file = next((af for af in input_api.change.AffectedFiles()
+                      if af.AbsoluteLocalPath() == CHROMEOS_STRINGS_PATH), None)
+  if strings_file is None:
+    return []
+
+  old_info_strings = _GetInfoStrings(
+      minidom.parseString('\n'.join(strings_file.OldContents())))
+  new_info_strings = _GetInfoStrings(
+      minidom.parseString('\n'.join(strings_file.NewContents())))
+  if set(old_info_strings) == set(new_info_strings):
+    return []
+
+  if input_api.change.issue == 0:
+    # First upload, notify about string changes.
+    return [
+        output_api.PresubmitNotifyResult(
+            UPDATE_TEXT_VERSION_MESSAGE % "v<GERRIT_CL_NUMBER>"),
+        output_api.PresubmitNotifyResult(
+            UPDATE_INVALIDATION_VERSION_MESSAGE % "iv<GERRIT_CL_NUMBER>"),
+    ]
+
+  new_text_version = "v" + str(input_api.change.issue)
+  new_invalidation_version = "iv" + str(input_api.change.issue)
+
+  text_version_file = next((af for af in input_api.change.AffectedFiles()
+                            if af.AbsoluteLocalPath() == TEXT_VERSION_PATH),
+                            None)
+  result = []
+  # Check if text version was updated.
+  if text_version_file is None or new_text_version not in '\n'.join(
+      text_version_file.NewContents()):
+    result.append(
+        output_api.PresubmitError(
+            UPDATE_TEXT_VERSION_MESSAGE % new_text_version))
+  # Check if invalidation version was updated.
+  if text_version_file is None or new_invalidation_version not in '\n'.join(
+      text_version_file.NewContents()):
+    result.append(
+        output_api.PresubmitNotifyResult(
+            UPDATE_INVALIDATION_VERSION_MESSAGE % new_invalidation_version))
+  return result
+
 def _CommonChecks(input_api, output_api):
   """Checks common to both upload and commit."""
   results = []
   results.extend(_CheckNoProductNameInGeneratedResources(input_api, output_api))
   results.extend(_CheckFlagsMessageNotTranslated(input_api, output_api))
+  results.extend(
+    _CheckCrOsStringsEduLoginInfoTextVersion(input_api, output_api))
   return results
 
 def CheckChangeOnUpload(input_api, output_api):
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index c08ce670..ab40675 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -5124,7 +5124,7 @@
   <!--
     ==================================================================================
     If you change any IDS_EDU_LOGIN_INFO_* string, update kConsentScreenTextVersion in
-    chrome/browser/supervised_user/child_accounts/secondary_account_consent_logger.cc
+    chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.cc
     to the value of "v<GERRIT_CL_NUMBER>"
   -->
   <message name="IDS_EDU_LOGIN_INFO_TITLE" desc="Title for the parent information screen in EDU account addition flow.">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 727a0eb..3632a07 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4359,6 +4359,10 @@
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) ||
         // defined(OS_CHROMEOS)
 
+    {"sharing-prefer-vapid", flag_descriptions::kSharingPreferVapidName,
+     flag_descriptions::kSharingPreferVapidDescription, kOsAll,
+     FEATURE_VALUE_TYPE(kSharingPreferVapid)},
+
     {"sharing-qr-code-generator",
      flag_descriptions::kSharingQRCodeGeneratorName,
      flag_descriptions::kSharingQRCodeGeneratorDescription, kOsDesktop,
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc
index 8333671..b859217 100644
--- a/chrome/browser/browser_about_handler.cc
+++ b/chrome/browser/browser_about_handler.cc
@@ -49,9 +49,11 @@
   std::string host(url->host());
   std::string path;
 
+#if !defined(OS_CHROMEOS)
   // Handle chrome://settings.
   if (host == chrome::kChromeUISettingsHost)
     return true;  // Prevent further rewriting - this is a valid URL.
+#endif            // !defined(OS_CHROMEOS)
 
   // Do not handle chrome://help.
   if (host == chrome::kChromeUIHelpHost)
@@ -64,16 +66,6 @@
   if (host == chrome::kChromeUISyncHost) {
     // Replace sync with sync-internals (for legacy reasons).
     host = chrome::kChromeUISyncInternalsHost;
-// Redirect chrome://extensions and chrome://settings/extensions all to
-// chrome://extensions and forward path.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  } else if (host == chrome::kChromeUIExtensionsHost ||
-             (host == chrome::kChromeUISettingsHost &&
-              url->path() ==
-                  std::string("/") + chrome::kDeprecatedExtensionsSubPage)) {
-    host = chrome::kChromeUIExtensionsHost;
-    path = url->path();
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   } else if (host == chrome::kChromeUIHistoryHost) {
     // Redirect chrome://history.
     path = url->path();
diff --git a/chrome/browser/chromeos/account_manager/account_manager_policy_controller.cc b/chrome/browser/chromeos/account_manager/account_manager_policy_controller.cc
index 2544fe9..b9f43b009 100644
--- a/chrome/browser/chromeos/account_manager/account_manager_policy_controller.cc
+++ b/chrome/browser/chromeos/account_manager/account_manager_policy_controller.cc
@@ -9,6 +9,7 @@
 #include "base/callback.h"
 #include "base/sequence_checker.h"
 #include "chrome/browser/chromeos/account_manager/account_manager_util.h"
+#include "chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/components/account_manager/account_manager.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -51,6 +52,12 @@
                  base::Unretained(this)));
   // Take any necessary initial action based on the current value.
   OnChildAccountTypeChanged(user_data->value());
+
+  if (profile_->IsChild()) {
+    // Invalidate secondary accounts if parental consent text version for EDU
+    // accounts addition has changed.
+    CheckEduCoexistenceSecondaryAccountsInvalidationVersion();
+  }
 }
 
 void AccountManagerPolicyController::RemoveSecondaryAccounts(
@@ -112,6 +119,52 @@
                      weak_factory_.GetWeakPtr()));
 }
 
+void AccountManagerPolicyController::
+    CheckEduCoexistenceSecondaryAccountsInvalidationVersion() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(profile_->IsChild());
+
+  const std::string stored_version = profile_->GetPrefs()->GetString(
+      chromeos::prefs::kEduCoexistenceSecondaryAccountsInvalidationVersion);
+  const std::string current_version =
+      SecondaryAccountConsentLogger::GetSecondaryAccountsInvalidationVersion();
+
+  if (stored_version == current_version)
+    return;
+
+  account_manager_->GetAccounts(
+      base::BindOnce(&AccountManagerPolicyController::
+                         InvalidateSecondaryAccountsOnEduConsentChange,
+                     weak_factory_.GetWeakPtr(), current_version));
+}
+
+void AccountManagerPolicyController::
+    InvalidateSecondaryAccountsOnEduConsentChange(
+        const std::string& new_invalidation_version,
+        const std::vector<AccountManager::Account>& accounts) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  for (const auto& account : accounts) {
+    if (account.key.account_type !=
+        account_manager::AccountType::ACCOUNT_TYPE_GAIA) {
+      continue;
+    }
+
+    if (device_account_id_.GetAccountType() == AccountType::GOOGLE &&
+        account.key.id == device_account_id_.GetGaiaId()) {
+      // Do not invalidate the Device Account.
+      continue;
+    }
+
+    // This account is a Secondary Gaia account. Invalidate it.
+    account_manager_->UpdateToken(account.key, AccountManager::kInvalidToken);
+  }
+
+  profile_->GetPrefs()->SetString(
+      chromeos::prefs::kEduCoexistenceSecondaryAccountsInvalidationVersion,
+      new_invalidation_version);
+}
+
 void AccountManagerPolicyController::Shutdown() {
   child_account_type_changed_subscription_.reset();
 }
diff --git a/chrome/browser/chromeos/account_manager/account_manager_policy_controller.h b/chrome/browser/chromeos/account_manager/account_manager_policy_controller.h
index 64fd277..4a61ce89 100644
--- a/chrome/browser/chromeos/account_manager/account_manager_policy_controller.h
+++ b/chrome/browser/chromeos/account_manager/account_manager_policy_controller.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_POLICY_CONTROLLER_H_
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "base/macros.h"
@@ -47,6 +48,16 @@
   // |type_changed| is be set to true.
   void OnChildAccountTypeChanged(bool type_changed);
 
+  // Checks if invalidation version for parental consent in EDU accounts
+  // addition has changed. If so, calls
+  // |InvalidateSecondaryAccountsOnEduConsentChange|.
+  void CheckEduCoexistenceSecondaryAccountsInvalidationVersion();
+
+  // Invalidates all secondary accounts and updates consent text version.
+  void InvalidateSecondaryAccountsOnEduConsentChange(
+      const std::string& new_invalidation_version,
+      const std::vector<AccountManager::Account>& accounts);
+
   // KeyedService implementation.
   void Shutdown() override;
 
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc
index 253f3db..4b797e83 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.cc
@@ -34,7 +34,7 @@
 namespace {
 
 template <typename Container, typename Value>
-void EraseKey(Container& container, const Value& value) {
+void EraseByKey(Container& container, const Value& value) {
   auto iter = container.find(value);
   if (iter == container.end()) {
     return;
@@ -319,7 +319,10 @@
 
   RecordEvent(cert_scope_, CertProvisioningEvent::kWorkerRetryManual);
 
+  EraseByKey(failed_cert_profiles_, cert_profile_id);
+
   if (!CheckInternetConnection()) {
+    WaitForInternetConnection();
     return;
   }
 
@@ -334,7 +337,9 @@
 void CertProvisioningScheduler::UpdateCerts() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  is_waiting_for_online_ = false;
   if (!CheckInternetConnection()) {
+    WaitForInternetConnection();
     return;
   }
 
@@ -527,9 +532,17 @@
 bool CertProvisioningScheduler::CheckInternetConnection() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   const NetworkState* network = network_state_handler_->DefaultNetwork();
-  bool is_online = network && network->IsOnline();
-  is_waiting_for_online_ = !is_online;
-  return is_online;
+  return network && network->IsOnline();
+}
+
+void CertProvisioningScheduler::WaitForInternetConnection() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  VLOG(0) << "Waiting for internet connection";
+  is_waiting_for_online_ = true;
+  for (auto& kv : workers_) {
+    auto& worker_ptr = kv.second;
+    worker_ptr->Pause();
+  }
 }
 
 void CertProvisioningScheduler::OnNetworkChange(
@@ -537,6 +550,13 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (is_waiting_for_online_ && network && network->IsOnline()) {
     UpdateCerts();
+    return;
+  }
+
+  if (!is_waiting_for_online_ && !workers_.empty() &&
+      !CheckInternetConnection()) {
+    WaitForInternetConnection();
+    return;
   }
 }
 
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h
index d0cdcbfc..c455ed5 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_scheduler.h
@@ -120,6 +120,7 @@
   CertProvisioningWorker* FindWorker(CertProfileId profile_id);
 
   bool CheckInternetConnection();
+  void WaitForInternetConnection();
   void OnNetworkChange(const NetworkState* network);
   // NetworkStateHandlerObserver
   void DefaultNetworkChanged(const NetworkState* network) override;
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
index cdd578c..f7a2a56e 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
@@ -194,6 +194,50 @@
 
 CertProvisioningWorkerImpl::~CertProvisioningWorkerImpl() = default;
 
+bool CertProvisioningWorkerImpl::IsWaiting() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return is_waiting_;
+}
+
+const CertProfile& CertProvisioningWorkerImpl::GetCertProfile() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return cert_profile_;
+}
+
+const std::string& CertProvisioningWorkerImpl::GetPublicKey() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return public_key_;
+}
+
+CertProvisioningWorkerState CertProvisioningWorkerImpl::GetState() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return state_;
+}
+
+CertProvisioningWorkerState CertProvisioningWorkerImpl::GetPreviousState()
+    const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return prev_state_;
+}
+
+base::Time CertProvisioningWorkerImpl::GetLastUpdateTime() const {
+  return last_update_time_;
+}
+
+void CertProvisioningWorkerImpl::Stop(CertProvisioningWorkerState state) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsFinalState(state));
+
+  CancelScheduledTasks();
+  UpdateState(state);
+}
+
+void CertProvisioningWorkerImpl::Pause() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CancelScheduledTasks();
+  is_waiting_ = true;
+}
+
 void CertProvisioningWorkerImpl::DoStep() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -236,14 +280,6 @@
   NOTREACHED() << " " << static_cast<uint>(state_);
 }
 
-void CertProvisioningWorkerImpl::Stop(CertProvisioningWorkerState state) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(IsFinalState(state));
-
-  CancelScheduledTasks();
-  UpdateState(state);
-}
-
 void CertProvisioningWorkerImpl::UpdateState(
     CertProvisioningWorkerState new_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -551,36 +587,6 @@
   UpdateState(CertProvisioningWorkerState::kSucceeded);
 }
 
-bool CertProvisioningWorkerImpl::IsWaiting() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return is_waiting_;
-}
-
-const CertProfile& CertProvisioningWorkerImpl::GetCertProfile() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return cert_profile_;
-}
-
-const std::string& CertProvisioningWorkerImpl::GetPublicKey() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return public_key_;
-}
-
-CertProvisioningWorkerState CertProvisioningWorkerImpl::GetPreviousState()
-    const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return prev_state_;
-}
-
-base::Time CertProvisioningWorkerImpl::GetLastUpdateTime() const {
-  return last_update_time_;
-}
-
-CertProvisioningWorkerState CertProvisioningWorkerImpl::GetState() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return state_;
-}
-
 bool CertProvisioningWorkerImpl::ProcessResponseErrors(
     policy::DeviceManagementStatus status,
     base::Optional<CertProvisioningResponseErrorType> error,
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h
index c6625d44..77383cb 100644
--- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h
+++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h
@@ -80,6 +80,8 @@
   // clean ups (deletes serialized state, keys, and so on) and returns |state|
   // via callback.
   virtual void Stop(CertProvisioningWorkerState state) = 0;
+  // Make worker pause all activity and wait for DoStep.
+  virtual void Pause() = 0;
   // Returns true, if the worker is waiting for some future event. |DoStep| can
   // be called to try continue right now.
   virtual bool IsWaiting() const = 0;
@@ -111,6 +113,7 @@
   // CertProvisioningWorker
   void DoStep() override;
   void Stop(CertProvisioningWorkerState state) override;
+  void Pause() override;
   bool IsWaiting() const override;
   const CertProfile& GetCertProfile() const override;
   const std::string& GetPublicKey() const override;
diff --git a/chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_worker.h b/chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_worker.h
index 16d7ad5..6f1c4da 100644
--- a/chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_worker.h
+++ b/chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_worker.h
@@ -65,6 +65,7 @@
 
   MOCK_METHOD(void, DoStep, (), (override));
   MOCK_METHOD(void, Stop, (CertProvisioningWorkerState), (override));
+  MOCK_METHOD(void, Pause, (), (override));
   MOCK_METHOD(bool, IsWaiting, (), (const override));
   MOCK_METHOD(const CertProfile&, GetCertProfile, (), (const override));
   MOCK_METHOD(const std::string&, GetPublicKey, (), (const override));
diff --git a/chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.cc b/chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.cc
index ef87461..38d024a1 100644
--- a/chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.cc
+++ b/chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.cc
@@ -36,9 +36,20 @@
 
 constexpr char kConsentApiPath[] =
     "people/me/consentsForSecondaryAccounts:create";
-// Format of the text version is "v<GERRIT_CL_NUMBER>" with number of the last
-// CL where strings with information for parents were changed.
+
+// Version of the parental consent text. Must be updated when consent text is
+// changed. Format of the text version is "v<GERRIT_CL_NUMBER>" with number of
+// the last CL where strings with information for parents were changed.
 constexpr char kConsentScreenTextVersion[] = "v2153049";
+// The text version which requires invalidation of the secondary accounts added
+// before consent text changes. Format of the invalidation version is
+// "iv<GERRIT_CL_NUMBER>".
+// =============================================================================
+// WARNING: change of the current version will result in invalidation of
+// secondary accounts added with previous versions.
+// =============================================================================
+constexpr char kSecondaryAccountsInvalidationVersion[] = "iv2153049";
+
 constexpr int kNumConsentLogRetries = 1;
 constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
     net::DefineNetworkTrafficAnnotation("secondary_account_consent_logger",
@@ -78,6 +89,15 @@
     PrefRegistrySimple* registry) {
   registry->RegisterStringPref(chromeos::prefs::kEduCoexistenceId,
                                std::string() /* default_value */);
+  registry->RegisterStringPref(
+      chromeos::prefs::kEduCoexistenceSecondaryAccountsInvalidationVersion,
+      "iv2153049" /* default_value, the first invalidation version */);
+}
+
+// static
+std::string
+SecondaryAccountConsentLogger::GetSecondaryAccountsInvalidationVersion() {
+  return kSecondaryAccountsInvalidationVersion;
 }
 
 SecondaryAccountConsentLogger::SecondaryAccountConsentLogger(
diff --git a/chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.h b/chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.h
index 4952897..715eba66 100644
--- a/chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.h
+++ b/chrome/browser/chromeos/child_accounts/secondary_account_consent_logger.h
@@ -65,6 +65,10 @@
 
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
+  // Returns the text version which reqires invalidation of the secondary
+  // accounts added before the consent text changes.
+  static std::string GetSecondaryAccountsInvalidationVersion();
+
   // Logs the consent.
   void StartLogging();
 
diff --git a/chrome/browser/chromeos/login/screens/update_required_screen.cc b/chrome/browser/chromeos/login/screens/update_required_screen.cc
index 17319ee5..de4307f 100644
--- a/chrome/browser/chromeos/login/screens/update_required_screen.cc
+++ b/chrome/browser/chromeos/login/screens/update_required_screen.cc
@@ -90,6 +90,7 @@
   }
   // Check network state to set initial screen UI.
   RefreshNetworkState();
+
   version_updater_->GetEolInfo(base::BindOnce(
       &UpdateRequiredScreen::OnGetEolInfo, weak_factory_.GetWeakPtr()));
 }
@@ -147,7 +148,10 @@
 }
 
 void UpdateRequiredScreen::RefreshNetworkState() {
-  if (!view_)
+  // Do not refresh the UI if the update process has started. This can be
+  // encountered if error screen is shown and later hidden due to captive portal
+  // after starting the update process.
+  if (!view_ || is_updating_now_)
     return;
 
   const NetworkState* network =
@@ -159,19 +163,28 @@
   // till the update process is triggered. Post that, update engine status
   // drives the UI state.
   if (!network || !network->IsConnectedState()) {
+    // No network is available for the update process to start.
     view_->SetUIState(UpdateRequiredView::UPDATE_NO_NETWORK);
-    waiting_for_connection_ = true;
+    waiting_for_connection_ = false;
   } else if (network->IsUsingMobileData()) {
+    // The device is either connected to a metered network at the start or has
+    // switched to one.
     view_->SetUIState(UpdateRequiredView::UPDATE_NEED_PERMISSION);
     waiting_for_connection_ = true;
   } else if (waiting_for_connection_) {
-    // Network is good for the update process to start. Start the update process
-    // and unsubscribe from the network change notifications as any change in
-    // network state is reflected in the update engine result.
+    // The device has switched from a metered network to a good network. Start
+    // the update process automatically and unsubscribe from the network change
+    // notifications as any change in network state is reflected in the update
+    // engine result.
     waiting_for_connection_ = false;
+    is_updating_now_ = true;
     view_->SetUIState(UpdateRequiredView::UPDATE_PROCESS);
     StopObservingNetworkState();
     version_updater_->StartNetworkCheck();
+  } else {
+    // The device is either connected to a good network at the start or has
+    // switched from no network to good network.
+    view_->SetUIState(UpdateRequiredView::UPDATE_REQUIRED_MESSAGE);
   }
 }
 
diff --git a/chrome/browser/chromeos/login/screens/update_required_screen_browsertest.cc b/chrome/browser/chromeos/login/screens/update_required_screen_browsertest.cc
index a881dc880..16bbceee 100644
--- a/chrome/browser/chromeos/login/screens/update_required_screen_browsertest.cc
+++ b/chrome/browser/chromeos/login/screens/update_required_screen_browsertest.cc
@@ -278,8 +278,8 @@
 }
 
 // This tests the state of update required screen when the device is initially
-// not connected to any network and the user connects to Wifi to start the
-// update.
+// not connected to any network and the user connects to Wifi to show update
+// required screen.
 IN_PROC_BROWSER_TEST_F(UpdateRequiredScreenTest, TestUpdateRequiredNoNetwork) {
   // Disconnect from all networks and show update required screen.
   network_state_test_helper_->service_test()->ClearServices();
@@ -292,22 +292,17 @@
       {kUpdateRequiredScreen, kUpdateRequiredDialog});
   test::OobeJS().ExpectVisiblePath({kUpdateRequiredScreen, KNoNetworkDialog});
 
-  // Connect to a WiFi network and update starts automatically.
+  // Connect to a WiFi network.
   network_state_test_helper_->service_test()->AddService(
       kWifiServicePath, kWifiServicePath, kWifiServicePath /* name */,
       shill::kTypeWifi, shill::kStateOnline, true);
 
+  // Update required screen is shown when user moves from no network to a good
+  // network.
   test::OobeJS()
-      .CreateVisibilityWaiter(true, {kUpdateRequiredScreen, kUpdateProcess})
+      .CreateVisibilityWaiter(true,
+                              {kUpdateRequiredScreen, kUpdateRequiredDialog})
       ->Wait();
-
-  // Expect screen to show progress of the update process.
-  test::OobeJS().ExpectHiddenPath({kUpdateRequiredScreen, KNoNetworkDialog});
-  test::OobeJS().ExpectVisiblePath({kUpdateRequiredScreen, kUpdateProcess});
-
-  SetUpdateEngineStatus(update_engine::Operation::UPDATED_NEED_REBOOT);
-  // UpdateStatusChanged(status) calls RebootAfterUpdate().
-  EXPECT_EQ(1, update_engine_client()->reboot_after_update_call_count());
 }
 
 // This tests the condition when the user switches to a metered network during
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
index debc98d..8eb88bd 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
@@ -946,13 +946,63 @@
         }
 
         case cros_healthd::CpuResult::Tag::CPU_INFO: {
-          for (const auto& cpu : cpu_result->get_cpu_info()) {
+          const auto& cpu_info = cpu_result->get_cpu_info();
+
+          if (cpu_info.is_null()) {
+            LOG(ERROR) << "Null CpuInfo from cros_healthd";
+            break;
+          }
+
+          long clock_ticks_per_second = sysconf(_SC_CLK_TCK);
+          if (clock_ticks_per_second == -1 || clock_ticks_per_second == 0) {
+            LOG(ERROR) << "Failed getting number of clock ticks per second";
+            break;
+          }
+
+          em::GlobalCpuInfo* const global_cpu_info_out =
+              response_params_.device_status->mutable_global_cpu_info();
+          global_cpu_info_out->set_num_total_threads(
+              cpu_info->num_total_threads);
+
+          for (const auto& physical_cpu : cpu_info->physical_cpus) {
+            if (physical_cpu.is_null())
+              continue;
+
             em::CpuInfo* const cpu_info_out =
                 response_params_.device_status->add_cpu_info();
-            cpu_info_out->set_model_name(cpu->model_name);
+            cpu_info_out->set_model_name(physical_cpu->model_name);
             cpu_info_out->set_architecture(
-                em::CpuInfo::Architecture(cpu->architecture));
-            cpu_info_out->set_max_clock_speed_khz(cpu->max_clock_speed_khz);
+                static_cast<em::CpuInfo::Architecture>(cpu_info->architecture));
+
+            for (const auto& logical_cpu : physical_cpu->logical_cpus) {
+              if (logical_cpu.is_null())
+                continue;
+
+              em::LogicalCpuInfo* const logical_cpu_info_out =
+                  cpu_info_out->add_logical_cpus();
+              logical_cpu_info_out->set_scaling_max_frequency_khz(
+                  logical_cpu->scaling_max_frequency_khz);
+              logical_cpu_info_out->set_scaling_current_frequency_khz(
+                  logical_cpu->scaling_current_frequency_khz);
+              logical_cpu_info_out->set_idle_time_seconds(
+                  logical_cpu->idle_time_user_hz / clock_ticks_per_second);
+
+              if (!cpu_info_out->has_max_clock_speed_khz()) {
+                cpu_info_out->set_max_clock_speed_khz(
+                    logical_cpu->max_clock_speed_khz);
+              }
+
+              for (const auto& c_state : logical_cpu->c_states) {
+                if (c_state.is_null())
+                  continue;
+
+                em::CpuCStateInfo* const c_state_info_out =
+                    logical_cpu_info_out->add_c_states();
+                c_state_info_out->set_name(c_state->name);
+                c_state_info_out->set_time_in_state_since_last_boot_us(
+                    c_state->time_in_state_since_last_boot_us);
+              }
+            }
           }
           break;
         }
diff --git a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
index 270434d..24513a4 100644
--- a/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -142,12 +142,20 @@
 // Cached VPD test values:
 constexpr char kFakeSkuNumber[] = "fake_sku_number";
 // CPU test values:
+constexpr uint32_t kFakeNumTotalThreads = 8;
 constexpr char kFakeModelName[] = "fake_cpu_model_name";
 constexpr cros_healthd::CpuArchitectureEnum kFakeMojoArchitecture =
     cros_healthd::CpuArchitectureEnum::kX86_64;
 constexpr em::CpuInfo::Architecture kFakeProtoArchitecture =
     em::CpuInfo::X86_64;
 constexpr uint32_t kFakeMaxClockSpeed = 3400000;
+constexpr uint32_t kFakeScalingMaxFrequency = 2700000;
+constexpr uint32_t kFakeScalingCurFrequency = 2400000;
+// Since this number is divided by the result of the sysconf(_SC_CLK_TCK)
+// syscall, we need it to be 0 to avoid flaky tests,
+constexpr uint32_t kFakeIdleTime = 0;
+constexpr char kFakeCStateName[] = "fake_c_state_name";
+constexpr uint64_t kFakeTimeInStateSinceLastBoot = 87;
 // CPU Temperature test values:
 constexpr char kFakeCpuLabel[] = "fake_cpu_label";
 constexpr int kFakeCpuTemp = 91832;
@@ -471,11 +479,31 @@
       cros_healthd::CachedVpdInfo::New(kFakeSkuNumber));
 }
 
+std::vector<cros_healthd::CpuCStateInfoPtr> CreateCStateInfo() {
+  std::vector<cros_healthd::CpuCStateInfoPtr> c_states;
+  c_states.push_back(cros_healthd::CpuCStateInfo::New(
+      kFakeCStateName, kFakeTimeInStateSinceLastBoot));
+  return c_states;
+}
+
+std::vector<cros_healthd::LogicalCpuInfoPtr> CreateLogicalCpu() {
+  std::vector<cros_healthd::LogicalCpuInfoPtr> logical_cpus;
+  logical_cpus.push_back(cros_healthd::LogicalCpuInfo::New(
+      kFakeMaxClockSpeed, kFakeScalingMaxFrequency, kFakeScalingCurFrequency,
+      kFakeIdleTime, CreateCStateInfo()));
+  return logical_cpus;
+}
+
+std::vector<cros_healthd::PhysicalCpuInfoPtr> CreatePhysicalCpu() {
+  std::vector<cros_healthd::PhysicalCpuInfoPtr> physical_cpus;
+  physical_cpus.push_back(
+      cros_healthd::PhysicalCpuInfo::New(kFakeModelName, CreateLogicalCpu()));
+  return physical_cpus;
+}
+
 cros_healthd::CpuResultPtr CreateCpuResult() {
-  std::vector<cros_healthd::CpuInfoPtr> cpu_vector;
-  cpu_vector.push_back(cros_healthd::CpuInfo::New(
-      kFakeModelName, kFakeMojoArchitecture, kFakeMaxClockSpeed));
-  return cros_healthd::CpuResult::NewCpuInfo(std::move(cpu_vector));
+  return cros_healthd::CpuResult::NewCpuInfo(cros_healthd::CpuInfo::New(
+      kFakeNumTotalThreads, kFakeMojoArchitecture, CreatePhysicalCpu()));
 }
 
 cros_healthd::TimezoneResultPtr CreateTimezoneResult() {
@@ -3034,11 +3062,29 @@
   EXPECT_EQ(device_status_.system_status().vpd_sku_number(), kFakeSkuNumber);
 
   // Verify the CPU data.
+  ASSERT_TRUE(device_status_.has_global_cpu_info());
+  EXPECT_EQ(device_status_.global_cpu_info().num_total_threads(),
+            kFakeNumTotalThreads);
+
+  // Verify the physical CPU.
   ASSERT_EQ(device_status_.cpu_info_size(), 1);
   const auto& cpu = device_status_.cpu_info(0);
   EXPECT_EQ(cpu.model_name(), kFakeModelName);
   EXPECT_EQ(cpu.architecture(), kFakeProtoArchitecture);
   EXPECT_EQ(cpu.max_clock_speed_khz(), kFakeMaxClockSpeed);
+  // Verify the logical CPU.
+  ASSERT_EQ(cpu.logical_cpus_size(), 1);
+  const auto& logical_cpu = cpu.logical_cpus(0);
+  EXPECT_EQ(logical_cpu.scaling_max_frequency_khz(), kFakeScalingMaxFrequency);
+  EXPECT_EQ(logical_cpu.scaling_current_frequency_khz(),
+            kFakeScalingCurFrequency);
+  EXPECT_EQ(logical_cpu.idle_time_seconds(), kFakeIdleTime);
+  // Verify the C-state data.
+  ASSERT_EQ(logical_cpu.c_states_size(), 1);
+  const auto& c_state = logical_cpu.c_states(0);
+  EXPECT_EQ(c_state.name(), kFakeCStateName);
+  EXPECT_EQ(c_state.time_in_state_since_last_boot_us(),
+            kFakeTimeInStateSinceLastBoot);
 
   // Verify the Timezone info.
   ASSERT_TRUE(device_status_.has_timezone_info());
diff --git a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc
index aa81b0b0..59c239b 100644
--- a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc
+++ b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc
@@ -12,6 +12,10 @@
 
 namespace policy {
 
+const char kCameraFeature[] = "camera";
+const char kBrowserSettingsFeature[] = "browser_settings";
+const char kOsSettingsFeature[] = "os_settings";
+
 SystemFeaturesDisableListPolicyHandler::SystemFeaturesDisableListPolicyHandler()
     : policy::ListPolicyHandler(key::kSystemFeaturesDisableList,
                                 base::Value::Type::STRING) {}
@@ -43,11 +47,11 @@
 
 SystemFeature SystemFeaturesDisableListPolicyHandler::ConvertToEnum(
     const std::string& system_feature) {
-  if (system_feature == "camera")
+  if (system_feature == kCameraFeature)
     return SystemFeature::CAMERA;
-  if (system_feature == "os_settings")
+  if (system_feature == kOsSettingsFeature)
     return SystemFeature::OS_SETTINGS;
-  if (system_feature == "browser_settings")
+  if (system_feature == kBrowserSettingsFeature)
     return SystemFeature::BROWSER_SETTINGS;
 
   NOTREACHED() << "Unsupported system feature: " << system_feature;
diff --git a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h
index 0d60cc9..198b98d 100644
--- a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h
+++ b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h
@@ -24,6 +24,10 @@
   LAST_SYSTEM_FEATURE
 };
 
+extern const char kCameraFeature[];
+extern const char kBrowserSettingsFeature[];
+extern const char kOsSettingsFeature[];
+
 class SystemFeaturesDisableListPolicyHandler
     : public policy::ListPolicyHandler {
  public:
diff --git a/chrome/browser/content_settings/content_settings_usages_state_unittest.cc b/chrome/browser/content_settings/content_settings_usages_state_unittest.cc
index cad7f44ec..252f9de4 100644
--- a/chrome/browser/content_settings/content_settings_usages_state_unittest.cc
+++ b/chrome/browser/content_settings/content_settings_usages_state_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/content_settings/core/browser/content_settings_usages_state.h"
+#include "components/content_settings/browser/content_settings_usages_state.h"
 
 #include <string>
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 3dff525..7bd7e95 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2445,7 +2445,7 @@
   {
     "name": "files-unified-media-view",
     "owners": [ "fukino", "weifangsun" ],
-    "expiry_milestone": 84
+    "expiry_milestone": 87
   },
   {
     "name": "files-zip-no-nacl",
@@ -3769,6 +3769,11 @@
     "expiry_milestone": 84
   },
   {
+    "name": "sharing-prefer-vapid",
+    "owners": [ "//chrome/browser/sharing/OWNERS" ],
+    "expiry_milestone": 87
+  },
+  {
     "name": "sharing-qr-code-generator",
     "owners": [ "//components/send_tab_to_self/OWNERS" ],
     "expiry_milestone": 86
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 10379ab..46b5a0c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1769,6 +1769,11 @@
     "Enables the sender devices to connect with chosen device using a peer to "
     "peer connection for transferring data.";
 
+const char kSharingPreferVapidName[] =
+    "Prefer sending Sharing message via VAPID";
+const char kSharingPreferVapidDescription[] =
+    "Prefer sending Sharing message via FCM WebPush authenticated using VAPID.";
+
 const char kSharingQRCodeGeneratorName[] = "Enable sharing page via QR Code";
 const char kSharingQRCodeGeneratorDescription[] =
     "Enables right-click UI to share the page's URL via a generated QR Code.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 243f3f8..3e7a2f7 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1037,6 +1037,9 @@
 extern const char kSharingPeerConnectionSenderName[];
 extern const char kSharingPeerConnectionSenderDescription[];
 
+extern const char kSharingPreferVapidName[];
+extern const char kSharingPreferVapidDescription[];
+
 extern const char kSharingQRCodeGeneratorName[];
 extern const char kSharingQRCodeGeneratorDescription[];
 
diff --git a/chrome/browser/geolocation/geolocation_browsertest.cc b/chrome/browser/geolocation/geolocation_browsertest.cc
index 3cb677b..5204116 100644
--- a/chrome/browser/geolocation/geolocation_browsertest.cc
+++ b/chrome/browser/geolocation/geolocation_browsertest.cc
@@ -26,8 +26,8 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/content_settings/browser/content_settings_usages_state.h"
 #include "components/content_settings/browser/tab_specific_content_settings.h"
-#include "components/content_settings/core/browser/content_settings_usages_state.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/permissions/features.h"
 #include "components/permissions/permission_request_manager.h"
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
index aaf578b..48b2343 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -96,6 +96,17 @@
                            ->GetString(prefs::kHomePage);
 }
 
+std::unique_ptr<base::trace_event::TracedValue> CumulativeShiftScoreTraceData(
+    float layout_shift_score,
+    float layout_shift_score_before_input_or_scroll) {
+  std::unique_ptr<base::trace_event::TracedValue> data =
+      std::make_unique<base::trace_event::TracedValue>();
+  data->SetDouble("layoutShiftScore", layout_shift_score);
+  data->SetDouble("layoutShiftScoreBeforeInputOrScroll",
+                  layout_shift_score_before_input_or_scroll);
+  return data;
+}
+
 }  // namespace
 
 // static
@@ -585,6 +596,14 @@
       LayoutShiftUmaValue(
           GetDelegate().GetPageRenderData().layout_shift_score));
 
+  TRACE_EVENT_INSTANT1("loading", "CumulativeShiftScore::AllFrames::UMA",
+                       TRACE_EVENT_SCOPE_THREAD, "data",
+                       CumulativeShiftScoreTraceData(
+                           GetDelegate().GetPageRenderData().layout_shift_score,
+                           GetDelegate()
+                               .GetPageRenderData()
+                               .layout_shift_score_before_input_or_scroll));
+
   UMA_HISTOGRAM_COUNTS_100(
       "PageLoad.LayoutInstability.CumulativeShiftScore.MainFrame",
       LayoutShiftUmaValue(
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 0da0effa..e0d97bb 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -910,8 +910,7 @@
   popup_controller_ = PasswordGenerationPopupControllerImpl::GetOrCreate(
       popup_controller_, element_bounds_in_screen_space, ui_data,
       driver->AsWeakPtr(), observer_, web_contents(),
-      password_generation_driver_receivers_.GetCurrentTargetFrame(),
-      GetPasswordSyncState());
+      password_generation_driver_receivers_.GetCurrentTargetFrame());
   DCHECK(!password_value.empty());
   popup_controller_->UpdatePassword(password_value);
   popup_controller_->Show(
@@ -1307,7 +1306,7 @@
   popup_controller_ = PasswordGenerationPopupControllerImpl::GetOrCreate(
       popup_controller_, element_bounds_in_screen_space, ui_data,
       driver->AsWeakPtr(), observer_, web_contents(),
-      driver->render_frame_host(), GetPasswordSyncState());
+      driver->render_frame_host());
   popup_controller_->Show(PasswordGenerationPopupController::kOfferGeneration);
 }
 
diff --git a/chrome/browser/performance_manager/browser_child_process_watcher.cc b/chrome/browser/performance_manager/browser_child_process_watcher.cc
index 522fcf0..523fc80 100644
--- a/chrome/browser/performance_manager/browser_child_process_watcher.cc
+++ b/chrome/browser/performance_manager/browser_child_process_watcher.cc
@@ -32,8 +32,8 @@
   DCHECK(!browser_process_node_);
   DCHECK(gpu_process_nodes_.empty());
 
-  browser_process_node_ =
-      PerformanceManagerImpl::CreateProcessNode(RenderProcessHostProxy());
+  browser_process_node_ = PerformanceManagerImpl::CreateProcessNode(
+      content::PROCESS_TYPE_BROWSER, RenderProcessHostProxy());
   OnProcessLaunched(base::Process::Current(), browser_process_node_.get());
   BrowserChildProcessObserver::Add(this);
 }
@@ -57,7 +57,8 @@
     const content::ChildProcessData& data) {
   if (data.process_type == content::PROCESS_TYPE_GPU) {
     std::unique_ptr<ProcessNodeImpl> gpu_node =
-        PerformanceManagerImpl::CreateProcessNode(RenderProcessHostProxy());
+        PerformanceManagerImpl::CreateProcessNode(content::PROCESS_TYPE_GPU,
+                                                  RenderProcessHostProxy());
     OnProcessLaunched(data.GetProcess(), gpu_node.get());
     gpu_process_nodes_[data.id] = std::move(gpu_node);
   }
diff --git a/chrome/browser/permissions/contextual_notification_permission_ui_selector.cc b/chrome/browser/permissions/contextual_notification_permission_ui_selector.cc
index 6a8a939..1eb8074bc 100644
--- a/chrome/browser/permissions/contextual_notification_permission_ui_selector.cc
+++ b/chrome/browser/permissions/contextual_notification_permission_ui_selector.cc
@@ -61,6 +61,7 @@
       return UiToUse::kNormalUi;
     case CrowdDenyPreloadData::SiteReputation::UNSOLICITED_PROMPTS:
       return UiToUse::kQuietUi;
+    case CrowdDenyPreloadData::SiteReputation::ABUSIVE_PROMPTS:
     case CrowdDenyPreloadData::SiteReputation::UNKNOWN:
       return base::nullopt;
   }
diff --git a/chrome/browser/permissions/crowd_deny.proto b/chrome/browser/permissions/crowd_deny.proto
index 0985ad17..89d7b65 100644
--- a/chrome/browser/permissions/crowd_deny.proto
+++ b/chrome/browser/permissions/crowd_deny.proto
@@ -18,10 +18,20 @@
     UNKNOWN = 0;
     ACCEPTABLE = 1;
     UNSOLICITED_PROMPTS = 2;
+    ABUSIVE_PROMPTS = 3;  // Supported as of M84.
   }
 
   // The quality of the experience users have with notifications on this site.
   optional NotificationUserExperienceQuality notification_ux_quality = 2;
+
+  // Whether the reputation propagates to all subdomains of |domain|. Supported
+  // as of M84. Older versions will always perform exact domain matching.
+  optional bool include_subdomains = 3;
+
+  // Whether to merely print a console warning and not enforce. Supported as of
+  // M84. Older versions will always enforce if |notification_ux_quality| is
+  // set to a value recognized by the version, other than `ACCEPTABLE`.
+  optional bool warning_only = 4;
 }
 
 // Information, about popular sites, relevant for making permission decisions.
diff --git a/chrome/browser/permissions/crowd_deny_preload_data.cc b/chrome/browser/permissions/crowd_deny_preload_data.cc
index 9395813..18bbabb 100644
--- a/chrome/browser/permissions/crowd_deny_preload_data.cc
+++ b/chrome/browser/permissions/crowd_deny_preload_data.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/permissions/crowd_deny_preload_data.h"
 
+#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -15,6 +16,7 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/task_runner_util.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 #include "url/url_constants.h"
@@ -66,11 +68,21 @@
   if (origin.scheme() != url::kHttpsScheme)
     return nullptr;
 
-  // For now, do not allow subdomain matches.
-  const auto it = domain_to_reputation_map_.find(origin.host());
-  if (it == domain_to_reputation_map_.end())
-    return nullptr;
-  return &it->second;
+  const auto it_exact_match = domain_to_reputation_map_.find(origin.host());
+  if (it_exact_match != domain_to_reputation_map_.end())
+    return &it_exact_match->second;
+
+  const std::string registerable_domain =
+      net::registry_controlled_domains::GetDomainAndRegistry(
+          origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+  const auto it_domain_suffix_match =
+      domain_to_reputation_map_.find(registerable_domain);
+  if (it_domain_suffix_match != domain_to_reputation_map_.end() &&
+      it_domain_suffix_match->second.include_subdomains()) {
+    return &it_domain_suffix_match->second;
+  }
+
+  return nullptr;
 }
 
 void CrowdDenyPreloadData::LoadFromDisk(const base::FilePath& proto_path) {
diff --git a/chrome/browser/permissions/crowd_deny_preload_data_unittest.cc b/chrome/browser/permissions/crowd_deny_preload_data_unittest.cc
index 11a19018..73dd803 100644
--- a/chrome/browser/permissions/crowd_deny_preload_data_unittest.cc
+++ b/chrome/browser/permissions/crowd_deny_preload_data_unittest.cc
@@ -21,13 +21,19 @@
 constexpr char kTestDomainBeta[] = "beta.com";
 constexpr char kTestDomainGamma[] = "gamma.com";
 constexpr char kTestDomainDelta[] = "delta.com";
+constexpr char kTestSubdomainOfDelta1[] = "one.delta.com";
 constexpr char kTestDomainEpsilon[] = "epsilon.com";
 
 constexpr char kTestOriginAlpha[] = "https://alpha.com";
+constexpr char kTestOriginSubdomainOfAlpha[] = "https://foo.alpha.com";
 constexpr char kTestOriginBeta[] = "https://beta.com";
 constexpr char kTestOriginGamma[] = "https://gamma.com";
 constexpr char kTestOriginDelta[] = "https://delta.com";
+constexpr char kTestOriginSubdomainOfDelta1[] = "https://one.delta.com";
+constexpr char kTestOriginSubdomainOfDelta2[] = "https://foo.two.delta.com";
+constexpr char kTestOriginNotSubdomainOfDelta[] = "https://notdelta.com";
 constexpr char kTestOriginEpsilon[] = "https://epsilon.com";
+constexpr char kTestOriginZeta[] = "https://zeta.com";
 
 constexpr const char* kAllTestingOrigins[] = {
     kTestOriginAlpha, kTestOriginBeta, kTestOriginGamma, kTestOriginDelta,
@@ -90,17 +96,28 @@
 
     auto* beta_site_reputation = test_data.add_site_reputations();
     beta_site_reputation->set_domain(kTestDomainBeta);
+    beta_site_reputation->set_include_subdomains(false);
     beta_site_reputation->set_notification_ux_quality(
         SiteReputation::ACCEPTABLE);
 
     auto* gamma_site_reputation = test_data.add_site_reputations();
     gamma_site_reputation->set_domain(kTestDomainGamma);
+    gamma_site_reputation->set_warning_only(false);
     gamma_site_reputation->set_notification_ux_quality(
         SiteReputation::UNSOLICITED_PROMPTS);
 
     auto* delta_site_reputation = test_data.add_site_reputations();
     delta_site_reputation->set_domain(kTestDomainDelta);
+    delta_site_reputation->set_include_subdomains(true);
+    delta_site_reputation->set_warning_only(true);
+    delta_site_reputation->set_notification_ux_quality(
+        SiteReputation::ABUSIVE_PROMPTS);
+
+    auto* epsilon_site_reputation = test_data.add_site_reputations();
+    epsilon_site_reputation->set_domain(kTestDomainEpsilon);
     // No |notification_ux_quality| field.
+    // No |include_subdomains| field.
+    // No |warning_only| field.
 
     ASSERT_NO_FATAL_FAILURE(SerializeAndLoadTestData(std::move(test_data)));
   }
@@ -146,7 +163,7 @@
   ExpectEmptyPreloadData();
 }
 
-TEST_F(CrowdDenyPreloadDataTest, NotificationUserExperienceQuality) {
+TEST_F(CrowdDenyPreloadDataTest, DataIntegrityAndDefaults) {
   ASSERT_NO_FATAL_FAILURE(SerializeAndLoadCannedTestData());
 
   const auto* data = preload_data()->GetReputationDataForSite(
@@ -154,12 +171,16 @@
   ASSERT_TRUE(data);
   EXPECT_EQ(kTestDomainAlpha, data->domain());
   EXPECT_EQ(SiteReputation::UNKNOWN, data->notification_ux_quality());
+  EXPECT_FALSE(data->include_subdomains());
+  EXPECT_FALSE(data->warning_only());
 
   data = preload_data()->GetReputationDataForSite(
       url::Origin::Create(GURL(kTestOriginBeta)));
   ASSERT_TRUE(data);
   EXPECT_EQ(kTestDomainBeta, data->domain());
   EXPECT_EQ(SiteReputation::ACCEPTABLE, data->notification_ux_quality());
+  EXPECT_FALSE(data->include_subdomains());
+  EXPECT_FALSE(data->warning_only());
 
   data = preload_data()->GetReputationDataForSite(
       url::Origin::Create(GURL(kTestOriginGamma)));
@@ -167,15 +188,27 @@
   EXPECT_EQ(kTestDomainGamma, data->domain());
   EXPECT_EQ(SiteReputation::UNSOLICITED_PROMPTS,
             data->notification_ux_quality());
+  EXPECT_FALSE(data->include_subdomains());
+  EXPECT_FALSE(data->warning_only());
 
   data = preload_data()->GetReputationDataForSite(
       url::Origin::Create(GURL(kTestOriginDelta)));
   ASSERT_TRUE(data);
   EXPECT_EQ(kTestDomainDelta, data->domain());
-  EXPECT_EQ(SiteReputation::UNKNOWN, data->notification_ux_quality());
+  EXPECT_EQ(SiteReputation::ABUSIVE_PROMPTS, data->notification_ux_quality());
+  EXPECT_TRUE(data->include_subdomains());
+  EXPECT_TRUE(data->warning_only());
 
   data = preload_data()->GetReputationDataForSite(
       url::Origin::Create(GURL(kTestOriginEpsilon)));
+  ASSERT_TRUE(data);
+  EXPECT_EQ(kTestDomainEpsilon, data->domain());
+  EXPECT_EQ(SiteReputation::UNKNOWN, data->notification_ux_quality());
+  EXPECT_FALSE(data->include_subdomains());
+  EXPECT_FALSE(data->warning_only());
+
+  data = preload_data()->GetReputationDataForSite(
+      url::Origin::Create(GURL(kTestOriginZeta)));
   EXPECT_FALSE(data);
 }
 
@@ -207,6 +240,72 @@
       url::Origin::Create(GURL("https://alpha.com:1234"))));
 }
 
+TEST_F(CrowdDenyPreloadDataTest, GetReputationWithSubdomainMatching) {
+  ASSERT_NO_FATAL_FAILURE(SerializeAndLoadCannedTestData());
+
+  const auto* data = preload_data()->GetReputationDataForSite(
+      url::Origin::Create(GURL(kTestOriginDelta)));
+  ASSERT_TRUE(data);
+  EXPECT_EQ(kTestDomainDelta, data->domain());
+  ASSERT_TRUE(data->include_subdomains());
+
+  data = preload_data()->GetReputationDataForSite(
+      url::Origin::Create(GURL(kTestOriginSubdomainOfDelta1)));
+  ASSERT_TRUE(data);
+  EXPECT_EQ(kTestDomainDelta, data->domain());
+
+  data = preload_data()->GetReputationDataForSite(
+      url::Origin::Create(GURL(kTestOriginSubdomainOfDelta2)));
+  ASSERT_TRUE(data);
+  EXPECT_EQ(kTestDomainDelta, data->domain());
+
+  data = preload_data()->GetReputationDataForSite(
+      url::Origin::Create(GURL(kTestOriginNotSubdomainOfDelta)));
+  EXPECT_FALSE(data);
+
+  data = preload_data()->GetReputationDataForSite(
+      url::Origin::Create(GURL(kTestOriginAlpha)));
+  ASSERT_TRUE(data);
+  EXPECT_EQ(kTestDomainAlpha, data->domain());
+  ASSERT_FALSE(data->include_subdomains());
+
+  // Should not return `alpha.com` because |include_subdomains| is not set.
+  data = preload_data()->GetReputationDataForSite(
+      url::Origin::Create(GURL(kTestOriginSubdomainOfAlpha)));
+  EXPECT_FALSE(data);
+}
+
+TEST_F(CrowdDenyPreloadDataTest, SubdomainSpecificOverride) {
+  chrome_browser_crowd_deny::PreloadData test_data;
+
+  auto* delta_site_reputation = test_data.add_site_reputations();
+  delta_site_reputation->set_domain(kTestDomainDelta);
+  delta_site_reputation->set_include_subdomains(true);
+  delta_site_reputation->set_notification_ux_quality(
+      SiteReputation::UNSOLICITED_PROMPTS);
+
+  auto* subdomain_site_reputation = test_data.add_site_reputations();
+  subdomain_site_reputation->set_domain(kTestSubdomainOfDelta1);
+  subdomain_site_reputation->set_include_subdomains(true);
+  subdomain_site_reputation->set_notification_ux_quality(
+      SiteReputation::ACCEPTABLE);
+
+  ASSERT_NO_FATAL_FAILURE(SerializeAndLoadTestData(std::move(test_data)));
+
+  const auto* data = preload_data()->GetReputationDataForSite(
+      url::Origin::Create(GURL(kTestOriginSubdomainOfDelta1)));
+  ASSERT_TRUE(data);
+  EXPECT_EQ(kTestSubdomainOfDelta1, data->domain());
+  EXPECT_EQ(SiteReputation::ACCEPTABLE, data->notification_ux_quality());
+
+  data = preload_data()->GetReputationDataForSite(
+      url::Origin::Create(GURL(kTestOriginSubdomainOfDelta2)));
+  ASSERT_TRUE(data);
+  EXPECT_EQ(kTestDomainDelta, data->domain());
+  EXPECT_EQ(SiteReputation::UNSOLICITED_PROMPTS,
+            data->notification_ux_quality());
+}
+
 TEST_F(CrowdDenyPreloadDataTest, Update) {
   ASSERT_NO_FATAL_FAILURE(SerializeAndLoadCannedTestData());
 
diff --git a/chrome/browser/permissions/quiet_notification_permission_ui_config.cc b/chrome/browser/permissions/quiet_notification_permission_ui_config.cc
index 92e8b4b..dfd07db 100644
--- a/chrome/browser/permissions/quiet_notification_permission_ui_config.cc
+++ b/chrome/browser/permissions/quiet_notification_permission_ui_config.cc
@@ -20,6 +20,15 @@
     "crowd_deny_hold_back_chance";
 
 // static
+const char
+    QuietNotificationPermissionUiConfig::kEnableAbusiveRequestBlocking[] =
+        "enable_abusive_request_triggering";
+
+// static
+const char QuietNotificationPermissionUiConfig::kEnableAbusiveRequestWarning[] =
+    "enable_abusive_request_warning";
+
+// static
 bool QuietNotificationPermissionUiConfig::IsAdaptiveActivationEnabled() {
   if (!base::FeatureList::IsEnabled(features::kQuietNotificationPrompts))
     return false;
@@ -44,3 +53,23 @@
   return base::GetFieldTrialParamByFeatureAsDouble(
       features::kQuietNotificationPrompts, kCrowdDenyHoldBackChance, 0);
 }
+
+// static
+bool QuietNotificationPermissionUiConfig::IsAbusiveRequestBlockingEnabled() {
+  if (!base::FeatureList::IsEnabled(features::kQuietNotificationPrompts))
+    return false;
+
+  return base::GetFieldTrialParamByFeatureAsBool(
+      features::kQuietNotificationPrompts, kEnableAbusiveRequestBlocking,
+      false /* default */);
+}
+
+// static
+bool QuietNotificationPermissionUiConfig::IsAbusiveRequestWarningEnabled() {
+  if (!base::FeatureList::IsEnabled(features::kQuietNotificationPrompts))
+    return false;
+
+  return base::GetFieldTrialParamByFeatureAsBool(
+      features::kQuietNotificationPrompts, kEnableAbusiveRequestWarning,
+      false /* default */);
+}
diff --git a/chrome/browser/permissions/quiet_notification_permission_ui_config.h b/chrome/browser/permissions/quiet_notification_permission_ui_config.h
index f847c1b..f9326e0 100644
--- a/chrome/browser/permissions/quiet_notification_permission_ui_config.h
+++ b/chrome/browser/permissions/quiet_notification_permission_ui_config.h
@@ -21,6 +21,16 @@
   // rate.
   static const char kEnableCrowdDenyTriggering[];
 
+  // Name of the boolean variation parameter that determines if the quiet
+  // notification permission prompt UI should be enabled as a one-off on sites
+  // with abusive permission request flows.
+  static const char kEnableAbusiveRequestBlocking[];
+
+  // Name of the boolean variation parameter that determines if a console
+  // message in Developer Tools should be printed on sites that are on the
+  // warning list for abusive permission request flows.
+  static const char kEnableAbusiveRequestWarning[];
+
   // Name of the variation parameter that represents the chance that a
   // quiet notifications permission prompt UI triggered by crowd deny will be
   // replaced by the normal UI. This ensures that a small percentage of
@@ -43,6 +53,16 @@
   // crowd deny will be replaced by the normal UI. This is per individual
   // permission prompt.
   static double GetCrowdDenyHoldBackChance();
+
+  // Whether or not triggering via the abusive requests list is enabled. This
+  // means that on sites with abusive permission request flows, the quiet UI
+  // will be shown as a one-off, even when it is not turned on for all sites in
+  // prefs.
+  static bool IsAbusiveRequestBlockingEnabled();
+
+  // Whether or not showing a console message in Developer Tools is enabled for
+  // sites on the abusive requests warning list is enabled.
+  static bool IsAbusiveRequestWarningEnabled();
 };
 
 #endif  // CHROME_BROWSER_PERMISSIONS_QUIET_NOTIFICATION_PERMISSION_UI_CONFIG_H_
diff --git a/chrome/browser/policy/system_features_policy_browsertest.cc b/chrome/browser/policy/system_features_policy_browsertest.cc
index 2216206..69b7c9ba 100644
--- a/chrome/browser/policy/system_features_policy_browsertest.cc
+++ b/chrome/browser/policy/system_features_policy_browsertest.cc
@@ -11,21 +11,35 @@
 #include "chrome/browser/policy/policy_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "components/policy/core/common/policy_pref_names.h"
 #include "components/policy/policy_constants.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/constants.h"
 
 namespace policy {
 
-class SystemFeaturesPolicyTest : public PolicyTest {};
+class SystemFeaturesPolicyTest : public PolicyTest {
+ protected:
+  content::WebUI* GetCommittedWebUI(const GURL& url) {
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    ui_test_utils::NavigateToURL(browser(), url);
+
+    content::WaitForLoadStop(web_contents);
+
+    return web_contents->GetCommittedWebUI();
+  }
+};
 
 IN_PROC_BROWSER_TEST_F(SystemFeaturesPolicyTest, DisableCameraBeforeInstall) {
   PolicyMap policies;
   std::unique_ptr<base::Value> system_features =
       std::make_unique<base::Value>(base::Value::Type::LIST);
-  system_features->Append("camera");
+  system_features->Append(kCameraFeature);
   policies.Set(key::kSystemFeaturesDisableList, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
                std::move(system_features), nullptr);
@@ -85,7 +99,7 @@
   PolicyMap policies;
   std::unique_ptr<base::Value> system_features =
       std::make_unique<base::Value>(base::Value::Type::LIST);
-  system_features->Append("camera");
+  system_features->Append(kCameraFeature);
   policies.Set(key::kSystemFeaturesDisableList, POLICY_LEVEL_MANDATORY,
                POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
                std::move(system_features), nullptr);
@@ -124,4 +138,25 @@
       });
 }
 
+IN_PROC_BROWSER_TEST_F(SystemFeaturesPolicyTest, RedirectChromeSettingsURL) {
+  PolicyMap policies;
+  std::unique_ptr<base::Value> system_features =
+      std::make_unique<base::Value>(base::Value::Type::LIST);
+  system_features->Append(kBrowserSettingsFeature);
+  policies.Set(key::kSystemFeaturesDisableList, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               std::move(system_features), nullptr);
+  UpdateProviderPolicy(policies);
+
+  GURL settings_url = GURL(chrome::kChromeUISettingsURL);
+  ASSERT_FALSE(GetCommittedWebUI(settings_url));
+
+  policies.Set(key::kSystemFeaturesDisableList, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               std::make_unique<base::Value>(), nullptr);
+  UpdateProviderPolicy(policies);
+
+  ASSERT_TRUE(GetCommittedWebUI(settings_url));
+}
+
 }  // namespace policy
diff --git a/chrome/browser/resources/chromeos/login/cr_ui.js b/chrome/browser/resources/chromeos/login/cr_ui.js
index 0bce1a3..66299a1 100644
--- a/chrome/browser/resources/chromeos/login/cr_ui.js
+++ b/chrome/browser/resources/chromeos/login/cr_ui.js
@@ -439,7 +439,11 @@
 (function() {
 'use strict';
 
-document.addEventListener('DOMContentLoaded', function() {
+function initializeOobe() {
+  if (document.readyState === 'loading')
+    return;
+  document.removeEventListener('DOMContentLoaded', initializeOobe);
+
   try {
     Oobe.initialize();
   } finally {
@@ -451,11 +455,21 @@
     // readyForTesting even on failures, just to make test bots happy.
     Oobe.readyForTesting = true;
   }
-});
+}
 
 // Install a global error handler so stack traces are included in logs.
 window.onerror = function(message, file, line, column, error) {
   if (error && error.stack)
     console.error(error.stack);
 };
+
+/**
+ * Final initialization performed after DOM and all scripts have loaded.
+ */
+if (document.readyState === 'loading') {
+  document.addEventListener('DOMContentLoaded', initializeOobe);
+} else {
+  initializeOobe();
+}
+
 })();
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.html
index fb934b1..e4af047 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.html
@@ -38,19 +38,22 @@
         padding-inline-start: 0;
       }
     </style>
-    <div class="settings-box row first">
-      <settings-localized-link
-          localized-string="$i18n{a11yExplanation}"
-          link-url="$i18nRaw{a11yLearnMoreUrl}">
-      </settings-localized-link>
-    </div>
+    <template is="dom-if" if="[[!isKioskModeActive_]]">
+      <div class="settings-box row first">
+        <settings-localized-link
+            localized-string="$i18n{a11yExplanation}"
+            link-url="$i18nRaw{a11yLearnMoreUrl}">
+        </settings-localized-link>
+      </div>
+    </template>
     <h2>$i18n{textToSpeechHeading}</h2>
     <settings-toggle-button
         pref="{{prefs.settings.accessibility}}"
         label="$i18n{chromeVoxLabel}">
     </settings-toggle-button>
     <iron-collapse opened="[[prefs.settings.accessibility.value]]">
-      <cr-link-row class="settings-box" on-click="onChromeVoxSettingsTap_"
+      <cr-link-row id="chromeVoxSubpageButton"
+          class="settings-box" on-click="onChromeVoxSettingsTap_"
           label="$i18n{chromeVoxOptionsLabel}" external></cr-link-row>
     </iron-collapse>
     <settings-toggle-button
@@ -64,7 +67,8 @@
             '$i18nPolymer{selectToSpeakDescriptionWithoutKeyboard}')]]">
     </settings-toggle-button>
     <iron-collapse opened="[[prefs.settings.a11y.select_to_speak.value]]">
-      <cr-link-row class="settings-box" on-click="onSelectToSpeakSettingsTap_"
+      <cr-link-row id="selectToSpeakSubpageButton"
+          class="settings-box" on-click="onSelectToSpeakSettingsTap_"
           label="$i18n{selectToSpeakOptionsLabel}" external></cr-link-row>
     </iron-collapse>
 
@@ -115,15 +119,17 @@
         </settings-dropdown-menu>
       </div>
     </template>
-    <cr-link-row id="displaySubpageButton" class="hr"
-        label="$i18n{displaySettingsTitle}" on-click="onDisplayTap_"
-        sub-label="$i18n{displaySettingsDescription}"
-        role-description="$i18n{subpageArrowRoleDescription}" embedded>
-    </cr-link-row>
-    <cr-link-row id="appearanceSubpageButton" class="hr"
-        label="$i18n{appearanceSettingsTitle}" on-click="onAppearanceTap_"
-        sub-label="$i18n{appearanceSettingsDescription}" external
-        embedded></cr-link-row>
+    <template is="dom-if" if="[[!isKioskModeActive_]]">
+      <cr-link-row id="displaySubpageButton" class="hr"
+          label="$i18n{displaySettingsTitle}" on-click="onDisplayTap_"
+          sub-label="$i18n{displaySettingsDescription}"
+          role-description="$i18n{subpageArrowRoleDescription}" embedded>
+      </cr-link-row>
+      <cr-link-row id="appearanceSubpageButton" class="hr"
+          label="$i18n{appearanceSettingsTitle}" on-click="onAppearanceTap_"
+          sub-label="$i18n{appearanceSettingsDescription}" external
+          embedded></cr-link-row>
+    </template>
 
     <h2>$i18n{keyboardAndTextInputHeading}</h2>
     <settings-toggle-button
@@ -151,7 +157,7 @@
         pref="{{prefs.settings.a11y.caret_highlight}}"
         label="$i18n{caretHighlightLabel}">
     </settings-toggle-button>
-    <template is="dom-if" if="[[showExperimentalSwitchAccess_]]">
+    <template is="dom-if" if="[[shouldShowExperimentalSwitchAccess_]]">
       <settings-toggle-button
           class="hr"
           pref="{{prefs.settings.a11y.switch_access.enabled}}"
@@ -166,11 +172,13 @@
         </cr-link-row>
       </iron-collapse>
     </template>
-    <cr-link-row id="keyboardSubpageButton" class="hr"
-        label="$i18n{keyboardSettingsTitle}" on-click="onKeyboardTap_"
-        sub-label="$i18n{keyboardSettingsDescription}"
-        role-description="$i18n{subpageArrowRoleDescription}" embedded>
-    </cr-link-row>
+    <template is="dom-if" if="[[!isKioskModeActive_]]">
+      <cr-link-row id="keyboardSubpageButton" class="hr"
+          label="$i18n{keyboardSettingsTitle}" on-click="onKeyboardTap_"
+          sub-label="$i18n{keyboardSettingsDescription}"
+          role-description="$i18n{subpageArrowRoleDescription}" embedded>
+      </cr-link-row>
+    </template>
 
     <h2>$i18n{mouseAndTouchpadHeading}</h2>
     <settings-toggle-button
@@ -264,9 +272,11 @@
     </settings-toggle-button>
 
     <h2>$i18n{audioAndCaptionsHeading}</h2>
-    <cr-link-row id="captionsSubpageButton" class="first"
-        label="$i18n{captionsTitle}" on-click="onCaptionsClick_"
-        role-description="$i18n{subpageArrowRoleDescription}"></cr-link-row>
+    <template is="dom-if" if="[[!isKioskModeActive_]]">
+      <cr-link-row id="captionsSubpageButton" class="first"
+          label="$i18n{captionsTitle}" on-click="onCaptionsClick_"
+          role-description="$i18n{subpageArrowRoleDescription}"></cr-link-row>
+    </template>
     <settings-toggle-button
         class="hr"
         pref="{{prefs.settings.a11y.mono_audio}}"
@@ -279,9 +289,10 @@
         label="$i18n{startupSoundLabel}">
     </settings-toggle-button>
 
-    <template is="dom-if" if="[[!isGuest_]]">
+    <template is="dom-if"
+        if="[[shouldShowAdditionalFeaturesLink_(isGuest_, isKioskModeActive_)]]">
       <a class="settings-box inherit-color no-outline" tabindex="-1"
-          target="_blank"
+          id="additionalFeaturesLink" target="_blank"
           href="https://chrome.google.com/webstore/category/collection/accessibility">
         <div class="start settings-box-text">
           $i18n{additionalFeaturesTitle}
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
index e5fb732..ffa19ac 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/manage_a11y_page.js
@@ -98,7 +98,7 @@
       },
     },
 
-    showExperimentalSwitchAccess_: {
+    allowExperimentalSwitchAccess_: {
       type: Boolean,
       value() {
         return loadTimeData.getBoolean(
@@ -107,6 +107,25 @@
     },
 
     /**
+     * Whether the user is in kiosk mode.
+     * @private
+     */
+    isKioskModeActive_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('isKioskModeActive');
+      }
+    },
+
+    /** @private */
+    shouldShowExperimentalSwitchAccess_: {
+      type: Boolean,
+      computed: 'computeShouldShowExperimentalSwitchAccess_(' +
+          'allowExperimentalSwitchAccess_,' +
+          'isKioskModeActive_)',
+    },
+
+    /**
      * Whether a setting for enabling shelf navigation buttons in tablet mode
      * should be displayed in the accessibility settings.
      * @private
@@ -167,7 +186,7 @@
   },
 
   observers: [
-    'pointersChanged_(hasMouse_, hasTouchpad_)',
+    'pointersChanged_(hasMouse_, hasTouchpad_, isKioskModeActive_)',
   ],
 
   /** settings.RouteOriginBehavior override */
@@ -207,8 +226,9 @@
    * @param {boolean} hasTouchpad
    * @private
    */
-  pointersChanged_(hasMouse, hasTouchpad) {
-    this.$.pointerSubpageButton.hidden = !hasMouse && !hasTouchpad;
+  pointersChanged_(hasMouse, hasTouchpad, isKioskModeActive) {
+    this.$.pointerSubpageButton.hidden =
+        (!hasMouse && !hasTouchpad) || isKioskModeActive;
   },
 
   /**
@@ -370,6 +390,26 @@
   onManageAllyPageReady_(startup_sound_enabled, tablet_mode_supported) {
     this.$.startupSoundEnabled.checked = startup_sound_enabled;
     this.showShelfNavigationButtonsSettings_ = tablet_mode_supported &&
-        loadTimeData.getBoolean('showTabletModeShelfNavigationButtonsSettings');
+        loadTimeData.getBoolean(
+            'showTabletModeShelfNavigationButtonsSettings') &&
+        !this.isKioskModeActive_;
+  },
+  /*
+   * Whether additional features link should be shown.
+   * @param {boolean} isKiosk
+   * @param {boolean} isGuest
+   * @return {boolean}
+   * @private
+   */
+  shouldShowAdditionalFeaturesLink_(isKiosk, isGuest) {
+    return !isKiosk && !isGuest;
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  computeShouldShowExperimentalSwitchAccess_() {
+    return this.allowExperimentalSwitchAccess_ && !this.isKioskModeActive_;
   },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.html
index 20e0dc8..299b22e 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.html
@@ -20,17 +20,19 @@
     <settings-animated-pages id="pages" current-route="{{currentRoute}}"
         section="osAccessibility" focus-config="[[focusConfig_]]">
       <div route-path="default">
-        <settings-toggle-button
-            id="a11yImageLabels"
-            class="hr"
-            hidden$="[[!showAccessibilityLabelsSetting_]]"
-            pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
-            on-change="onToggleAccessibilityImageLabels_"
-            label="$i18n{accessibleImageLabelsTitle}"
-            sub-label="$i18n{accessibleImageLabelsSubtitle}">
-        </settings-toggle-button>
+        <template is="dom-if" if="[[showAccessibilityLabelsSetting_]]">
+          <settings-toggle-button
+              id="a11yImageLabels"
+              class="hr"
+              pref="{{prefs.settings.a11y.enable_accessibility_image_labels}}"
+              on-change="onToggleAccessibilityImageLabels_"
+              label="$i18n{accessibleImageLabelsTitle}"
+              sub-label="$i18n{accessibleImageLabelsSubtitle}">
+          </settings-toggle-button>
+        </template>
         <settings-toggle-button id="optionsInMenuToggle"
             class="hr"
+            hidden="[[isKioskModeActive_]]"
             label="$i18n{optionsInMenuLabel}"
             pref="{{prefs.settings.a11y.enable_menu}}">
         </settings-toggle-button>
@@ -45,7 +47,8 @@
       <template is="dom-if" route-path="/manageAccessibility">
         <settings-subpage
             associated-control="[[$$('#subpage-trigger')]]"
-            page-title="$i18n{manageAccessibilityFeatures}">
+            page-title="$i18n{manageAccessibilityFeatures}"
+            hide-close-button="[[isKioskModeActive_]]">
           <settings-manage-a11y-page prefs="{{prefs}}">
           </settings-manage-a11y-page>
         </settings-subpage>
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js
index 256b027..7c8704aa 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/os_a11y_page.js
@@ -61,6 +61,18 @@
             'showExperimentalAccessibilitySwitchAccess');
       },
     },
+
+    /**
+     * Whether the user is in kiosk mode.
+     * @private
+     */
+    isKioskModeActive_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('isKioskModeActive');
+      }
+    },
+
   },
 
   /** @override */
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
index d147426..85c7f61 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.html
@@ -107,20 +107,42 @@
       }
     </style>
     <settings-prefs id="prefs" prefs="{{prefs}}"></settings-prefs>
-    <os-toolbar on-os-toolbar-menu-tap="onMenuButtonTap_"
-        spinner-active="[[toolbarSpinnerActive_]]"
-        on-search-changed="onSearchChanged_"
-        role="banner"
-        narrow="{{isNarrow}}"
-        narrow-threshold="980"
-        show-menu="[[isNarrow]]">
-    </os-toolbar>
-    <cr-drawer id="drawer" on-close="onMenuClose_" heading="$i18n{settings}"
-        align="$i18n{textdirection}" icon-name="cr20:menu"
-        icon-title="$i18n{close}">
-      <div class="drawer-content">
-        <template is="dom-if" id="drawerTemplate">
-          <os-settings-menu
+    <iron-media-query query="(max-width: [[narrowThreshold_]]px)"
+        query-matches="{{isNarrow}}">
+    </iron-media-query>
+    <template is="dom-if" if="[[showToolbar_]]">
+      <os-toolbar on-os-toolbar-menu-tap="onMenuButtonTap_"
+          spinner-active="[[toolbarSpinnerActive_]]"
+          on-search-changed="onSearchChanged_"
+          role="banner"
+          narrow="[[isNarrow]]"
+          narrow-threshold="980"
+          show-menu="[[isNarrow]]">
+      </os-toolbar>
+    </template>
+    <template is="dom-if" if="[[showNavMenu_]]">
+      <cr-drawer id="drawer" on-close="onMenuClose_" heading="$i18n{settings}"
+          align="$i18n{textdirection}" icon-name="cr20:menu"
+          icon-title="$i18n{close}">
+        <div class="drawer-content">
+          <template is="dom-if" id="drawerTemplate">
+            <os-settings-menu
+                show-apps="[[showApps_]]"
+                show-crostini="[[showCrostini_]]"
+                show-plugin-vm="[[showPluginVm_]]"
+                show-reset="[[showReset_]]"
+                have-play-store-app="[[havePlayStoreApp_]]"
+                on-iron-activate="onIronActivate_"
+                advanced-opened="{{advancedOpenedInMenu_}}">
+            </os-settings-menu>
+          </template>
+        </div>
+      </cr-drawer>
+    </template>
+    <div id="container" class="no-outline">
+      <div id="left">
+        <template is="dom-if" if="[[showNavMenu_]]">
+          <os-settings-menu page-visibility="[[pageVisibility_]]"
               show-crostini="[[showCrostini_]]"
               show-plugin-vm="[[showPluginVm_]]"
               show-reset="[[showReset_]]"
@@ -130,18 +152,6 @@
           </os-settings-menu>
         </template>
       </div>
-    </cr-drawer>
-    <div id="container" class="no-outline">
-      <div id="left">
-        <os-settings-menu page-visibility="[[pageVisibility_]]"
-            show-crostini="[[showCrostini_]]"
-            show-plugin-vm="[[showPluginVm_]]"
-            show-reset="[[showReset_]]"
-            have-play-store-app="[[havePlayStoreApp_]]"
-            on-iron-activate="onIronActivate_"
-            advanced-opened="{{advancedOpenedInMenu_}}">
-        </os-settings-menu>
-      </div>
       <os-settings-main id="main" prefs="{{prefs}}"
           toolbar-spinner-active="{{toolbarSpinnerActive_}}"
           page-visibility="[[pageVisibility_]]"
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
index a1af487..b86f907 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_ui/os_settings_ui.js
@@ -64,6 +64,9 @@
        */
       isNarrow: {
         type: Boolean,
+        value: false,
+        readonly: true,
+        notify: true,
         observer: 'onNarrowChanged_',
       },
 
@@ -82,6 +85,12 @@
       showCrostini_: Boolean,
 
       /** @private */
+      showToolbar_: Boolean,
+
+      /** @private */
+      showNavMenu_: Boolean,
+
+      /** @private */
       showPluginVm_: Boolean,
 
       /** @private */
@@ -92,6 +101,16 @@
         type: String,
         value: '',
       },
+
+      /**
+       * The threshold at which the toolbar will change from normal to narrow
+       * mode, in px.
+       * @private {boolean}
+       */
+      narrowThreshold_: {
+        type: Number,
+        value: 900,
+      },
     },
 
     listeners: {
@@ -117,15 +136,6 @@
      * ES5 strict mode.
      */
     ready() {
-      // Lazy-create the drawer the first time it is opened or swiped into view.
-      listenOnce(this.$.drawer, 'cr-drawer-opening', () => {
-        this.$.drawerTemplate.if = true;
-      });
-
-      window.addEventListener('popstate', e => {
-        this.$.drawer.cancel();
-      });
-
       CrPolicyStrings = {
         controlledSettingExtension:
             loadTimeData.getString('controlledSettingExtension'),
@@ -153,6 +163,8 @@
       this.showAndroidApps_ = loadTimeData.getBoolean('androidAppsVisible');
       this.showCrostini_ = loadTimeData.getBoolean('showCrostini');
       this.showPluginVm_ = loadTimeData.getBoolean('showPluginVm');
+      this.showNavMenu_ = !loadTimeData.getBoolean('isKioskModeActive');
+      this.showToolbar_ = !loadTimeData.getBoolean('isKioskModeActive');
       this.showReset_ = loadTimeData.getBoolean('allowPowerwash');
 
       this.addEventListener('show-container', () => {
@@ -162,6 +174,25 @@
       this.addEventListener('hide-container', () => {
         this.$.container.style.visibility = 'hidden';
       });
+
+      // If navigation menu is not shown, do not listen to the drawer.
+      if (!this.showNavMenu_) {
+        return;
+      }
+
+      this.async(() => {
+        // Lazy-create the drawer the first time it is opened or swiped into
+        // view.
+        const drawer = this.$$('#drawer');
+        assert(drawer);
+        listenOnce(drawer, 'cr-drawer-opening', () => {
+          this.$$('#drawerTemplate').if = true;
+        });
+
+        window.addEventListener('popstate', e => {
+          drawer.cancel();
+        });
+      });
     },
 
     /** @override */
@@ -253,6 +284,11 @@
 
       this.lastSearchQuery_ = urlSearchQuery;
 
+      // If toolbar is hidden, do not update anything.
+      if (!this.showToolbar_) {
+        return;
+      }
+
       const toolbar = /** @type {!OsToolbarElement} */ (this.$$('os-toolbar'));
       const searchField =
           /** @type {?CrToolbarSearchFieldElement} */ (
@@ -282,7 +318,7 @@
 
     // Override FindShortcutBehavior methods.
     handleFindShortcut(modalContextOpen) {
-      if (modalContextOpen) {
+      if (modalContextOpen || !this.showToolbar_) {
         return false;
       }
       this.$$('os-toolbar').getSearchField().showAndFocus();
@@ -291,6 +327,9 @@
 
     // Override FindShortcutBehavior methods.
     searchInputHasFocus() {
+      if (!this.showToolbar_) {
+        return;
+      }
       return this.$$('os-toolbar').getSearchField().isSearchFocused();
     },
 
@@ -335,6 +374,7 @@
      * @private
      */
     onIronActivate_(e) {
+      assert(this.showNavMenu_);
       const section = e.detail.selected;
       const path = new URL(section).pathname;
       const route = settings.Router.getInstance().getRouteForPath(path);
@@ -344,7 +384,7 @@
       if (this.isNarrow) {
         // If the onIronActivate event came from the drawer, close the drawer
         // and wait for the menu to close before navigating to |activeRoute_|.
-        this.$.drawer.close();
+        this.$$('#drawer').close();
         return;
       }
       this.navigateToActiveRoute_();
@@ -352,10 +392,12 @@
 
     /** @private */
     onMenuButtonTap_() {
-      this.$.drawer.toggle();
+      if (!this.showNavMenu_) {
+        return;
+      }
+      this.$$('#drawer').toggle();
     },
 
-
     /**
      * Navigates to |activeRoute_| if set. Used to delay navigation until after
      * animations complete to ensure focus ends up in the right place.
@@ -380,7 +422,7 @@
      * @private
      */
     onMenuClose_() {
-      if (!this.$.drawer.wasCanceled()) {
+      if (!this.$$('#drawer').wasCanceled()) {
         // If a navigation happened, MainPageBehavior#currentRouteChanged
         // handles focusing the corresponding section when we call
         // settings.NavigateTo().
@@ -415,8 +457,8 @@
 
     /** @private */
     onNarrowChanged_() {
-      if (this.$.drawer.open && !this.isNarrow) {
-        this.$.drawer.close();
+      if (this.showNavMenu_ && this.$$('#drawer').open && !this.isNarrow) {
+        this.$$('#drawer').close();
       }
     },
   });
diff --git a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.html b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.html
index 15a21ff..57e57ddb 100644
--- a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.html
+++ b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.html
@@ -132,9 +132,6 @@
             showing-search="{{showingSearch_}}">
         </os-settings-search-box>
       </template>
-      <iron-media-query query="(max-width: [[narrowThreshold]]px)"
-          query-matches="{{narrow}}">
-      </iron-media-query>
     </div>
 
     <div id="rightContent">
diff --git a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js
index 6ca377a..181ccbd 100644
--- a/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js
+++ b/chrome/browser/resources/settings/chromeos/os_toolbar/os_toolbar.js
@@ -20,18 +20,8 @@
     // True when the toolbar is displaying in narrow mode.
     narrow: {
       type: Boolean,
+      value: false,
       reflectToAttribute: true,
-      readonly: true,
-      notify: true,
-    },
-
-    /**
-     * The threshold at which the toolbar will change from normal to narrow
-     * mode, in px.
-     */
-    narrowThreshold: {
-      type: Number,
-      value: 900,
     },
 
     /** @private */
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.html b/chrome/browser/resources/settings/settings_page/settings_subpage.html
index 7fc1975..2251a7bd 100644
--- a/chrome/browser/resources/settings/settings_page/settings_subpage.html
+++ b/chrome/browser/resources/settings/settings_page/settings_subpage.html
@@ -76,7 +76,8 @@
     </style>
     <div class="cr-row first" id="headerLine">
       <cr-icon-button class="icon-arrow-back" id="closeButton"
-          on-click="onTapBack_" aria-label="$i18n{back}"></cr-icon-button>
+            hidden="[[hideCloseButton]]" on-click="onTapBack_"
+            aria-label="$i18n{back}"></cr-icon-button>
       <template is="dom-if" if="[[titleIcon]]">
         <img id="title-icon" src="[[titleIcon]]" aria-hidden="true">
       </template>
diff --git a/chrome/browser/resources/settings/settings_page/settings_subpage.js b/chrome/browser/resources/settings/settings_page/settings_subpage.js
index 159d60b3..6302b73 100644
--- a/chrome/browser/resources/settings/settings_page/settings_subpage.js
+++ b/chrome/browser/resources/settings/settings_page/settings_subpage.js
@@ -49,6 +49,14 @@
     },
 
     /**
+     * Whether we should hide the "close" button to get to the previous page.
+     */
+    hideCloseButton: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
      * Indicates which element triggers this subpage. Used by the searching
      * algorithm to show search bubbles. It is |null| for subpages that are
      * skipped during searching.
@@ -91,6 +99,9 @@
 
   /** Focuses the back button when page is loaded. */
   initialFocus() {
+    if (this.hideCloseButton) {
+      return;
+    }
     Polymer.RenderStatus.afterNextRender(
         this, () => cr.ui.focusWithoutInk(this.$.closeButton));
   },
diff --git a/chrome/browser/sharing/features.cc b/chrome/browser/sharing/features.cc
index cb7fd38..e43c6ca0 100644
--- a/chrome/browser/sharing/features.cc
+++ b/chrome/browser/sharing/features.cc
@@ -42,3 +42,6 @@
 
 const base::Feature kSharingSendViaSync{"SharingSendViaSync",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kSharingPreferVapid{"SharingPreferVapid",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/sharing/features.h b/chrome/browser/sharing/features.h
index db0ff1a..3acb082 100644
--- a/chrome/browser/sharing/features.h
+++ b/chrome/browser/sharing/features.h
@@ -54,4 +54,7 @@
 // Feature flag for sending sharing message via Sync.
 extern const base::Feature kSharingSendViaSync;
 
+// Feature flag for prefer sending sharing message using VAPID.
+extern const base::Feature kSharingPreferVapid;
+
 #endif  // CHROME_BROWSER_SHARING_FEATURES_H_
diff --git a/chrome/browser/sharing/sharing_fcm_sender.cc b/chrome/browser/sharing/sharing_fcm_sender.cc
index 2a17ebd..337358a 100644
--- a/chrome/browser/sharing/sharing_fcm_sender.cc
+++ b/chrome/browser/sharing/sharing_fcm_sender.cc
@@ -70,11 +70,18 @@
     SendMessageCallback callback) {
   TRACE_EVENT0("sharing", "SharingFCMSender::SendMessageToFcmTarget");
 
-  if (base::FeatureList::IsEnabled(kSharingSendViaSync) &&
+  bool canSendViaSync =
+      base::FeatureList::IsEnabled(kSharingSendViaSync) &&
       sync_service_->GetActiveDataTypes().Has(syncer::SHARING_MESSAGE) &&
       !fcm_configuration.sender_id_fcm_token().empty() &&
       !fcm_configuration.sender_id_p256dh().empty() &&
-      !fcm_configuration.sender_id_auth_secret().empty()) {
+      !fcm_configuration.sender_id_auth_secret().empty();
+  bool canSendViaVapid = !fcm_configuration.vapid_fcm_token().empty() &&
+                         !fcm_configuration.vapid_p256dh().empty() &&
+                         !fcm_configuration.vapid_auth_secret().empty();
+
+  if (canSendViaSync && (!canSendViaVapid ||
+                         !base::FeatureList::IsEnabled(kSharingPreferVapid))) {
     message.set_message_id(base::GenerateGUID());
     EncryptMessage(
         kSharingSenderID, fcm_configuration.sender_id_p256dh(),
@@ -87,9 +94,7 @@
     return;
   }
 
-  if (!fcm_configuration.vapid_fcm_token().empty() &&
-      !fcm_configuration.vapid_p256dh().empty() &&
-      !fcm_configuration.vapid_auth_secret().empty()) {
+  if (canSendViaVapid) {
     base::Optional<SharingSyncPreference::FCMRegistration> fcm_registration =
         sync_preference_->GetFCMRegistration();
     if (!fcm_registration || !fcm_registration->authorized_entity) {
diff --git a/chrome/browser/sharing/sharing_fcm_sender_unittest.cc b/chrome/browser/sharing/sharing_fcm_sender_unittest.cc
index 06604c5..3753d4a4 100644
--- a/chrome/browser/sharing/sharing_fcm_sender_unittest.cc
+++ b/chrome/browser/sharing/sharing_fcm_sender_unittest.cc
@@ -93,14 +93,14 @@
 
   const std::string& fcm_token() { return fcm_token_; }
   crypto::ECPrivateKey* vapid_key() { return vapid_key_; }
-  const WebPushMessage& message() { return message_; }
+  const base::Optional<WebPushMessage>& message() { return message_; }
 
   void set_result(SendWebPushMessageResult result) { result_ = result; }
 
  private:
   std::string fcm_token_;
   crypto::ECPrivateKey* vapid_key_;
-  WebPushMessage message_;
+  base::Optional<WebPushMessage> message_;
   SendWebPushMessageResult result_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeWebPushSender);
@@ -127,7 +127,9 @@
     return nullptr;
   }
 
-  const sync_pb::SharingMessageSpecifics& specifics() { return specifics_; }
+  const base::Optional<sync_pb::SharingMessageSpecifics>& specifics() {
+    return specifics_;
+  }
 
   void set_error_code(
       const sync_pb::SharingMessageCommitError::ErrorCode& error_code) {
@@ -135,7 +137,7 @@
   }
 
  private:
-  sync_pb::SharingMessageSpecifics specifics_;
+  base::Optional<sync_pb::SharingMessageSpecifics> specifics_;
   sync_pb::SharingMessageCommitError::ErrorCode error_code_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeSharingMessageBridge);
@@ -330,6 +332,100 @@
   EXPECT_EQ(SharingChannelType::kUnknown, channel_type);
 }
 
+TEST_F(SharingFCMSenderTest, PreferVapid) {
+  scoped_feature_list_.InitAndEnableFeature(kSharingPreferVapid);
+  test_sync_service_.SetActiveDataTypes({syncer::SHARING_MESSAGE});
+  sync_prefs_.SetFCMRegistration(SharingSyncPreference::FCMRegistration(
+      kAuthorizedEntity, base::Time::Now()));
+
+  fake_web_push_sender_->set_result(SendWebPushMessageResult::kSuccessful);
+  fake_sharing_message_bridge_.set_error_code(
+      sync_pb::SharingMessageCommitError::NONE);
+
+  std::unique_ptr<crypto::ECPrivateKey> vapid_key =
+      crypto::ECPrivateKey::Create();
+  ON_CALL(vapid_key_manager_, GetOrCreateKey())
+      .WillByDefault(testing::Return(vapid_key.get()));
+
+  chrome_browser_sharing::FCMChannelConfiguration fcm_channel;
+  // Set both VAPID and Sender ID channel.
+  fcm_channel.set_vapid_fcm_token(kVapidFcmToken);
+  fcm_channel.set_vapid_p256dh(kVapidP256dh);
+  fcm_channel.set_vapid_auth_secret(kVapidAuthSecret);
+  fcm_channel.set_sender_id_fcm_token(kSenderIdFcmToken);
+  fcm_channel.set_sender_id_p256dh(kSenderIdP256dh);
+  fcm_channel.set_sender_id_auth_secret(kSenderIdAuthSecret);
+
+  SharingSendMessageResult result;
+  base::Optional<std::string> message_id;
+  SharingChannelType channel_type;
+  chrome_browser_sharing::SharingMessage sharing_message;
+  sharing_message.mutable_ping_message();
+  sharing_fcm_sender_.SendMessageToFcmTarget(
+      fcm_channel, base::TimeDelta::FromSeconds(kTtlSeconds),
+      std::move(sharing_message),
+      base::BindOnce(&SharingFCMSenderTest::OnMessageSent,
+                     base::Unretained(this), &result, &message_id,
+                     &channel_type));
+
+  EXPECT_EQ(SharingSendMessageResult::kSuccessful, result);
+  // Ensures that a Ping message is sent through WebPushSender.
+  chrome_browser_sharing::SharingMessage message_sent;
+  ASSERT_TRUE(fake_web_push_sender_->message());
+  message_sent.ParseFromString(fake_web_push_sender_->message()->payload);
+  EXPECT_TRUE(message_sent.has_ping_message());
+  // Ensures that no message is sent through SharingMessageBridge.
+  EXPECT_FALSE(fake_sharing_message_bridge_.specifics());
+}
+
+TEST_F(SharingFCMSenderTest, PreferSync) {
+  scoped_feature_list_.InitAndDisableFeature(kSharingPreferVapid);
+  test_sync_service_.SetActiveDataTypes({syncer::SHARING_MESSAGE});
+  sync_prefs_.SetFCMRegistration(SharingSyncPreference::FCMRegistration(
+      kAuthorizedEntity, base::Time::Now()));
+
+  fake_web_push_sender_->set_result(SendWebPushMessageResult::kSuccessful);
+  fake_sharing_message_bridge_.set_error_code(
+      sync_pb::SharingMessageCommitError::NONE);
+
+  std::unique_ptr<crypto::ECPrivateKey> vapid_key =
+      crypto::ECPrivateKey::Create();
+  ON_CALL(vapid_key_manager_, GetOrCreateKey())
+      .WillByDefault(testing::Return(vapid_key.get()));
+
+  chrome_browser_sharing::FCMChannelConfiguration fcm_channel;
+  // Set both VAPID and Sender ID channel.
+  fcm_channel.set_vapid_fcm_token(kVapidFcmToken);
+  fcm_channel.set_vapid_p256dh(kVapidP256dh);
+  fcm_channel.set_vapid_auth_secret(kVapidAuthSecret);
+  fcm_channel.set_sender_id_fcm_token(kSenderIdFcmToken);
+  fcm_channel.set_sender_id_p256dh(kSenderIdP256dh);
+  fcm_channel.set_sender_id_auth_secret(kSenderIdAuthSecret);
+
+  SharingSendMessageResult result;
+  base::Optional<std::string> message_id;
+  SharingChannelType channel_type;
+  chrome_browser_sharing::SharingMessage sharing_message;
+  sharing_message.mutable_ping_message();
+  sharing_fcm_sender_.SendMessageToFcmTarget(
+      fcm_channel, base::TimeDelta::FromSeconds(kTtlSeconds),
+      std::move(sharing_message),
+      base::BindOnce(&SharingFCMSenderTest::OnMessageSent,
+                     base::Unretained(this), &result, &message_id,
+                     &channel_type));
+
+  EXPECT_EQ(SharingSendMessageResult::kSuccessful, result);
+  // Ensures that a Ping message is sent through SharingMessageBridge.
+  chrome_browser_sharing::SharingMessage message_sent;
+  ASSERT_TRUE(fake_sharing_message_bridge_.specifics());
+  ASSERT_TRUE(fake_sharing_message_bridge_.specifics()->has_payload());
+  message_sent.ParseFromString(
+      fake_sharing_message_bridge_.specifics()->payload());
+  EXPECT_TRUE(message_sent.has_ping_message());
+  // Ensures that no message is sent through WebPushSender.
+  EXPECT_FALSE(fake_web_push_sender_->message());
+}
+
 struct WebPushResultTestData {
   const SendWebPushMessageResult web_push_result;
   const SharingSendMessageResult expected_result;
@@ -392,11 +488,12 @@
 
   EXPECT_EQ(kVapidFcmToken, fake_web_push_sender_->fcm_token());
   EXPECT_EQ(vapid_key.get(), fake_web_push_sender_->vapid_key());
-  EXPECT_EQ(kTtlSeconds, fake_web_push_sender_->message().time_to_live);
+  EXPECT_EQ(kTtlSeconds, fake_web_push_sender_->message()->time_to_live);
   EXPECT_EQ(WebPushMessage::Urgency::kHigh,
-            fake_web_push_sender_->message().urgency);
+            fake_web_push_sender_->message()->urgency);
   chrome_browser_sharing::SharingMessage message_sent;
-  message_sent.ParseFromString(fake_web_push_sender_->message().payload);
+  ASSERT_TRUE(fake_web_push_sender_->message());
+  message_sent.ParseFromString(fake_web_push_sender_->message()->payload);
   EXPECT_TRUE(message_sent.has_ping_message());
 
   EXPECT_EQ(GetParam().expected_result, result);
@@ -469,16 +566,17 @@
   EXPECT_EQ(kSenderIdAuthSecret, fake_gcm_driver_.auth_secret());
 
   auto specifics = fake_sharing_message_bridge_.specifics();
-  ASSERT_TRUE(specifics.has_channel_configuration());
-  ASSERT_TRUE(specifics.channel_configuration().has_fcm());
-  auto fcm = specifics.channel_configuration().fcm();
+  ASSERT_TRUE(specifics);
+  ASSERT_TRUE(specifics->has_channel_configuration());
+  ASSERT_TRUE(specifics->channel_configuration().has_fcm());
+  auto fcm = specifics->channel_configuration().fcm();
   EXPECT_EQ(kSenderIdFcmToken, fcm.token());
   EXPECT_EQ(kTtlSeconds, fcm.ttl());
   EXPECT_EQ(10, fcm.priority());
 
   chrome_browser_sharing::SharingMessage message_sent;
-  ASSERT_TRUE(specifics.has_payload());
-  message_sent.ParseFromString(specifics.payload());
+  ASSERT_TRUE(specifics->has_payload());
+  message_sent.ParseFromString(specifics->payload());
   EXPECT_TRUE(message_sent.has_ping_message());
 
   EXPECT_EQ(GetParam().expected_result, result);
@@ -518,12 +616,13 @@
   EXPECT_EQ(kServerAuthSecret, fake_gcm_driver_.auth_secret());
 
   auto specifics = fake_sharing_message_bridge_.specifics();
-  ASSERT_TRUE(specifics.has_channel_configuration());
-  EXPECT_EQ(kServerConfiguration, specifics.channel_configuration().server());
+  ASSERT_TRUE(specifics);
+  ASSERT_TRUE(specifics->has_channel_configuration());
+  EXPECT_EQ(kServerConfiguration, specifics->channel_configuration().server());
 
   chrome_browser_sharing::SharingMessage message_sent;
-  ASSERT_TRUE(specifics.has_payload());
-  message_sent.ParseFromString(specifics.payload());
+  ASSERT_TRUE(specifics->has_payload());
+  message_sent.ParseFromString(specifics->payload());
   EXPECT_TRUE(message_sent.has_ping_message());
 
   EXPECT_EQ(SharingSendMessageResult::kSuccessful, result);
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 2f2961da..0d7efec 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -448,6 +448,8 @@
   }
 #endif  // defined(OS_CHROMEOS)
 
+// Chrome prefers OS provided spell checkers where they exist. So only sync the
+// custom dictionary on platforms that typically don't provide one.
 #if defined(OS_LINUX) || defined(OS_WIN)
   // Dictionary sync is enabled by default.
   if (!disabled_types.Has(syncer::DICTIONARY)) {
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
index b5035e7..4f4845b 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
@@ -289,6 +289,91 @@
   EXPECT_EQ(GetRegistrar(destProfile).GetAppIconInfos(app_id).size(), 1u);
 }
 
+// Tests that we don't use the manifest start_url if it differs from what came
+// through sync.
+IN_PROC_BROWSER_TEST_P(TwoClientWebAppsSyncTest, SyncUsingStartUrlFallback) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  Profile* source_profile = GetProfile(0);
+  Profile* dest_profile = GetProfile(1);
+
+  WebAppInstallObserver dest_install_observer(dest_profile);
+
+  // Install app with name.
+  WebApplicationInfo info;
+  info.title = base::UTF8ToUTF16("Test app");
+  info.app_url =
+      embedded_test_server()->GetURL("/web_apps/different_start_url.html");
+  AppId app_id = InstallApp(info, GetProfile(0));
+  EXPECT_EQ(GetRegistrar(source_profile).GetAppShortName(app_id), "Test app");
+  EXPECT_EQ(GetRegistrar(source_profile).GetAppLaunchURL(app_id), info.app_url);
+
+  // Wait for app to sync across.
+  AppId synced_app_id = dest_install_observer.AwaitNextInstall();
+  ASSERT_EQ(synced_app_id, app_id);
+  EXPECT_EQ(GetRegistrar(dest_profile).GetAppShortName(app_id), "Test app");
+  EXPECT_EQ(GetRegistrar(dest_profile).GetAppLaunchURL(app_id), info.app_url);
+}
+
+// Tests that we don't use the page title if there's no manifest.
+// Pages without a manifest are usually not the correct page to draw information
+// from e.g. login redirects or loading pages.
+// Context: https://crbug.com/1078286
+IN_PROC_BROWSER_TEST_P(TwoClientWebAppsSyncTest, SyncUsingNameFallback) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  Profile* source_profile = GetProfile(0);
+  Profile* dest_profile = GetProfile(1);
+
+  WebAppInstallObserver dest_install_observer(dest_profile);
+
+  // Install app with name.
+  WebApplicationInfo info;
+  info.title = base::UTF8ToUTF16("Correct App Name");
+  info.app_url =
+      embedded_test_server()->GetURL("/web_apps/bad_title_only.html");
+  AppId app_id = InstallApp(info, GetProfile(0));
+  EXPECT_EQ(GetRegistrar(source_profile).GetAppShortName(app_id),
+            "Correct App Name");
+
+  // Wait for app to sync across.
+  AppId synced_app_id = dest_install_observer.AwaitNextInstall();
+  EXPECT_EQ(synced_app_id, app_id);
+  EXPECT_EQ(GetRegistrar(dest_profile).GetAppShortName(app_id),
+            "Correct App Name");
+}
+
+// Negative test of SyncUsingNameFallback above. Don't use the app name fallback
+// if there's a name provided by the manifest during sync.
+IN_PROC_BROWSER_TEST_P(TwoClientWebAppsSyncTest, SyncWithoutUsingNameFallback) {
+  ASSERT_TRUE(SetupSync());
+  ASSERT_TRUE(AllProfilesHaveSameWebAppIds());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  Profile* source_profile = GetProfile(0);
+  Profile* dest_profile = GetProfile(1);
+
+  WebAppInstallObserver dest_install_observer(dest_profile);
+
+  // Install app with name.
+  WebApplicationInfo info;
+  info.title = base::UTF8ToUTF16("Incorrect App Name");
+  info.app_url = embedded_test_server()->GetURL("/web_apps/basic.html");
+  AppId app_id = InstallApp(info, GetProfile(0));
+  EXPECT_EQ(GetRegistrar(source_profile).GetAppShortName(app_id),
+            "Incorrect App Name");
+
+  // Wait for app to sync across.
+  AppId synced_app_id = dest_install_observer.AwaitNextInstall();
+  EXPECT_EQ(synced_app_id, app_id);
+  EXPECT_EQ(GetRegistrar(dest_profile).GetAppShortName(app_id),
+            "Basic web app");
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          TwoClientWebAppsSyncTest,
                          ::testing::Values(ProviderType::kBookmarkApps,
diff --git a/chrome/browser/touch_to_fill/DEPS b/chrome/browser/touch_to_fill/DEPS
index 72d9b1a..f3d400f 100644
--- a/chrome/browser/touch_to_fill/DEPS
+++ b/chrome/browser/touch_to_fill/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+chrome/android",
   "+chrome/browser/ui/android/favicon/java",
+  "+components/browser_ui/android/bottomsheet",
 ]
diff --git a/chrome/browser/touch_to_fill/android/BUILD.gn b/chrome/browser/touch_to_fill/android/BUILD.gn
index d8ad039a..de15117 100644
--- a/chrome/browser/touch_to_fill/android/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/BUILD.gn
@@ -31,6 +31,7 @@
     # TODO(crbug.com/1004415): Remove dependency on chrome_java and depend on
     # bottomsheet directly. Add public_java to chrome_java target instead.
     "//chrome/android:chrome_java",
+    "//components/browser_ui/android/bottomsheet:java",
     "//ui/android:ui_java",
   ]
 
@@ -73,6 +74,7 @@
     "//chrome/android:chrome_test_util_java",
     "//chrome/browser/touch_to_fill/android/internal:java",
     "//chrome/test/android:chrome_java_test_support",
+    "//components/browser_ui/android/bottomsheet:java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
     "//third_party/espresso:espresso_all_java",
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
index cdd5807f..74da9b5 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java
@@ -21,10 +21,10 @@
 
 import org.chromium.base.Callback;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
 import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 
 /**
  * This class is responsible for rendering the bottom sheet which displays the touch to fill
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java
index 1d15685..9d677faf 100644
--- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java
+++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java
@@ -38,11 +38,11 @@
 import org.chromium.base.test.util.ScalableTimeout;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.touch_to_fill.data.Credential;
-import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetContent;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TouchCommon;
 
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 40dfbfc..6d490f5 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3055,13 +3055,16 @@
         Running in Chrome
       </message>
       <message name="IDS_TWA_RUNNING_IN_CHROME_V2" desc="Updated message on a snackbar indicating that the current Activity may use Chrome data (the rest of the app may not be).">
-        History is shared with Chrome. Preferences are shared with <ph name="SITE_NAME">%1$s<ex>www.youtube.com</ex></ph>.
+        You'll see your <ph name="SITE_NAME">%1$s<ex>www.youtube.com</ex></ph> sign-in status, browsing data, and site data in Chrome.
+      </message>
+      <message name="IDS_GOT_IT" desc="Button for the user to accept a disclosure/message">
+        Got it
       </message>
       <message name="IDS_TWA_RUNNING_IN_CHROME_CHANNEL_NAME_INITIAL" desc="Notification channel name for the initial, urgent priority notification telling the user a TWA is running in Chrome.">
-        Running in Chrome (Initial)
+        Web apps
       </message>
       <message name="IDS_TWA_RUNNING_IN_CHROME_CHANNEL_NAME_SUBSEQUENT" desc="Notification channel name for follow up notifications telling the user a TWA is running in Chrome.">
-        Running in Chrome
+        Web apps (quiet)
       </message>
       <message name="IDS_TWA_CLEAR_DATA_DIALOG_TITLE" desc="Title of the clear data dialog showing after user uninstalls or clears data of an app hosting a Trusted Web Activity">
         <ph name="APP_NAME">%1$s<ex>YouTube</ex></ph> also has data in Chrome
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_GOT_IT.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_GOT_IT.png.sha1
new file mode 100644
index 0000000..1bbc160
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_GOT_IT.png.sha1
@@ -0,0 +1 @@
+9c78b07f1b793b62a17d066b22122894ad6c4206
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TWA_RUNNING_IN_CHROME_CHANNEL_NAME_INITIAL.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TWA_RUNNING_IN_CHROME_CHANNEL_NAME_INITIAL.png.sha1
new file mode 100644
index 0000000..b6ebf0d2
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TWA_RUNNING_IN_CHROME_CHANNEL_NAME_INITIAL.png.sha1
@@ -0,0 +1 @@
+dd654d769393beec4e6cb37a5049fa9bdd0b3f0c
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TWA_RUNNING_IN_CHROME_CHANNEL_NAME_SUBSEQUENT.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TWA_RUNNING_IN_CHROME_CHANNEL_NAME_SUBSEQUENT.png.sha1
new file mode 100644
index 0000000..b6ebf0d2
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TWA_RUNNING_IN_CHROME_CHANNEL_NAME_SUBSEQUENT.png.sha1
@@ -0,0 +1 @@
+dd654d769393beec4e6cb37a5049fa9bdd0b3f0c
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TWA_RUNNING_IN_CHROME_V2.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TWA_RUNNING_IN_CHROME_V2.png.sha1
new file mode 100644
index 0000000..babc2028b
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_TWA_RUNNING_IN_CHROME_V2.png.sha1
@@ -0,0 +1 @@
+7934e5b7a2b7441d96b73ae5b96c13a6a93bd7f1
\ No newline at end of file
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index c8cde92..84eb6db9 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/content_settings/tab_specific_content_settings_delegate.h"
 #include "chrome/browser/infobars/mock_infobar_service.h"
 #include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h"
 #include "chrome/browser/ssl/tls_deprecation_test_utils.h"
@@ -147,6 +148,10 @@
     ASSERT_TRUE(cert_);
 
     MockInfoBarService::CreateForWebContents(web_contents());
+    content_settings::TabSpecificContentSettings::CreateForWebContents(
+        web_contents(),
+        std::make_unique<chrome::TabSpecificContentSettingsDelegate>(
+            web_contents()));
 
     // Setup mock ui.
     ResetMockUI();
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller.h b/chrome/browser/ui/passwords/password_generation_popup_controller.h
index 2df2127..a888a80ff 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller.h
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller.h
@@ -24,9 +24,6 @@
   // Called by the view when the password was selected.
   virtual void SetSelected() = 0;
 
-  // Whether the view should display an additional right-aligned G icon.
-  virtual bool ShouldShowGoogleIcon() const = 0;
-
   // Accessors
   virtual GenerationUIState state() const = 0;
   virtual bool password_selected() const = 0;
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
index 26e45d0..b388a1c 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc
@@ -84,8 +84,7 @@
     const base::WeakPtr<password_manager::PasswordManagerDriver>& driver,
     PasswordGenerationPopupObserver* observer,
     content::WebContents* web_contents,
-    content::RenderFrameHost* frame,
-    password_manager::SyncState password_sync_state) {
+    content::RenderFrameHost* frame) {
   if (previous.get() && previous->element_bounds() == bounds &&
       previous->web_contents() == web_contents &&
       previous->driver_.get() == driver.get() &&
@@ -98,8 +97,7 @@
 
   PasswordGenerationPopupControllerImpl* controller =
       new PasswordGenerationPopupControllerImpl(bounds, ui_data, driver,
-                                                observer, web_contents, frame,
-                                                password_sync_state);
+                                                observer, web_contents, frame);
   return controller->GetWeakPtr();
 }
 
@@ -109,8 +107,7 @@
     const base::WeakPtr<password_manager::PasswordManagerDriver>& driver,
     PasswordGenerationPopupObserver* observer,
     content::WebContents* web_contents,
-    content::RenderFrameHost* frame,
-    password_manager::SyncState password_sync_state)
+    content::RenderFrameHost* frame)
     : content::WebContentsObserver(web_contents),
       view_(nullptr),
       form_data_(ui_data.form_data),
@@ -128,8 +125,7 @@
                          web_contents->GetNativeView()),
       password_selected_(false),
       state_(kOfferGeneration),
-      key_press_handler_manager_(new KeyPressRegistrator(frame)),
-      password_sync_state_(password_sync_state) {
+      key_press_handler_manager_(new KeyPressRegistrator(frame)) {
 #if !defined(OS_ANDROID)
   zoom::ZoomController* zoom_controller =
       zoom::ZoomController::FromWebContents(web_contents);
@@ -310,17 +306,6 @@
   PasswordSelected(true);
 }
 
-bool PasswordGenerationPopupControllerImpl::ShouldShowGoogleIcon() const {
-  // If the user has just opted in to passwords account storage, it's possible
-  // the state still evaluates to NOT_SYNCING. The popup is not shown in any
-  // other situation where the state is NOT_SYNCING, so adding it to the check
-  // here is fine.
-  return password_sync_state_ ==
-             password_manager::SyncState::
-                 ACCOUNT_PASSWORDS_ACTIVE_NORMAL_ENCRYPTION ||
-         password_sync_state_ == password_manager::SyncState::NOT_SYNCING;
-}
-
 gfx::NativeView PasswordGenerationPopupControllerImpl::container_view() const {
   return controller_common_.container_view;
 }
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
index 42bf114..cbe8aa9 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
@@ -20,7 +20,6 @@
 #include "components/autofill/core/common/password_form.h"
 #include "components/autofill/core/common/renderer_id.h"
 #include "components/autofill/core/common/signatures.h"
-#include "components/password_manager/core/browser/password_manager_client.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -79,8 +78,7 @@
       const base::WeakPtr<password_manager::PasswordManagerDriver>& driver,
       PasswordGenerationPopupObserver* observer,
       content::WebContents* web_contents,
-      content::RenderFrameHost* frame,
-      password_manager::SyncState password_sync_state);
+      content::RenderFrameHost* frame);
   ~PasswordGenerationPopupControllerImpl() override;
 
   // Create a PasswordGenerationPopupView if one doesn't already exist.
@@ -122,8 +120,7 @@
       const base::WeakPtr<password_manager::PasswordManagerDriver>& driver,
       PasswordGenerationPopupObserver* observer,
       content::WebContents* web_contents,
-      content::RenderFrameHost* frame,
-      password_manager::SyncState password_sync_state);
+      content::RenderFrameHost* frame);
 
   // Handle to the popup. May be NULL if popup isn't showing.
   PasswordGenerationPopupView* view_;
@@ -135,7 +132,6 @@
   void ViewDestroyed() override;
   void SelectionCleared() override;
   void SetSelected() override;
-  bool ShouldShowGoogleIcon() const override;
   void PasswordAccepted() override;
   gfx::NativeView container_view() const override;
   const gfx::RectF& element_bounds() const override;
@@ -193,9 +189,6 @@
 
   std::unique_ptr<KeyPressRegistrator> key_press_handler_manager_;
 
-  // The state of password sync when the popup was created.
-  password_manager::SyncState password_sync_state_;
-
   base::WeakPtrFactory<PasswordGenerationPopupControllerImpl> weak_ptr_factory_{
       this};
 
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc b/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
index 9868ed1..ef9cba9 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc
@@ -35,8 +35,6 @@
     : public ChromeRenderViewHostTestHarness {
  public:
   std::unique_ptr<MockPasswordManagerDriver> CreateDriver();
-  const password_manager::SyncState kPasswordSyncState =
-      password_manager::SyncState::SYNCING_NORMAL_ENCRYPTION;
 };
 
 std::unique_ptr<MockPasswordManagerDriver>
@@ -55,12 +53,12 @@
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller1 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           nullptr, ui_data.bounds, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller2 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           controller1, ui_data.bounds, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   EXPECT_EQ(controller1.get(), controller2.get());
 }
@@ -77,13 +75,13 @@
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller1 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           nullptr, rect, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   rect = gfx::RectF(200, 30);
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller2 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           controller1, rect, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   EXPECT_FALSE(controller1);
   EXPECT_TRUE(controller2);
@@ -100,13 +98,13 @@
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller1 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           nullptr, ui_data.bounds, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   web_contents = CreateTestWebContents();
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller2 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           controller1, ui_data.bounds, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   EXPECT_FALSE(controller1);
   EXPECT_TRUE(controller2);
@@ -123,13 +121,13 @@
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller1 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           nullptr, ui_data.bounds, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   driver = CreateDriver();
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller2 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           controller1, ui_data.bounds, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   EXPECT_FALSE(controller1);
   EXPECT_TRUE(controller2);
@@ -147,13 +145,13 @@
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller1 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           nullptr, ui_data.bounds, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   ui_data.generation_element_id = autofill::FieldRendererId(200);
   base::WeakPtr<PasswordGenerationPopupControllerImpl> controller2 =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           controller1, ui_data.bounds, ui_data, driver->AsWeakPtr(), nullptr,
-          web_contents.get(), main_rfh(), kPasswordSyncState);
+          web_contents.get(), main_rfh());
 
   EXPECT_FALSE(controller1);
   EXPECT_TRUE(controller2);
@@ -170,7 +168,7 @@
   base::WeakPtr<PasswordGenerationPopupController> controller =
       PasswordGenerationPopupControllerImpl::GetOrCreate(
           nullptr /*previous*/, ui_data.bounds, ui_data, driver->AsWeakPtr(),
-          nullptr, web_contents.get(), main_rfh(), kPasswordSyncState);
+          nullptr, web_contents.get(), main_rfh());
 
   // Destroying the controller in GeneratedPasswordAccepted() should not cause a
   // crash.
diff --git a/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc b/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc
index 821970a..7fbfcfe 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc
+++ b/chrome/browser/ui/passwords/password_generation_popup_view_browsertest.cc
@@ -41,8 +41,7 @@
                     ->AsWeakPtr(),
             nullptr /* PasswordGenerationPopupObserver*/,
             web_contents,
-            web_contents->GetMainFrame(),
-            password_manager::SyncState::SYNCING_NORMAL_ENCRYPTION) {}
+            web_contents->GetMainFrame()) {}
 
   ~TestPasswordGenerationPopupController() override {}
 
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index 9861c62..7c1b8e8 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -539,12 +539,6 @@
   // before PageInfo updates trigger child layouts.
   SetSize(GetPreferredSize());
 
-  // When |web_contents| is not from a Tab, |web_contents| does not have a
-  // |TabSpecificContentSettings| and need to create one; otherwise, noop.
-  content_settings::TabSpecificContentSettings::CreateForWebContents(
-      web_contents,
-      std::make_unique<chrome::TabSpecificContentSettingsDelegate>(
-          web_contents));
   presenter_ = std::make_unique<PageInfo>(
       std::make_unique<ChromePageInfoDelegate>(web_contents), web_contents,
       url);
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 0318ba5..c0ef631 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
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/content_settings/tab_specific_content_settings_delegate.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
@@ -201,6 +202,10 @@
     parent_window_->Init(std::move(parent_params));
 
     content::WebContents* web_contents = web_contents_helper_.web_contents();
+    content_settings::TabSpecificContentSettings::CreateForWebContents(
+        web_contents,
+        std::make_unique<chrome::TabSpecificContentSettingsDelegate>(
+            web_contents));
     api_ = std::make_unique<test::PageInfoBubbleViewTestApi>(
         parent_window_->GetNativeView(), web_contents_helper_.profile(),
         web_contents);
diff --git a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
index f5b4f36..2b869e2 100644
--- a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
+++ b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
@@ -9,9 +9,6 @@
 
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
-#include "build/branding_buildflags.h"
-#include "build/buildflag.h"
-#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/passwords/password_generation_popup_controller.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
@@ -20,13 +17,10 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
-#include "ui/gfx/favicon_size.h"
 #include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/paint_vector_icon.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
-#include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/grid_layout.h"
@@ -74,8 +68,7 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
 
   // Construct a ColumnSet with one view on the left and another on the right.
-  static void BuildColumnSet(views::GridLayout* layout,
-                             bool should_show_google_icon);
+  static void BuildColumnSet(views::GridLayout* layout);
 
   views::Label* suggestion_label_ = nullptr;
   views::Label* password_label_ = nullptr;
@@ -87,7 +80,7 @@
   controller_ = controller;
   views::GridLayout* layout =
       SetLayoutManager(std::make_unique<views::GridLayout>());
-  BuildColumnSet(layout, controller->ShouldShowGoogleIcon());
+  BuildColumnSet(layout);
   layout->StartRow(views::GridLayout::kFixedSize, 0);
 
   suggestion_label_ = layout->AddView(std::make_unique<views::Label>(
@@ -101,15 +94,6 @@
   password_label_ = layout->AddView(std::make_unique<views::Label>(
       controller_->password(), ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
       STYLE_SECONDARY_MONOSPACED));
-
-  if (controller->ShouldShowGoogleIcon()) {
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-    auto* icon_view = layout->AddView(std::make_unique<views::ImageView>());
-    icon_view->SetImage(gfx::CreateVectorIcon(
-        kGoogleGLogoIcon, gfx::kFaviconSize, gfx::kPlaceholderColor));
-    icon_view->set_can_process_events_within_subtree(false);
-#endif
-  }
 }
 
 void PasswordGenerationPopupViewViews::GeneratedPasswordBox::OnMouseEntered(
@@ -157,8 +141,7 @@
 
 // static
 void PasswordGenerationPopupViewViews::GeneratedPasswordBox::BuildColumnSet(
-    views::GridLayout* layout,
-    bool should_show_google_icon) {
+    views::GridLayout* layout) {
   views::ColumnSet* column_set = layout->AddColumnSet(0);
   column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
                         0 /* resize_percent */,
@@ -170,15 +153,6 @@
   column_set->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
                         1.0 /* resize_percent */,
                         views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
-  if (should_show_google_icon) {
-    // TODO(crbug.com/1060131): Set the final margins for the icon.
-    column_set->AddPaddingColumn(0 /* resize_percent */,
-                                 ChromeLayoutProvider::Get()->GetDistanceMetric(
-                                     views::DISTANCE_RELATED_LABEL_HORIZONTAL));
-    column_set->AddColumn(views::GridLayout::TRAILING,
-                          views::GridLayout::CENTER, 0 /* resize_percent */,
-                          views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
-  }
 }
 
 PasswordGenerationPopupViewViews::PasswordGenerationPopupViewViews(
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index ded2eac78..862e1b9a 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -389,6 +389,7 @@
         std::vector<new_tab_page::mojom::BackgroundCollectionPtr>());
     return;
   }
+  background_collections_request_start_time_ = base::TimeTicks::Now();
   background_collections_callback_ = std::move(callback);
   ntp_background_service_->FetchCollectionInfo();
 }
@@ -406,6 +407,7 @@
     return;
   }
   images_request_collection_id_ = collection_id;
+  background_images_request_start_time_ = base::TimeTicks::Now();
   background_images_callback_ = std::move(callback);
   ntp_background_service_->FetchCollectionImageInfo(collection_id);
 }
@@ -765,6 +767,21 @@
     return;
   }
 
+  base::TimeDelta duration =
+      base::TimeTicks::Now() - background_collections_request_start_time_;
+  UMA_HISTOGRAM_MEDIUM_TIMES(
+      "NewTabPage.BackgroundService.Collections.RequestLatency", duration);
+  // Any response where no collections are returned is considered a failure.
+  if (ntp_background_service_->collection_info().empty()) {
+    UMA_HISTOGRAM_MEDIUM_TIMES(
+        "NewTabPage.BackgroundService.Collections.RequestLatency.Failure",
+        duration);
+  } else {
+    UMA_HISTOGRAM_MEDIUM_TIMES(
+        "NewTabPage.BackgroundService.Collections.RequestLatency.Success",
+        duration);
+  }
+
   std::vector<new_tab_page::mojom::BackgroundCollectionPtr> collections;
   for (const auto& info : ntp_background_service_->collection_info()) {
     auto collection = new_tab_page::mojom::BackgroundCollection::New();
@@ -780,6 +797,20 @@
   if (!background_images_callback_) {
     return;
   }
+
+  base::TimeDelta duration =
+      base::TimeTicks::Now() - background_images_request_start_time_;
+  UMA_HISTOGRAM_MEDIUM_TIMES(
+      "NewTabPage.BackgroundService.Images.RequestLatency", duration);
+  // Any response where no images are returned is considered a failure.
+  if (ntp_background_service_->collection_images().empty()) {
+    UMA_HISTOGRAM_MEDIUM_TIMES(
+        "NewTabPage.BackgroundService.Images.RequestLatency.Failure", duration);
+  } else {
+    UMA_HISTOGRAM_MEDIUM_TIMES(
+        "NewTabPage.BackgroundService.Images.RequestLatency.Success", duration);
+  }
+
   std::vector<new_tab_page::mojom::BackgroundImagePtr> images;
   if (ntp_background_service_->collection_images().empty()) {
     std::move(background_images_callback_).Run(std::move(images));
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
index 7bb7428..7338498 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
@@ -173,8 +173,10 @@
   search_provider_logos::LogoService* logo_service_;
   GURL last_blacklisted_;
   GetBackgroundCollectionsCallback background_collections_callback_;
+  base::TimeTicks background_collections_request_start_time_;
   std::string images_request_collection_id_;
   GetBackgroundImagesCallback background_images_callback_;
+  base::TimeTicks background_images_request_start_time_;
   std::vector<GetOneGoogleBarPartsCallback> one_google_bar_parts_callbacks_;
   OneGoogleBarService* one_google_bar_service_;
   ScopedObserver<OneGoogleBarService, OneGoogleBarServiceObserver>
diff --git a/chrome/browser/ui/webui/settings/chromeos/main_section.cc b/chrome/browser/ui/webui/settings/chromeos/main_section.cc
index 4cf97e6..9afd588 100644
--- a/chrome/browser/ui/webui/settings/chromeos/main_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/main_section.cc
@@ -116,6 +116,9 @@
   html_source->AddBoolean("isOSSettings", true);
 
   html_source->AddBoolean("isGuest", features::IsGuestModeActive());
+  html_source->AddBoolean(
+      "isKioskModeActive",
+      user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp());
   html_source->AddBoolean("isSupervised", profile()->IsSupervised());
 
   // Add the System Web App resources for Settings.
diff --git a/chrome/browser/web_applications/components/install_manager.h b/chrome/browser/web_applications/components/install_manager.h
index 96019d5..ddcddec7 100644
--- a/chrome/browser/web_applications/components/install_manager.h
+++ b/chrome/browser/web_applications/components/install_manager.h
@@ -113,6 +113,9 @@
     // URL to be used as start_url if manifest is unavailable.
     GURL fallback_start_url;
 
+    // App name to be used if manifest is unavailable.
+    base::Optional<base::string16> fallback_app_name;
+
     bool locally_installed = true;
     // These OS shortcut fields can't be true if |locally_installed| is false.
     bool add_to_applications_menu = true;
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc
index 6a0b336..7d1e0651 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc
@@ -175,6 +175,12 @@
     const base::string16& app_name_extension) {
   base::FilePath web_app_path =
       GetOsIntegrationResourcesDirectoryForApp(profile_path, app_id, GURL());
+  if (!base::CreateDirectory(web_app_path)) {
+    DPLOG(ERROR) << "Unable to create web app dir";
+    RecordRegistration(RegistrationResult::kFailToCopyFromGenericLauncher);
+    return;
+  }
+
   base::string16 utf16_app_name = base::UTF8ToUTF16(app_name);
   base::FilePath icon_path =
       internals::GetIconFilePath(web_app_path, utf16_app_name);
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc
index 6eaf2a00..256088e 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration_win_unittest.cc
@@ -107,8 +107,7 @@
     std::string sanitized_app_name(app_name);
     sanitized_app_name.append(app_name_extension);
     base::FilePath expected_app_launcher_path =
-        CreateDataDirectoryAndGetLauncherPathForApp(profile, app_id(),
-                                                    sanitized_app_name);
+        GetLauncherPathForApp(profile, app_id(), sanitized_app_name);
     apps::FileHandlers file_handlers =
         GetFileHandlersWithFileExtensions({".txt", ".doc"});
 
@@ -137,18 +136,13 @@
     return app_specific_launcher_filepath;
   }
 
-  // Creates a "Web Applications" directory containing a subdirectory for
-  // |app_id| inside |profile|'s data directory, then returns the expected app-
-  // launcher path inside the subdirectory for |app_id|.
-  base::FilePath CreateDataDirectoryAndGetLauncherPathForApp(
-      Profile* profile,
-      const AppId app_id,
-      const std::string& sanitized_app_name) {
+  // Returns the expected app launcher path inside the subdirectory for
+  // |app_id|.
+  base::FilePath GetLauncherPathForApp(Profile* profile,
+                                       const AppId app_id,
+                                       const std::string& sanitized_app_name) {
     base::FilePath web_app_dir(GetOsIntegrationResourcesDirectoryForApp(
         profile->GetPath(), app_id, GURL()));
-    // Make sure web app dir exists. Normally installing an extension would
-    // handle this.
-    EXPECT_TRUE(base::CreateDirectory(web_app_dir));
     base::FilePath app_specific_launcher_filepath =
         GetAppSpecificLauncherFilePath(sanitized_app_name);
 
@@ -360,8 +354,7 @@
   // with '_'.
   std::string app_name("app*name");
   base::FilePath app_specific_launcher_path =
-      CreateDataDirectoryAndGetLauncherPathForApp(profile(), app_id(),
-                                                  "app_name");
+      GetLauncherPathForApp(profile(), app_id(), "app_name");
   apps::FileHandlers file_handlers =
       GetFileHandlersWithFileExtensions({".txt"});
 
@@ -381,7 +374,7 @@
   // "con" is a reserved filename on Windows, so it should have '_' prepended.
   std::string app_name("con");
   base::FilePath app_specific_launcher_path =
-      CreateDataDirectoryAndGetLauncherPathForApp(profile(), app_id(), "_con");
+      GetLauncherPathForApp(profile(), app_id(), "_con");
   apps::FileHandlers file_handlers =
       GetFileHandlersWithFileExtensions({".txt"});
 
@@ -400,11 +393,10 @@
 TEST_F(WebAppFileHandlerRegistrationWinTest, AppNameContainsDot) {
   // "some.app.name" should become "some_app_name" on Windows 7.
   std::string app_name("some.app.name");
-  base::FilePath app_specific_launcher_path =
-      CreateDataDirectoryAndGetLauncherPathForApp(
-          profile(), app_id(),
-          base::win::GetVersion() > base::win::Version::WIN7 ? "some.app.name"
-                                                             : "some_app_name");
+  base::FilePath app_specific_launcher_path = GetLauncherPathForApp(
+      profile(), app_id(),
+      base::win::GetVersion() > base::win::Version::WIN7 ? "some.app.name"
+                                                         : "some_app_name");
   apps::FileHandlers file_handlers =
       GetFileHandlersWithFileExtensions({".txt"});
 
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_win.cc b/chrome/browser/web_applications/components/web_app_shortcut_win.cc
index 5eb21a2..6a8a1c49 100644
--- a/chrome/browser/web_applications/components/web_app_shortcut_win.cc
+++ b/chrome/browser/web_applications/components/web_app_shortcut_win.cc
@@ -178,11 +178,6 @@
                             const std::vector<base::FilePath>& shortcut_paths,
                             web_app::ShortcutCreationReason creation_reason,
                             std::vector<base::FilePath>* out_filenames) {
-  // Ensure web_app_path exists.
-  if (!base::PathExists(web_app_path) && !base::CreateDirectory(web_app_path)) {
-    return false;
-  }
-
   // Generates file name to use with persisted ico and shortcut file.
   base::FilePath icon_file =
       web_app::internals::GetIconFilePath(web_app_path, shortcut_info.title);
@@ -342,9 +337,6 @@
   ui::win::SetRelaunchDetailsForWindow(command_line.GetCommandLineString(),
                                        shortcut_info.title, hwnd);
 
-  if (!base::PathExists(web_app_path) && !base::CreateDirectory(web_app_path))
-    return;
-
   ui::win::SetAppIconForWindow(icon_file, 0, hwnd);
   web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon, true);
 }
@@ -385,6 +377,9 @@
   if (!ShouldUpdateIcon(icon_file, image))
     return true;
 
+  if (!base::CreateDirectory(icon_file.DirName()))
+    return false;
+
   if (!SaveIconWithCheckSum(icon_file, image))
     return false;
 
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_win.h b/chrome/browser/web_applications/components/web_app_shortcut_win.h
index 7c4aee7..88b2e1f2 100644
--- a/chrome/browser/web_applications/components/web_app_shortcut_win.h
+++ b/chrome/browser/web_applications/components/web_app_shortcut_win.h
@@ -26,6 +26,7 @@
 // refreshed, ensuring the correct icon is displayed, but causing a flicker.
 // Refreshing the icon cache is not necessary on shortcut creation as the shell
 // will be notified when the shortcut is created.
+// Creates the parent dir of icon_file, if it doesn't exist.
 bool CheckAndSaveIcon(const base::FilePath& icon_file,
                       const gfx::ImageFamily& image,
                       bool refresh_shell_icon_cache);
diff --git a/chrome/browser/web_applications/web_app_install_manager.cc b/chrome/browser/web_applications/web_app_install_manager.cc
index 0be694a..6173e261 100644
--- a/chrome/browser/web_applications/web_app_install_manager.cc
+++ b/chrome/browser/web_applications/web_app_install_manager.cc
@@ -23,6 +23,33 @@
 
 namespace web_app {
 
+namespace {
+
+#if defined(OS_CHROMEOS)
+constexpr bool kLocallyInstallWebAppsOnSync = true;
+#else
+constexpr bool kLocallyInstallWebAppsOnSync = false;
+#endif
+
+InstallManager::InstallParams CreateSyncInstallParams(
+    const GURL& start_url,
+    const base::string16& app_name,
+    DisplayMode user_display_mode) {
+  InstallManager::InstallParams params;
+  params.user_display_mode = user_display_mode;
+  params.fallback_start_url = start_url;
+  params.fallback_app_name = app_name;
+  // If app is not locally installed then no OS integration like OS shortcuts.
+  params.locally_installed = kLocallyInstallWebAppsOnSync;
+  params.add_to_applications_menu = kLocallyInstallWebAppsOnSync;
+  params.add_to_desktop = kLocallyInstallWebAppsOnSync;
+  // Never add the app to the quick launch bar after sync.
+  params.add_to_quick_launch_bar = false;
+  return params;
+}
+
+}  // namespace
+
 WebAppInstallManager::WebAppInstallManager(Profile* profile)
     : InstallManager(profile),
       url_loader_(std::make_unique<WebAppUrlLoader>()) {
@@ -127,12 +154,6 @@
     return;
   }
 
-  bool is_locally_installed = false;
-#if defined(OS_CHROMEOS)
-  // On Chrome OS, sync always locally installs an app.
-  is_locally_installed = true;
-#endif
-
   // If bookmark_app_id is not installed enqueue full background installation
   // flow. This install may produce a web app or an extension-based bookmark
   // app, depending on the BMO flag.
@@ -143,25 +164,15 @@
       finalizer(), data_retriever_factory_.Run());
 
   task->ExpectAppId(bookmark_app_id);
+  task->SetInstallParams(CreateSyncInstallParams(
+      launch_url, web_application_info->title,
+      web_application_info->open_as_window ? DisplayMode::kStandalone
+                                           : DisplayMode::kBrowser));
 
-  InstallParams params;
-  params.user_display_mode = web_application_info->open_as_window
-                                 ? DisplayMode::kStandalone
-                                 : DisplayMode::kBrowser;
-  params.fallback_start_url = launch_url;
-  // If app is not locally installed then no OS integration like OS shortcuts.
-  params.locally_installed = is_locally_installed;
-  params.add_to_applications_menu = is_locally_installed;
-  params.add_to_desktop = is_locally_installed;
-
-  // Never add the app to the quick launch bar after sync.
-  params.add_to_quick_launch_bar = false;
-  task->SetInstallParams(params);
-
-  OnceInstallCallback task_completed_callback = base::BindOnce(
-      &WebAppInstallManager::OnBookmarkAppInstalledAfterSync,
-      base::Unretained(this), bookmark_app_id, std::move(web_application_info),
-      is_locally_installed, std::move(callback));
+  OnceInstallCallback task_completed_callback =
+      base::BindOnce(&WebAppInstallManager::OnBookmarkAppInstalledAfterSync,
+                     base::Unretained(this), bookmark_app_id,
+                     std::move(web_application_info), std::move(callback));
 
   base::OnceClosure start_task = base::BindOnce(
       &WebAppInstallTask::LoadAndInstallWebAppFromManifestWithFallback,
@@ -211,6 +222,9 @@
         finalizer(), data_retriever_factory_.Run());
 
     task->ExpectAppId(web_app->app_id());
+    task->SetInstallParams(CreateSyncInstallParams(
+        web_app->launch_url(), base::UTF8ToUTF16(web_app->sync_data().name),
+        web_app->user_display_mode()));
 
     OnceInstallCallback sync_install_callback =
         base::BindOnce(&WebAppInstallManager::OnWebAppInstalledAfterSync,
@@ -251,7 +265,6 @@
 void WebAppInstallManager::OnBookmarkAppInstalledAfterSync(
     const AppId& bookmark_app_id,
     std::unique_ptr<WebApplicationInfo> web_application_info,
-    bool is_locally_installed,
     OnceInstallCallback callback,
     const AppId& web_app_id,
     InstallResultCode code) {
@@ -264,7 +277,7 @@
     // Install failed. Do the fallback install from info.
     InstallFinalizer::FinalizeOptions options;
     options.install_source = WebappInstallSource::SYNC;
-    options.locally_installed = is_locally_installed;
+    options.locally_installed = kLocallyInstallWebAppsOnSync;
 
     FilterAndResizeIconsGenerateMissing(web_application_info.get(),
                                         /*icons_map=*/nullptr);
diff --git a/chrome/browser/web_applications/web_app_install_manager.h b/chrome/browser/web_applications/web_app_install_manager.h
index facab4f..ce2b076 100644
--- a/chrome/browser/web_applications/web_app_install_manager.h
+++ b/chrome/browser/web_applications/web_app_install_manager.h
@@ -88,7 +88,6 @@
   void OnBookmarkAppInstalledAfterSync(
       const AppId& bookmark_app_id,
       std::unique_ptr<WebApplicationInfo> web_application_info,
-      bool is_locally_installed,
       OnceInstallCallback callback,
       const AppId& web_app_id,
       InstallResultCode code);
diff --git a/chrome/browser/web_applications/web_app_install_task.cc b/chrome/browser/web_applications/web_app_install_task.cc
index c863462..0415c54 100644
--- a/chrome/browser/web_applications/web_app_install_task.cc
+++ b/chrome/browser/web_applications/web_app_install_task.cc
@@ -439,6 +439,9 @@
     DCHECK(install_params_->fallback_start_url.is_valid());
     web_app_info->app_url = install_params_->fallback_start_url;
 
+    if (install_params_->fallback_app_name.has_value())
+      web_app_info->title = install_params_->fallback_app_name.value();
+
     // If `additional_search_terms` was a manifest property, it would be
     // sanitized while parsing the manifest. Since it's not, we sanitize it
     // here.
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 3fedc6ce..9052c604 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -367,7 +367,6 @@
 const char kCloudPrintersSubPage[] = "cloudPrinters";
 const char kContentSettingsSubPage[] = "content";
 const char kCookieSettingsSubPage[] = "cookies";
-const char kDeprecatedExtensionsSubPage[] = "extensions";
 const char kDownloadsSubPage[] = "downloads";
 const char kHandlerSettingsSubPage[] = "handlers";
 const char kImportDataSubPage[] = "importData";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 53a5168..f866a8d 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -328,7 +328,6 @@
 extern const char kContentSettingsSubPage[];
 extern const char kCookieSettingsSubPage[];
 extern const char kCreateProfileSubPage[];
-extern const char kDeprecatedExtensionsSubPage[];
 extern const char kDownloadsSubPage[];
 extern const char kHandlerSettingsSubPage[];
 extern const char kImportDataSubPage[];
diff --git a/chrome/installer/util/install_service_work_item.h b/chrome/installer/util/install_service_work_item.h
index 7a2dbb18..5a2fe889 100644
--- a/chrome/installer/util/install_service_work_item.h
+++ b/chrome/installer/util/install_service_work_item.h
@@ -41,8 +41,9 @@
   // the SCM. For example,
   // "C:\Program Files (x86)\Google\Chrome\ElevationService.exe" /svc
   //
-  // |registry_path| is the path in HKEY_LOCAL_MACHINE under which
-  // the service persists information, for instance if the service has to
+  // NOTE: |registry_path| is mapped to the 32-bit view of the registry for
+  // legacy reasons. |registry_path| is the path in HKEY_LOCAL_MACHINE under
+  // which the service persists information, for instance if the service has to
   // persist a versioned service name. An example |registry_path| is
   // "Software\ProductFoo".
   //
diff --git a/chrome/installer/util/install_service_work_item_impl.h b/chrome/installer/util/install_service_work_item_impl.h
index 46b432f..d4125191 100644
--- a/chrome/installer/util/install_service_work_item_impl.h
+++ b/chrome/installer/util/install_service_work_item_impl.h
@@ -166,7 +166,8 @@
   const base::CommandLine service_cmd_line_;
 
   // The path under HKEY_LOCAL_MACHINE where the service persists information,
-  // such as a versioned service name.
+  // such as a versioned service name. For legacy reasons, this path is mapped
+  // to the 32-bit view of the registry.
   const base::string16 registry_path_;
 
   // If COM CLSID/AppId registration is required, |clsid| would contain a valid
diff --git a/chrome/installer/util/install_service_work_item_unittest.cc b/chrome/installer/util/install_service_work_item_unittest.cc
index ecd6268..06c1670 100644
--- a/chrome/installer/util/install_service_work_item_unittest.cc
+++ b/chrome/installer/util/install_service_work_item_unittest.cc
@@ -76,8 +76,10 @@
   }
 
   void TearDown() override {
-    base::win::RegKey key(HKEY_LOCAL_MACHINE, L"", KEY_READ | KEY_WOW64_32KEY);
-    key.DeleteKey(kProductRegPath);
+    base::win::RegKey(HKEY_LOCAL_MACHINE, L"", KEY_READ | KEY_WOW64_32KEY)
+        .DeleteKey(kProductRegPath);
+
+    base::win::RegKey key(HKEY_LOCAL_MACHINE, L"", KEY_READ);
     key.DeleteKey(kClsidRegPath);
     key.DeleteKey(kAppidRegPath);
     key.DeleteKey(IID_REGISTRY_PATH);
diff --git a/chrome/renderer/extensions/extension_localization_peer.cc b/chrome/renderer/extensions/extension_localization_peer.cc
index f1cc030..2c0ade9 100644
--- a/chrome/renderer/extensions/extension_localization_peer.cc
+++ b/chrome/renderer/extensions/extension_localization_peer.cc
@@ -61,7 +61,8 @@
 
 bool ExtensionLocalizationPeer::OnReceivedRedirect(
     const net::RedirectInfo& redirect_info,
-    network::mojom::URLResponseHeadPtr head) {
+    network::mojom::URLResponseHeadPtr head,
+    std::vector<std::string>*) {
   NOTREACHED();
   return false;
 }
diff --git a/chrome/renderer/extensions/extension_localization_peer.h b/chrome/renderer/extensions/extension_localization_peer.h
index 757c3ba..c0a05b0 100644
--- a/chrome/renderer/extensions/extension_localization_peer.h
+++ b/chrome/renderer/extensions/extension_localization_peer.h
@@ -56,7 +56,8 @@
   // content::RequestPeer methods.
   void OnUploadProgress(uint64_t position, uint64_t size) override;
   bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
-                          network::mojom::URLResponseHeadPtr head) override;
+                          network::mojom::URLResponseHeadPtr head,
+                          std::vector<std::string>*) override;
   void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override;
   void OnStartLoadingResponseBody(
       mojo::ScopedDataPipeConsumerHandle body) override;
diff --git a/chrome/renderer/extensions/extension_localization_peer_unittest.cc b/chrome/renderer/extensions/extension_localization_peer_unittest.cc
index 4e22060e..29a4f10e 100644
--- a/chrome/renderer/extensions/extension_localization_peer_unittest.cc
+++ b/chrome/renderer/extensions/extension_localization_peer_unittest.cc
@@ -68,9 +68,10 @@
   ~MockRequestPeer() override {}
 
   MOCK_METHOD2(OnUploadProgress, void(uint64_t position, uint64_t size));
-  MOCK_METHOD2(OnReceivedRedirect,
+  MOCK_METHOD3(OnReceivedRedirect,
                bool(const net::RedirectInfo& redirect_info,
-                    network::mojom::URLResponseHeadPtr head));
+                    network::mojom::URLResponseHeadPtr head,
+                    std::vector<std::string>*));
   MOCK_METHOD1(OnReceivedResponse,
                void(network::mojom::URLResponseHeadPtr head));
   void OnStartLoadingResponseBody(
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b4659b8..eecf106 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6445,6 +6445,7 @@
     data = [
       "//chrome/test/data/password/",
       "//chrome/test/data/sync/",
+      "//chrome/test/data/web_apps/",
       "//net/tools/testserver/",
       "//third_party/pywebsocket3/src/mod_pywebsocket/",
       "//third_party/tlslite/",
diff --git a/chrome/test/android/DEPS b/chrome/test/android/DEPS
index 7ed1d966..05be35d5 100644
--- a/chrome/test/android/DEPS
+++ b/chrome/test/android/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/browser_ui/android/bottomsheet",
   # This test code needs to depend on sync related classes and test tools.
   "+components/sync/android/java/src/org/chromium/components/sync/signin",
   "+components/sync/test/android/javatests/src/org/chromium/components/sync/test/util",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetTestRule.java b/chrome/test/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetTestRule.java
index 33548f0..e5809e3 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetTestRule.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetTestRule.java
@@ -14,6 +14,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
 /**
diff --git a/chrome/test/data/web_apps/bad_title_only.html b/chrome/test/data/web_apps/bad_title_only.html
new file mode 100644
index 0000000..c418467
--- /dev/null
+++ b/chrome/test/data/web_apps/bad_title_only.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Incorrect App Name</title>
+</head>
+<body>
+  <h1>Incorrect App Name</h1>
+</body>
+</html>
diff --git a/chrome/test/data/web_apps/different_start_url.html b/chrome/test/data/web_apps/different_start_url.html
new file mode 100644
index 0000000..4e979c1
--- /dev/null
+++ b/chrome/test/data/web_apps/different_start_url.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <link rel="manifest" href="basic.json">
+  <link rel="icon" href="basic-48.png">
+</head>
+<body>
+  <h1>Different start url</h1>
+  start_url points to basic.html instead of this page.
+  <script>
+    navigator.serviceWorker.register('/web_apps/service_worker.js');
+  </script>
+</body>
+</html>
diff --git a/chrome/test/data/webui/settings/chromeos/manage_accessibility_page_tests.js b/chrome/test/data/webui/settings/chromeos/manage_accessibility_page_tests.js
index b278a3b..ba39b2d 100644
--- a/chrome/test/data/webui/settings/chromeos/manage_accessibility_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/manage_accessibility_page_tests.js
@@ -2,6 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/**
+ * Checks whether a given element is visible to the user.
+ * @param {!Element} element
+ * @returns {boolean}
+ */
+function isVisible(element) {
+  return !!(element && element.getBoundingClientRect().width > 0);
+}
+
 suite('ManageAccessibilityPageTests', function() {
   let page = null;
   let browserProxy = null;
@@ -34,21 +43,27 @@
     }
   }
 
+  function initPage() {
+    page = document.createElement('settings-manage-a11y-page');
+    document.body.appendChild(page);
+  }
+
   setup(function() {
     browserProxy = new TestDevicePageBrowserProxy();
     settings.DevicePageBrowserProxyImpl.instance_ = browserProxy;
 
     PolymerTest.clearBody();
 
-    page = document.createElement('settings-manage-a11y-page');
-    document.body.appendChild(page);
   });
 
   teardown(function() {
-    page.remove();
+    if (page) {
+      page.remove();
+    }
   });
 
   test('Pointers row only visible if mouse/touchpad present', function() {
+    initPage();
     const row = page.$$('#pointerSubpageButton');
     assertFalse(row.hidden);
 
@@ -68,4 +83,32 @@
     browserProxy.hasTouchpad = true;
     assertFalse(row.hidden);
   });
+
+  test('some parts are hidden in kiosk mode', function() {
+    loadTimeData.overrideValues({
+      isKioskModeActive: true,
+    });
+    initPage();
+    // Add mouse and touchpad to show some hidden settings.
+    browserProxy.hasMouse = true;
+    browserProxy.hasTouchpad = true;
+    Polymer.dom.flush();
+
+    // Accessibility learn more link should be hidden.
+    assertFalse(isVisible(page.$$('setings-localized-link')));
+
+    const allowed_subpages = [
+      'chromeVoxSubpageButton', 'selectToSpeakSubpageButton', 'ttsSubpageButton'
+    ];
+
+    const subpages = page.root.querySelectorAll('cr-link-row');
+    subpages.forEach(function(subpage) {
+      if (isVisible(subpage)) {
+        assertTrue(allowed_subpages.includes(subpage.id));
+      }
+    });
+
+    // Additional features link is not visible.
+    assertFalse(isVisible(page.$.additionalFeaturesLink));
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
index b32d2db..39f37ac 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui_browsertest.js
@@ -14,6 +14,15 @@
 // https://crbug.com/1003483
 GEN('#if defined(NDEBUG)');
 
+/**
+ * Checks whether a given element is visible to the user.
+ * @param {!Element} element
+ * @returns {boolean}
+ */
+function isVisible(element) {
+  return !!(element && element.getBoundingClientRect().width > 0);
+}
+
 // Test fixture for the top-level OS settings UI.
 // eslint-disable-next-line no-var
 var OSSettingsUIBrowserTest = class extends PolymerTest {
@@ -39,13 +48,14 @@
     suiteSetup(() => {
       testing.Test.disableAnimationsAndTransitions();
       ui = assert(document.querySelector('os-settings-ui'));
-      ui.$.drawerTemplate.restamp = true;
+      Polymer.dom.flush();
+      ui.$$('#drawerTemplate').restamp = true;
     });
 
     setup(() => {
       userActionRecorder = new settings.FakeUserActionRecorder();
       settings.setUserActionRecorderForTesting(userActionRecorder);
-      ui.$.drawerTemplate.if = false;
+      ui.$$('#drawerTemplate').if = false;
       Polymer.dom.flush();
     });
 
@@ -70,16 +80,16 @@
 
     test('showing menu in toolbar is dependent on narrow mode', () => {
       const toolbar = assert(ui.$$('os-toolbar'));
-      toolbar.narrow = true;
+      ui.isNarrow = true;
       assertTrue(toolbar.showMenu);
 
-      toolbar.narrow = false;
+      ui.isNarrow = false;
       assertFalse(toolbar.showMenu);
     });
 
     test('app drawer', async () => {
       assertEquals(null, ui.$$('cr-drawer os-settings-menu'));
-      const drawer = ui.$.drawer;
+      const drawer = ui.$$('#drawer');
       assertFalse(drawer.open);
 
       drawer.openDrawer();
@@ -98,16 +108,16 @@
     });
 
     test('app drawer closes when exiting narrow mode', async () => {
-      const drawer = ui.$.drawer;
+      const drawer = ui.$$('#drawer');
       const toolbar = ui.$$('os-toolbar');
 
       // Mimic narrow mode and open the drawer.
-      toolbar.narrow = true;
+      ui.isNarrow = true;
       drawer.openDrawer();
       Polymer.dom.flush();
       await test_util.eventToPromise('cr-drawer-opened', drawer);
 
-      toolbar.narrow = false;
+      ui.isNarrow = false;
       Polymer.dom.flush();
       await test_util.eventToPromise('close', drawer);
       assertFalse(drawer.open);
@@ -134,7 +144,7 @@
       assertTrue(floatingMenu.advancedOpened);
       assertTrue(main.advancedToggleExpanded);
 
-      ui.$.drawerTemplate.if = true;
+      ui.$$('#drawerTemplate').if = true;
       Polymer.dom.flush();
 
       const drawerMenu = ui.$$('cr-drawer os-settings-menu');
@@ -282,6 +292,22 @@
       searchField.setValue('GOOGLE');
       assertEquals(userActionRecorder.searchCount, 1);
     });
+
+    test('toolbar and nav menu are hidden in kiosk mode', function() {
+      loadTimeData.overrideValues({
+        isKioskModeActive: true,
+      });
+
+      settings.Router.getInstance().resetRouteForTesting();
+      ui = document.createElement('os-settings-ui');
+      document.body.appendChild(ui);
+      Polymer.dom.flush();
+
+      // Toolbar should be hidden.
+      assertFalse(isVisible(ui.$$('os-toolbar')));
+      // All navigation settings menus should be hidden.
+      assertFalse(isVisible(ui.$$('os-settings-menu')));
+    });
   });
 
   mocha.run();
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index 5b82cd2..e45eee4 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -1273,7 +1273,17 @@
 // is present in the DNS cache with the NetworkIsolationKey associated with the
 // foreground WebContents - this is needed so as not to leak what hostnames were
 // looked up across tabs with different first party origins.
-void CheckTestHostNameUsedWithCorrectNetworkIsolationKey(Browser* browser) {
+//
+// The lookup checks for an IPv4-only cache entry, as UNSPECIFIED lookups are
+// sometimes replaced with IPv4-only lookups, based on the result of that probe
+// of whether the local network has IPv6 connectivity. For unknown reasons, the
+// results of this probe can change in the middle of a test on the bots, which
+// changes the used cache key. To avoid that issue, just use A lookups.
+//
+// See https://crbug.com/1042354 for some discussion of the issue.
+void CheckTestHostNameUsedWithCorrectNetworkIsolationKey(
+    Browser* browser,
+    bool include_canonical_name) {
   network::mojom::NetworkContext* network_context =
       content::BrowserContext::GetDefaultStoragePartition(browser->profile())
           ->GetNetworkContext();
@@ -1284,8 +1294,14 @@
       network::mojom::ResolveHostParameters::New();
   // Cache only lookup.
   params->source = net::HostResolverSource::LOCAL_ONLY;
+  // PPAPI tests are somewhat slow, and the DNS cache timeout is relatively
+  // short, so allow stale results to avoid flaky failures.
+  params->cache_usage =
+      network::mojom::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
+  // A-only lookup.
+  params->dns_query_type = net::DnsQueryType::A;
   // Match the parameters used by the test.
-  params->include_canonical_name = true;
+  params->include_canonical_name = include_canonical_name;
   net::NetworkIsolationKey network_isolation_key =
       browser->tab_strip_model()
           ->GetActiveWebContents()
@@ -1304,6 +1320,10 @@
   params = network::mojom::ResolveHostParameters::New();
   // Cache only lookup.
   params->source = net::HostResolverSource::LOCAL_ONLY;
+  // PPAPI tests are somewhat slow, and the DNS cache timeout is relatively
+  // short, so allow stale results to avoid flaky failures.
+  params->cache_usage =
+      network::mojom::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
   // Match the parameters used by the test.
   params->include_canonical_name = true;
   network::DnsLookupResult result2 =
@@ -1317,7 +1337,8 @@
 #define RUN_HOST_RESOLVER_SUBTESTS                                             \
   RunTestViaHTTP(LIST_TEST(HostResolver_Empty) LIST_TEST(HostResolver_Resolve) \
                      LIST_TEST(HostResolver_ResolveIPv4));                     \
-  CheckTestHostNameUsedWithCorrectNetworkIsolationKey(browser())
+  CheckTestHostNameUsedWithCorrectNetworkIsolationKey(                         \
+      browser(), true /* include_canonical_name */)
 
 IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, HostResolverCrash_Basic) {
   if (content::IsInProcessNetworkService())
@@ -2186,7 +2207,33 @@
 
 TEST_PPAPI_NACL(MouseCursor)
 
-TEST_PPAPI_NACL(NetworkProxy)
+// Proxy configuration test. The PPAPI code used by these tests is in
+// ppapi/tests/test_network_proxy.cc.
+
+IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, NetworkProxy) {
+  RunTestViaHTTP(STRIP_PREFIXES(NetworkProxy));
+  CheckTestHostNameUsedWithCorrectNetworkIsolationKey(
+      browser(), false /* include_canonical_name */);
+}
+
+IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, NetworkProxy) {
+  RunTestViaHTTP(STRIP_PREFIXES(NetworkProxy));
+  CheckTestHostNameUsedWithCorrectNetworkIsolationKey(
+      browser(), false /* include_canonical_name */);
+}
+
+IN_PROC_BROWSER_TEST_F(PPAPINaClPNaClTest, MAYBE_PPAPI_NACL(NetworkProxy)) {
+  RunTestViaHTTP(STRIP_PREFIXES(NetworkProxy));
+  CheckTestHostNameUsedWithCorrectNetworkIsolationKey(
+      browser(), false /* include_canonical_name */);
+}
+
+IN_PROC_BROWSER_TEST_F(PPAPINaClPNaClNonSfiTest,
+                       MAYBE_PNACL_NONSFI(NetworkProxy)) {
+  RunTestViaHTTP(STRIP_PREFIXES(NetworkProxy));
+  CheckTestHostNameUsedWithCorrectNetworkIsolationKey(
+      browser(), false /* include_canonical_name */);
+}
 
 // TODO(crbug.com/619765): get working on CrOS build.
 #if defined(OS_CHROMEOS)
diff --git a/chrome/test/ppapi/ppapi_test.cc b/chrome/test/ppapi/ppapi_test.cc
index fdf7dcf..cae6891 100644
--- a/chrome/test/ppapi/ppapi_test.cc
+++ b/chrome/test/ppapi/ppapi_test.cc
@@ -160,9 +160,24 @@
 
   // Smooth scrolling confuses the scrollbar test.
   command_line->AppendSwitch(switches::kDisableSmoothScrolling);
+
+  // For a particular host name, resolve another specific host name (which
+  // should then be added to the DNS cache), and then return a particular proxy.
+  // Otherwise, return DIRECT.
+  command_line->AppendSwitchASCII(switches::kProxyPacUrl,
+                                  "data:,"
+                                  "function FindProxyForURL(url, host) {"
+                                  "  if (host == 'use.proxy.test') {"
+                                  "    dnsResolve('host_resolver.test');"
+                                  "    return 'PROXY proxy.test';"
+                                  "  }"
+                                  "  return 'DIRECT'"
+                                  "}");
 }
 
 void PPAPITestBase::SetUpOnMainThread() {
+  host_resolver()->AddRule("host_resolver.test",
+                           embedded_test_server()->host_port_pair().host());
   host_resolver()->AddRuleWithFlags(
       "host_resolver.test", embedded_test_server()->host_port_pair().host(),
       net::HOST_RESOLVER_CANONNAME);
diff --git a/chromeos/constants/chromeos_pref_names.cc b/chromeos/constants/chromeos_pref_names.cc
index c41c642b8c..3eb3f71a 100644
--- a/chromeos/constants/chromeos_pref_names.cc
+++ b/chromeos/constants/chromeos_pref_names.cc
@@ -42,6 +42,12 @@
 // consent in EDU account addition flow.
 const char kEduCoexistenceId[] = "account_manager.edu_coexistence_id";
 
+// A string pref storing a parental consent text version that requires
+// invalidation of the secondary accounts added with the previous consent
+// versions.
+const char kEduCoexistenceSecondaryAccountsInvalidationVersion[] =
+    "account_manager.edu_coexistence_secondary_accounts_invalidation_version";
+
 // A dictionary of info for Quirks Client/Server interaction, mostly last server
 // request times, keyed to display product_id's.
 const char kQuirksClientLastServerCheck[] = "quirks_client.last_server_check";
diff --git a/chromeos/constants/chromeos_pref_names.h b/chromeos/constants/chromeos_pref_names.h
index 712238e..feb4350 100644
--- a/chromeos/constants/chromeos_pref_names.h
+++ b/chromeos/constants/chromeos_pref_names.h
@@ -19,6 +19,8 @@
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kAudioDevicesState[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kEduCoexistenceId[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const char kEduCoexistenceSecondaryAccountsInvalidationVersion[];
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kQuirksClientLastServerCheck[];
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const char kDeviceWiFiFastTransitionEnabled[];
diff --git a/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc b/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
index 8b97515..840bdb7 100644
--- a/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
+++ b/chromeos/services/cros_healthd/public/cpp/service_connection_unittest.cc
@@ -104,17 +104,42 @@
       mojom::CachedVpdInfo::New("fake_sku_number" /* sku_number */));
 }
 
+std::vector<mojom::CpuCStateInfoPtr> MakeCStateInfo() {
+  std::vector<mojom::CpuCStateInfoPtr> c_states;
+  c_states.push_back(mojom::CpuCStateInfo::New(
+      "c_state_0" /* name */, 679 /* time_in_state_since_last_boot_us */));
+  c_states.push_back(mojom::CpuCStateInfo::New(
+      "c_state_1" /* name */, 12354 /* time_in_state_since_last_boot_us */));
+  return c_states;
+}
+
+std::vector<mojom::LogicalCpuInfoPtr> MakeLogicalCpus() {
+  std::vector<mojom::LogicalCpuInfoPtr> logical_cpus;
+  logical_cpus.push_back(mojom::LogicalCpuInfo::New(
+      11 /* max_clock_speed_khz */, 14 /* scaling_max_frequency_khz */,
+      99 /* scaling_current_frequency_khz */, 889 /* idle_time_user_hz */,
+      MakeCStateInfo()));
+  logical_cpus.push_back(mojom::LogicalCpuInfo::New(
+      987 /* max_clock_speed_khz */, 543 /* scaling_max_frequency_khz */,
+      2349 /* scaling_current_frequency_khz */, 688 /* idle_time_user_hz */,
+      MakeCStateInfo()));
+  return logical_cpus;
+}
+
+std::vector<mojom::PhysicalCpuInfoPtr> MakePhysicalCpus() {
+  std::vector<mojom::PhysicalCpuInfoPtr> physical_cpus;
+  physical_cpus.push_back(mojom::PhysicalCpuInfo::New(
+      "Dank CPU 1" /* model_name */, MakeLogicalCpus()));
+  physical_cpus.push_back(mojom::PhysicalCpuInfo::New(
+      "Dank CPU 2" /* model_name */, MakeLogicalCpus()));
+  return physical_cpus;
+}
+
 mojom::CpuResultPtr MakeCpuResult() {
-  std::vector<mojom::CpuInfoPtr> cpu_info;
-  cpu_info.push_back(mojom::CpuInfo::New(
-      "Dank CPU 1" /* model_name */,
+  return mojom::CpuResult::NewCpuInfo(mojom::CpuInfo::New(
+      10 /* num_total_threads */,
       mojom::CpuArchitectureEnum::kX86_64 /* architecture */,
-      3400000 /* max_clock_speed_khz */));
-  cpu_info.push_back(mojom::CpuInfo::New(
-      "Dank CPU 2" /* model_name */,
-      mojom::CpuArchitectureEnum::kX86_64 /* architecture */,
-      2600000 /* max_clock_speed_khz */));
-  return mojom::CpuResult::NewCpuInfo(std::move(cpu_info));
+      MakePhysicalCpus()));
 }
 
 mojom::TimezoneResultPtr MakeTimezoneResult() {
diff --git a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
index 804d4dfc..0375942 100644
--- a/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
+++ b/chromeos/services/cros_healthd/public/mojom/cros_healthd_probe.mojom
@@ -150,19 +150,51 @@
 // retrieving the information.
 union CpuResult {
   // Valid CpuInfo.
-  array<CpuInfo> cpu_info;
+  CpuInfo cpu_info;
   // The error that occurred attempting to retrieve the CpuInfo.
   ProbeError error;
 };
 
-// Information related to a device's CPU.
+// Information about the device's CPUs.
 struct CpuInfo {
+  // Number of total threads available.
+  uint32 num_total_threads;
+  // The CPU architecture - it's assumed all of a device's CPUs share an
+  // architecture.
+  CpuArchitectureEnum architecture;
+  // Information about the device's physical CPUs.
+  array<PhysicalCpuInfo> physical_cpus;
+};
+
+// Information related to a particular physical CPU.
+struct PhysicalCpuInfo {
   // The CPU model name.
   string model_name;
-  // The CPU architecture.
-  CpuArchitectureEnum architecture;
+  // Logical CPUs corresponding to this physical CPU.
+  array<LogicalCpuInfo> logical_cpus;
+};
+
+// Information related to a particular logical CPU.
+struct LogicalCpuInfo {
   // The max CPU clock speed in kHz.
   uint32 max_clock_speed_khz;
+  // Maximum frequency the CPU is allowed to run at, by policy.
+  uint32 scaling_max_frequency_khz;
+  // Current frequency the CPU is running at.
+  uint32 scaling_current_frequency_khz;
+  // Idle time since last boot. USER_HZ can be converted to seconds with the
+  // conversion factor given by sysconf(_SC_CLK_TCK).
+  uint32 idle_time_user_hz;
+  // Information about the logical CPU's time in various C-states.
+  array<CpuCStateInfo> c_states;
+};
+
+// Information about a CPU's C-states.
+struct CpuCStateInfo {
+  // Name of the state.
+  string name;
+  // Time spent in the state since the last reboot, in microseconds.
+  uint64 time_in_state_since_last_boot_us;
 };
 
 // Timezone probe result. Can either be populated with the TimezoneInfo or an
diff --git a/components/autofill/core/common/autofill_regex_constants.cc b/components/autofill/core/common/autofill_regex_constants.cc
index 0a370c7..d3b9273 100644
--- a/components/autofill/core/common/autofill_regex_constants.cc
+++ b/components/autofill/core/common/autofill_regex_constants.cc
@@ -42,6 +42,7 @@
     "|morada|endereço"                        // pt-BR, pt-PT
     "|Адрес"                                  // ru
     "|地址"                                   // zh-CN
+    "|(\\b|_)adres(\\b|_)"                      // tr
     "|^주소.?$|주소.?1";                      // ko-KR
 const char kAddressLine1LabelRe[] =
     "(^\\W*address)"
@@ -53,6 +54,7 @@
     "|indirizzo"  // it-IT
     "|住所"       // ja-JP
     "|地址"       // zh-CN
+    "|(\\b|_)adres(\\b|_)"  // tr
     "|주소";      // ko-KR
 const char kAddressLine2Re[] =
     "address[_-]?line(2|two)|address2|addr2|street|suite|unit"
@@ -84,6 +86,7 @@
     "|(?<!(入|出))国"      // ja-JP
     "|国家"                // zh-CN
     "|국가|나라"           // ko-KR
+    "|(\\b|_)ulce(\\b|_)"     // tr
     "|کشور";               // fa
 const char kCountryLocationRe[] = "location";
 const char kZipCodeRe[] =
@@ -100,6 +103,7 @@
     "|പിന്‍കോഡ്"  // ml
     "|邮政编码|邮编"                // zh-CN
     "|郵遞區號"                     // zh-TW
+    "|(\\b|_)posta kodu(\\b|_)"       // tr
     "|우편.?번호";                  // ko-KR
 const char kZip4Re[] =
     "zip|^-$|post2"
@@ -120,6 +124,7 @@
     "|शहर"                                   // hi for city
     "|ग्राम|गाँव"                              // hi for village
     "|നഗരം|ഗ്രാമം"                            // ml for town|village
+    "|((\\b|_)(il|ilimiz|sehir|kent)(\\b|_))"  // tr
     "|^시[^도·・]|시[·・]?군[·・]?구";       // ko-KR
 const char kStateRe[] =
     "(?<!(united|hist|history).?)state|county|region|province"
@@ -132,6 +137,7 @@
     "|സംസ്ഥാനം"              // ml
     "|استان"                // fa
     "|राज्य"                 // hi
+    "|(\\b|_)ilce|ilcemiz(\\b|_)" // tr
     "|^시[·・]?도";         // ko-KR
 
 /////////////////////////////////////////////////////////////////////////////
@@ -269,6 +275,7 @@
     "മെയിൽ"                                        // ml
     "|ایمیل|پست.*الکترونیک"                        // fa
     "|ईमेल|इलॅक्ट्रॉनिक.?मेल"                           // hi
+    "|(\\b|_)eposta(\\b|_)"                          // tr
     "|(?:이메일|전자.?우편|[Ee]-?mail)(.?주소)?";  // ko-KR
 
 /////////////////////////////////////////////////////////////////////////////
@@ -288,6 +295,7 @@
     "|^nome"                 // pt-BR, pt-PT
     "|نام.*نام.*خانوادگی"    // fa
     "|姓名"                  // zh-CN
+    "|(\\b|_)ad soyad(\\b|_)"  // tr
     "|성명";                 // ko-KR
 const char kNameSpecificRe[] =
     "^name"
@@ -304,6 +312,7 @@
     "|نام"                     // fa
     "|이름"                    // ko-KR
     "|പേര്"                     // ml
+    "|(\\b|_)ad(\\b|_)"           // tr
     "|नाम";                    // hi
 const char kMiddleInitialRe[] = "middle.*initial|m\\.i\\.|mi$|\\bmi\\b";
 const char kMiddleNameRe[] =
@@ -321,6 +330,7 @@
     "|نام.*خانوادگی"                       // fa
     "|उपनाम"                               // hi
     "|മറുപേര്"                               // ml
+    "|(\\b|_)soyad(\\b|_)"                   // tr
     "|\\b성(?:[^명]|\\b)";                 // ko-KR
 
 /////////////////////////////////////////////////////////////////////////////
diff --git a/components/autofill_assistant/browser/basic_interactions.cc b/components/autofill_assistant/browser/basic_interactions.cc
index 42655e2..149429e 100644
--- a/components/autofill_assistant/browser/basic_interactions.cc
+++ b/components/autofill_assistant/browser/basic_interactions.cc
@@ -90,76 +90,85 @@
                    const std::string& result_model_identifier,
                    const ToStringProto& proto) {
   auto value = user_model->GetValue(proto.value());
-  std::string result;
-
   if (!value.has_value()) {
     DVLOG(2) << "Error evaluating " << __func__ << ": " << proto.value()
              << " not found in model";
     return false;
   }
-  if (!AreAllValuesOfSize({*value}, 1)) {
-    DVLOG(2) << "Error evaluating " << __func__
-             << ": expected single value, but got a list instead";
+  if (GetValueSize(*value) == 0) {
+    DVLOG(2) << "Error evaluating " << __func__ << ": input value empty";
     return false;
   }
-  if (AreAllValuesOfType({*value}, ValueProto::kUserActions)) {
-    DVLOG(2) << "Error evaluating " << __func__
-             << ": does not support stringifying user actions";
-    return false;
-  }
-  switch (value->kind_case()) {
-    case ValueProto::kStrings:
-      result = value->strings().values(0);
-      break;
-    case ValueProto::kBooleans:
-      result = value->booleans().values(0) ? "true" : "false";
-      break;
-    case ValueProto::kInts:
-      result = base::NumberToString(value->ints().values(0));
-      break;
-    case ValueProto::kUserActions:
-      NOTREACHED();
-      return false;
-    case ValueProto::kDates: {
-      if (proto.date_format().date_format().empty()) {
-        DVLOG(2) << "Error evaluating " << __func__ << ": date_format not set";
-        return false;
-      }
-      auto date = value->dates().values(0);
-      base::Time::Exploded exploded_time = {date.year(),
-                                            date.month(),
-                                            /* day_of_week = */ -1,
-                                            date.day(),
-                                            /* hour = */ 0,
-                                            /* minute = */ 0,
-                                            /* second = */ 0,
-                                            /* millisecond = */ 0};
-      base::Time time;
-      if (!base::Time::FromLocalExploded(exploded_time, &time)) {
-        DVLOG(2) << "Error evaluating " << __func__ << ": invalid date "
-                 << *value;
-        return false;
-      }
 
-      result = base::UTF16ToUTF8(base::TimeFormatWithPattern(
-          time, proto.date_format().date_format().c_str()));
-      break;
-    }
+  switch (value->kind_case()) {
+    case ValueProto::kUserActions:
+    case ValueProto::kLoginOptions:
     case ValueProto::kCreditCards:
     case ValueProto::kProfiles:
-    case ValueProto::kLoginOptions:
     case ValueProto::kCreditCardResponse:
     case ValueProto::kLoginOptionResponse:
-      DVLOG(2) << "Error evaluating " << __func__ << ": kind not supported for "
-               << *value;
+      DVLOG(2) << "Error evaluating " << __func__
+               << ": does not support values of type " << value->kind_case();
       return false;
-    case ValueProto::KIND_NOT_SET:
-      DVLOG(2) << "Error evaluating " << __func__ << ": kind not set";
-      return false;
+    default:
+      break;
   }
 
-  user_model->SetValue(result_model_identifier,
-                       SimpleValue(result, value->is_client_side_only()));
+  ValueProto result;
+  result.set_is_client_side_only(value->is_client_side_only());
+  for (int i = 0; i < GetValueSize(*value); ++i) {
+    switch (value->kind_case()) {
+      case ValueProto::kStrings:
+        result.mutable_strings()->add_values(value->strings().values(i));
+        break;
+      case ValueProto::kBooleans:
+        result.mutable_strings()->add_values(
+            value->booleans().values(i) ? "true" : "false");
+        break;
+      case ValueProto::kInts:
+        result.mutable_strings()->add_values(
+            base::NumberToString(value->ints().values(i)));
+        break;
+      case ValueProto::kDates: {
+        if (proto.date_format().date_format().empty()) {
+          DVLOG(2) << "Error evaluating " << __func__
+                   << ": date_format not set";
+          return false;
+        }
+        auto date = value->dates().values(i);
+        base::Time::Exploded exploded_time = {date.year(),
+                                              date.month(),
+                                              /* day_of_week = */ -1,
+                                              date.day(),
+                                              /* hour = */ 0,
+                                              /* minute = */ 0,
+                                              /* second = */ 0,
+                                              /* millisecond = */ 0};
+        base::Time time;
+        if (!base::Time::FromLocalExploded(exploded_time, &time)) {
+          DVLOG(2) << "Error evaluating " << __func__ << ": invalid date "
+                   << *value;
+          return false;
+        }
+
+        result.mutable_strings()->add_values(
+            base::UTF16ToUTF8(base::TimeFormatWithPattern(
+                time, proto.date_format().date_format().c_str())));
+        break;
+      }
+      case ValueProto::kUserActions:
+      case ValueProto::kLoginOptions:
+      case ValueProto::kCreditCards:
+      case ValueProto::kProfiles:
+      case ValueProto::kCreditCardResponse:
+      case ValueProto::kLoginOptionResponse:
+      case ValueProto::KIND_NOT_SET:
+        NOTREACHED();
+        return false;
+    }
+  }
+
+  user_model->SetValue(result_model_identifier, result);
   return true;
 }
 
diff --git a/components/autofill_assistant/browser/basic_interactions_unittest.cc b/components/autofill_assistant/browser/basic_interactions_unittest.cc
index 6b9e10a..fb1316e 100644
--- a/components/autofill_assistant/browser/basic_interactions_unittest.cc
+++ b/components/autofill_assistant/browser/basic_interactions_unittest.cc
@@ -11,6 +11,7 @@
 #include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
 #include "components/autofill_assistant/browser/generic_ui.pb.h"
 #include "components/autofill_assistant/browser/user_model.h"
+#include "components/autofill_assistant/browser/value_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
@@ -77,7 +78,7 @@
   user_model_.SetValue("value_3",
                        SimpleValue(true, /* is_client_side_only = */ true));
   EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
-  EXPECT_EQ(user_model_.GetValue("output"), SimpleValue(true, true));
+  EXPECT_TRUE(user_model_.GetValue("output")->is_client_side_only());
 
   // Mixing types is not allowed.
   user_model_.SetValue("value_1", ValueProto());
@@ -116,7 +117,7 @@
   user_model_.SetValue("value_2",
                        SimpleValue(true, /* is_client_side_only = */ true));
   EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
-  EXPECT_EQ(user_model_.GetValue("output"), SimpleValue(true, true));
+  EXPECT_TRUE(user_model_.GetValue("output")->is_client_side_only());
 
   // Mixing types is not allowed.
   user_model_.SetValue("value_1", ValueProto());
@@ -149,7 +150,7 @@
   user_model_.SetValue("value",
                        SimpleValue(false, /* is_client_side_only = */ true));
   EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
-  EXPECT_EQ(user_model_.GetValue("value"), SimpleValue(true, true));
+  EXPECT_TRUE(user_model_.GetValue("value")->is_client_side_only());
 
   // Not a boolean.
   user_model_.SetValue("value", ValueProto());
@@ -166,8 +167,8 @@
 TEST_F(BasicInteractionsTest, ComputeValueToString) {
   ComputeValueProto proto;
   proto.mutable_to_string()->mutable_value()->set_model_identifier("value");
-  user_model_.SetValue("value", SimpleValue(1),
-                       /* is_client_side_only = */ true);
+  user_model_.SetValue("value", SimpleValue(1,
+                                            /* is_client_side_only = */ true));
 
   // Result model identifier not set.
   EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
@@ -222,12 +223,32 @@
   user_model_.SetValue("value", ValueProto());
   EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
 
-  // Multi value fails.
+  // Multi value succeeds.
   ValueProto multi_value;
   multi_value.mutable_booleans()->add_values(true);
   multi_value.mutable_booleans()->add_values(false);
   user_model_.SetValue("value", multi_value);
-  EXPECT_FALSE(basic_interactions_.ComputeValue(proto));
+  EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+  ValueProto expected_result;
+  expected_result.mutable_strings()->add_values("true");
+  expected_result.mutable_strings()->add_values("false");
+  EXPECT_EQ(*user_model_.GetValue("output"), expected_result);
+
+  multi_value.Clear();
+  multi_value.mutable_ints()->add_values(5);
+  multi_value.mutable_ints()->add_values(13);
+  user_model_.SetValue("value", multi_value);
+  EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+  expected_result.Clear();
+  expected_result.mutable_strings()->add_values("5");
+  expected_result.mutable_strings()->add_values("13");
+  EXPECT_EQ(*user_model_.GetValue("output"), expected_result);
+
+  // is_client_side_only flag is sticky.
+  multi_value.set_is_client_side_only(true);
+  user_model_.SetValue("value", multi_value);
+  EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
+  EXPECT_TRUE(user_model_.GetValue("output")->is_client_side_only());
 }
 
 TEST_F(BasicInteractionsTest, ComputeValueIntegerSum) {
@@ -268,7 +289,7 @@
   user_model_.SetValue("value_b",
                        SimpleValue(5, /* is_client_side_only = */ true));
   EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
-  EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(7, true));
+  EXPECT_TRUE(user_model_.GetValue("result")->is_client_side_only());
 }
 
 TEST_F(BasicInteractionsTest, EndActionWithoutCallbackFails) {
@@ -394,7 +415,7 @@
   user_model_.SetValue("value_b",
                        SimpleValue(1, /* is_client_side_only = */ true));
   EXPECT_TRUE(basic_interactions_.ComputeValue(proto));
-  EXPECT_EQ(user_model_.GetValue("result"), SimpleValue(true, true));
+  EXPECT_EQ(*user_model_.GetValue("result"), SimpleValue(true, true));
 }
 
 TEST_F(BasicInteractionsTest, ComputeValueCreateCreditCardResponse) {
diff --git a/components/autofill_assistant/browser/generic_ui.proto b/components/autofill_assistant/browser/generic_ui.proto
index 5e136197..d8722840 100644
--- a/components/autofill_assistant/browser/generic_ui.proto
+++ b/components/autofill_assistant/browser/generic_ui.proto
@@ -151,7 +151,8 @@
   reserved 1;
 }
 
-// Creates a string representation of the specified value.
+// Creates a string representation of the specified value. For input lists, will
+// create a corresponding string result list.
 message ToStringProto {
   // The value to stringify.
   optional ValueReferenceProto value = 3;
diff --git a/components/autofill_assistant/browser/user_model.cc b/components/autofill_assistant/browser/user_model.cc
index df113f1..2130602 100644
--- a/components/autofill_assistant/browser/user_model.cc
+++ b/components/autofill_assistant/browser/user_model.cc
@@ -59,7 +59,9 @@
                          const ValueProto& value,
                          bool force_notification) {
   auto result = values_.emplace(identifier, value);
-  if (!force_notification && !result.second && result.first->second == value) {
+  if (!force_notification && !result.second && result.first->second == value &&
+      value.is_client_side_only() ==
+          result.first->second.is_client_side_only()) {
     return;
   } else if (!result.second) {
     result.first->second = value;
diff --git a/components/autofill_assistant/browser/user_model_unittest.cc b/components/autofill_assistant/browser/user_model_unittest.cc
index e88182c..799d606 100644
--- a/components/autofill_assistant/browser/user_model_unittest.cc
+++ b/components/autofill_assistant/browser/user_model_unittest.cc
@@ -351,4 +351,19 @@
   EXPECT_THAT(model_.GetProfile(profile_b.guid())->Compare(profile_b), Eq(0));
 }
 
+TEST_F(UserModelTest, ClientSideOnlyNotifications) {
+  testing::InSequence seq;
+  model_.SetValue("identifier",
+                  SimpleValue(1, /* is_client_side_only = */ false));
+  EXPECT_CALL(mock_observer_, OnValueChanged("identifier", _)).Times(0);
+  model_.SetValue("identifier",
+                  SimpleValue(1, /* is_client_side_only = */ false));
+
+  EXPECT_CALL(mock_observer_, OnValueChanged("identifier", _)).Times(1);
+  model_.SetValue("identifier",
+                  SimpleValue(1, /* is_client_side_only = */ true));
+
+  EXPECT_TRUE(GetValues().at("identifier").is_client_side_only());
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/value_util.cc b/components/autofill_assistant/browser/value_util.cc
index 8511690..24b0175 100644
--- a/components/autofill_assistant/browser/value_util.cc
+++ b/components/autofill_assistant/browser/value_util.cc
@@ -40,6 +40,8 @@
 
 // Compares two |ValueProto| instances and returns true if they exactly match.
 bool operator==(const ValueProto& value_a, const ValueProto& value_b) {
+  // Note: this comparison intentionally ignores |is_client_side_only|, as that
+  // flag is metadata and should not affect this comparison.
   if (value_a.kind_case() != value_b.kind_case()) {
     return false;
   }
@@ -283,7 +285,6 @@
       break;
     case ValueProto::kLoginOptionResponse:
       out << value.login_option_response();
-      ;
       break;
     case ValueProto::KIND_NOT_SET:
       break;
diff --git a/components/browser_ui/android/bottomsheet/BUILD.gn b/components/browser_ui/android/bottomsheet/BUILD.gn
new file mode 100644
index 0000000..a9965b2
--- /dev/null
+++ b/components/browser_ui/android/bottomsheet/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/config.gni")
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  sources = [ "java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java" ]
+
+  deps = [ "//third_party/android_deps:androidx_annotation_annotation_java" ]
+}
diff --git a/components/browser_ui/android/bottomsheet/OWNERS b/components/browser_ui/android/bottomsheet/OWNERS
new file mode 100644
index 0000000..3a71a718
--- /dev/null
+++ b/components/browser_ui/android/bottomsheet/OWNERS
@@ -0,0 +1,7 @@
+mdjones@chromium.org
+twellington@chromium.org
+
+# Team: chrome-android-app@chromium.org
+# Component: UI>Browser>Mobile>NavPanel
+# OS: Android
+
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContent.java b/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java
similarity index 98%
rename from chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContent.java
rename to components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java
index 0f546b6..554352d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetContent.java
+++ b/components/browser_ui/android/bottomsheet/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java
@@ -2,7 +2,7 @@
 // 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.widget.bottomsheet;
+package org.chromium.components.browser_ui.bottomsheet;
 
 import android.view.View;
 
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java
index 56f79b14..150fe66 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/MoreProgressButtonTest.java
@@ -134,7 +134,7 @@
     public void testStateAfterBindAction() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             boolean buttonShownBefore = mActivity.findViewById(R.id.action_button).isShown();
-            boolean spinnerShownBefore = mActivity.findViewById(R.id.action_button).isShown();
+            boolean spinnerShownBefore = mActivity.findViewById(R.id.progress_spinner).isShown();
 
             mMoreProgressButton.setOnClickRunnable(() -> changeTextView(""));
 
diff --git a/components/content_settings/browser/BUILD.gn b/components/content_settings/browser/BUILD.gn
index 7c12a11..8377ccc 100644
--- a/components/content_settings/browser/BUILD.gn
+++ b/components/content_settings/browser/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "content_settings_manager_impl.cc",
     "content_settings_manager_impl.h",
+    "content_settings_usages_state.cc",
+    "content_settings_usages_state.h",
     "tab_specific_content_settings.cc",
     "tab_specific_content_settings.h",
   ]
@@ -18,6 +20,7 @@
     "//components/page_load_metrics/browser",
     "//components/prefs",
     "//components/security_state/core",
+    "//components/url_formatter",
     "//content/public/browser",
     "//content/public/common",
   ]
diff --git a/components/content_settings/browser/DEPS b/components/content_settings/browser/DEPS
index 3e11c59..cb87cc1c 100644
--- a/components/content_settings/browser/DEPS
+++ b/components/content_settings/browser/DEPS
@@ -3,6 +3,7 @@
   "+components/page_load_metrics/browser",
   "+components/security_state/core",
   "+components/sync_preferences",
+  "+components/url_formatter",
   "+content/public/browser",
   "+content/public/common",
   "+content/public/test",
diff --git a/components/content_settings/core/browser/content_settings_usages_state.cc b/components/content_settings/browser/content_settings_usages_state.cc
similarity index 86%
rename from components/content_settings/core/browser/content_settings_usages_state.cc
rename to components/content_settings/browser/content_settings_usages_state.cc
index b9fbf9f..b65036d 100644
--- a/components/content_settings/core/browser/content_settings_usages_state.cc
+++ b/components/content_settings/browser/content_settings_usages_state.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/content_settings/core/browser/content_settings_usages_state.h"
+#include "components/content_settings/browser/content_settings_usages_state.h"
 
 #include <string>
 
@@ -13,15 +13,12 @@
 ContentSettingsUsagesState::ContentSettingsUsagesState(
     HostContentSettingsMap* host_content_settings_map,
     ContentSettingsType type)
-    : host_content_settings_map_(host_content_settings_map),
-      type_(type) {
-}
+    : host_content_settings_map_(host_content_settings_map), type_(type) {}
 
-ContentSettingsUsagesState::~ContentSettingsUsagesState() {
-}
+ContentSettingsUsagesState::~ContentSettingsUsagesState() {}
 
-void ContentSettingsUsagesState::OnPermissionSet(
-    const GURL& requesting_origin, bool allowed) {
+void ContentSettingsUsagesState::OnPermissionSet(const GURL& requesting_origin,
+                                                 bool allowed) {
   state_map_[requesting_origin] =
       allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
 }
@@ -71,9 +68,9 @@
       std::string formatted_host = GURLToFormattedHost(i->first);
       std::string final_formatted_host =
           repeated_formatted_hosts.find(formatted_host) ==
-          repeated_formatted_hosts.end() ?
-          formatted_host :
-          i->first.spec();
+                  repeated_formatted_hosts.end()
+              ? formatted_host
+              : i->first.spec();
       (*formatted_hosts_per_state)[i->second].insert(final_formatted_host);
     }
 
diff --git a/components/content_settings/core/browser/content_settings_usages_state.h b/components/content_settings/browser/content_settings_usages_state.h
similarity index 83%
rename from components/content_settings/core/browser/content_settings_usages_state.h
rename to components/content_settings/browser/content_settings_usages_state.h
index cec45230..a34ed72 100644
--- a/components/content_settings/core/browser/content_settings_usages_state.h
+++ b/components/content_settings/browser/content_settings_usages_state.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_USAGES_STATE_H_
-#define COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_CONTENT_SETTINGS_USAGES_STATE_H_
+#ifndef COMPONENTS_CONTENT_SETTINGS_BROWSER_CONTENT_SETTINGS_USAGES_STATE_H_
+#define COMPONENTS_CONTENT_SETTINGS_BROWSER_CONTENT_SETTINGS_USAGES_STATE_H_
 
 #include <map>
 #include <set>
@@ -20,15 +20,12 @@
 // the content setting usage.
 class ContentSettingsUsagesState {
  public:
-  ContentSettingsUsagesState(
-      HostContentSettingsMap* host_content_settings_map,
-      ContentSettingsType type);
+  ContentSettingsUsagesState(HostContentSettingsMap* host_content_settings_map,
+                             ContentSettingsType type);
   ~ContentSettingsUsagesState();
 
   typedef std::map<GURL, ContentSetting> StateMap;
-  const StateMap& state_map() const {
-    return state_map_;
-  }
+  const StateMap& state_map() const { return state_map_; }
 
   // Sets the state for |requesting_origin|.
   void OnPermissionSet(const GURL& requesting_origin, bool allowed);
@@ -52,7 +49,7 @@
   };
 
   // Maps ContentSetting to a set of hosts formatted for presentation.
-  typedef std::map<ContentSetting, std::set<std::string> >
+  typedef std::map<ContentSetting, std::set<std::string>>
       FormattedHostsPerState;
 
   void GetDetailedInfo(FormattedHostsPerState* formatted_hosts_per_state,
diff --git a/components/content_settings/browser/tab_specific_content_settings.h b/components/content_settings/browser/tab_specific_content_settings.h
index 6e55188..21a7dc4 100644
--- a/components/content_settings/browser/tab_specific_content_settings.h
+++ b/components/content_settings/browser/tab_specific_content_settings.h
@@ -18,8 +18,8 @@
 #include "base/scoped_observer.h"
 #include "build/build_config.h"
 #include "components/browsing_data/content/local_shared_objects_container.h"
+#include "components/content_settings/browser/content_settings_usages_state.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
-#include "components/content_settings/core/browser/content_settings_usages_state.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
diff --git a/components/content_settings/core/browser/BUILD.gn b/components/content_settings/core/browser/BUILD.gn
index 80a7385..f4d4b5a 100644
--- a/components/content_settings/core/browser/BUILD.gn
+++ b/components/content_settings/core/browser/BUILD.gn
@@ -35,8 +35,6 @@
     "content_settings_registry.h",
     "content_settings_rule.cc",
     "content_settings_rule.h",
-    "content_settings_usages_state.cc",
-    "content_settings_usages_state.h",
     "content_settings_utils.cc",
     "content_settings_utils.h",
     "cookie_settings.cc",
diff --git a/components/metrics/metrics_log.cc b/components/metrics/metrics_log.cc
index ab4e04f..c200c87 100644
--- a/components/metrics/metrics_log.cc
+++ b/components/metrics/metrics_log.cc
@@ -169,6 +169,10 @@
   RecordCoreSystemProfile(client->GetVersionString(), client->GetChannel(),
                           client->GetApplicationLocale(),
                           client->GetAppPackageName(), system_profile);
+
+  std::string brand_code;
+  if (client->GetBrand(&brand_code))
+    system_profile->set_brand_code(brand_code);
 }
 
 // static
@@ -319,10 +323,6 @@
   WriteMetricsEnableDefault(client_->GetMetricsReportingDefaultState(),
                             system_profile);
 
-  std::string brand_code;
-  if (client_->GetBrand(&brand_code))
-    system_profile->set_brand_code(brand_code);
-
   delegating_provider->ProvideSystemProfileMetricsWithLogCreationTime(
       creation_time_, system_profile);
 
diff --git a/components/metrics/metrics_log_unittest.cc b/components/metrics/metrics_log_unittest.cc
index 20305bc..52d54a9 100644
--- a/components/metrics/metrics_log_unittest.cc
+++ b/components/metrics/metrics_log_unittest.cc
@@ -151,6 +151,7 @@
   system_profile->set_client_uuid(kClientId);
   system_profile->set_channel(client.GetChannel());
   system_profile->set_application_locale(client.GetApplicationLocale());
+  system_profile->set_brand_code(TestMetricsServiceClient::kBrandForTesting);
 
 #if defined(ADDRESS_SANITIZER) || DCHECK_IS_ON()
   system_profile->set_is_instrumented_build(true);
diff --git a/components/page_info/page_info.cc b/components/page_info/page_info.cc
index 63bf012d..906fc1b79 100644
--- a/components/page_info/page_info.cc
+++ b/components/page_info/page_info.cc
@@ -1103,10 +1103,6 @@
 
 content_settings::TabSpecificContentSettings*
 PageInfo::GetTabSpecificContentSettings() const {
-  // When |web_contents| is not from a Tab, |web_contents| does not have a
-  // |TabSpecificContentSettings| and need to create one; otherwise, noop.
-  content_settings::TabSpecificContentSettings::CreateForWebContents(
-      web_contents(), delegate_->GetTabSpecificContentSettingsDelegate());
   return content_settings::TabSpecificContentSettings::FromWebContents(
       web_contents());
 }
diff --git a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
index 0b1f0b2..a885037 100644
--- a/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/core_page_load_metrics_observer.cc
@@ -61,6 +61,19 @@
                             internal::FIRST_MEANINGFUL_PAINT_LAST_ENTRY);
 }
 
+std::unique_ptr<base::trace_event::TracedValue> FirstInputDelayTraceData(
+    const page_load_metrics::mojom::PageLoadTiming& timing) {
+  std::unique_ptr<base::trace_event::TracedValue> data =
+      std::make_unique<base::trace_event::TracedValue>();
+  data->SetDouble(
+      "firstInputDelayInMilliseconds",
+      timing.interactive_timing->first_input_delay->InMillisecondsF());
+  data->SetDouble(
+      "navStartToFirstInputTimestampInMilliseconds",
+      timing.interactive_timing->first_input_timestamp->InMillisecondsF());
+  return data;
+}
+
 }  // namespace
 
 namespace internal {
@@ -583,6 +596,11 @@
       50);
   PAGE_LOAD_HISTOGRAM(internal::kHistogramFirstInputTimestamp,
                       timing.interactive_timing->first_input_timestamp.value());
+  TRACE_EVENT_MARK_WITH_TIMESTAMP1(
+      "loading", "FirstInputDelay::AllFrames::UMA",
+      GetDelegate().GetNavigationStart() +
+          timing.interactive_timing->first_input_timestamp.value(),
+      "data", FirstInputDelayTraceData(timing));
 }
 
 void CorePageLoadMetricsObserver::OnParseStart(
@@ -828,6 +846,11 @@
     UMA_HISTOGRAM_ENUMERATION(
         internal::kHistogramLargestContentfulPaintContentType,
         all_frames_largest_contentful_paint.Type());
+    TRACE_EVENT_MARK_WITH_TIMESTAMP1(
+        "loading", "NavStartToLargestContentfulPaint::AllFrames::UMA",
+        GetDelegate().GetNavigationStart() +
+            all_frames_largest_contentful_paint.Time().value(),
+        "data", all_frames_largest_contentful_paint.DataAsTraceValue());
 
     // Note: This depends on PageLoadMetrics internally processing loading
     // behavior before timing metrics if they come in the same IPC update.
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
index 255c19df..275c31a 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
@@ -42,6 +42,7 @@
      */
     public void addSubFrame(PlayerFrameCoordinator subFrame, Rect clipRect) {
         mMediator.addSubFrame(subFrame.getView(), clipRect);
+        subFrame.mView.getGestureDetector().setParentGestureDetector(mView.getGestureDetector());
     }
 
     /**
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java
index 1296cc4d..756a79d6 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameGestureDetector.java
@@ -19,6 +19,17 @@
     private ScaleGestureDetector mScaleGestureDetector;
     private boolean mCanDetectZoom;
     private PlayerFrameViewDelegate mPlayerFrameViewDelegate;
+    private PlayerFrameGestureDetector mParentGestureDetector;
+    /**
+     * Last horizontal scroll distance that was detected by this {@link PlayerFrameGestureDetector}
+     * and consumed by {@link #mParentGestureDetector}.
+     */
+    private float mLastParentScrollX;
+    /**
+     * Last vertical scroll distance that was detected by this {@link PlayerFrameGestureDetector}
+     * and consumed by {@link #mParentGestureDetector}.
+     */
+    private float mLastParentScrollY;
 
     /**
      * @param context Used for initializing {@link GestureDetector} and
@@ -36,6 +47,15 @@
     }
 
     /**
+     * Sets the {@link PlayerFrameGestureDetector} that corresponds to the parent view of this
+     * {@link PlayerFrameGestureDetector}'s view. This is used for forwarding unconsumed touch
+     * events.
+     */
+    void setParentGestureDetector(PlayerFrameGestureDetector parentGestureDetector) {
+        mParentGestureDetector = parentGestureDetector;
+    }
+
+    /**
      * This should be called on every touch event.
      * @return Whether the event was consumed.
      */
@@ -63,7 +83,27 @@
 
     @Override
     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-        return mPlayerFrameViewDelegate.scrollBy(distanceX, distanceY);
+        if (mPlayerFrameViewDelegate.scrollBy(distanceX, distanceY)) {
+            mLastParentScrollX = 0f;
+            mLastParentScrollY = 0f;
+            return true;
+        }
+
+        // We need to keep track of the distance passed to the parent
+        // {@link PlayerFrameGestureDetector} and accumulate them for the following events. This is
+        // because if the parent view scrolls, the coordinates of the future touch events that this
+        // view received will be transformed since the View associated with this
+        // {@link PlayerFrameGestureDetector} moves along with the parent.
+        mLastParentScrollX += distanceX;
+        mLastParentScrollY += distanceY;
+        if (mParentGestureDetector != null
+                && mParentGestureDetector.onScroll(
+                        e1, e2, mLastParentScrollX, mLastParentScrollY)) {
+            return true;
+        }
+        mLastParentScrollX = 0f;
+        mLastParentScrollY = 0f;
+        return false;
     }
 
     @Override
@@ -71,7 +111,12 @@
 
     @Override
     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-        return mPlayerFrameViewDelegate.onFling(velocityX, velocityY);
+        if (mPlayerFrameViewDelegate.onFling(velocityX, velocityY)) return true;
+
+        if (mParentGestureDetector != null) {
+            return mParentGestureDetector.onFling(e1, e2, velocityX, velocityY);
+        }
+        return false;
     }
 
     @Override
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
index 8dc81d3..4b90049 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
@@ -7,7 +7,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.util.Pair;
 import android.view.View;
 import android.widget.Scroller;
 
@@ -45,10 +44,25 @@
     /** The content height inside this frame, at a scale factor of 1. */
     private final int mContentHeight;
     /**
-     * A list of {@link PlayerFrameCoordinator}s and {@link Rect}s representing this frame's
-     * sub-frames and their coordinates.
+     * Contains all {@link View}s corresponding to this frame's sub-frames.
      */
-    private final List<Pair<View, Rect>> mSubFrames = new ArrayList<>();
+    private final List<View> mSubFrameViews = new ArrayList<>();
+    /**
+     * Contains all clip rects corresponding to this frame's sub-frames.
+     */
+    private final List<Rect> mSubFrameRects = new ArrayList<>();
+    /**
+     * Contains scaled clip rects corresponding to this frame's sub-frames.
+     */
+    private final List<Rect> mSubFrameScaledRects = new ArrayList<>();
+    /**
+     * Contains views for currently visible sub-frames according to {@link #mViewPort}.
+     */
+    private final List<View> mVisibleSubFrameViews = new ArrayList<>();
+    /**
+     * Contains scaled clip rects for currently visible sub-frames according to {@link #mViewPort}.
+     */
+    private final List<Rect> mVisibleSubFrameScaledRects = new ArrayList<>();
     private final PropertyModel mModel;
     private final PlayerCompositorDelegate mCompositorDelegate;
     private final Scroller mScroller;
@@ -78,6 +92,9 @@
     PlayerFrameMediator(PropertyModel model, PlayerCompositorDelegate compositorDelegate,
             Scroller scroller, UnguessableToken frameGuid, int contentWidth, int contentHeight) {
         mModel = model;
+        mModel.set(PlayerFrameProperties.SUBFRAME_VIEWS, mVisibleSubFrameViews);
+        mModel.set(PlayerFrameProperties.SUBFRAME_RECTS, mVisibleSubFrameScaledRects);
+
         mCompositorDelegate = compositorDelegate;
         mScroller = scroller;
         mGuid = frameGuid;
@@ -92,7 +109,9 @@
      * @param clipRect     The bounds of the sub-frame, relative to this frame.
      */
     void addSubFrame(View subFrameView, Rect clipRect) {
-        mSubFrames.add(new Pair<>(subFrameView, clipRect));
+        mSubFrameViews.add(subFrameView);
+        mSubFrameRects.add(clipRect);
+        mSubFrameScaledRects.add(new Rect());
     }
 
     @Override
@@ -147,6 +166,7 @@
         // Update mViewportRect and let the view know. PropertyModelChangeProcessor is smart about
         // this and will only update the view if mViewportRect is actually changed.
         mViewportRect.offset(distanceX, distanceY);
+        updateSubFrames();
         mModel.set(PlayerFrameProperties.TILE_DIMENSIONS, tileDimensions);
         mModel.set(PlayerFrameProperties.VIEWPORT, mViewportRect);
 
@@ -181,17 +201,31 @@
                 requestBitmapForAdjacentTiles(tileWidth, tileHeight, row, col, scaleFactor);
             }
         }
+    }
 
-        // Add visible sub-frames to the view.
-        List<Pair<View, Rect>> visibleSubFrames = new ArrayList<>();
-        for (int i = 0; i < mSubFrames.size(); i++) {
-            // TODO(crbug.com/1020702): These values should be scaled for scale factors other than
-            // 1.
-            if (Rect.intersects(mSubFrames.get(i).second, mViewportRect)) {
-                visibleSubFrames.add(mSubFrames.get(i));
+    private void updateSubFrames() {
+        mVisibleSubFrameViews.clear();
+        mVisibleSubFrameScaledRects.clear();
+        for (int i = 0; i < mSubFrameRects.size(); i++) {
+            Rect subFrameScaledRect = mSubFrameScaledRects.get(i);
+            scaleRect(mSubFrameRects.get(i), subFrameScaledRect, mScaleFactor);
+            if (Rect.intersects(subFrameScaledRect, mViewportRect)) {
+                int transformedLeft = subFrameScaledRect.left - mViewportRect.left;
+                int transformedTop = subFrameScaledRect.top - mViewportRect.top;
+                subFrameScaledRect.set(transformedLeft, transformedTop,
+                        transformedLeft + subFrameScaledRect.width(),
+                        transformedTop + subFrameScaledRect.height());
+                mVisibleSubFrameViews.add(mSubFrameViews.get(i));
+                mVisibleSubFrameScaledRects.add(subFrameScaledRect);
             }
         }
-        mModel.set(PlayerFrameProperties.SUBFRAME_VIEWS, visibleSubFrames);
+    }
+
+    private void scaleRect(Rect inRect, Rect outRect, float scaleFactor) {
+        outRect.set((int) (((float) inRect.left) * scaleFactor),
+                (int) (((float) inRect.top) * scaleFactor),
+                (int) (((float) inRect.right) * scaleFactor),
+                (int) (((float) inRect.bottom) * scaleFactor));
     }
 
     private void requestBitmapForAdjacentTiles(
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java
index 45ca084..6404d7f 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameProperties.java
@@ -6,7 +6,6 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Rect;
-import android.util.Pair;
 import android.view.View;
 
 import org.chromium.ui.modelutil.PropertyKey;
@@ -31,12 +30,15 @@
     static final PropertyModel.WritableObjectPropertyKey<Rect> VIEWPORT =
             new PropertyModel.WritableObjectPropertyKey<>(true);
     /**
-     * A list of sub-frames that are currently visible. Each element in the list is a {@link Pair}
-     * consists of a {@link View}, that displays the sub-frame's content, and a {@link Rect}, that
-     * contains its location.
+     * List of currently visible sub-frame {@link View}s.
      */
-    static final PropertyModel.WritableObjectPropertyKey<List<Pair<View, Rect>>> SUBFRAME_VIEWS =
-            new PropertyModel.WritableObjectPropertyKey<>(true);
+    static final PropertyModel.WritableObjectPropertyKey<List<View>> SUBFRAME_VIEWS =
+            new PropertyModel.WritableObjectPropertyKey<>();
+    /**
+     * List of currently visible sub-frame clip rects.
+     */
+    static final PropertyModel.WritableObjectPropertyKey<List<Rect>> SUBFRAME_RECTS =
+            new PropertyModel.WritableObjectPropertyKey<>();
     static final PropertyKey[] ALL_KEYS = {
-            BITMAP_MATRIX, TILE_DIMENSIONS, VIEWPORT, SUBFRAME_VIEWS};
+            BITMAP_MATRIX, TILE_DIMENSIONS, VIEWPORT, SUBFRAME_VIEWS, SUBFRAME_RECTS};
 }
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
index 56264fcf..bfd4713 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameView.java
@@ -8,7 +8,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -27,7 +26,8 @@
     private PlayerFrameBitmapPainter mBitmapPainter;
     private PlayerFrameGestureDetector mGestureDetector;
     private PlayerFrameViewDelegate mDelegate;
-    private List<Pair<View, Rect>> mSubFrames;
+    private List<View> mSubFrameViews;
+    private List<Rect> mSubFrameRects;
 
     /**
      * @param context Used for initialization.
@@ -44,38 +44,58 @@
                 new PlayerFrameGestureDetector(context, canDetectZoom, playerFrameViewDelegate);
     }
 
+    PlayerFrameGestureDetector getGestureDetector() {
+        return mGestureDetector;
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         mDelegate.setLayoutDimensions(getWidth(), getHeight());
-
-        for (int i = 0; i < mSubFrames.size(); i++) {
-            View childView = getChildAt(i);
-            if (childView == null) {
-                continue;
-            }
-
-            Rect childRect = mSubFrames.get(i).second;
-            childView.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
-        }
     }
 
     /**
-     * Updates the sub-frames that this {@link PlayerFrameView} should display, along with their
-     * coordinates.
-     * @param subFrames List of all sub-frames, along with their coordinates.
+     * Updates the sub-frame views that this {@link PlayerFrameView} should display.
+     * @param subFrameViews List of all sub-frame views.
      */
-    void updateSubFrames(List<Pair<View, Rect>> subFrames) {
-        // TODO(mahmoudi): Removing all views every time is not smart. Only remove the views that
-        // are not in subFrames.first.
-        mSubFrames = subFrames;
-        removeAllViews();
-        for (int i = 0; i < subFrames.size(); i++) {
-            addView(subFrames.get(i).first, i);
-        }
+    void updateSubFrameViews(List<View> subFrameViews) {
+        mSubFrameViews = subFrameViews;
+    }
+
+    /**
+     * Updates clip rects for sub-frames that this {@link PlayerFrameView} should display.
+     * @param subFrameRects List of all sub-frames clip rects.
+     */
+    void updateSubFrameRects(List<Rect> subFrameRects) {
+        mSubFrameRects = subFrameRects;
     }
 
     void updateViewPort(int left, int top, int right, int bottom) {
         mBitmapPainter.updateViewPort(left, top, right, bottom);
+
+        // Remove all views if there are no sub-frames.
+        if (mSubFrameViews == null || mSubFrameRects == null) {
+            removeAllViews();
+            return;
+        }
+
+        // Layout the sub-frames.
+        for (int i = 0; i < mSubFrameViews.size(); i++) {
+            if (mSubFrameViews.get(i).getParent() == null) {
+                addView(mSubFrameViews.get(i));
+            } else if (mSubFrameViews.get(i).getParent() != this) {
+                throw new IllegalStateException("Sub-frame view already has a parent.");
+            }
+            Rect layoutRect = mSubFrameRects.get(i);
+            mSubFrameViews.get(i).layout(
+                    layoutRect.left, layoutRect.top, layoutRect.right, layoutRect.bottom);
+        }
+
+        for (int i = 0; i < getChildCount(); i++) {
+            if (!mSubFrameViews.contains(getChildAt(i))) {
+                removeViewAt(i);
+                --i;
+            }
+        }
     }
 
     void updateBitmapMatrix(Bitmap[][] bitmapMatrix) {
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewBinder.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewBinder.java
index 15ab578f..d18f49d0 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewBinder.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewBinder.java
@@ -22,7 +22,9 @@
             Rect viewPort = model.get(PlayerFrameProperties.VIEWPORT);
             view.updateViewPort(viewPort.left, viewPort.top, viewPort.right, viewPort.bottom);
         } else if (key.equals(PlayerFrameProperties.SUBFRAME_VIEWS)) {
-            view.updateSubFrames(model.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+            view.updateSubFrameViews(model.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+        } else if (key.equals(PlayerFrameProperties.SUBFRAME_RECTS)) {
+            view.updateSubFrameRects(model.get(PlayerFrameProperties.SUBFRAME_RECTS));
         }
     }
 }
diff --git a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
index d67f5c3..f9badc9 100644
--- a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
+++ b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
@@ -595,26 +595,40 @@
 
         // Initial view port setup.
         mMediator.updateViewportSize(100, 200, 1f);
-        List<Pair<View, Rect>> expectedVisibleViews = new ArrayList<>();
-        expectedVisibleViews.add(subFrame1);
-        expectedVisibleViews.add(subFrame2);
+        List<View> expectedVisibleViews = new ArrayList<>();
+        List<Rect> expectedVisibleRects = new ArrayList<>();
+        expectedVisibleViews.add(subFrame1.first);
+        expectedVisibleViews.add(subFrame2.first);
+        expectedVisibleRects.add(subFrame1.second);
+        expectedVisibleRects.add(subFrame2.second);
         Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+        Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
 
         mMediator.scrollBy(100, 0);
         expectedVisibleViews.clear();
-        expectedVisibleViews.add(subFrame3);
+        expectedVisibleRects.clear();
+        expectedVisibleViews.add(subFrame3.first);
+        expectedVisibleRects.add(new Rect(20, 35, 50, 65));
         Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+        Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
 
         mMediator.scrollBy(-50, 0);
         expectedVisibleViews.clear();
-        expectedVisibleViews.add(subFrame1);
-        expectedVisibleViews.add(subFrame2);
-        expectedVisibleViews.add(subFrame3);
+        expectedVisibleRects.clear();
+        expectedVisibleViews.add(subFrame1.first);
+        expectedVisibleViews.add(subFrame2.first);
+        expectedVisibleViews.add(subFrame3.first);
+        expectedVisibleRects.add(new Rect(-40, 20, 10, 120));
+        expectedVisibleRects.add(new Rect(-20, 130, 20, 160));
+        expectedVisibleRects.add(new Rect(70, 35, 100, 65));
         Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+        Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
 
         mMediator.scrollBy(0, 200);
         expectedVisibleViews.clear();
+        expectedVisibleRects.clear();
         Assert.assertEquals(expectedVisibleViews, mModel.get(PlayerFrameProperties.SUBFRAME_VIEWS));
+        Assert.assertEquals(expectedVisibleRects, mModel.get(PlayerFrameProperties.SUBFRAME_RECTS));
     }
 
     /**
diff --git a/components/performance_manager/BUILD.gn b/components/performance_manager/BUILD.gn
index 8ab786b..94b2cac 100644
--- a/components/performance_manager/BUILD.gn
+++ b/components/performance_manager/BUILD.gn
@@ -19,6 +19,7 @@
     "embedder/binders.h",
     "embedder/performance_manager_lifetime.h",
     "embedder/performance_manager_registry.h",
+    "features.cc",
     "frame_priority/boosting_vote_aggregator.cc",
     "frame_priority/frame_priority.cc",
     "frame_priority/max_vote_aggregator.cc",
@@ -65,6 +66,7 @@
     "graph/worker_node_impl.h",
     "graph/worker_node_impl_describer.cc",
     "graph/worker_node_impl_describer.h",
+    "mechanisms/tab_loading_frame_navigation_scheduler.cc",
     "performance_manager.cc",
     "performance_manager_feature_observer_client.cc",
     "performance_manager_feature_observer_client.h",
@@ -81,6 +83,7 @@
     "public/decorators/page_live_state_decorator.h",
     "public/decorators/page_load_tracker_decorator_helper.h",
     "public/decorators/tab_properties_decorator.h",
+    "public/features.h",
     "public/frame_priority/boosting_vote_aggregator.h",
     "public/frame_priority/frame_priority.h",
     "public/frame_priority/max_vote_aggregator.h",
@@ -101,6 +104,7 @@
     "public/graph/process_node.h",
     "public/graph/system_node.h",
     "public/graph/worker_node.h",
+    "public/mechanisms/tab_loading_frame_navigation_scheduler.h",
     "public/performance_manager.h",
     "public/performance_manager_main_thread_observer.h",
     "public/render_frame_host_proxy.h",
@@ -230,6 +234,7 @@
   defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
   sources = [
+    "mechanisms/tab_loading_frame_navigation_scheduler_browsertest.cc",
     "performance_manager_browsertest.cc",
     "render_process_host_proxy_browsertest.cc",
   ]
@@ -242,6 +247,7 @@
     "//content/shell:content_shell_lib",
     "//content/test:browsertest_support",
     "//content/test:test_support",
+    "//net:test_support",
     "//testing/gmock",
     "//testing/gtest",
   ]
diff --git a/components/performance_manager/DEPS b/components/performance_manager/DEPS
index 528b3ad..5809451a 100644
--- a/components/performance_manager/DEPS
+++ b/components/performance_manager/DEPS
@@ -5,6 +5,7 @@
   "+content/shell/browser",
   "+mojo/public",
   "+net/base",
+  "+net/test",
   "+services/metrics/public/cpp",
   "+services/service_manager/public/cpp",
   "+third_party/blink/public/mojom/favicon",
diff --git a/components/performance_manager/features.cc b/components/performance_manager/features.cc
new file mode 100644
index 0000000..057d2fb4
--- /dev/null
+++ b/components/performance_manager/features.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This header contains field trial and variations definitions for policies,
+// mechanisms and features in the performance_manager component.
+
+#include "components/performance_manager/public/features.h"
+
+#include "base/metrics/field_trial_params.h"
+
+namespace performance_manager {
+namespace features {
+
+const base::Feature kTabLoadingFrameNavigationThrottles{
+    "TabLoadingFrameNavigationThrottles", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Parameters associated with the "TabLoadingFrameNavigationThrottles"
+// feature.
+const base::FeatureParam<int> kMinimumThrottleTimeoutMilliseconds = {
+    &kTabLoadingFrameNavigationThrottles, "MinimumThrottleTimeoutMilliseconds",
+    1000};
+// This defaults to the 99th %ile of LargestContentfulPaint (LCP).
+const base::FeatureParam<int> kMaximumThrottleTimeoutMilliseconds = {
+    &kTabLoadingFrameNavigationThrottles, "MaximumThrottleTimeoutMilliseconds",
+    40000};
+
+TabLoadingFrameNavigationThrottlesParams::
+    TabLoadingFrameNavigationThrottlesParams() = default;
+
+TabLoadingFrameNavigationThrottlesParams::
+    ~TabLoadingFrameNavigationThrottlesParams() = default;
+
+// static
+TabLoadingFrameNavigationThrottlesParams
+TabLoadingFrameNavigationThrottlesParams::GetParams() {
+  TabLoadingFrameNavigationThrottlesParams params;
+  params.minimum_throttle_timeout = base::TimeDelta::FromMilliseconds(
+      kMinimumThrottleTimeoutMilliseconds.Get());
+  params.maximum_throttle_timeout = base::TimeDelta::FromMilliseconds(
+      kMaximumThrottleTimeoutMilliseconds.Get());
+  return params;
+}
+
+}  // namespace features
+}  // namespace performance_manager
diff --git a/components/performance_manager/graph/frame_node_impl_unittest.cc b/components/performance_manager/graph/frame_node_impl_unittest.cc
index baf144f0..cb971e44 100644
--- a/components/performance_manager/graph/frame_node_impl_unittest.cc
+++ b/components/performance_manager/graph/frame_node_impl_unittest.cc
@@ -59,10 +59,12 @@
 }
 
 TEST_F(FrameNodeImplTest, GetFrameNodeById) {
-  RenderProcessHostProxy render_process_proxy_a(42);
-  RenderProcessHostProxy render_process_proxy_b(43);
-  auto process_a = CreateNode<ProcessNodeImpl>(render_process_proxy_a);
-  auto process_b = CreateNode<ProcessNodeImpl>(render_process_proxy_b);
+  auto process_a =
+      CreateNode<ProcessNodeImpl>(content::PROCESS_TYPE_RENDERER,
+                                  RenderProcessHostProxy::CreateForTesting(42));
+  auto process_b =
+      CreateNode<ProcessNodeImpl>(content::PROCESS_TYPE_RENDERER,
+                                  RenderProcessHostProxy::CreateForTesting(43));
   auto page = CreateNode<PageNodeImpl>();
   auto frame_a1 = CreateFrameNodeAutoId(process_a.get(), page.get());
   auto frame_a2 = CreateFrameNodeAutoId(process_a.get(), page.get());
diff --git a/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc
index bfd7d3a..4b969f7 100644
--- a/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc
+++ b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy.cc
@@ -5,9 +5,12 @@
 #include "components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h"
 
 #include "base/bind.h"
+#include "base/no_destructor.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "components/performance_manager/graph/page_node_impl.h"
+#include "components/performance_manager/public/features.h"
+#include "components/performance_manager/public/mechanisms/tab_loading_frame_navigation_scheduler.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -21,16 +24,42 @@
 
 namespace {
 
+// A default implementation of the mechanism delegate that uses the
+// TabLoadingFrameNavigationScheduler.
+class DefaultMechanismDelegate
+    : public TabLoadingFrameNavigationPolicy::MechanismDelegate {
+ public:
+  using MechanismClass = mechanisms::TabLoadingFrameNavigationScheduler;
+
+  DefaultMechanismDelegate() = default;
+  ~DefaultMechanismDelegate() override = default;
+
+  // MechanismDelegate implementation:
+  void SetThrottlingEnabled(bool enabled) override {
+    return MechanismClass::SetThrottlingEnabled(enabled);
+  }
+  void StopThrottling(content::WebContents* contents,
+                      int64_t last_navigation_id) override {
+    MechanismClass::StopThrottling(contents, last_navigation_id);
+  }
+
+  static DefaultMechanismDelegate* Instance() {
+    static base::NoDestructor<DefaultMechanismDelegate> default_mechanism;
+    return default_mechanism.get();
+  }
+};
+
 bool CanThrottleUrlScheme(const GURL& url) {
   return url.SchemeIs("http") || url.SchemeIs("https");
 }
 
 }  // namespace
 
-TabLoadingFrameNavigationPolicy::TabLoadingFrameNavigationPolicy(
-    StopThrottlingCallback stop_throttling_callback)
-    : stop_throttling_callback_(stop_throttling_callback) {
-  DCHECK(!stop_throttling_callback.is_null());
+TabLoadingFrameNavigationPolicy::TabLoadingFrameNavigationPolicy()
+    : mechanism_(DefaultMechanismDelegate::Instance()) {
+  auto params = features::TabLoadingFrameNavigationThrottlesParams::GetParams();
+  timeout_min_ = params.minimum_throttle_timeout;
+  timeout_max_ = params.maximum_throttle_timeout;
 }
 
 TabLoadingFrameNavigationPolicy::~TabLoadingFrameNavigationPolicy() {
@@ -113,15 +142,16 @@
   if (!frame_node->IsMainFrame() || !frame_node->IsCurrent())
     return;
 
-  // Wait for another FCP time period, waiting a minimum of 1 second.
+  // Wait for another FCP time period, but enforce a lower bound.
   double fcp_ms = time_since_navigation_start.InMillisecondsF();
-  double delta_ms = std::max(1000.0, fcp_ms);
+  double delta_ms = std::max(timeout_min_.InMillisecondsF(), fcp_ms);
   MaybeUpdatePageTimeout(frame_node->GetPageNode(),
                          base::TimeDelta::FromMillisecondsD(delta_ms));
 }
 
 void TabLoadingFrameNavigationPolicy::OnPassedToGraph(Graph* graph) {
   DCHECK(NothingRegistered(graph));
+  mechanism_->SetThrottlingEnabled(true);
   graph->AddFrameNodeObserver(this);
   graph->AddPageNodeObserver(this);
   graph->RegisterObject(this);
@@ -132,6 +162,7 @@
   graph->UnregisterObject(this);
   graph->RemovePageNodeObserver(this);
   graph->RemoveFrameNodeObserver(this);
+  mechanism_->SetThrottlingEnabled(false);
 }
 
 // static
@@ -169,8 +200,7 @@
   }
 
   // Create a brand new timeout for the page.
-  // TODO(chrisha): Make this configurable via Finch experiment.
-  CreatePageTimeout(page_node, timeout_);
+  CreatePageTimeout(page_node, timeout_max_);
 }
 
 void TabLoadingFrameNavigationPolicy::CreatePageTimeout(
@@ -279,18 +309,19 @@
     timeouts_.pop();
 
     // Post a task to the UI thread to notify the mechanism to stop throttling
-    // the contents.
+    // the contents. Note that |mechanism_| is expected to effectively live
+    // forever (it is only a testing seam, in production it is a static
+    // singleton), so passing base::Unretained is safe.
     base::PostTask(
         FROM_HERE,
         {content::BrowserThread::UI, base::TaskPriority::USER_VISIBLE},
         base::BindOnce(
-            [](StopThrottlingCallback stop_throttling_callback,
-               const WebContentsProxy& proxy) {
+            [](MechanismDelegate* mechanism, const WebContentsProxy& proxy) {
               auto* contents = proxy.Get();
               if (contents)
-                stop_throttling_callback.Run(contents);
+                mechanism->StopThrottling(contents, proxy.LastNavigationId());
             },
-            stop_throttling_callback_, page_node->GetContentsProxy()));
+            base::Unretained(mechanism_), page_node->GetContentsProxy()));
   }
 }
 
diff --git a/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy_unittest.cc b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy_unittest.cc
index ccb6bdc..6332d5a 100644
--- a/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy_unittest.cc
+++ b/components/performance_manager/graph/policies/tab_loading_frame_navigation_policy_unittest.cc
@@ -8,7 +8,9 @@
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "components/performance_manager/public/features.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/test_support/performance_manager_test_harness.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -26,7 +28,10 @@
 namespace {
 
 class TabLoadingFrameNavigationPolicyTest
-    : public PerformanceManagerTestHarness {
+    : public PerformanceManagerTestHarness,
+      public TabLoadingFrameNavigationPolicy::MechanismDelegate {
+  using Super = PerformanceManagerTestHarness;
+
  public:
   TabLoadingFrameNavigationPolicyTest()
       : PerformanceManagerTestHarness(
@@ -38,25 +43,51 @@
   ~TabLoadingFrameNavigationPolicyTest() override = default;
 
   void SetUp() override {
-    PerformanceManagerTestHarness::SetUp();
+    Super::SetUp();
 
-    // The unittest fixture outlives the graph under test, so passing an
-    // unretained pointer is safe.
-    auto callback = base::BindRepeating(
-        &TabLoadingFrameNavigationPolicyTest::OnStopThrottlingWrapper,
-        base::Unretained(this));
+    // When the policy is created we expect it to enable the mechanism.
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    EXPECT_CALL(*this, OnSetThrottlingEnabled(true));
+    PerformanceManager::CallOnGraph(
+        FROM_HERE,
+        base::BindLambdaForTesting([policy = this->policy_](Graph* graph) {
+          // Destroy the policy object early, so that it invokes
+          // StopThrottlingEverything in a controlled way.
+          graph->TakeFromGraph(policy);
+        }));
 
     // Create the policy object and inject it into the graph.
     std::unique_ptr<TabLoadingFrameNavigationPolicy> policy =
-        std::make_unique<TabLoadingFrameNavigationPolicy>(callback);
+        std::make_unique<TabLoadingFrameNavigationPolicy>();
+    policy->SetMechanismDelegateForTesting(this);
     policy_ = policy.get();
     PerformanceManager::PassToGraph(FROM_HERE, std::move(policy));
+    run_loop.Run();
+    testing::Mock::VerifyAndClearExpectations(this);
 
     // Ensure that the policy default initializes with no throttles in place.
     ExpectThrottledPageCount(0);
     start_ = task_environment()->GetMockTickClock()->NowTicks();
   }
 
+  void TearDown() override {
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    EXPECT_CALL(*this, OnSetThrottlingEnabled(false));
+    PerformanceManager::CallOnGraph(
+        FROM_HERE,
+        base::BindLambdaForTesting([policy = this->policy_](Graph* graph) {
+          // Destroy the policy object early, so that it invokes
+          // StopThrottlingEverything in a controlled way.
+          graph->TakeFromGraph(policy);
+        }));
+    policy_ = nullptr;
+    run_loop.Run();
+    testing::Mock::VerifyAndClearExpectations(this);
+    Super::TearDown();
+  }
+
   void RunUntilStopThrottling() {
     base::RunLoop run_loop;
     quit_closure_ = run_loop.QuitClosure();
@@ -98,7 +129,8 @@
     run_loop.Run();
   }
 
-  // Invoked by the policy when throttling should stop for a given contents.
+  // The MechanismDelegate calls redirect here.
+  MOCK_METHOD1(OnSetThrottlingEnabled, void(bool));
   MOCK_METHOD1(OnStopThrottling, void(content::WebContents*));
 
   // Accessors.
@@ -110,12 +142,19 @@
     base::TimeTicks now = task_environment()->GetMockTickClock()->NowTicks();
     base::TimeDelta elapsed = now - start_;
     double relative =
-        elapsed.InSecondsF() / policy_->GetTimeoutForTesting().InSecondsF();
+        elapsed.InSecondsF() / policy_->GetMaxTimeoutForTesting().InSecondsF();
     return relative;
   }
 
  private:
-  void OnStopThrottlingWrapper(content::WebContents* contents) {
+  // MechanismDelegate implementation:
+  void SetThrottlingEnabled(bool enabled) override {
+    OnSetThrottlingEnabled(enabled);
+    // Always expect the quit closure to be set for calls to this.
+    quit_closure_.Run();
+  }
+  void StopThrottling(content::WebContents* contents,
+                      int64_t last_navigation_id_unused) override {
     OnStopThrottling(contents);
 
     // Time can be manually advanced as well, so we're not always in a RunLoop.
@@ -246,7 +285,7 @@
   ASSERT_EQ(0.0, GetRelativeTime());
 
   // Advance time by half of a timeout. No callbacks should fire.
-  task_environment()->FastForwardBy(policy()->GetTimeoutForTesting() * 0.5);
+  task_environment()->FastForwardBy(policy()->GetMaxTimeoutForTesting() * 0.5);
   testing::Mock::VerifyAndClearExpectations(this);
 
   // We are now at time T / 2.
@@ -264,7 +303,7 @@
   RunUntilStopThrottling();
   ExpectThrottledPageCount(1);
   base::TimeTicks stop1 = task_environment()->GetMockTickClock()->NowTicks();
-  EXPECT_EQ(policy()->GetTimeoutForTesting(), stop1 - start());
+  EXPECT_EQ(policy()->GetMaxTimeoutForTesting(), stop1 - start());
 
   // We are now at time T.
   ASSERT_EQ(1.0, GetRelativeTime());
@@ -279,7 +318,7 @@
   // Advance time by a quarter of the timeout period, bringing us to time
   // 1.25 T. This means there will be 1/4 of the timeout left on the second as
   // it expires at 1.5 T.
-  task_environment()->FastForwardBy(policy()->GetTimeoutForTesting() * 0.25);
+  task_environment()->FastForwardBy(policy()->GetMaxTimeoutForTesting() * 0.25);
   testing::Mock::VerifyAndClearExpectations(this);
   ExpectThrottledPageCount(2);
 
@@ -296,7 +335,7 @@
 
   // Advance time to a time past when the second notification *would* have
   // expired, and expect no notifications. We'll go to 1.6 T, so 0.35 T further.
-  task_environment()->FastForwardBy(policy()->GetTimeoutForTesting() * 0.35);
+  task_environment()->FastForwardBy(policy()->GetMaxTimeoutForTesting() * 0.35);
   testing::Mock::VerifyAndClearExpectations(this);
 
   // We are now at time 1.6 T.
@@ -307,11 +346,31 @@
   RunUntilStopThrottling();
   ExpectThrottledPageCount(0);
   base::TimeTicks stop3 = task_environment()->GetMockTickClock()->NowTicks();
-  EXPECT_EQ(policy()->GetTimeoutForTesting(), stop3 - stop1);
+  EXPECT_EQ(policy()->GetMaxTimeoutForTesting(), stop3 - stop1);
 
   // We are now at time 2 T.
   ASSERT_EQ(2.0, GetRelativeTime());
 }
 
+TEST(TabLoadingFrameNavigationThrottlesParams, FeatureParamsWork) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      features::kTabLoadingFrameNavigationThrottles,
+      {{"MinimumThrottleTimeoutMilliseconds", "2500"},
+       {"MaximumThrottleTimeoutMilliseconds", "25000"}});
+
+  // Make sure the parsing works.
+  auto params = features::TabLoadingFrameNavigationThrottlesParams::GetParams();
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(2500),
+            params.minimum_throttle_timeout);
+  EXPECT_EQ(base::TimeDelta::FromSeconds(25), params.maximum_throttle_timeout);
+
+  // And make sure the plumbing works.
+  std::unique_ptr<TabLoadingFrameNavigationPolicy> policy =
+      std::make_unique<TabLoadingFrameNavigationPolicy>();
+  EXPECT_EQ(params.minimum_throttle_timeout, policy->GetMinTimeoutForTesting());
+  EXPECT_EQ(params.maximum_throttle_timeout, policy->GetMaxTimeoutForTesting());
+}
+
 }  // namespace policies
 }  // namespace performance_manager
diff --git a/components/performance_manager/graph/process_node_impl.cc b/components/performance_manager/graph/process_node_impl.cc
index 20755d9..2dcd86f 100644
--- a/components/performance_manager/graph/process_node_impl.cc
+++ b/components/performance_manager/graph/process_node_impl.cc
@@ -13,8 +13,10 @@
 
 namespace performance_manager {
 
-ProcessNodeImpl::ProcessNodeImpl(RenderProcessHostProxy render_process_proxy)
-    : render_process_host_proxy_(std::move(render_process_proxy)) {
+ProcessNodeImpl::ProcessNodeImpl(content::ProcessType process_type,
+                                 RenderProcessHostProxy render_process_proxy)
+    : process_type_(process_type),
+      render_process_host_proxy_(std::move(render_process_proxy)) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
@@ -142,6 +144,11 @@
   process_.SetAndNotify(this, std::move(process));
 }
 
+content::ProcessType ProcessNodeImpl::GetProcessType() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return process_type();
+}
+
 base::ProcessId ProcessNodeImpl::GetProcessId() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return process_id();
diff --git a/components/performance_manager/graph/process_node_impl.h b/components/performance_manager/graph/process_node_impl.h
index 0344530..9e9a4012 100644
--- a/components/performance_manager/graph/process_node_impl.h
+++ b/components/performance_manager/graph/process_node_impl.h
@@ -45,7 +45,8 @@
  public:
   static constexpr NodeTypeEnum Type() { return NodeTypeEnum::kProcess; }
 
-  explicit ProcessNodeImpl(RenderProcessHostProxy render_process_proxy);
+  ProcessNodeImpl(content::ProcessType process_type,
+                  RenderProcessHostProxy render_process_proxy);
 
   ~ProcessNodeImpl() override;
 
@@ -78,6 +79,7 @@
   // Otherwise, returns nullptr.
   PageNodeImpl* GetPageNodeIfExclusive() const;
 
+  content::ProcessType process_type() const { return process_type_; }
   // Use process_id() in preference to process().Pid(). It's always valid to
   // access, but will return kNullProcessId when the process is not valid. It
   // will also retain the process ID for a process that has exited.
@@ -128,6 +130,7 @@
 
   // ProcessNode implementation. These are private so that users of the impl use
   // the private getters rather than the public interface.
+  content::ProcessType GetProcessType() const override;
   base::ProcessId GetProcessId() const override;
   const base::Process& GetProcess() const override;
   base::Time GetLaunchTime() const override;
@@ -160,6 +163,7 @@
   base::Time launch_time_;
   base::Optional<int32_t> exit_status_;
 
+  const content::ProcessType process_type_;
   const RenderProcessHostProxy render_process_host_proxy_;
 
   ObservedProperty::NotifiesAlways<
diff --git a/components/performance_manager/graph/process_node_impl_describer.cc b/components/performance_manager/graph/process_node_impl_describer.cc
index 58d0cc8..b255fc7 100644
--- a/components/performance_manager/graph/process_node_impl_describer.cc
+++ b/components/performance_manager/graph/process_node_impl_describer.cc
@@ -109,6 +109,14 @@
     ret.SetIntKey("render_process_id", impl->GetRenderProcessId());
   }
 
+  // The content function returns "Tab" for renderers - whereas "Renderer" is
+  // the common vernacular here.
+  std::string process_type =
+      content::GetProcessTypeNameInEnglish(impl->process_type());
+  if (impl->process_type() == content::PROCESS_TYPE_RENDERER)
+    process_type = "Renderer";
+  ret.SetStringKey("process_type", process_type);
+
   ret.SetStringKey("pid", base::NumberToString(impl->process_id()));
 
   ret.SetKey("process", GetProcessValueDict(impl->process()));
diff --git a/components/performance_manager/graph/process_node_impl_unittest.cc b/components/performance_manager/graph/process_node_impl_unittest.cc
index 8de842b..5aea9f2 100644
--- a/components/performance_manager/graph/process_node_impl_unittest.cc
+++ b/components/performance_manager/graph/process_node_impl_unittest.cc
@@ -196,6 +196,22 @@
   graph()->RemoveProcessNodeObserver(&obs);
 }
 
+TEST_F(ProcessNodeImplTest, ConstructionArguments) {
+  constexpr int kRenderProcessHostId = 0xF0B;
+  auto process_node = CreateNode<ProcessNodeImpl>(
+      content::PROCESS_TYPE_GPU,
+      RenderProcessHostProxy::CreateForTesting(kRenderProcessHostId));
+
+  const ProcessNode* public_process_node = process_node.get();
+
+  EXPECT_EQ(content::PROCESS_TYPE_GPU, process_node->process_type());
+  EXPECT_EQ(content::PROCESS_TYPE_GPU, public_process_node->GetProcessType());
+
+  EXPECT_EQ(kRenderProcessHostId,
+            public_process_node->GetRenderProcessHostProxy()
+                .render_process_host_id());
+}
+
 TEST_F(ProcessNodeImplTest, PublicInterface) {
   auto process_node = CreateNode<ProcessNodeImpl>();
   const ProcessNode* public_process_node = process_node.get();
@@ -209,6 +225,8 @@
 
   // Simply test that the public interface impls yield the same result as their
   // private counterpart.
+  EXPECT_EQ(process_node->process_type(),
+            public_process_node->GetProcessType());
 
   const base::Process self = base::Process::Current();
   const base::Time launch_time = base::Time::Now();
diff --git a/components/performance_manager/mechanisms/tab_loading_frame_navigation_scheduler.cc b/components/performance_manager/mechanisms/tab_loading_frame_navigation_scheduler.cc
new file mode 100644
index 0000000..208c938
--- /dev/null
+++ b/components/performance_manager/mechanisms/tab_loading_frame_navigation_scheduler.cc
@@ -0,0 +1,242 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/performance_manager/public/mechanisms/tab_loading_frame_navigation_scheduler.h"
+
+#include "base/no_destructor.h"
+#include "components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/web_contents.h"
+
+namespace performance_manager {
+namespace mechanisms {
+
+namespace {
+
+using PolicyDelegate = TabLoadingFrameNavigationScheduler::PolicyDelegate;
+
+// The default policy delegate that delegates directly to the
+// TabLoadingFrameNavigationPolicy class.
+class DefaultPolicyDelegate : public PolicyDelegate {
+ public:
+  using PolicyClass = policies::TabLoadingFrameNavigationPolicy;
+
+  DefaultPolicyDelegate() = default;
+  DefaultPolicyDelegate(const DefaultPolicyDelegate&) = delete;
+  DefaultPolicyDelegate& operator=(const DefaultPolicyDelegate&) = delete;
+  ~DefaultPolicyDelegate() override = default;
+
+  // PolicyDelegate implementation:
+  bool ShouldThrottleWebContents(content::WebContents* contents) override {
+    return PolicyClass::ShouldThrottleWebContents(contents);
+  }
+  bool ShouldThrottleNavigation(content::NavigationHandle* handle) override {
+    return PolicyClass::ShouldThrottleNavigation(handle);
+  }
+
+  static DefaultPolicyDelegate* Instance() {
+    static base::NoDestructor<DefaultPolicyDelegate> default_policy_delegate;
+    return default_policy_delegate.get();
+  }
+};
+
+PolicyDelegate* g_policy_delegate = nullptr;
+bool g_throttling_enabled = false;
+TabLoadingFrameNavigationScheduler* g_root = nullptr;
+
+PolicyDelegate* GetPolicyDelegate() {
+  if (!g_policy_delegate)
+    g_policy_delegate = DefaultPolicyDelegate::Instance();
+  return g_policy_delegate;
+}
+
+}  // namespace
+
+// A very simple throttle that always defers until Resume is called.
+class TabLoadingFrameNavigationScheduler::Throttle
+    : public content::NavigationThrottle {
+ public:
+  explicit Throttle(content::NavigationHandle* handle)
+      : content::NavigationThrottle(handle) {}
+  ~Throttle() override = default;
+
+  // content::NavigationThrottle implementation
+  const char* GetNameForLogging() override {
+    static constexpr char kName[] =
+        "TabLoadingFrameNavigationScheduler::Throttle";
+    return kName;
+  }
+  content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override {
+    return content::NavigationThrottle::DEFER;
+  }
+
+  // Make this public so the scheduler can invoke it.
+  using content::NavigationThrottle::Resume;
+};
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(TabLoadingFrameNavigationScheduler)
+
+TabLoadingFrameNavigationScheduler::~TabLoadingFrameNavigationScheduler() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  DCHECK(throttles_.empty());
+
+  // Unlink ourselves from the linked list.
+  if (g_root == this) {
+    DCHECK_EQ(nullptr, prev_);
+    g_root = next_;
+  }
+  if (prev_) {
+    DCHECK_EQ(this, prev_->next_);
+    prev_->next_ = next_;
+    // Do not null |prev_| so we can access it below if needed.
+  }
+  if (next_) {
+    DCHECK_EQ(this, next_->prev_);
+    next_->prev_ = prev_;
+    next_ = nullptr;
+  }
+  prev_ = nullptr;
+}
+
+// static
+std::unique_ptr<content::NavigationThrottle>
+TabLoadingFrameNavigationScheduler::MaybeCreateThrottleForNavigation(
+    content::NavigationHandle* handle) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  std::unique_ptr<content::NavigationThrottle> empty_throttle;
+
+  if (!g_throttling_enabled)
+    return empty_throttle;
+
+  // Get the contents, and the associated scheduler if it exists.
+  auto* contents = handle->GetWebContents();
+  auto* scheduler = FromWebContents(contents);
+
+  // If this is a non-main frame and no scheduler exists, then the decision was
+  // already made to *not* throttle this contents, so we can return early.
+  if (!handle->IsInMainFrame() && !scheduler) {
+    return empty_throttle;
+  }
+
+  // If a scheduler exists and this is a main frame navigation then its a
+  // renavigation and the contents is being reused. In this case we need to
+  // tear down the existing scheduler, and potentially create a new one.
+  if (handle->IsInMainFrame() && scheduler) {
+    DCHECK_NE(handle->GetNavigationId(), scheduler->navigation_id_);
+    scheduler->StopThrottlingImpl();  // Causes |scheduler| to delete itself.
+    scheduler = FromWebContents(contents);
+    DCHECK_EQ(nullptr, scheduler);
+  }
+
+  // If there's no scheduler for this contents, check the policy object to see
+  // if one should be created.
+  if (!scheduler) {
+    DCHECK(handle->IsInMainFrame());
+    if (!GetPolicyDelegate()->ShouldThrottleWebContents(contents)) {
+      return empty_throttle;
+    }
+    CreateForWebContents(contents);
+    scheduler = FromWebContents(contents);
+    DCHECK(scheduler);
+    scheduler->navigation_id_ = handle->GetNavigationId();
+    // The main frame should never be throttled, so we can return early.
+    return empty_throttle;
+  }
+
+  // At this point we have a scheduler, and the navigation is for a child
+  // frame. Determine whether the child frame should be throttled.
+  if (!GetPolicyDelegate()->ShouldThrottleNavigation(handle)) {
+    return empty_throttle;
+  }
+
+  // Getting here indicates that the navigation is to be throttled. Create a
+  // throttle and remember it.
+  std::unique_ptr<Throttle> throttle(new Throttle(handle));
+  auto result =
+      scheduler->throttles_.insert(std::make_pair(handle, throttle.get()));
+  DCHECK(result.second);
+  return throttle;
+}
+
+// static
+void TabLoadingFrameNavigationScheduler::SetThrottlingEnabled(bool enabled) {
+  if (enabled == g_throttling_enabled)
+    return;
+  g_throttling_enabled = enabled;
+  if (enabled)
+    return;
+
+  // At this point the throttling is being disabled. Stop throttling all
+  // currently-throttled contents.
+  while (g_root)
+    g_root->StopThrottlingImpl();  // Causes |g_root| to delete itself.
+}
+
+// static
+void TabLoadingFrameNavigationScheduler::StopThrottling(
+    content::WebContents* contents,
+    int64_t last_navigation_id) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // This should never be called for a |contents| without an associated
+  // scheduler, as besides contents re-use, all schedulers eventually receive
+  // a StopThrottling notification.
+  auto* scheduler = FromWebContents(contents);
+  // There is a race between renavigations and policy messages. Only dispatch
+  // this if its intended for the appropriate navigation ID.
+  if (scheduler->navigation_id_ != last_navigation_id)
+    return;
+  scheduler->StopThrottlingImpl();
+}
+
+// static
+void TabLoadingFrameNavigationScheduler::SetPolicyDelegateForTesting(
+    PolicyDelegate* policy_delegate) {
+  g_policy_delegate = policy_delegate;
+}
+
+// static
+bool TabLoadingFrameNavigationScheduler::IsThrottlingEnabledForTesting() {
+  return g_throttling_enabled;
+}
+
+TabLoadingFrameNavigationScheduler::TabLoadingFrameNavigationScheduler(
+    content::WebContents* contents)
+    : content::WebContentsObserver(contents) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // Link ourselves into the linked list.
+  prev_ = nullptr;
+  next_ = g_root;
+  if (next_)
+    next_->prev_ = this;
+  g_root = this;
+}
+
+void TabLoadingFrameNavigationScheduler::DidFinishNavigation(
+    content::NavigationHandle* handle) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // If we're throttling a canceled navigation then stop tracking it. The
+  // |handle| becomes invalid shortly after this function returns.
+  throttles_.erase(handle);
+}
+
+void TabLoadingFrameNavigationScheduler::StopThrottlingImpl() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // Release all of the throttles.
+  for (auto& entry : throttles_) {
+    auto* throttle = entry.second;
+    throttle->Resume();
+  }
+  throttles_.clear();
+
+  // Tear down this object. This must be called last so as not to UAF ourselves.
+  // Note that this is always called from static functions in this translation
+  // unit, thus there are no other frames on the stack belonging to this object.
+  web_contents()->RemoveUserData(UserDataKey());
+}
+
+}  // namespace mechanisms
+}  // namespace performance_manager
diff --git a/components/performance_manager/mechanisms/tab_loading_frame_navigation_scheduler_browsertest.cc b/components/performance_manager/mechanisms/tab_loading_frame_navigation_scheduler_browsertest.cc
new file mode 100644
index 0000000..fd995521
--- /dev/null
+++ b/components/performance_manager/mechanisms/tab_loading_frame_navigation_scheduler_browsertest.cc
@@ -0,0 +1,316 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/performance_manager/public/mechanisms/tab_loading_frame_navigation_scheduler.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "components/performance_manager/test_support/performance_manager_browsertest_harness.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "content/shell/browser/shell_content_browser_client.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_manager {
+namespace mechanisms {
+
+namespace {
+
+class LenientMockPolicyDelegate
+    : public TabLoadingFrameNavigationScheduler::PolicyDelegate {
+ public:
+  LenientMockPolicyDelegate() = default;
+  ~LenientMockPolicyDelegate() override = default;
+
+  // PolicyDelegate implementation:
+  MOCK_METHOD1(ShouldThrottleWebContents, bool(content::WebContents*));
+  MOCK_METHOD1(ShouldThrottleNavigation, bool(content::NavigationHandle*));
+};
+
+using MockPolicyDelegate = ::testing::StrictMock<LenientMockPolicyDelegate>;
+
+class TabLoadingFrameNavigationSchedulerTest
+    : public PerformanceManagerBrowserTestHarness {
+  using Super = PerformanceManagerBrowserTestHarness;
+
+ public:
+  TabLoadingFrameNavigationSchedulerTest() = default;
+  ~TabLoadingFrameNavigationSchedulerTest() override = default;
+
+  // Used as an embedder hook. Allows us to add navigation throttles to new
+  // navigations. This is hooked up to the ShellContentBrowserClient in
+  // "CreatedBrowserMainParts".
+  std::vector<std::unique_ptr<content::NavigationThrottle>>
+  CreateThrottlesForNavigation(content::NavigationHandle* handle) {
+    std::vector<std::unique_ptr<content::NavigationThrottle>> ret;
+    // Invoke the class under test here. We control the outcome of this via
+    // the MockPolicyDelegate.
+    std::unique_ptr<content::NavigationThrottle> throttle =
+        TabLoadingFrameNavigationScheduler::MaybeCreateThrottleForNavigation(
+            handle);
+    if (throttle)
+      ret.push_back(std::move(throttle));
+    return ret;
+  }
+
+  // content::BrowserTestBase overrides:
+  void CreatedBrowserMainParts(
+      content::BrowserMainParts* browser_main_parts) override {
+    Super::CreatedBrowserMainParts(browser_main_parts);
+
+    TabLoadingFrameNavigationScheduler::SetPolicyDelegateForTesting(
+        &mock_policy_delegate_);
+
+    // Enable the mechanism at the beginning of all tests.
+    EXPECT_FALSE(
+        TabLoadingFrameNavigationScheduler::IsThrottlingEnabledForTesting());
+    TabLoadingFrameNavigationScheduler::SetThrottlingEnabled(true);
+    EXPECT_TRUE(
+        TabLoadingFrameNavigationScheduler::IsThrottlingEnabledForTesting());
+
+    // Register a callback so we can set navigation throttles. Passing
+    // |this| is fine because we clear the callback before we are torn down.
+    content::ShellContentBrowserClient::Get()
+        ->set_create_throttles_for_navigation_callback(
+            base::BindRepeating(&TabLoadingFrameNavigationSchedulerTest::
+                                    CreateThrottlesForNavigation,
+                                base::Unretained(this)));
+  }
+  void PostRunTestOnMainThread() override {
+    // Set an empty callback so we stop getting called.
+    base::RepeatingCallback<
+        std::vector<std::unique_ptr<content::NavigationThrottle>>(
+            content::NavigationHandle*)>
+        callback;
+    content::ShellContentBrowserClient::Get()
+        ->set_create_throttles_for_navigation_callback(callback);
+
+    // Disable at the end of all tests.
+    TabLoadingFrameNavigationScheduler::SetThrottlingEnabled(false);
+    EXPECT_FALSE(
+        TabLoadingFrameNavigationScheduler::IsThrottlingEnabledForTesting());
+
+    TabLoadingFrameNavigationScheduler::SetPolicyDelegateForTesting(nullptr);
+
+    Super::PostRunTestOnMainThread();
+  }
+
+  // Helper function for getting the scheduler instance associated with the
+  // given |contents|.
+  TabLoadingFrameNavigationScheduler* GetScheduler(
+      content::WebContents* contents) {
+    return TabLoadingFrameNavigationScheduler::FromWebContents(contents);
+  }
+
+ protected:
+  MockPolicyDelegate mock_policy_delegate_;
+};
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(TabLoadingFrameNavigationSchedulerTest,
+                       ThrottlingDisabled) {
+  GURL url(embedded_test_server()->GetURL("a.com", "/a.html"));
+  auto* contents = shell()->web_contents();
+
+  TabLoadingFrameNavigationScheduler::SetThrottlingEnabled(false);
+  EXPECT_FALSE(
+      TabLoadingFrameNavigationScheduler::IsThrottlingEnabledForTesting());
+
+  // No scheduler should be created if the mechanism is disabled. There will be
+  // no calls to the mock policy engine.
+  StartNavigation(contents, url);
+  WaitForLoad(contents);
+  testing::Mock::VerifyAndClearExpectations(this);
+}
+
+IN_PROC_BROWSER_TEST_F(TabLoadingFrameNavigationSchedulerTest,
+                       SchedulerNotCreated) {
+  GURL url(embedded_test_server()->GetURL("a.com", "/a.html"));
+  auto* contents = shell()->web_contents();
+
+  // No scheduler should be created if "ShouldThrottleWebContents" returns
+  // false.
+  base::RunLoop run_loop;
+  EXPECT_CALL(mock_policy_delegate_, ShouldThrottleWebContents(contents))
+      .WillOnce(testing::Invoke([&run_loop](content::WebContents*) -> bool {
+        run_loop.Quit();
+        return false;
+      }));
+  StartNavigation(contents, url);
+  run_loop.Run();
+  auto* scheduler = GetScheduler(contents);
+  EXPECT_EQ(nullptr, scheduler);
+
+  // Wait for the load to finish so that it's not ongoing while the test
+  // fixture tears down.
+  WaitForLoad(contents);
+}
+
+IN_PROC_BROWSER_TEST_F(TabLoadingFrameNavigationSchedulerTest,
+                       SchedulerCreatedAndDestroyed) {
+  GURL url1(embedded_test_server()->GetURL("a.com", "/a.html"));
+  GURL url2(embedded_test_server()->GetURL("b.com", "/b.html"));
+  auto* contents1 = shell()->web_contents();
+  auto* shell2 = CreateShell();
+  auto* contents2 = shell2->web_contents();
+
+  // A scheduler *should* be created if "ShouldThrottleWebContents" returns
+  // true.
+  {
+    base::RunLoop run_loop;
+    EXPECT_CALL(mock_policy_delegate_, ShouldThrottleWebContents(contents1))
+        .WillOnce(testing::Invoke([&run_loop](content::WebContents*) -> bool {
+          run_loop.Quit();
+          return true;
+        }));
+    StartNavigation(contents1, url1);
+    run_loop.Run();
+    auto* scheduler = GetScheduler(contents1);
+    EXPECT_NE(nullptr, scheduler);
+    EXPECT_EQ(0u, scheduler->GetThrottleCountForTesting());
+  }
+
+  // Start another navigation and expect another scheduler to be created.
+  {
+    base::RunLoop run_loop;
+    EXPECT_CALL(mock_policy_delegate_, ShouldThrottleWebContents(contents2))
+        .WillOnce(testing::Invoke([&run_loop](content::WebContents*) -> bool {
+          run_loop.Quit();
+          return true;
+        }));
+    StartNavigation(contents2, url2);
+    run_loop.Run();
+    auto* scheduler = GetScheduler(contents2);
+    EXPECT_NE(nullptr, scheduler);
+    EXPECT_EQ(0u, scheduler->GetThrottleCountForTesting());
+  }
+
+  // Disable throttling and expect all schedulers to be torn down.
+  TabLoadingFrameNavigationScheduler::SetThrottlingEnabled(false);
+  EXPECT_FALSE(
+      TabLoadingFrameNavigationScheduler::IsThrottlingEnabledForTesting());
+  EXPECT_EQ(nullptr, GetScheduler(contents1));
+  EXPECT_EQ(nullptr, GetScheduler(contents2));
+
+  // Wait for the load to finish so that it's not ongoing while the test
+  // fixture tears down.
+  WaitForLoad(contents1);
+  WaitForLoad(contents2);
+}
+
+IN_PROC_BROWSER_TEST_F(TabLoadingFrameNavigationSchedulerTest,
+                       ChildFrameThrottled) {
+  GURL url(embedded_test_server()->GetURL("a.com", "/a_embeds_b.html"));
+  auto* contents = shell()->web_contents();
+
+  // Throttle the navigation, and throttle the child frame.
+  base::RunLoop run_loop1;
+  base::RunLoop run_loop2;
+  EXPECT_CALL(mock_policy_delegate_, ShouldThrottleWebContents(contents))
+      .WillOnce(testing::Invoke([&run_loop1](content::WebContents*) -> bool {
+        run_loop1.Quit();
+        return true;
+      }));
+  EXPECT_CALL(mock_policy_delegate_, ShouldThrottleNavigation(testing::_))
+      .WillOnce(
+          testing::Invoke([&run_loop2](content::NavigationHandle*) -> bool {
+            run_loop2.Quit();
+            return true;
+          }));
+
+  // Start the navigation, and expect scheduler to have been created.
+  StartNavigation(contents, url);
+  run_loop1.Run();
+  auto* scheduler = GetScheduler(contents);
+  EXPECT_NE(nullptr, scheduler);
+  EXPECT_EQ(0u, scheduler->GetThrottleCountForTesting());
+
+  // Wait for the child navigation to have started. We'll know once
+  // the policy function has been invoked, which will quit the runloop.
+  run_loop2.Run();
+
+  // At this point the child frame navigation should be throttled, waiting for
+  // the policy object to notify that the throttles should be removed.
+  EXPECT_EQ(1u, scheduler->GetThrottleCountForTesting());
+
+  // Release the throttles. This also causes the scheduler to be deleted, which
+  // we confirm.
+  scheduler->StopThrottlingForTesting();
+  scheduler = GetScheduler(contents);
+  EXPECT_EQ(nullptr, scheduler);
+
+  // Wait for the load to finish so that it's not ongoing while the test
+  // fixture tears down.
+  WaitForLoad(contents);
+}
+
+IN_PROC_BROWSER_TEST_F(TabLoadingFrameNavigationSchedulerTest,
+                       NavigationInterruptsThrottling) {
+  GURL url(embedded_test_server()->GetURL("a.com", "/a_embeds_b.html"));
+  auto* contents = shell()->web_contents();
+
+  // Throttle the navigation, and throttle the child frame.
+  base::RunLoop run_loop1;
+  base::RunLoop run_loop2;
+  EXPECT_CALL(mock_policy_delegate_, ShouldThrottleWebContents(contents))
+      .WillOnce(testing::Invoke([&run_loop1](content::WebContents*) -> bool {
+        run_loop1.Quit();
+        return true;
+      }));
+  EXPECT_CALL(mock_policy_delegate_, ShouldThrottleNavigation(testing::_))
+      .WillOnce(
+          testing::Invoke([&run_loop2](content::NavigationHandle*) -> bool {
+            run_loop2.Quit();
+            return true;
+          }));
+
+  // Start the navigation, and expect scheduler to have been created.
+  StartNavigation(contents, url);
+  run_loop1.Run();
+  auto* scheduler = GetScheduler(contents);
+  EXPECT_NE(nullptr, scheduler);
+  EXPECT_EQ(0u, scheduler->GetThrottleCountForTesting());
+
+  // Wait for the child navigation to have started. We'll know once
+  // the policy function has been invoked, which will quit the runloop.
+  run_loop2.Run();
+
+  // At this point the child frame navigation should be throttled, waiting for
+  // the policy object to notify that the throttles should be removed.
+  EXPECT_EQ(1u, scheduler->GetThrottleCountForTesting());
+
+  // Reuse the contents for another navigation. This should result in another
+  // call to ShouldThrottleWebContents, and the scheduler should be recreated
+  // with no throttles.
+  url = embedded_test_server()->GetURL("b.com", "/b.html");
+  base::RunLoop run_loop3;
+  EXPECT_CALL(mock_policy_delegate_, ShouldThrottleWebContents(contents))
+      .WillOnce(
+          testing::Invoke([&run_loop3](content::WebContents* contents) -> bool {
+            run_loop3.Quit();
+            return true;
+          }));
+  StartNavigation(contents, url);
+  run_loop3.Run();
+  scheduler = GetScheduler(contents);
+  EXPECT_NE(nullptr, scheduler);
+  EXPECT_EQ(0u, scheduler->GetThrottleCountForTesting());
+
+  // Wait for the load to finish so that it's not ongoing while the test
+  // fixture tears down.
+  WaitForLoad(contents);
+}
+
+}  // namespace mechanisms
+}  // namespace performance_manager
diff --git a/components/performance_manager/performance_manager_impl.cc b/components/performance_manager/performance_manager_impl.cc
index 2080b53..59d50ab 100644
--- a/components/performance_manager/performance_manager_impl.cc
+++ b/components/performance_manager/performance_manager_impl.cc
@@ -143,9 +143,10 @@
 
 // static
 std::unique_ptr<ProcessNodeImpl> PerformanceManagerImpl::CreateProcessNode(
+    content::ProcessType process_type,
     RenderProcessHostProxy proxy) {
   return CreateNodeImpl<ProcessNodeImpl>(
-      base::OnceCallback<void(ProcessNodeImpl*)>(), proxy);
+      base::OnceCallback<void(ProcessNodeImpl*)>(), process_type, proxy);
 }
 
 // static
diff --git a/components/performance_manager/performance_manager_impl.h b/components/performance_manager/performance_manager_impl.h
index 4975947..30579d1 100644
--- a/components/performance_manager/performance_manager_impl.h
+++ b/components/performance_manager/performance_manager_impl.h
@@ -21,6 +21,7 @@
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/public/render_process_host_proxy.h"
 #include "components/performance_manager/public/web_contents_proxy.h"
+#include "content/public/common/process_type.h"
 
 class GURL;
 
@@ -103,6 +104,7 @@
       bool is_audible,
       base::TimeTicks visibility_change_time);
   static std::unique_ptr<ProcessNodeImpl> CreateProcessNode(
+      content::ProcessType process_type,
       RenderProcessHostProxy proxy);
   static std::unique_ptr<WorkerNodeImpl> CreateWorkerNode(
       const std::string& browser_context_id,
diff --git a/components/performance_manager/performance_manager_impl_unittest.cc b/components/performance_manager/performance_manager_impl_unittest.cc
index 4817902..b5087c9 100644
--- a/components/performance_manager/performance_manager_impl_unittest.cc
+++ b/components/performance_manager/performance_manager_impl_unittest.cc
@@ -51,7 +51,8 @@
   int next_render_frame_id = 0;
 
   std::unique_ptr<ProcessNodeImpl> process_node =
-      PerformanceManagerImpl::CreateProcessNode(RenderProcessHostProxy());
+      PerformanceManagerImpl::CreateProcessNode(content::PROCESS_TYPE_RENDERER,
+                                                RenderProcessHostProxy());
   EXPECT_NE(nullptr, process_node.get());
   std::unique_ptr<PageNodeImpl> page_node =
       PerformanceManagerImpl::CreatePageNode(WebContentsProxy(), std::string(),
@@ -75,7 +76,8 @@
   int next_render_frame_id = 0;
   // Create a page node and a small hierarchy of frames.
   std::unique_ptr<ProcessNodeImpl> process_node =
-      PerformanceManagerImpl::CreateProcessNode(RenderProcessHostProxy());
+      PerformanceManagerImpl::CreateProcessNode(content::PROCESS_TYPE_RENDERER,
+                                                RenderProcessHostProxy());
   std::unique_ptr<PageNodeImpl> page_node =
       PerformanceManagerImpl::CreatePageNode(WebContentsProxy(), std::string(),
                                              GURL(), false, false,
diff --git a/components/performance_manager/public/features.h b/components/performance_manager/public/features.h
new file mode 100644
index 0000000..0c4e76f
--- /dev/null
+++ b/components/performance_manager/public/features.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This header contains field trial and variations definitions for policies,
+// mechanisms and features in the performance_manager component.
+
+#include "base/feature_list.h"
+#include "base/time/time.h"
+
+#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_FEATURES_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_FEATURES_H_
+
+namespace performance_manager {
+namespace features {
+
+// The feature that gates the TabLoadingFrameNavigationPolicy, and its
+// mechanism counterpart the TabLoadingFrameNavigationScheduler.
+extern const base::Feature kTabLoadingFrameNavigationThrottles;
+
+// Parameters controlling the TabLoadingFrameNavigationThrottles feature.
+struct TabLoadingFrameNavigationThrottlesParams {
+  TabLoadingFrameNavigationThrottlesParams();
+  ~TabLoadingFrameNavigationThrottlesParams();
+
+  static TabLoadingFrameNavigationThrottlesParams GetParams();
+
+  // The minimum and maximum amount of time throttles will be applied to
+  // non-primary content frames.
+  base::TimeDelta minimum_throttle_timeout;
+  base::TimeDelta maximum_throttle_timeout;
+};
+
+}  // namespace features
+}  // namespace performance_manager
+
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_FEATURES_H_
\ No newline at end of file
diff --git a/components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h b/components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h
index ffe9fc9..d49831a4 100644
--- a/components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h
+++ b/components/performance_manager/public/graph/policies/tab_loading_frame_navigation_policy.h
@@ -29,7 +29,9 @@
 // helper functions for "pulling" those decisions from the policy engine, rather
 // than having them "pushed" to the mechanism. Once throttling has started the
 // subsequent policy decision of when to stop throttling is pushed to the
-// mechanism as is more typical.
+// mechanism as is more typical. By default the policy object will connect to
+// the TabLoadingFrameNavigationScheduler as its policy mechanism, but this can
+// be overridden for tests.
 //
 // At this moment the policy is the following:
 //
@@ -51,13 +53,11 @@
       public GraphRegisteredImpl<TabLoadingFrameNavigationPolicy>,
       public PageNode::ObserverDefaultImpl {
  public:
+  class MechanismDelegate;
   using StopThrottlingCallback =
       base::RepeatingCallback<void(content::WebContents*)>;
 
-  // The |stop_throttling_callback| is used to inform the mechanism of when
-  // navigation throttles applied to a WebContents can be released.
-  explicit TabLoadingFrameNavigationPolicy(
-      StopThrottlingCallback stop_throttling_callback);
+  TabLoadingFrameNavigationPolicy();
   ~TabLoadingFrameNavigationPolicy() override;
 
   // Exposes policy decisions to the scheduler. This must be called on the UI
@@ -76,7 +76,20 @@
 
   // Exposed for testing. Can be called on any sequence, as this is initialized
   // at construction and stays constant afterwards.
-  base::TimeDelta GetTimeoutForTesting() const { return timeout_; }
+  base::TimeDelta GetMinTimeoutForTesting() const { return timeout_min_; }
+  base::TimeDelta GetMaxTimeoutForTesting() const { return timeout_max_; }
+
+  // Exposed for testing. Allows setting a MechanismDelegate. This should be
+  // done immediately after construction and *before* passing to the PM graph.
+  // Note that the provided mechanism will always be invoked on the UI thread.
+  // Note also that it is expected to live until this policy object is taken
+  // from the graph. In production the mechanism is backed by a static
+  // singleton, but code using this seam must manually ensure lifetime
+  // semantics are observed.
+  void SetMechanismDelegateForTesting(MechanismDelegate* mechanism) {
+    DCHECK(mechanism);
+    mechanism_ = mechanism;
+  }
 
  private:
   // PageNodeObserver:
@@ -143,10 +156,10 @@
   };
   base::IntrusiveHeap<Timeout> timeouts_;
 
-  // The timeout after which throttling is stopped. This defaults to the 99th
-  // %ile of LargestContentfulPaint (LCP).
-  // TODO(chrisha): Make this Finch configurable.
-  const base::TimeDelta timeout_ = base::TimeDelta::FromSeconds(40);
+  // The timeout after which throttling is stopped. Configured via Finch.
+  // See features.cc.
+  base::TimeDelta timeout_min_;
+  base::TimeDelta timeout_max_;
 
   // A one shot timer that is used to timeout existing throttles. This will be
   // running whenever |timeouts_| is not empty.
@@ -159,12 +172,30 @@
   // timeout_timer_.desired_run_time(), but this isn't precise.
   base::TimeTicks scheduled_timer_ = base::TimeTicks::Min();
 
-  // The callback that is invoked to inform the mechanism that throttling should
-  // stop.
-  StopThrottlingCallback stop_throttling_callback_;
+  // The mechanism delegate that this object is using
+  MechanismDelegate* mechanism_ = nullptr;
+};
+
+class TabLoadingFrameNavigationPolicy::MechanismDelegate {
+ public:
+  MechanismDelegate() = default;
+  virtual ~MechanismDelegate() = default;
+
+  // Notifies the mechanism when it is enabled/disabled. Mechanisms should start
+  // in a disabled state, and only start throttling when explicitly enabled.
+  // When they are subsequently disabled they should release any outstanding
+  // throttles, and stop creating new ones.
+  virtual void SetThrottlingEnabled(bool enabled) = 0;
+
+  // Notifies a single |contents| that it should stop throttling the specified
+  // |last_navigation_id|. The navigation ID is specified because of the race
+  // between navigations on the UI thread and policy messages dispatched from
+  // the PM sequence.
+  virtual void StopThrottling(content::WebContents* contents,
+                              int64_t last_navigation_id) = 0;
 };
 
 }  // namespace policies
 }  // namespace performance_manager
 
-#endif  // COMPONENTS_PERFORMANCE_MANAGER_GRAPH_POLICIES_TAB_LOADING_FRAME_NAVIGATION_POLICY_H_
\ No newline at end of file
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_GRAPH_POLICIES_TAB_LOADING_FRAME_NAVIGATION_POLICY_H_
diff --git a/components/performance_manager/public/graph/process_node.h b/components/performance_manager/public/graph/process_node.h
index 2893296..21e6e51d 100644
--- a/components/performance_manager/public/graph/process_node.h
+++ b/components/performance_manager/public/graph/process_node.h
@@ -11,6 +11,7 @@
 #include "base/process/process.h"
 #include "base/task/task_traits.h"
 #include "components/performance_manager/public/graph/node.h"
+#include "content/public/common/process_type.h"
 
 namespace base {
 class Process;
@@ -44,6 +45,9 @@
   ProcessNode();
   ~ProcessNode() override;
 
+  // Returns the type of this process.
+  virtual content::ProcessType GetProcessType() const = 0;
+
   // Returns the process ID associated with this process. Use this in preference
   // to querying GetProcess.Pid(). It's always valid to access, but will return
   // kNullProcessId if the process has yet started. It will also retain the
diff --git a/components/performance_manager/public/mechanisms/tab_loading_frame_navigation_scheduler.h b/components/performance_manager/public/mechanisms/tab_loading_frame_navigation_scheduler.h
new file mode 100644
index 0000000..27f08339
--- /dev/null
+++ b/components/performance_manager/public/mechanisms/tab_loading_frame_navigation_scheduler.h
@@ -0,0 +1,114 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_MECHANISMS_TAB_LOADING_FRAME_NAVIGATION_SCHEDULER_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_MECHANISMS_TAB_LOADING_FRAME_NAVIGATION_SCHEDULER_H_
+
+#include "base/callback.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class NavigationHandle;
+class NavigationThrottle;
+}  // namespace content
+
+namespace performance_manager {
+namespace mechanisms {
+
+// The mechanism half of the TabLoadingFrameNavigation system. The mechanism is
+// responsible for applying NavigationThrottles to frames in accordance with
+// policy decisions, and for releasing these throttles when the policy decides
+// the navigation should continue unimpeded. This object lives on the UI thread,
+// as WebContentsUserData. They are created for a WebContents if that contents
+// needs to be throttled, and destroyed when the throttling is complete, or when
+// the WebContents is destroyed, whichever comes first.
+//
+// Policy decisions about whether to apply scheduling to a WebContents or to
+// apply a NavigationThrottle to a particular frame need to be made
+// synchronously on the UI thread. As such, the mechanism pulls those
+// decisions from the policy object.
+//
+// This entire class lives on the UI thread and can only be accessed from there.
+class TabLoadingFrameNavigationScheduler
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<TabLoadingFrameNavigationScheduler> {
+ public:
+  class PolicyDelegate;
+
+  ~TabLoadingFrameNavigationScheduler() override;
+
+  // Invoked by the embedder hooks. Depending on policy decisions this may end
+  // up creating an instance of a scheduler for the associated WebContents, and
+  // may additionally create a NavigationThrottle for the provided |handle|.
+  static std::unique_ptr<content::NavigationThrottle>
+  MaybeCreateThrottleForNavigation(content::NavigationHandle* handle);
+
+  // Notifies the mechanism when it is enabled/disabled. The mechanisms starts
+  // in a disabled state, and only starts throttling when explicitly enabled.
+  // When subsequently disabled all outstanding throttles are released. Can be
+  // toggled multiple times, but this should only really happen in tests.
+  static void SetThrottlingEnabled(bool enabled);
+
+  // Stops throttling the given |contents|, only if it is currently treating the
+  // specified |last_navigation_id|. Invoked by the policy engine.
+  static void StopThrottling(content::WebContents* contents,
+                             int64_t last_navigation_id);
+
+  // Testing seams.
+  static void SetPolicyDelegateForTesting(PolicyDelegate* policy_delegate);
+  static bool IsThrottlingEnabledForTesting();
+  void StopThrottlingForTesting() { StopThrottlingImpl(); }
+  size_t GetThrottleCountForTesting() const { return throttles_.size(); }
+
+ private:
+  friend class content::WebContentsUserData<TabLoadingFrameNavigationScheduler>;
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  class Throttle;
+
+  explicit TabLoadingFrameNavigationScheduler(content::WebContents* contents);
+
+  // WebContentsObserver implementation:
+  // This is used to know that a navigation is about to be destroyed.
+  void DidFinishNavigation(content::NavigationHandle* handle) override;
+
+  // Invoked by the policy object to indicate that throttling should stop for
+  // the given contents. This causes the object to delete itself, so care must
+  // be taken in using this.
+  void StopThrottlingImpl();
+
+  // The navigation ID that this scheduler applies to. Set immediately after
+  // object creation.
+  int64_t navigation_id_ = 0;
+
+  // The set of Throttles that have been created by this object, and the
+  // navigation handles to which they are associated.
+  base::flat_map<content::NavigationHandle*, Throttle*> throttles_;
+
+  // Linked list mechanism for the collection of all mechanism instances. This
+  // is used to implement StopThrottlingEverything.
+  TabLoadingFrameNavigationScheduler* prev_ = nullptr;
+  TabLoadingFrameNavigationScheduler* next_ = nullptr;
+};
+
+// The policy delegate that the scheduler uses to make policy decisions. By
+// default the scheduler will use TabLoadingFrameNavigationPolicy as a delegate,
+// but this can be redirected as a testing seam.
+class TabLoadingFrameNavigationScheduler::PolicyDelegate {
+ public:
+  PolicyDelegate() = default;
+  PolicyDelegate(const PolicyDelegate&) = delete;
+  PolicyDelegate& operator=(const PolicyDelegate&) = delete;
+  virtual ~PolicyDelegate() = default;
+
+  // See TabLoadingFramNavigationPolicy for full descriptions.
+  virtual bool ShouldThrottleWebContents(content::WebContents* contents) = 0;
+  virtual bool ShouldThrottleNavigation(content::NavigationHandle* handle) = 0;
+};
+
+}  // namespace mechanisms
+}  // namespace performance_manager
+
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_MECHANISMS_TAB_LOADING_FRAME_NAVIGATION_SCHEDULER_H_
\ No newline at end of file
diff --git a/components/performance_manager/public/render_process_host_proxy.h b/components/performance_manager/public/render_process_host_proxy.h
index 70bda799..3f6cb55b3 100644
--- a/components/performance_manager/public/render_process_host_proxy.h
+++ b/components/performance_manager/public/render_process_host_proxy.h
@@ -35,9 +35,10 @@
   // not a renderer.
   int render_process_host_id() const { return render_process_host_id_; }
 
+  static RenderProcessHostProxy CreateForTesting(int render_process_host_id);
+
  protected:
   friend class RenderProcessUserData;
-  FRIEND_TEST_ALL_PREFIXES(FrameNodeImplTest, GetFrameNodeById);
 
   explicit RenderProcessHostProxy(int render_process_host_id);
 
diff --git a/components/performance_manager/render_process_host_proxy.cc b/components/performance_manager/render_process_host_proxy.cc
index 87507b4..14ca57b 100644
--- a/components/performance_manager/render_process_host_proxy.cc
+++ b/components/performance_manager/render_process_host_proxy.cc
@@ -24,4 +24,10 @@
   DCHECK(render_process_host_id_ >= 0);
 }
 
+// static
+RenderProcessHostProxy RenderProcessHostProxy::CreateForTesting(
+    int render_process_host_id) {
+  return RenderProcessHostProxy(render_process_host_id);
+}
+
 }  // namespace performance_manager
diff --git a/components/performance_manager/render_process_user_data.cc b/components/performance_manager/render_process_user_data.cc
index c00f8f5..20e5178 100644
--- a/components/performance_manager/render_process_user_data.cc
+++ b/components/performance_manager/render_process_user_data.cc
@@ -31,7 +31,7 @@
     : host_(render_process_host) {
   host_->AddObserver(this);
   process_node_ = PerformanceManagerImpl::CreateProcessNode(
-      RenderProcessHostProxy(host_->GetID()));
+      content::PROCESS_TYPE_RENDERER, RenderProcessHostProxy(host_->GetID()));
 }
 
 RenderProcessUserData::~RenderProcessUserData() {
diff --git a/components/performance_manager/test_support/BUILD.gn b/components/performance_manager/test_support/BUILD.gn
index 096768f..7b16850f 100644
--- a/components/performance_manager/test_support/BUILD.gn
+++ b/components/performance_manager/test_support/BUILD.gn
@@ -61,7 +61,9 @@
     "//components/performance_manager",
     "//content/shell:content_shell_lib",
     "//content/test:browsertest_support",
+    "//net:test_support",
     "//testing/gmock",
     "//testing/gtest",
+    "//ui/base",
   ]
 }
diff --git a/components/performance_manager/test_support/DEPS b/components/performance_manager/test_support/DEPS
new file mode 100644
index 0000000..a087a93
--- /dev/null
+++ b/components/performance_manager/test_support/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+net/dns",
+  "+ui/base",
+]
diff --git a/components/performance_manager/test_support/graph_test_harness.h b/components/performance_manager/test_support/graph_test_harness.h
index ca4cbfd..0041e34f 100644
--- a/components/performance_manager/test_support/graph_test_harness.h
+++ b/components/performance_manager/test_support/graph_test_harness.h
@@ -93,9 +93,10 @@
 template <>
 struct TestNodeWrapper<ProcessNodeImpl>::Factory {
   static std::unique_ptr<ProcessNodeImpl> Create(
+      content::ProcessType process_type = content::PROCESS_TYPE_RENDERER,
       RenderProcessHostProxy proxy = RenderProcessHostProxy()) {
     // Provide an empty RenderProcessHostProxy by default.
-    return std::make_unique<ProcessNodeImpl>(std::move(proxy));
+    return std::make_unique<ProcessNodeImpl>(process_type, std::move(proxy));
   }
 };
 
diff --git a/components/performance_manager/test_support/mock_graphs.cc b/components/performance_manager/test_support/mock_graphs.cc
index e54bd1e2..5b85154 100644
--- a/components/performance_manager/test_support/mock_graphs.cc
+++ b/components/performance_manager/test_support/mock_graphs.cc
@@ -18,7 +18,8 @@
 namespace performance_manager {
 
 TestProcessNodeImpl::TestProcessNodeImpl()
-    : ProcessNodeImpl(RenderProcessHostProxy()) {}
+    : ProcessNodeImpl(content::PROCESS_TYPE_RENDERER,
+                      RenderProcessHostProxy()) {}
 
 void TestProcessNodeImpl::SetProcessWithPid(base::ProcessId pid,
                                             base::Process process,
diff --git a/components/performance_manager/test_support/performance_manager_browsertest_harness.cc b/components/performance_manager/test_support/performance_manager_browsertest_harness.cc
index bd73ef9..aa347db9 100644
--- a/components/performance_manager/test_support/performance_manager_browsertest_harness.cc
+++ b/components/performance_manager/test_support/performance_manager_browsertest_harness.cc
@@ -5,11 +5,14 @@
 #include "components/performance_manager/test_support/performance_manager_browsertest_harness.h"
 
 #include "base/bind_helpers.h"
+#include "base/run_loop.h"
 #include "components/performance_manager/embedder/performance_manager_registry.h"
 #include "content/public/common/content_switches.h"
 #include "content/shell/browser/shell.h"
 #include "content/shell/browser/shell_content_browser_client.h"
 #include "mojo/public/cpp/bindings/binder_map.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 
 namespace performance_manager {
@@ -25,6 +28,12 @@
   Super::PreRunTestOnMainThread();
   helper_->SetUp();
   helper_->OnWebContentsCreated(shell()->web_contents());
+
+  // Set up the embedded web server.
+  host_resolver()->AddRule("*", "127.0.0.1");
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "components/test/data/performance_manager");
+  ASSERT_TRUE(embedded_test_server()->Start());
 }
 
 void PerformanceManagerBrowserTestHarness::PostRunTestOnMainThread() {
@@ -73,4 +82,44 @@
   return shell;
 }
 
+void PerformanceManagerBrowserTestHarness::StartNavigation(
+    content::WebContents* contents,
+    const GURL& url) {
+  // See content/public/test/browser_test_utils.cc
+  content::NavigationController::LoadURLParams params(url);
+  params.transition_type = ui::PageTransitionFromInt(
+      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
+  contents->GetController().LoadURLWithParams(params);
+  contents->Focus();
+}
+
+namespace {
+
+class WaitForLoadObserver : public content::WebContentsObserver {
+ public:
+  explicit WaitForLoadObserver(content::WebContents* contents)
+      : content::WebContentsObserver(contents) {}
+  ~WaitForLoadObserver() override = default;
+
+  void Wait() {
+    if (!web_contents()->IsLoading())
+      return;
+    run_loop_.Run();
+  }
+
+ private:
+  // WebContentsObserver implementation
+  void DidStopLoading() override { run_loop_.Quit(); }
+
+  base::RunLoop run_loop_;
+};
+
+}  // namespace
+
+void PerformanceManagerBrowserTestHarness::WaitForLoad(
+    content::WebContents* contents) {
+  WaitForLoadObserver observer(contents);
+  observer.Wait();
+}
+
 }  // namespace performance_manager
diff --git a/components/performance_manager/test_support/performance_manager_browsertest_harness.h b/components/performance_manager/test_support/performance_manager_browsertest_harness.h
index bd4da0e..f3218a95 100644
--- a/components/performance_manager/test_support/performance_manager_browsertest_harness.h
+++ b/components/performance_manager/test_support/performance_manager_browsertest_harness.h
@@ -38,6 +38,12 @@
   // call this if you need multiple independent WebContents.
   content::Shell* CreateShell();
 
+  // Starts a navigation for the given |contents|.
+  void StartNavigation(content::WebContents* contents, const GURL& url);
+
+  // Waits for an ongoing navigation to terminate on the given |contents|.
+  void WaitForLoad(content::WebContents* contents);
+
  private:
   std::unique_ptr<PerformanceManagerTestHarnessHelper> helper_;
 };
diff --git a/components/performance_manager/worker_watcher_unittest.cc b/components/performance_manager/worker_watcher_unittest.cc
index bc9668e1..5131728 100644
--- a/components/performance_manager/worker_watcher_unittest.cc
+++ b/components/performance_manager/worker_watcher_unittest.cc
@@ -449,8 +449,8 @@
   int render_process_id = GenerateNextId();
 
   // Create the process node and insert it into the map.
-  auto process_node =
-      PerformanceManagerImpl::CreateProcessNode(RenderProcessHostProxy());
+  auto process_node = PerformanceManagerImpl::CreateProcessNode(
+      content::PROCESS_TYPE_RENDERER, RenderProcessHostProxy());
   bool inserted =
       process_node_map_.insert({render_process_id, std::move(process_node)})
           .second;
diff --git a/components/permissions/contexts/geolocation_permission_context_unittest.cc b/components/permissions/contexts/geolocation_permission_context_unittest.cc
index ca00eea7..d3c6b32 100644
--- a/components/permissions/contexts/geolocation_permission_context_unittest.cc
+++ b/components/permissions/contexts/geolocation_permission_context_unittest.cc
@@ -26,8 +26,8 @@
 #include "base/test/simple_test_clock.h"
 #include "base/time/clock.h"
 #include "build/build_config.h"
+#include "components/content_settings/browser/content_settings_usages_state.h"
 #include "components/content_settings/browser/tab_specific_content_settings.h"
-#include "components/content_settings/core/browser/content_settings_usages_state.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/permissions/features.h"
 #include "components/permissions/permission_context_base.h"
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 38cb098..0f032b9 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -1014,7 +1014,28 @@
   optional string vpd_sku_number = 1;
 }
 
-// Status of a single physical CPU.
+// Status of a single C-state. C-states are various modes the CPU can transition
+// to in order to use more or less power.
+message CpuCStateInfo {
+  // Name of the state.
+  optional string name = 1;
+  // Time spent in the state since the last reboot, in microseconds.
+  optional uint64 time_in_state_since_last_boot_us = 2;
+}
+
+// Status of a single logical CPU.
+message LogicalCpuInfo {
+  // Maximum frequency the CPU is allowed to run at, by policy.
+  optional uint32 scaling_max_frequency_khz = 1;
+  // Current frequency the CPU is running at.
+  optional uint32 scaling_current_frequency_khz = 2;
+  // Idle time since last boot.
+  optional uint32 idle_time_seconds = 3;
+  // Information about the logical CPU's time in various C-states.
+  repeated CpuCStateInfo c_states = 4;
+}
+
+// Status of a single physical CPU on the device.
 message CpuInfo {
   // The CPU model name.
   optional string model_name = 1;
@@ -1028,6 +1049,14 @@
 
   // The max CPU clock speed in kHz.
   optional uint32 max_clock_speed_khz = 3;
+
+  repeated LogicalCpuInfo logical_cpus = 4;
+}
+
+// Overall CPU information for the device.
+message GlobalCpuInfo {
+  // Total number of threads on the device.
+  optional uint32 num_total_threads = 1;
 }
 
 // Status for a single display.  A display screen with resolution 1920x1080
@@ -1236,6 +1265,9 @@
 
   // Information about the device's fans.
   repeated FanInfo fan_info = 38;
+
+  // Overall information about the device's CPUs.
+  optional GlobalCpuInfo global_cpu_info = 39;
 }
 
 message OsUpdateStatus {
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 790ab330..a1c7554 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -10871,15 +10871,11 @@
       'id': 188,
       'caption': '''Show accessibility options in system tray menu''',
       'tags': [],
-      'desc': '''If this policy is set to true, Accessibility options always appear in system tray menu.
+      'desc': '''Setting the policy to True displays the accessibility options in the system tray menu. If you set the policy to False, the options don't appear in the menu.
 
-          If this policy is set to false, Accessibility options never appear in system tray menu.
+      If you set the policy, users can't change it. If not set, accessibility options don't appear in the menu, but users can make them appear through the Settings page.
 
-          If you set this policy, users cannot change or override it.
-
-          If this policy is left unset, Accessibility options will not appear in the system tray menu, but the user can cause the Accessibility options to appear via the Settings page.
-
-          When accessibility features are enabled by other means (e.g by a key combination), Accessibility options will always appear in system tray menu.'''
+      If you turn on accessibility features by other means (for example, by key combination), accessibility options always appear in the system tray menu.'''
     },
     {
       'name': 'DeviceLoginScreenShowOptionsInSystemTrayMenu',
@@ -10896,15 +10892,11 @@
       'id': 646,
       'caption': '''Show accessibility options in system tray menu in the login screen''',
       'tags': [],
-      'desc': '''If this policy is set to true, Accessibility options always appear in system tray menu.
+      'desc': '''Setting the policy to True displays the accessibility options in the system tray menu. If you set the policy to False, the options don't appear in the menu.
 
-          If this policy is set to false, Accessibility options never appear in system tray menu.
+      If you set the policy, users can't change it. If not set, accessibility options don't appear in the menu, but users can make them appear through the Settings page.
 
-          If you set this policy, users cannot change or override it.
-
-          If this policy is left unset, Accessibility options will not appear in the system tray menu, but the user can cause the Accessibility options to appear via the Settings page.
-
-          When accessibility features are enabled by other means (e.g by a key combination), Accessibility options will always appear in system tray menu.'''
+      If you turn on accessibility features by other means (for example, by key combination), accessibility options always appear in the system tray menu.'''
     },
     {
       'name': 'LargeCursorEnabled',
@@ -10921,15 +10913,9 @@
       'id': 211,
       'caption': '''Enable large cursor''',
       'tags': [],
-      'desc': '''Enable the large cursor accessibility feature.
+      'desc': '''Setting the policy to True keeps the large cursor on. Setting the policy to False keeps the large cursor off.
 
-          If this policy is set to true, the large cursor will always be enabled.
-
-          If this policy is set to false, the large cursor will always be disabled.
-
-          If you set this policy, users cannot change or override it.
-
-          If this policy is left unset, the large cursor is disabled initially but can be enabled by the user anytime.'''
+      If you set the policy, users can't change the feature. If not set, the large cursor is off at first, but users can turn it on any time.'''
     },
     {
       'name': 'SpokenFeedbackEnabled',
@@ -10946,15 +10932,9 @@
       'id': 212,
       'caption': '''Enable spoken feedback''',
       'tags': [],
-      'desc': '''Enable the spoken feedback accessibility feature.
+      'desc': '''Setting the policy to True keeps spoken feedback on. Setting the policy to False keeps spoken feedback off.
 
-          If this policy is set to true, spoken feedback will always be enabled.
-
-          If this policy is set to false, spoken feedback will always be disabled.
-
-          If you set this policy, users cannot change or override it.
-
-          If this policy is left unset, spoken feedback is disabled initially but can be enabled by the user anytime.'''
+       If you set the policy, users can't change it. If not set, spoken feedback is off at first, but users can turn it on any time.'''
     },
     {
       'name': 'HighContrastEnabled',
@@ -10971,15 +10951,9 @@
       'id': 213,
       'caption': '''Enable high contrast mode''',
       'tags': [],
-      'desc': '''Enable the high contrast mode accessibility feature.
+      'desc': '''Setting the policy to True keeps High-contrast mode on. Setting the policy to False keeps High-contrast mode off.
 
-          If this policy is set to true, high contrast mode will always be enabled.
-
-          If this policy is set to false, high contrast mode will always be disabled.
-
-          If you set this policy, users cannot change or override it.
-
-          If this policy is left unset, high contrast mode is disabled initially but can be enabled by the user anytime.'''
+      If you set the policy, users can't change it. If not set, High-contrast mode is off, but users can turn it on any time.'''
     },
     {
       'name': 'DeviceLoginScreenAccessibilityShortcutsEnabled',
@@ -11046,15 +11020,9 @@
       'id': 255,
       'caption': '''Enable on-screen keyboard''',
       'tags': [],
-      'desc': '''Enable the on-screen keyboard accessibility feature.
+      'desc': '''Setting the policy to True keeps the on-screen keyboard on. Setting the policy to False keeps the on-screen keyboard off.
 
-          If this policy is set to true, the on-screen keyboard will always be enabled.
-
-          If this policy is set to false, the on-screen keyboard will always be disabled.
-
-          If you set this policy, users cannot change or override it.
-
-          If this policy is left unset, the on-screen keyboard is disabled initially but can be enabled by the user anytime.'''
+      If you set the policy, users can't change it. If not set, the on-screen keyboard is off at first, but users can turn it on any time.'''
     },
     {
       'name': 'StickyKeysEnabled',
@@ -11071,15 +11039,9 @@
       'id': 561,
       'caption': '''Enable sticky keys''',
       'tags': [],
-      'desc': '''Enable the sticky keys accessibility feature.
+      'desc': '''Setting the policy to True keeps sticky keys on. Setting the policy to False keeps sticky keys off.
 
-          If this policy is set to true, the sticky keys will always be enabled.
-
-          If this policy is set to false, the sticky keys will always be disabled.
-
-          If you set this policy, users cannot change or override it.
-
-          If this policy is left unset, the sticky keys is disabled initially but can be enabled by the user anytime.'''
+      If you set the policy, users can't change it. If not set, sticky keys is off at first, but users can turn it on any time.'''
     },
     {
       'name': 'SelectToSpeakEnabled',
@@ -11281,11 +11243,9 @@
       'id': 260,
       'caption': '''Media keys default to function keys''',
       'tags': [],
-      'desc': '''Changes the default behaviour of the top row keys to function keys.
+      'desc': '''Setting the policy to True makes the top row of keys on the keyboard act as function key commands. Pressing the Search key changes their behavior back to media keys.
 
-          If this policy is set to true, the keyboard's top row of keys will produce function key commands per default. The search key has to be pressed to revert their behavior back to media keys.
-
-          If this policy is set to false or left unset, the keyboard will produce media key commands per default and function key commands when the search key is held.'''
+      If set to False or not set, the keyboard defaults to producing media key commands. Pressing the Search key changes them to function keys.'''
     },
     {
       'name': 'ScreenMagnifierType',
@@ -11322,11 +11282,11 @@
       'id': 214,
       'caption': '''Set screen magnifier type''',
       'tags': [],
-      'desc': '''If this policy is set, it controls the type of screen magnifier that is enabled. Setting the policy to "None" disables the screen magnifier.
+      'desc': '''Setting the policy to None turns the screen magnifier off.
 
-          If you set this policy, users cannot change or override it.
+      If you set the policy, users can't change it. If not set, the screen magnifier is off at first, but users can turn it on any time.
 
-          If this policy is left unset, the screen magnifier is disabled initially but can be enabled by the user anytime.''',
+      Valid values: • 0 = Off • 1 = On • 2 = Docked magnifier on''',
     },
     {
       'name': 'DeviceLoginScreenDefaultLargeCursorEnabled',
@@ -11342,15 +11302,11 @@
       'id': 215,
       'caption': '''Set default state of the large cursor on the login screen''',
       'tags': [],
-      'desc': '''Set the default state of the large cursor accessibility feature on the login screen.
+      'desc': '''Setting the policy to True turns the large cursor on at the sign-in screen. Setting the policy to False turns the large cursor off at the sign-in screen.
 
-          If this policy is set to true, the large cursor will be enabled when the login screen is shown.
+      If you set the policy, users can temporarily turn the large cursor on or off. When the sign-in screen reloads or stays idle for a minute, it reverts to its original state.
 
-          If this policy is set to false, the large cursor will be disabled when the login screen is shown.
-
-          If you set this policy, users can temporarily override it by enabling or disabling the large cursor. However, the user's choice is not persistent and the default is restored whenever the login screen is shown anew or the user remains idle on the login screen for a minute.
-
-          If this policy is left unset, the large cursor is disabled when the login screen is first shown. Users can enable or disable the large cursor anytime and its status on the login screen is persisted between users.
+      If not set, the large cursor is off at the sign-in screen. Users can turn it on any time, and its status on the sign-in screen persists across users.
 
           Note: <ph name="DEVICE_LOGIN_SCREEN_LARGE_CURSOR_ENABLED">DeviceLoginScreenLargeCursorEnabled</ph> overrides this policy if the former is specified.''',
     },
@@ -11393,15 +11349,11 @@
       'id': 216,
       'caption': '''Set the default state of spoken feedback on the login screen''',
       'tags': [],
-      'desc': '''Set the default state of the spoken feedback accessibility feature on the login screen.
+      'desc': '''Setting the policy to True turns spoken feedback on at the sign-in screen. Setting the policy to False turns spoken feedback off at the screen.
 
-          If this policy is set to true, spoken feedback will be enabled when the login screen is shown.
+      If you set the policy, users can temporarily turn spoken feedback on or off. When the sign-in screen reloads or stays idle for a minute, it reverts to its original state.
 
-          If this policy is set to false, spoken feedback will be disabled when the login screen is shown.
-
-          If you set this policy, users can temporarily override it by enabling or disabling spoken feedback. However, the user's choice is not persistent and the default is restored whenever the login screen is shown anew or the user remains idle on the login screen for a minute.
-
-          If this policy is left unset, spoken feedback is disabled when the login screen is first shown. Users can enable or disable spoken feedback anytime and its status on the login screen is persisted between users.
+      If not set, spoken feedback is off at the sign-in screen. Users can turn it on any time, and its status on the sign-in screen persists across users.
 
           Note: <ph name="DEVICE_LOGIN_SCREEN_SPOKEN_FEEDBACK_ENABLED_POLICY_NAME">DeviceLoginScreenSpokenFeedbackEnabled</ph> overrides this policy if the former is specified.''',
     },
@@ -11444,15 +11396,11 @@
       'id': 217,
       'caption': '''Set the default state of high contrast mode on the login screen''',
       'tags': [],
-      'desc': '''Set the default state of the high contrast mode accessibility feature on the login screen.
+      'desc': '''Setting the policy to True turns High-contrast mode on at the sign-in screen. Setting the policy to False turns High-contrast mode off at the screen.
 
-          If this policy is set to true, high contrast mode will be enabled when the login screen is shown.
+      If you set the policy, users can temporarily change High-contrast mode, turning it on or off. When the sign-in screen reloads or stays idle for a minute, it reverts to its original state.
 
-          If this policy is set to false, high contrast mode will be disabled when the login screen is shown.
-
-          If you set this policy, users can temporarily override it by enabling or disabling high contrast mode. However, the user's choice is not persistent and the default is restored whenever the login screen is shown anew or the user remains idle on the login screen for a minute.
-
-          If this policy is left unset, high contrast mode is disabled when the login screen is first shown. Users can enable or disable high contrast mode anytime and its status on the login screen is persisted between users.
+      If not set, High-contrast mode is off at the sign-in screen. Users can turn it on any time, and its status on the sign-in screen persists across users.
 
           Note: <ph name="DEVICE_LOGIN_SCREEN_HIGH_CONTRAST_ENABLED_POLICY_NAME">DeviceLoginScreenHighContrastEnabled</ph> overrides this policy if the former is specified.''',
     },
@@ -11495,15 +11443,11 @@
       'id': 256,
       'caption': '''Set default state of the on-screen keyboard on the login screen''',
       'tags': [],
-      'desc': '''Set the default state of the on-screen keyboard accessibility feature on the login screen.
+      'desc': '''Setting the policy to True turns the on-screen keyboard on at sign-in. Setting the policy to False turns the on-screen keyboard off at sign-in.
 
-          If this policy is set to true, the on-screen keyboard will be enabled when the login screen is shown.
+      If you set the policy, users can temporarily turn the on-screen keyboard on or off. When the sign-in screen reloads or stays idle for a minute, it reverts to its original state.
 
-          If this policy is set to false, the on-screen keyboard will be disabled when the login screen is shown.
-
-          If you set this policy, users can temporarily override it by enabling or disabling the on-screen keyboard. However, the user's choice is not persistent and the default is restored whenever the login screen is shown anew or the user remains idle on the login screen for a minute.
-
-          If this policy is left unset, the on-screen keyboard is disabled when the login screen is first shown. Users can enable or disable the on-screen keyboard anytime and its status on the login screen is persisted between users.
+      If not set, the on-screen keyboard is off at the sign-in screen. Users can turn it on any time, and its status on the sign-in screen persists across users.
 
           Note: <ph name="DEVICE_LOGIN_SCREEN_VIRTUAL_KEYBOARD_ENABLED_POLICY_NAME">DeviceLoginScreenVirtualKeyboardEnabled</ph> overrides this policy if the former is specified.''',
     },
@@ -11642,13 +11586,13 @@
       'id': 218,
       'caption': '''Set the default screen magnifier type enabled on the login screen''',
       'tags': [],
-      'desc': '''Set the default type of screen magnifier that is enabled on the login screen.
+      'desc': '''Setting the policy to None turns screen magnification off at the sign-in screen.
 
-          If this policy is set, it controls the type of screen magnifier that is enabled when the login screen is shown. Setting the policy to "None" disables the screen magnifier.
+       If you set the policy, users can temporarily turn the screen magnifier on or off. When the sign-in screen reloads or stays idle for a minute, it reverts to its original state.
 
-          If you set this policy, users can temporarily override it by enabling or disabling the screen magnifier. However, the user's choice is not persistent and the default is restored whenever the login screen is shown anew or the user remains idle on the login screen for a minute.
+      If not set, the screen magnifier is off at the sign-in screen. Users can turn it on any time, and its status on the sign-in screen persists across users.
 
-          If this policy is left unset, the screen magnifier is disabled when the login screen is first shown. Users can enable or disable the screen magnifier anytime and its status on the login screen is persisted between users.
+       Valid values: • 0 = Off • 1 = On • 2 = Docked magnifier on
 
           Note: <ph name="DEVICE_LOGIN_SCREEN_SCREEN_MAGNIFIER_TYPE_POLICY_NAME">DeviceLoginScreenScreenMagnifierType</ph> overrides this policy if the former is specified.''',
     },
@@ -21173,9 +21117,11 @@
       'example_value': False,
       'default_for_enterprise_users': False,
       'id': 701,
-      'caption': '''Enable display password button on the login and lock screen.''',
+      'caption': '''Show the display password button on the login and lock screen''',
       'tags': [],
-      'desc': '''This feature allows, on the login and lock screen, the password to be displayed by the user after clicking on a button.''',
+      'desc': '''When enabled, this feature shows a button on the login and lock screen that allows the password to be displayed.
+          It is represented as an eye icon on the password textfield. The button is absent when the feature is disabled.
+      ''',
     },
     {
       'name': 'AccessibilityImageLabelsEnabled',
diff --git a/components/sync_bookmarks/bookmark_model_merger.cc b/components/sync_bookmarks/bookmark_model_merger.cc
index b8ea3db..10a41bbd 100644
--- a/components/sync_bookmarks/bookmark_model_merger.cc
+++ b/components/sync_bookmarks/bookmark_model_merger.cc
@@ -5,7 +5,6 @@
 #include "components/sync_bookmarks/bookmark_model_merger.h"
 
 #include <algorithm>
-#include <set>
 #include <string>
 #include <utility>
 
@@ -118,7 +117,7 @@
 void CheckNoDuplicatesInRemoteGUIDs(
     const UpdatesPerParentId& updates_per_parent_id) {
 #if DCHECK_IS_ON()
-  std::set<std::string> known_guids;
+  std::unordered_map<std::string, std::string> guid_to_sync_id;
 
   for (const auto& parent_id_and_updates : updates_per_parent_id) {
     for (const UpdateResponseData& update : parent_id_and_updates.second) {
@@ -131,8 +130,11 @@
       const std::string& guid_in_specifics =
           update.entity.specifics.bookmark().guid();
 
-      bool success = known_guids.insert(guid_in_specifics).second;
-      DCHECK(success);
+      auto it_and_success =
+          guid_to_sync_id.emplace(guid_in_specifics, update.entity.id);
+      DCHECK(it_and_success.second)
+          << " for new sync ID " << update.entity.id << " and original sync ID "
+          << it_and_success.first->second;
     }
   }
 #endif  // DCHECK_IS_ON()
diff --git a/components/sync_bookmarks/synced_bookmark_tracker.cc b/components/sync_bookmarks/synced_bookmark_tracker.cc
index 1427867f..812c9f3 100644
--- a/components/sync_bookmarks/synced_bookmark_tracker.cc
+++ b/components/sync_bookmarks/synced_bookmark_tracker.cc
@@ -30,7 +30,7 @@
 
 const base::Feature kInvalidateBookmarkSyncMetadataIfMismatchingGuid{
     "InvalidateBookmarkSyncMetadataIfMismatchingGuid",
-    base::FEATURE_ENABLED_BY_DEFAULT};
+    base::FEATURE_DISABLED_BY_DEFAULT};
 
 extern const base::Feature kInvalidateBookmarkSyncMetadataIfClientTagDuplicates{
     "InvalidateBookmarkSyncMetadataIfClientTagDuplicates",
diff --git a/components/test/data/performance_manager/OWNERS b/components/test/data/performance_manager/OWNERS
new file mode 100644
index 0000000..982cc08
--- /dev/null
+++ b/components/test/data/performance_manager/OWNERS
@@ -0,0 +1,3 @@
+file://components/performance_manager/OWNERS
+
+# COMPONENT: Internals>ResourceCoordinator
diff --git a/components/test/data/performance_manager/a.html b/components/test/data/performance_manager/a.html
new file mode 100644
index 0000000..d850435
--- /dev/null
+++ b/components/test/data/performance_manager/a.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html language="en">
+  <head>
+    <title>a.com</title>
+  </head>
+  <body onload="console.log('a.html loaded');">
+    Welcome to a.com.
+  </body>
+</html>
diff --git a/components/test/data/performance_manager/a_embeds_b.html b/components/test/data/performance_manager/a_embeds_b.html
new file mode 100644
index 0000000..4bd9303
--- /dev/null
+++ b/components/test/data/performance_manager/a_embeds_b.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html language="en">
+  <head>
+    <title>a.com</title>
+    <script language="javascript">
+      function onLoad() {
+        // The embedded iframe is dynamically loaded because we don't know
+        // the port where it is hosted until runtime.
+        console.log("a_embeds_b.html loaded");
+        let l = window.location;
+        let url = `${l.protocol}//b.com:${l.port}/b.html`;
+        console.log(`loading iframe [${url}]`);
+        let iframe = document.createElement('iframe');
+        iframe.setAttribute('src', url);
+        document.body.appendChild(iframe);
+      }
+    </script>
+  </head>
+  <body onload="onLoad();">
+    Welcome to a.com, embedding b.com!
+  </body>
+</html>
diff --git a/components/test/data/performance_manager/b.html b/components/test/data/performance_manager/b.html
new file mode 100644
index 0000000..988b260
--- /dev/null
+++ b/components/test/data/performance_manager/b.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html language="en">
+  <head>
+    <title>b.com</title>
+  </head>
+  <body onload="console.log('b.html loaded');">
+    Welcome to b.com.
+  </body>
+</html>
diff --git a/components/ukm/ukm_service_unittest.cc b/components/ukm/ukm_service_unittest.cc
index 619a8c7c..a0d6a91 100644
--- a/components/ukm/ukm_service_unittest.cc
+++ b/components/ukm/ukm_service_unittest.cc
@@ -427,6 +427,31 @@
   EXPECT_TRUE(provider->provide_system_profile_metrics_called());
 }
 
+// Currently just testing brand is set, would be good to test other core
+// system profile fields.
+TEST_F(UkmServiceTest, SystemProfileTest) {
+  UkmService service(&prefs_, &client_,
+                     true /* restrict_to_whitelisted_entries */,
+                     std::make_unique<MockDemographicMetricsProvider>());
+  TestRecordingHelper recorder(&service);
+
+  service.Initialize();
+
+  task_runner_->RunUntilIdle();
+  service.EnableRecording(/*extensions=*/false);
+  service.EnableReporting();
+
+  ukm::SourceId id = GetWhitelistedSourceId(0);
+  recorder.UpdateSourceURL(id, GURL("https://google.com/foobar"));
+  TestEvent1(id).Record(&service);
+  service.Flush();
+  EXPECT_EQ(GetPersistedLogCount(), 1);
+
+  Report proto_report = GetPersistedReport();
+  EXPECT_EQ(metrics::TestMetricsServiceClient::kBrandForTesting,
+            proto_report.system_profile().brand_code());
+}
+
 TEST_F(UkmServiceTest, AddUserDemograhicsWhenAvailableAndFeatureEnabled) {
   ScopedUkmFeatureParams params({{"WhitelistEntries", Entry1And2Whitelist()}});
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index f9cfed6..e11853c 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -926,8 +926,6 @@
     "frame_host/navigator.cc",
     "frame_host/navigator.h",
     "frame_host/navigator_delegate.h",
-    "frame_host/navigator_impl.cc",
-    "frame_host/navigator_impl.h",
     "frame_host/origin_policy_throttle.cc",
     "frame_host/origin_policy_throttle.h",
     "frame_host/popup_menu_helper_mac.h",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.cc b/content/browser/accessibility/accessibility_tree_formatter_base.cc
index a8d7ae6..14ccfb9 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_base.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.cc
@@ -189,6 +189,28 @@
   return FILE_PATH_LITERAL("");
 }
 
+bool AccessibilityTreeFormatterBase::FilterPropertyName(
+    const base::string16& text) {
+  // Find the first allow-filter matching the property name. The filter should
+  // be either an exact property match or a wildcard matching to support filter
+  // collections like AXRole* which matches AXRoleDescription.
+  const base::string16 delim = base::ASCIIToUTF16("=");
+  for (const auto& filter : property_filters_) {
+    base::String16Tokenizer tokenizer(filter.match_str, delim);
+    if (tokenizer.GetNext() && (text == tokenizer.token() ||
+                                base::MatchPattern(text, tokenizer.token()))) {
+      switch (filter.type) {
+        case PropertyFilter::ALLOW_EMPTY:
+        case PropertyFilter::ALLOW:
+          return true;
+        default:
+          break;
+      }
+    }
+  }
+  return false;
+}
+
 bool AccessibilityTreeFormatterBase::MatchesPropertyFilters(
     const base::string16& text,
     bool default_result) const {
diff --git a/content/browser/accessibility/accessibility_tree_formatter_base.h b/content/browser/accessibility/accessibility_tree_formatter_base.h
index 6dc193e..702bcd681 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_base.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_base.h
@@ -86,6 +86,9 @@
   // Overridden by platform subclasses.
   //
 
+  // Returns true if the property name matches a property filter.
+  bool FilterPropertyName(const base::string16& text);
+
   // Process accessibility tree with filters for output.
   // Given a dictionary that contains a platform-specific dictionary
   // representing an accessibility tree, and utilizing property_filters_ and
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
index bc6b73b3..7388129 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -210,8 +210,8 @@
 void AccessibilityTreeFormatterMac::AddDefaultFilters(
     std::vector<PropertyFilter>* property_filters) {
   static NSArray* default_attributes = [@[
-    @"AXAutocomplete*", @"AXDescription=*", @"AXTitle=*", @"AXTitleUIElement=*",
-    @"AXHelp=*", @"AXValue=*"
+    @"AXAutocompleteValue=*", @"AXDescription=*", @"AXRole=*", @"AXTitle=*",
+    @"AXTitleUIElement=*", @"AXHelp=*", @"AXValue=*"
   ] retain];
 
   for (NSString* attribute : default_attributes) {
@@ -278,9 +278,11 @@
 
   for (NSString* supportedAttribute in
        [cocoa_node accessibilityAttributeNames]) {
-    id value = [cocoa_node accessibilityAttributeValue:supportedAttribute];
-    if (value != nil) {
-      dict->Set(SysNSStringToUTF8(supportedAttribute), PopulateObject(value));
+    if (FilterPropertyName(SysNSStringToUTF16(supportedAttribute))) {
+      id value = [cocoa_node accessibilityAttributeValue:supportedAttribute];
+      if (value != nil) {
+        dict->Set(SysNSStringToUTF8(supportedAttribute), PopulateObject(value));
+      }
     }
   }
   dict->Set(kPositionDictAttr, PopulatePosition(*node));
diff --git a/content/browser/appcache/appcache_subresource_url_factory.cc b/content/browser/appcache/appcache_subresource_url_factory.cc
index b9b5df3..29e1451 100644
--- a/content/browser/appcache/appcache_subresource_url_factory.cc
+++ b/content/browser/appcache/appcache_subresource_url_factory.cc
@@ -136,13 +136,12 @@
       const net::HttpRequestHeaders& modified_headers,
       const net::HttpRequestHeaders& modified_cors_exempt_headers,
       const base::Optional<GURL>& new_url) override {
-    DCHECK(removed_headers.empty() && modified_headers.IsEmpty() &&
-           modified_cors_exempt_headers.IsEmpty())
+    DCHECK(modified_headers.IsEmpty() && modified_cors_exempt_headers.IsEmpty())
         << "Redirect with modified headers was not supported yet. "
            "crbug.com/845683";
     if (!handler_) {
       network_loader_->FollowRedirect(
-          {} /* removed_headers */, {} /* modified_headers */,
+          removed_headers, {} /* modified_headers */,
           {} /* modified_cors_exempt_headers */, base::nullopt /* new_url */);
       return;
     }
diff --git a/content/browser/browsing_instance.h b/content/browser/browsing_instance.h
index 906a1ee..06f0961 100644
--- a/content/browser/browsing_instance.h
+++ b/content/browser/browsing_instance.h
@@ -202,7 +202,7 @@
   // contain every active SiteInstance, because a race exists where two
   // SiteInstances can be assigned to the same site.  This is ok in rare cases.
   // It also does not contain SiteInstances which have not yet been assigned a
-  // site, such as about:blank.  See NavigatorImpl::ShouldAssignSiteForURL.
+  // site, such as about:blank.  See SiteInstance::ShouldAssignSiteForURL.
   // This map only contains instances that map to a single site. The
   // |default_site_instance_|, which associates multiple sites with a single
   // instance, is not contained in this map.
diff --git a/content/browser/client_hints/client_hints.cc b/content/browser/client_hints/client_hints.cc
index 7f2f00d..3230f8c1 100644
--- a/content/browser/client_hints/client_hints.cc
+++ b/content/browser/client_hints/client_hints.cc
@@ -20,6 +20,7 @@
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigator.h"
+#include "content/browser/frame_host/navigator_delegate.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/host_zoom_map.h"
@@ -423,14 +424,15 @@
 };
 
 bool ShouldAddClientHint(const ClientHintsExtendedData& data,
-                         network::mojom::WebClientHintsType type,
-                         blink::mojom::FeaturePolicyFeature feature) {
+                         network::mojom::WebClientHintsType type) {
   if (!data.hints.IsEnabled(type))
     return false;
   if (!IsFeaturePolicyForClientHintsEnabled() || data.is_main_frame)
     return data.is_1p_origin;
-  return data.feature_policy && data.feature_policy->IsFeatureEnabledForOrigin(
-                                    feature, data.resource_origin);
+  return data.feature_policy &&
+         data.feature_policy->IsFeatureEnabledForOrigin(
+             blink::kClientHintsFeaturePolicyMapping[static_cast<int>(type)],
+             data.resource_origin);
 }
 
 bool IsJavascriptEnabled(FrameTreeNode* frame_tree_node) {
@@ -506,37 +508,32 @@
     ClientHintsExtendedData data(url, frame_tree_node, delegate);
 
     if (ShouldAddClientHint(
-            data, network::mojom::WebClientHintsType::kUAFullVersion,
-            blink::mojom::FeaturePolicyFeature::kClientHintUAFullVersion)) {
+            data, network::mojom::WebClientHintsType::kUAFullVersion)) {
       AddUAHeader(headers, network::mojom::WebClientHintsType::kUAFullVersion,
                   SerializeHeaderString(ua_metadata->full_version));
     }
 
-    if (ShouldAddClientHint(
-            data, network::mojom::WebClientHintsType::kUAArch,
-            blink::mojom::FeaturePolicyFeature::kClientHintUAArch)) {
+    if (ShouldAddClientHint(data,
+                            network::mojom::WebClientHintsType::kUAArch)) {
       AddUAHeader(headers, network::mojom::WebClientHintsType::kUAArch,
                   SerializeHeaderString(ua_metadata->architecture));
     }
 
-    if (ShouldAddClientHint(
-            data, network::mojom::WebClientHintsType::kUAPlatform,
-            blink::mojom::FeaturePolicyFeature::kClientHintUAPlatform)) {
+    if (ShouldAddClientHint(data,
+                            network::mojom::WebClientHintsType::kUAPlatform)) {
       AddUAHeader(headers, network::mojom::WebClientHintsType::kUAPlatform,
                   SerializeHeaderString(ua_metadata->platform));
     }
 
     if (ShouldAddClientHint(
-            data, network::mojom::WebClientHintsType::kUAPlatformVersion,
-            blink::mojom::FeaturePolicyFeature::kClientHintUAPlatform)) {
+            data, network::mojom::WebClientHintsType::kUAPlatformVersion)) {
       AddUAHeader(headers,
                   network::mojom::WebClientHintsType::kUAPlatformVersion,
                   SerializeHeaderString(ua_metadata->platform_version));
     }
 
-    if (ShouldAddClientHint(
-            data, network::mojom::WebClientHintsType::kUAModel,
-            blink::mojom::FeaturePolicyFeature::kClientHintUAModel)) {
+    if (ShouldAddClientHint(data,
+                            network::mojom::WebClientHintsType::kUAModel)) {
       AddUAHeader(headers, network::mojom::WebClientHintsType::kUAModel,
                   SerializeHeaderString(ua_metadata->model));
     }
@@ -608,38 +605,30 @@
   const ClientHintsExtendedData data(url, frame_tree_node, delegate);
 
   // Add Headers
-  if (ShouldAddClientHint(
-          data, network::mojom::WebClientHintsType::kDeviceMemory,
-          blink::mojom::FeaturePolicyFeature::kClientHintDeviceMemory)) {
+  if (ShouldAddClientHint(data,
+                          network::mojom::WebClientHintsType::kDeviceMemory)) {
     AddDeviceMemoryHeader(headers);
   }
-  if (ShouldAddClientHint(data, network::mojom::WebClientHintsType::kDpr,
-                          blink::mojom::FeaturePolicyFeature::kClientHintDPR)) {
+  if (ShouldAddClientHint(data, network::mojom::WebClientHintsType::kDpr)) {
     AddDPRHeader(headers, context, url);
   }
-  if (ShouldAddClientHint(
-          data, network::mojom::WebClientHintsType::kViewportWidth,
-          blink::mojom::FeaturePolicyFeature::kClientHintViewportWidth)) {
+  if (ShouldAddClientHint(data,
+                          network::mojom::WebClientHintsType::kViewportWidth)) {
     AddViewportWidthHeader(headers, context, url);
   }
   network::NetworkQualityTracker* network_quality_tracker =
       delegate->GetNetworkQualityTracker();
-  if (ShouldAddClientHint(data, network::mojom::WebClientHintsType::kRtt,
-                          blink::mojom::FeaturePolicyFeature::kClientHintRTT)) {
+  if (ShouldAddClientHint(data, network::mojom::WebClientHintsType::kRtt)) {
     AddRttHeader(headers, network_quality_tracker, url);
   }
-  if (ShouldAddClientHint(
-          data, network::mojom::WebClientHintsType::kDownlink,
-          blink::mojom::FeaturePolicyFeature::kClientHintDownlink)) {
+  if (ShouldAddClientHint(data,
+                          network::mojom::WebClientHintsType::kDownlink)) {
     AddDownlinkHeader(headers, network_quality_tracker, url);
   }
-  if (ShouldAddClientHint(data, network::mojom::WebClientHintsType::kEct,
-                          blink::mojom::FeaturePolicyFeature::kClientHintECT)) {
+  if (ShouldAddClientHint(data, network::mojom::WebClientHintsType::kEct)) {
     AddEctHeader(headers, network_quality_tracker, url);
   }
-  if (ShouldAddClientHint(
-          data, network::mojom::WebClientHintsType::kLang,
-          blink::mojom::FeaturePolicyFeature::kClientHintLang)) {
+  if (ShouldAddClientHint(data, network::mojom::WebClientHintsType::kLang)) {
     AddLangHeader(headers, context);
   }
 
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index 63c92e8e..94c329f3 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -1298,12 +1298,6 @@
     const net::HttpRequestHeaders& modified_headers,
     const net::HttpRequestHeaders& modified_cors_exempt_headers,
     const base::Optional<GURL>& new_url) {
-  // TODO(arthursonzogni, juncai): This seems to be correctly implemented, but
-  // not used nor tested so far. Add tests and remove this DCHECK to support
-  // this feature if needed. See https://crbug.com/845683.
-  DCHECK(removed_headers.empty())
-      << "Redirect with removed headers is not supported yet. See "
-         "https://crbug.com/845683";
   DCHECK(!new_url.has_value()) << "Redirect with modified url was not "
                                   "supported yet. crbug.com/845683";
   DCHECK(!waiting_for_resolution_);
diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc
index e2f68c5..100cd80 100644
--- a/content/browser/frame_host/frame_tree.cc
+++ b/content/browser/frame_host/frame_tree.cc
@@ -19,7 +19,9 @@
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
+#include "content/browser/frame_host/navigator_delegate.h"
 #include "content/browser/frame_host/render_frame_host_factory.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/frame_host/render_frame_proxy_host.h"
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index 0bcc07b..d486497 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -21,6 +21,7 @@
 #include "content/browser/frame_host/navigation_controller_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
+#include "content/browser/frame_host/navigator_delegate.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/common/frame_messages.h"
diff --git a/content/browser/frame_host/frame_tree_unittest.cc b/content/browser/frame_host/frame_tree_unittest.cc
index c514e1c..1feeb329 100644
--- a/content/browser/frame_host/frame_tree_unittest.cc
+++ b/content/browser/frame_host/frame_tree_unittest.cc
@@ -9,7 +9,7 @@
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
-#include "content/browser/frame_host/navigator_impl.h"
+#include "content/browser/frame_host/navigator.h"
 #include "content/browser/frame_host/render_frame_host_factory.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index f03ac5b..807e835 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -27,7 +27,6 @@
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
-#include "content/browser/frame_host/navigator_impl.h"
 #include "content/browser/site_instance_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/webui/content_web_ui_controller_factory.h"
@@ -3003,7 +3002,7 @@
 
   // Suppose it aborts before committing, if it's a 204 or download or due to a
   // stop or a new navigation from the user.  The URL should remain visible.
-  static_cast<NavigatorImpl*>(main_test_rfh()->frame_tree_node()->navigator())
+  static_cast<Navigator*>(main_test_rfh()->frame_tree_node()->navigator())
       ->CancelNavigation(main_test_rfh()->frame_tree_node());
   EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
 
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index f0535102..46b7734 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -40,7 +40,7 @@
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigation_request_info.h"
 #include "content/browser/frame_host/navigator.h"
-#include "content/browser/frame_host/navigator_impl.h"
+#include "content/browser/frame_host/navigator_delegate.h"
 #include "content/browser/frame_host/origin_policy_throttle.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/loader/browser_initiated_resource_request.h"
@@ -109,6 +109,7 @@
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "services/network/public/mojom/web_sandbox_flags.mojom.h"
 #include "third_party/blink/public/common/blob/blob_utils.h"
+#include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
@@ -1272,7 +1273,7 @@
     // Select an appropriate RenderFrameHost.
     render_frame_host_ =
         frame_tree_node_->render_manager()->GetFrameHostForNavigation(this);
-    if (!NavigatorImpl::CheckWebUIRendererDoesNotDisplayNormalURL(
+    if (!Navigator::CheckWebUIRendererDoesNotDisplayNormalURL(
             render_frame_host_, common_params_->url,
             /* is_renderer_initiated_check */ false)) {
       // TODO(nasko): Convert this to CHECK once it is confirmed that it does
@@ -2047,7 +2048,7 @@
     render_frame_host_ =
         frame_tree_node_->render_manager()->GetFrameHostForNavigation(this);
 
-    if (!NavigatorImpl::CheckWebUIRendererDoesNotDisplayNormalURL(
+    if (!Navigator::CheckWebUIRendererDoesNotDisplayNormalURL(
             render_frame_host_, common_params_->url,
             /* is_renderer_initiated_check */ false)) {
       // TODO(nasko): Convert this to CHECK once it is confirmed that it does
@@ -2312,7 +2313,7 @@
   // to be committed in a WebUI process as shown in https://crbug.com/944086.
   if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(
           frame_tree_node_->IsMainFrame())) {
-    if (!NavigatorImpl::CheckWebUIRendererDoesNotDisplayNormalURL(
+    if (!Navigator::CheckWebUIRendererDoesNotDisplayNormalURL(
             render_frame_host_, common_params_->url,
             /* is_renderer_initiated_check */ false)) {
       // TODO(nasko): Convert this to CHECK once it is confirmed that it does
@@ -2603,7 +2604,12 @@
 
   net::HttpRequestHeaders modified_headers = TakeModifiedRequestHeaders();
   std::vector<std::string> removed_headers = TakeRemovedRequestHeaders();
+  // Removes all Client Hints from the request, that were passed on from the
+  // previous one.
+  for (size_t i = 0; i < blink::kClientHintsMappingsCount; ++i)
+    removed_headers.push_back(blink::kClientHintsHeaderMapping[i]);
 
+  // Add any required Client Hints to the current request.
   BrowserContext* browser_context =
       frame_tree_node_->navigator()->GetController()->GetBrowserContext();
   ClientHintsControllerDelegate* client_hints_delegate =
diff --git a/content/browser/frame_host/navigator.cc b/content/browser/frame_host/navigator.cc
index c07ed77..5d1d6af 100644
--- a/content/browser/frame_host/navigator.cc
+++ b/content/browser/frame_host/navigator.cc
@@ -1,31 +1,601 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "content/browser/frame_host/navigator.h"
 
+#include <utility>
+
+#include "base/check_op.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
+#include "base/strings/string_util.h"
 #include "base/time/time.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/frame_host/debug_urls.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/navigation_controller_impl.h"
+#include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/frame_host/navigation_request.h"
+#include "content/browser/frame_host/navigation_request_info.h"
+#include "content/browser/frame_host/navigator_delegate.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/browser/site_instance_impl.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/browser/web_package/web_bundle_handle_tracker.h"
+#include "content/browser/webui/web_ui_controller_factory_registry.h"
+#include "content/browser/webui/web_ui_impl.h"
+#include "content/common/frame_messages.h"
+#include "content/common/navigation_params.h"
+#include "content/common/navigation_params_utils.h"
+#include "content/common/page_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/global_request_id.h"
+#include "content/public/browser/invalidate_type.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/page_navigator.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/common/bindings_policy.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/content_constants.h"
+#include "content/public/common/navigation_policy.h"
+#include "content/public/common/url_utils.h"
+#include "net/base/net_errors.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "url/gurl.h"
+#include "url/url_util.h"
 
 namespace content {
 
+struct Navigator::NavigationMetricsData {
+  NavigationMetricsData(base::TimeTicks start_time,
+                        GURL url,
+                        RestoreType restore_type)
+      : start_time_(start_time), url_(url) {
+    is_restoring_from_last_session_ =
+        (restore_type == RestoreType::LAST_SESSION_EXITED_CLEANLY ||
+         restore_type == RestoreType::LAST_SESSION_CRASHED);
+  }
+
+  base::TimeTicks start_time_;
+  GURL url_;
+  bool is_restoring_from_last_session_;
+  base::TimeTicks url_job_start_time_;
+  base::TimeDelta before_unload_delay_;
+};
+
+Navigator::Navigator(NavigationControllerImpl* navigation_controller,
+                     NavigatorDelegate* delegate)
+    : controller_(navigation_controller), delegate_(delegate) {}
+
+Navigator::~Navigator() = default;
+
+// static
+bool Navigator::CheckWebUIRendererDoesNotDisplayNormalURL(
+    RenderFrameHostImpl* render_frame_host,
+    const GURL& url,
+    bool is_renderer_initiated_check) {
+  // In single process mode, everything runs in the same process, so the checks
+  // below are irrelevant.
+  if (RenderProcessHost::run_renderer_in_process())
+    return true;
+
+  ChildProcessSecurityPolicyImpl* security_policy =
+      ChildProcessSecurityPolicyImpl::GetInstance();
+  GURL process_lock_url =
+      security_policy->GetOriginLock(render_frame_host->GetProcess()->GetID());
+
+  // In the case of error page process, any URL is allowed to commit.
+  if (process_lock_url == GURL(kUnreachableWebDataURL))
+    return true;
+
+  bool frame_has_bindings = ((render_frame_host->GetEnabledBindings() &
+                              kWebUIBindingsPolicyMask) != 0);
+  bool is_allowed_in_web_ui_renderer =
+      WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
+          render_frame_host->GetProcess()->GetBrowserContext(), url);
+
+  // Embedders might disable locking for WebUI URLs, which is bad idea, however
+  // this method should take this into account.
+  bool should_lock_to_origin = SiteInstanceImpl::ShouldLockToOrigin(
+      render_frame_host->GetSiteInstance()->GetIsolationContext(), url,
+      render_frame_host->GetSiteInstance()->IsGuest());
+
+  // If the |render_frame_host| has any WebUI bindings, disallow URLs that are
+  // not allowed in a WebUI renderer process.
+  if (frame_has_bindings) {
+    // The process itself must have WebUI bit in the security policy.
+    // Otherwise it indicates that there is a bug in browser process logic and
+    // the browser process must be terminated.
+    // TODO(nasko): Convert to CHECK() once it is confirmed this is not
+    // violated in reality.
+    if (!security_policy->HasWebUIBindings(
+            render_frame_host->GetProcess()->GetID())) {
+      base::debug::DumpWithoutCrashing();
+    }
+
+    // Check whether the process must be locked and if so that the process lock
+    // is indeed in place.
+    if (should_lock_to_origin && process_lock_url.is_empty())
+      return false;
+
+    // There must be a WebUI on the frame.
+    if (!render_frame_host->web_ui())
+      return false;
+
+    // The |url| must be allowed in a WebUI process if the frame has WebUI.
+    if (!is_allowed_in_web_ui_renderer) {
+      // If this method is called in response to IPC message from the renderer
+      // process, it should be terminated, otherwise it is a bug in the
+      // navigation logic and the browser process should be terminated to avoid
+      // exposing users to security issues.
+      if (is_renderer_initiated_check)
+        return false;
+
+      CHECK(false);
+    }
+  }
+
+  // If |url| is one that is allowed in WebUI renderer process, ensure that its
+  // origin is either opaque or matches the origin of the process lock.
+  if (is_allowed_in_web_ui_renderer) {
+    url::Origin url_origin = url::Origin::Create(url.GetOrigin());
+
+    // Verify |url| matches the origin of the process lock, if one is in place.
+    if (should_lock_to_origin) {
+      url::Origin process_lock_origin = url::Origin::Create(process_lock_url);
+      if (!url_origin.opaque() && process_lock_origin != url_origin)
+        return false;
+    }
+  }
+
+  return true;
+}
+
+// A renderer-initiated navigation should be ignored iff a) there is an ongoing
+// request b) which is browser initiated and c) the renderer request is not
+// user-initiated.
+// static
+bool Navigator::ShouldIgnoreIncomingRendererRequest(
+    const NavigationRequest* ongoing_navigation_request,
+    bool has_user_gesture) {
+  return ongoing_navigation_request &&
+         ongoing_navigation_request->browser_initiated() && !has_user_gesture;
+}
+
 NavigatorDelegate* Navigator::GetDelegate() {
-  return nullptr;
+  return delegate_;
 }
 
 NavigationController* Navigator::GetController() {
-  return nullptr;
+  return controller_;
+}
+
+base::TimeTicks Navigator::GetCurrentLoadStart() {
+  return base::TimeTicks::Now();
+}
+
+void Navigator::DidFailLoadWithError(RenderFrameHostImpl* render_frame_host,
+                                     const GURL& url,
+                                     int error_code) {
+  if (delegate_) {
+    delegate_->DidFailLoadWithError(render_frame_host, url, error_code);
+  }
 }
 
 bool Navigator::StartHistoryNavigationInNewSubframe(
     RenderFrameHostImpl* render_frame_host,
     mojo::PendingAssociatedRemote<mojom::NavigationClient>* navigation_client) {
-  return false;
+  return controller_->StartHistoryNavigationInNewSubframe(render_frame_host,
+                                                          navigation_client);
 }
 
-base::TimeTicks Navigator::GetCurrentLoadStart() {
-  return base::TimeTicks::Now();
+void Navigator::DidNavigate(
+    RenderFrameHostImpl* render_frame_host,
+    const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
+    std::unique_ptr<NavigationRequest> navigation_request,
+    bool was_within_same_document) {
+  DCHECK(navigation_request);
+  FrameTreeNode* frame_tree_node = render_frame_host->frame_tree_node();
+  FrameTree* frame_tree = frame_tree_node->frame_tree();
+
+  bool is_same_document_navigation = controller_->IsURLSameDocumentNavigation(
+      params.url, params.origin, was_within_same_document, render_frame_host);
+
+  // If a frame claims the navigation was same-document, it must be the current
+  // frame, not a pending one.
+  if (is_same_document_navigation &&
+      render_frame_host !=
+          frame_tree_node->render_manager()->current_frame_host()) {
+    bad_message::ReceivedBadMessage(render_frame_host->GetProcess(),
+                                    bad_message::NI_IN_PAGE_NAVIGATION);
+    is_same_document_navigation = false;
+  }
+
+  if (ui::PageTransitionIsMainFrame(params.transition)) {
+    if (delegate_) {
+      // Run tasks that must execute just before the commit.
+      delegate_->DidNavigateMainFramePreCommit(is_same_document_navigation);
+    }
+  }
+
+  // For browser initiated navigation and same document navigation, frame policy
+  // in commit_params is nullopt and should use fallback value instead.
+  const blink::FramePolicy pending_frame_policy =
+      navigation_request->commit_params().frame_policy.value_or(
+          frame_tree_node->pending_frame_policy());
+
+  // DidNavigateFrame() must be called before replicating the new origin and
+  // other properties to proxies.  This is because it destroys the subframes of
+  // the frame we're navigating from, which might trigger those subframes to
+  // run unload handlers.  Those unload handlers should still see the old
+  // frame's origin.  See https://crbug.com/825283.
+  frame_tree_node->render_manager()->DidNavigateFrame(
+      render_frame_host, params.gesture == NavigationGestureUser,
+      is_same_document_navigation,
+      navigation_request
+          ->require_coop_browsing_instance_swap() /* clear_proxies_on_commit */,
+      pending_frame_policy);
+
+  // Save the new page's origin and other properties, and replicate them to
+  // proxies, including the proxy created in DidNavigateFrame() to replace the
+  // old frame in cross-process navigation cases.
+  frame_tree_node->SetCurrentOrigin(
+      params.origin, params.has_potentially_trustworthy_unique_origin);
+  frame_tree_node->SetInsecureRequestPolicy(params.insecure_request_policy);
+  frame_tree_node->SetInsecureNavigationsSet(params.insecure_navigations_set);
+
+  // Save the activation status of the previous page here before it gets reset
+  // in FrameTreeNode::ResetForNavigation.
+  bool previous_document_was_activated =
+      frame_tree->root()->HasStickyUserActivation();
+
+  if (!is_same_document_navigation) {
+    // Navigating to a new location means a new, fresh set of http headers
+    // and/or <meta> elements - we need to reset CSP and Feature Policy.
+    render_frame_host->ResetContentSecurityPolicies();
+    frame_tree_node->ResetForNavigation();
+
+    // Save the new document's embedding token and propagate to any parent
+    // document that embeds it. A token is only assigned to cross-process
+    // child frames.
+    render_frame_host->SetEmbeddingToken(params.embedding_token);
+  }
+
+  // Update the site of the SiteInstance if it doesn't have one yet, unless
+  // assigning a site is not necessary for this URL. In that case, the
+  // SiteInstance can still be considered unused until a navigation to a real
+  // page.
+  SiteInstanceImpl* site_instance = render_frame_host->GetSiteInstance();
+  if (!site_instance->HasSite() &&
+      SiteInstanceImpl::ShouldAssignSiteForURL(params.url)) {
+    site_instance->ConvertToDefaultOrSetSite(params.url);
+  }
+
+  // Need to update MIME type here because it's referred to in
+  // UpdateNavigationCommands() called by RendererDidNavigate() to
+  // determine whether or not to enable the encoding menu.
+  // It's updated only for the main frame. For a subframe,
+  // RenderView::UpdateURL does not set params.contents_mime_type.
+  // (see http://code.google.com/p/chromium/issues/detail?id=2929 )
+  // TODO(jungshik): Add a test for the encoding menu to avoid
+  // regressing it again.
+  // TODO(nasko): Verify the correctness of the above comment, since some of the
+  // code doesn't exist anymore. Also, move this code in the
+  // PageTransitionIsMainFrame code block above.
+  if (ui::PageTransitionIsMainFrame(params.transition) && delegate_)
+    delegate_->SetMainFrameMimeType(params.contents_mime_type);
+
+  int old_entry_count = controller_->GetEntryCount();
+  LoadCommittedDetails details;
+  bool did_navigate = controller_->RendererDidNavigate(
+      render_frame_host, params, &details, is_same_document_navigation,
+      previous_document_was_activated, navigation_request.get());
+
+  // If the history length and/or offset changed, update other renderers in the
+  // FrameTree.
+  if (old_entry_count != controller_->GetEntryCount() ||
+      details.previous_entry_index !=
+          controller_->GetLastCommittedEntryIndex()) {
+    frame_tree->root()->render_manager()->SendPageMessage(
+        new PageMsg_SetHistoryOffsetAndLength(
+            MSG_ROUTING_NONE, controller_->GetLastCommittedEntryIndex(),
+            controller_->GetEntryCount()),
+        site_instance);
+  }
+
+  render_frame_host->DidNavigate(params, is_same_document_navigation);
+
+  // Send notification about committed provisional loads. This notification is
+  // different from the NAV_ENTRY_COMMITTED notification which doesn't include
+  // the actual URL navigated to and isn't sent for AUTO_SUBFRAME navigations.
+  if (details.type != NAVIGATION_TYPE_NAV_IGNORE && delegate_) {
+    DCHECK_EQ(!render_frame_host->GetParent(),
+              did_navigate ? details.is_main_frame : false);
+    navigation_request->DidCommitNavigation(params, did_navigate,
+                                            details.did_replace_entry,
+                                            details.previous_url, details.type);
+    navigation_request.reset();
+  }
+
+  if (!did_navigate)
+    return;  // No navigation happened.
+
+  // DO NOT ADD MORE STUFF TO THIS FUNCTION! Your component should either listen
+  // for the appropriate notification (best) or you can add it to
+  // DidNavigateMainFramePostCommit / DidNavigateAnyFramePostCommit (only if
+  // necessary, please).
+
+  // TODO(carlosk): Move this out.
+  RecordNavigationMetrics(details, params, site_instance);
+
+  // Run post-commit tasks.
+  if (delegate_) {
+    if (details.is_main_frame) {
+      delegate_->DidNavigateMainFramePostCommit(render_frame_host, details,
+                                                params);
+    }
+
+    delegate_->DidNavigateAnyFramePostCommit(render_frame_host, details,
+                                             params);
+  }
+}
+
+void Navigator::Navigate(std::unique_ptr<NavigationRequest> request,
+                         ReloadType reload_type,
+                         RestoreType restore_type) {
+  TRACE_EVENT0("browser,navigation", "Navigator::Navigate");
+  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
+      "navigation,rail", "NavigationTiming navigationStart",
+      TRACE_EVENT_SCOPE_GLOBAL, request->common_params().navigation_start);
+
+  // Save destination url, as it is needed for
+  // DidStartNavigationToPendingEntry and request could be destroyed after
+  // BeginNavigation below.
+  GURL dest_url = request->common_params().url;
+  FrameTreeNode* frame_tree_node = request->frame_tree_node();
+
+  navigation_data_.reset(new NavigationMetricsData(
+      request->common_params().navigation_start, dest_url, restore_type));
+
+  // Check if the BeforeUnload event needs to execute before assigning the
+  // NavigationRequest to the FrameTreeNode. Assigning it to the FrameTreeNode
+  // has the side effect of initializing the current RenderFrameHost, which will
+  // return that it should execute the BeforeUnload event (even though we don't
+  // need to wait for it in the case of a brand new RenderFrameHost).
+  //
+  // We don't want to dispatch a beforeunload handler if
+  // is_history_navigation_in_new_child is true. This indicates a newly created
+  // child frame which does not have a beforeunload handler.
+  bool should_dispatch_beforeunload =
+      !NavigationTypeUtils::IsSameDocument(
+          request->common_params().navigation_type) &&
+      !request->common_params().is_history_navigation_in_new_child_frame &&
+      frame_tree_node->current_frame_host()->ShouldDispatchBeforeUnload(
+          false /* check_subframes_only */);
+
+  int nav_entry_id = request->nav_entry_id();
+  bool is_pending_entry =
+      controller_->GetPendingEntry() &&
+      (nav_entry_id == controller_->GetPendingEntry()->GetUniqueID());
+  frame_tree_node->CreatedNavigationRequest(std::move(request));
+  DCHECK(frame_tree_node->navigation_request());
+
+  // Have the current renderer execute its beforeunload event if needed. If it
+  // is not needed then NavigationRequest::BeginNavigation should be directly
+  // called instead.
+  if (should_dispatch_beforeunload) {
+    frame_tree_node->navigation_request()->SetWaitingForRendererResponse();
+    frame_tree_node->current_frame_host()->DispatchBeforeUnload(
+        RenderFrameHostImpl::BeforeUnloadType::BROWSER_INITIATED_NAVIGATION,
+        reload_type != ReloadType::NONE);
+  } else {
+    frame_tree_node->navigation_request()->BeginNavigation();
+    // WARNING: The NavigationRequest might have been destroyed in
+    // BeginNavigation(). Do not use |frame_tree_node->navigation_request()|
+    // after this point without null checking it first.
+  }
+
+  // Make sure no code called via RFH::Navigate clears the pending entry.
+  if (is_pending_entry)
+    CHECK_EQ(nav_entry_id, controller_->GetPendingEntry()->GetUniqueID());
+
+  // Notify observers about navigation.
+  if (delegate_ && is_pending_entry)
+    delegate_->DidStartNavigationToPendingEntry(dest_url, reload_type);
+}
+
+void Navigator::RequestOpenURL(
+    RenderFrameHostImpl* render_frame_host,
+    const GURL& url,
+    const GlobalFrameRoutingId& initiator_routing_id,
+    const base::Optional<url::Origin>& initiator_origin,
+    const scoped_refptr<network::ResourceRequestBody>& post_body,
+    const std::string& extra_headers,
+    const Referrer& referrer,
+    WindowOpenDisposition disposition,
+    bool should_replace_current_entry,
+    bool user_gesture,
+    blink::TriggeringEventInfo triggering_event_info,
+    const std::string& href_translate,
+    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+    const base::Optional<Impression>& impression) {
+  // Note: This can be called for subframes (even when OOPIFs are not possible)
+  // if the disposition calls for a different window.
+
+  // Only the current RenderFrameHost should be sending an OpenURL request.
+  // Pending RenderFrameHost should know where it is navigating and pending
+  // deletion RenderFrameHost shouldn't be trying to navigate.
+  if (render_frame_host !=
+      render_frame_host->frame_tree_node()->current_frame_host()) {
+    return;
+  }
+
+  SiteInstance* current_site_instance = render_frame_host->GetSiteInstance();
+
+  // TODO(creis): Pass the redirect_chain into this method to support client
+  // redirects.  http://crbug.com/311721.
+  std::vector<GURL> redirect_chain;
+
+  int frame_tree_node_id = -1;
+
+  // Send the navigation to the current FrameTreeNode if it's destined for a
+  // subframe in the current tab.  We'll assume it's for the main frame
+  // (possibly of a new or different WebContents) otherwise.
+  if (disposition == WindowOpenDisposition::CURRENT_TAB &&
+      render_frame_host->GetParent()) {
+    frame_tree_node_id =
+        render_frame_host->frame_tree_node()->frame_tree_node_id();
+  }
+
+  OpenURLParams params(url, referrer, frame_tree_node_id, disposition,
+                       ui::PAGE_TRANSITION_LINK,
+                       true /* is_renderer_initiated */);
+  params.post_data = post_body;
+  params.extra_headers = extra_headers;
+  if (redirect_chain.size() > 0)
+    params.redirect_chain = redirect_chain;
+  params.should_replace_current_entry = should_replace_current_entry;
+  params.user_gesture = user_gesture;
+  params.triggering_event_info = triggering_event_info;
+  params.initiator_origin = initiator_origin;
+  params.initiator_routing_id = initiator_routing_id;
+
+  // RequestOpenURL is used only for local frames, so we can get here only if
+  // the navigation is initiated by a frame in the same SiteInstance as this
+  // frame.  Note that navigations on RenderFrameProxies do not use
+  // RequestOpenURL and go through NavigateFromFrameProxy instead.
+  params.source_site_instance = current_site_instance;
+
+  params.source_render_frame_id = render_frame_host->GetRoutingID();
+  params.source_render_process_id = render_frame_host->GetProcess()->GetID();
+
+  if (render_frame_host->web_ui()) {
+    // Note that we hide the referrer for Web UI pages. We don't really want
+    // web sites to see a referrer of "chrome://blah" (and some chrome: URLs
+    // might have search terms or other stuff we don't want to send to the
+    // site), so we send no referrer.
+    params.referrer = Referrer();
+
+    // Navigations in Web UI pages count as browser-initiated navigations.
+    params.is_renderer_initiated = false;
+  }
+
+  params.blob_url_loader_factory = std::move(blob_url_loader_factory);
+  params.href_translate = href_translate;
+  params.impression = impression;
+
+  if (delegate_)
+    delegate_->OpenURL(params);
+}
+
+void Navigator::NavigateFromFrameProxy(
+    RenderFrameHostImpl* render_frame_host,
+    const GURL& url,
+    const GlobalFrameRoutingId& initiator_routing_id,
+    const url::Origin& initiator_origin,
+    SiteInstance* source_site_instance,
+    const Referrer& referrer,
+    ui::PageTransition page_transition,
+    bool should_replace_current_entry,
+    NavigationDownloadPolicy download_policy,
+    const std::string& method,
+    scoped_refptr<network::ResourceRequestBody> post_body,
+    const std::string& extra_headers,
+    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+    bool has_user_gesture,
+    const base::Optional<Impression>& impression) {
+  // |method != "POST"| should imply absence of |post_body|.
+  if (method != "POST" && post_body) {
+    NOTREACHED();
+    post_body = nullptr;
+  }
+
+  // Allow the delegate to cancel the transfer.
+  if (!delegate_->ShouldTransferNavigation(
+          render_frame_host->frame_tree_node()->IsMainFrame()))
+    return;
+
+  // TODO(creis): Determine if this transfer started as a browser-initiated
+  // navigation.  See https://crbug.com/495161.
+  bool is_renderer_initiated = true;
+  Referrer referrer_to_use(referrer);
+  if (render_frame_host->web_ui()) {
+    // Note that we hide the referrer for Web UI pages. We don't really want
+    // web sites to see a referrer of "chrome://blah" (and some chrome: URLs
+    // might have search terms or other stuff we don't want to send to the
+    // site), so we send no referrer.
+    referrer_to_use = Referrer();
+
+    // Navigations in Web UI pages count as browser-initiated navigations.
+    is_renderer_initiated = false;
+  }
+
+  if (is_renderer_initiated &&
+      ShouldIgnoreIncomingRendererRequest(
+          render_frame_host->frame_tree_node()->navigation_request(),
+          has_user_gesture)) {
+    return;
+  }
+
+  controller_->NavigateFromFrameProxy(
+      render_frame_host, url, initiator_routing_id, initiator_origin,
+      is_renderer_initiated, source_site_instance, referrer_to_use,
+      page_transition, should_replace_current_entry, download_policy, method,
+      post_body, extra_headers, std::move(blob_url_loader_factory), impression);
+}
+
+void Navigator::BeforeUnloadCompleted(FrameTreeNode* frame_tree_node,
+                                      bool proceed,
+                                      const base::TimeTicks& proceed_time) {
+  DCHECK(frame_tree_node);
+
+  NavigationRequest* navigation_request = frame_tree_node->navigation_request();
+
+  // The NavigationRequest may have been canceled while the renderer was
+  // executing the BeforeUnload event.
+  if (!navigation_request)
+    return;
+
+  // If the user chose not to proceed, cancel the ongoing navigation.
+  // Note: it might be a new navigation, and not the one that triggered the
+  // sending of the BeforeUnload IPC in the first place. However, the
+  // BeforeUnload where the user asked not to proceed will have taken place
+  // after the navigation started. The last user input should be respected, and
+  // the navigation cancelled anyway.
+  if (!proceed) {
+    CancelNavigation(frame_tree_node);
+    return;
+  }
+
+  // The browser-initiated NavigationRequest that triggered the sending of the
+  // BeforeUnload IPC might have been replaced by a renderer-initiated one while
+  // the BeforeUnload event executed in the renderer. In that case, the request
+  // will already have begun, so there is no need to start it again.
+  if (navigation_request->state() >
+      NavigationRequest::WAITING_FOR_RENDERER_RESPONSE) {
+    DCHECK(navigation_request->from_begin_navigation());
+    return;
+  }
+
+  // Update the navigation start: it should be when it was determined that the
+  // navigation will proceed.
+  navigation_request->set_navigation_start_time(proceed_time);
+
+  DCHECK_EQ(NavigationRequest::WAITING_FOR_RENDERER_RESPONSE,
+            navigation_request->state());
+
+  // Send the request to the IO thread.
+  navigation_request->BeginNavigation();
+  // DO NOT USE |navigation_request| BEYOND THIS POINT. It might have been
+  // destroyed in BeginNavigation().
+  // See https://crbug.com/770157.
 }
 
 void Navigator::OnBeginNavigation(
@@ -37,6 +607,239 @@
     mojo::PendingRemote<blink::mojom::NavigationInitiator> navigation_initiator,
     scoped_refptr<PrefetchedSignedExchangeCache>
         prefetched_signed_exchange_cache,
-    std::unique_ptr<WebBundleHandleTracker> web_bundle_handle_tracker) {}
+    std::unique_ptr<WebBundleHandleTracker> web_bundle_handle_tracker) {
+  // TODO(clamy): the url sent by the renderer should be validated with
+  // FilterURL.
+  // This is a renderer-initiated navigation.
+  DCHECK(frame_tree_node);
+
+  if (common_params->is_history_navigation_in_new_child_frame) {
+    // Try to find a FrameNavigationEntry that matches this frame instead, based
+    // on the frame's unique name.  If this can't be found, fall back to the
+    // default path below.
+    if (frame_tree_node->navigator()->StartHistoryNavigationInNewSubframe(
+            frame_tree_node->current_frame_host(), &navigation_client)) {
+      return;
+    }
+  }
+
+  NavigationRequest* ongoing_navigation_request =
+      frame_tree_node->navigation_request();
+
+  // Client redirects during the initial history navigation of a child frame
+  // should take precedence over the history navigation (despite being renderer-
+  // initiated).  See https://crbug.com/348447 and https://crbug.com/691168.
+  if (ongoing_navigation_request &&
+      ongoing_navigation_request->common_params()
+          .is_history_navigation_in_new_child_frame) {
+    // Preemptively clear this local pointer before deleting the request.
+    ongoing_navigation_request = nullptr;
+    frame_tree_node->ResetNavigationRequest(false);
+  }
+
+  // Verify this navigation has precedence.
+  if (ShouldIgnoreIncomingRendererRequest(ongoing_navigation_request,
+                                          common_params->has_user_gesture)) {
+    return;
+  }
+
+  NavigationEntryImpl* navigation_entry =
+      GetNavigationEntryForRendererInitiatedNavigation(*common_params,
+                                                       frame_tree_node);
+  const bool override_user_agent =
+      delegate_ &&
+      delegate_->ShouldOverrideUserAgentForRendererInitiatedNavigation();
+  frame_tree_node->CreatedNavigationRequest(
+      NavigationRequest::CreateRendererInitiated(
+          frame_tree_node, navigation_entry, std::move(common_params),
+          std::move(begin_params), controller_->GetLastCommittedEntryIndex(),
+          controller_->GetEntryCount(), override_user_agent,
+          std::move(blob_url_loader_factory), std::move(navigation_client),
+          std::move(navigation_initiator),
+          std::move(prefetched_signed_exchange_cache),
+          std::move(web_bundle_handle_tracker)));
+  NavigationRequest* navigation_request = frame_tree_node->navigation_request();
+
+  // This frame has already run beforeunload before it sent this IPC.  See if
+  // any of its cross-process subframes also need to run beforeunload.  If so,
+  // delay the navigation until beforeunload completion callbacks are invoked on
+  // those frames.
+  DCHECK(!NavigationTypeUtils::IsSameDocument(
+      navigation_request->common_params().navigation_type));
+  bool should_dispatch_beforeunload =
+      frame_tree_node->current_frame_host()->ShouldDispatchBeforeUnload(
+          true /* check_subframes_only */);
+  if (should_dispatch_beforeunload) {
+    frame_tree_node->navigation_request()->SetWaitingForRendererResponse();
+    frame_tree_node->current_frame_host()->DispatchBeforeUnload(
+        RenderFrameHostImpl::BeforeUnloadType::RENDERER_INITIATED_NAVIGATION,
+        NavigationTypeUtils::IsReload(
+            navigation_request->common_params().navigation_type));
+    return;
+  }
+
+  // For main frames, NavigationHandle will be created after the call to
+  // |DidStartMainFrameNavigation|, so it receives the most up to date pending
+  // entry from the NavigationController.
+  navigation_request->BeginNavigation();
+  // DO NOT USE |navigation_request| BEYOND THIS POINT. It might have been
+  // destroyed in BeginNavigation().
+  // See https://crbug.com/770157.
+}
+
+void Navigator::RestartNavigationAsCrossDocument(
+    std::unique_ptr<NavigationRequest> navigation_request) {
+  FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node();
+  // Don't restart the navigation if there is already another ongoing navigation
+  // in the FrameTreeNode.
+  if (frame_tree_node->navigation_request())
+    return;
+
+  navigation_request->ResetForCrossDocumentRestart();
+  frame_tree_node->CreatedNavigationRequest(std::move(navigation_request));
+  frame_tree_node->navigation_request()->BeginNavigation();
+  // DO NOT USE THE NAVIGATION REQUEST BEYOND THIS POINT. It might have been
+  // destroyed in BeginNavigation().
+  // See https://crbug.com/770157.
+}
+
+void Navigator::CancelNavigation(FrameTreeNode* frame_tree_node) {
+  if (frame_tree_node->navigation_request())
+    frame_tree_node->navigation_request()->set_net_error(net::ERR_ABORTED);
+  frame_tree_node->ResetNavigationRequest(false);
+  if (frame_tree_node->IsMainFrame())
+    navigation_data_.reset();
+}
+
+void Navigator::LogResourceRequestTime(base::TimeTicks timestamp,
+                                       const GURL& url) {
+  if (navigation_data_ && navigation_data_->url_ == url) {
+    navigation_data_->url_job_start_time_ = timestamp;
+    UMA_HISTOGRAM_TIMES(
+        "Navigation.TimeToURLJobStart",
+        navigation_data_->url_job_start_time_ - navigation_data_->start_time_);
+  }
+}
+
+void Navigator::LogBeforeUnloadTime(
+    const base::TimeTicks& renderer_before_unload_start_time,
+    const base::TimeTicks& renderer_before_unload_end_time) {
+  // Only stores the beforeunload delay if we're tracking a browser initiated
+  // navigation and it happened later than the navigation request.
+  if (navigation_data_ &&
+      renderer_before_unload_start_time > navigation_data_->start_time_) {
+    navigation_data_->before_unload_delay_ =
+        renderer_before_unload_end_time - renderer_before_unload_start_time;
+  }
+}
+
+void Navigator::RecordNavigationMetrics(
+    const LoadCommittedDetails& details,
+    const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
+    SiteInstance* site_instance) {
+  DCHECK(site_instance->HasProcess());
+
+  if (!details.is_main_frame || !navigation_data_ ||
+      navigation_data_->url_job_start_time_.is_null() ||
+      navigation_data_->url_ != params.original_request_url) {
+    return;
+  }
+
+  base::TimeDelta time_to_commit =
+      base::TimeTicks::Now() - navigation_data_->start_time_;
+  UMA_HISTOGRAM_TIMES("Navigation.TimeToCommit", time_to_commit);
+
+  time_to_commit -= navigation_data_->before_unload_delay_;
+  base::TimeDelta time_to_network = navigation_data_->url_job_start_time_ -
+                                    navigation_data_->start_time_ -
+                                    navigation_data_->before_unload_delay_;
+  if (navigation_data_->is_restoring_from_last_session_) {
+    UMA_HISTOGRAM_TIMES(
+        "Navigation.TimeToCommit_SessionRestored_BeforeUnloadDiscounted",
+        time_to_commit);
+    UMA_HISTOGRAM_TIMES(
+        "Navigation.TimeToURLJobStart_SessionRestored_BeforeUnloadDiscounted",
+        time_to_network);
+    navigation_data_.reset();
+    return;
+  }
+  bool navigation_created_new_renderer_process =
+      site_instance->GetProcess()->GetInitTimeForNavigationMetrics() >
+      navigation_data_->start_time_;
+  if (navigation_created_new_renderer_process) {
+    UMA_HISTOGRAM_TIMES(
+        "Navigation.TimeToCommit_NewRenderer_BeforeUnloadDiscounted",
+        time_to_commit);
+    UMA_HISTOGRAM_TIMES(
+        "Navigation.TimeToURLJobStart_NewRenderer_BeforeUnloadDiscounted",
+        time_to_network);
+  } else {
+    UMA_HISTOGRAM_TIMES(
+        "Navigation.TimeToCommit_ExistingRenderer_BeforeUnloadDiscounted",
+        time_to_commit);
+    UMA_HISTOGRAM_TIMES(
+        "Navigation.TimeToURLJobStart_ExistingRenderer_BeforeUnloadDiscounted",
+        time_to_network);
+  }
+  navigation_data_.reset();
+}
+
+NavigationEntryImpl*
+Navigator::GetNavigationEntryForRendererInitiatedNavigation(
+    const mojom::CommonNavigationParams& common_params,
+    FrameTreeNode* frame_tree_node) {
+  if (!frame_tree_node->IsMainFrame())
+    return nullptr;
+
+  // If there is no browser-initiated pending entry for this navigation and it
+  // is not for the error URL, create a pending entry and ensure the address bar
+  // updates accordingly.  We don't know the referrer or extra headers at this
+  // point, but the referrer will be set properly upon commit.  This does not
+  // set the SiteInstance for the pending entry, because it may change
+  // before the URL commits.
+  NavigationEntryImpl* pending_entry = controller_->GetPendingEntry();
+  bool has_browser_initiated_pending_entry =
+      pending_entry && !pending_entry->is_renderer_initiated();
+  if (has_browser_initiated_pending_entry)
+    return nullptr;
+
+  // A pending navigation entry is created in OnBeginNavigation(). The renderer
+  // sends a provisional load notification after that. We don't want to create
+  // a duplicate navigation entry here.
+  bool renderer_provisional_load_to_pending_url =
+      pending_entry && pending_entry->is_renderer_initiated() &&
+      (pending_entry->GetURL() == common_params.url);
+  if (renderer_provisional_load_to_pending_url)
+    return nullptr;
+
+  // If there is a transient entry, creating a new pending entry will result
+  // in deleting it, which leads to inconsistent state.
+  bool has_transient_entry = !!controller_->GetTransientEntry();
+  if (has_transient_entry)
+    return nullptr;
+
+  // Since GetNavigationEntryForRendererInitiatedNavigation is called from
+  // OnBeginNavigation, we can assume that no frame proxies are involved and
+  // therefore that |current_site_instance| is also the |source_site_instance|.
+  SiteInstance* current_site_instance =
+      frame_tree_node->current_frame_host()->GetSiteInstance();
+  SiteInstance* source_site_instance = current_site_instance;
+
+  std::unique_ptr<NavigationEntryImpl> entry =
+      NavigationEntryImpl::FromNavigationEntry(
+          NavigationControllerImpl::CreateNavigationEntry(
+              common_params.url, content::Referrer(),
+              common_params.initiator_origin, source_site_instance,
+              ui::PAGE_TRANSITION_LINK, true /* is_renderer_initiated */,
+              std::string() /* extra_headers */,
+              controller_->GetBrowserContext(),
+              nullptr /* blob_url_loader_factory */));
+
+  controller_->SetPendingEntry(std::move(entry));
+  if (delegate_)
+    delegate_->NotifyChangedNavigationState(content::INVALIDATE_TYPE_URL);
+
+  return controller_->GetPendingEntry();
+}
 
 }  // namespace content
diff --git a/content/browser/frame_host/navigator.h b/content/browser/frame_host/navigator.h
index 3576eb28..211298e 100644
--- a/content/browser/frame_host/navigator.h
+++ b/content/browser/frame_host/navigator.h
@@ -5,18 +5,21 @@
 #ifndef CONTENT_BROWSER_FRAME_HOST_NAVIGATOR_H_
 #define CONTENT_BROWSER_FRAME_HOST_NAVIGATOR_H_
 
+#include <memory>
+
+#include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/time/time.h"
-#include "content/browser/frame_host/navigation_request.h"
-#include "content/browser/frame_host/navigator_delegate.h"
 #include "content/common/content_export.h"
-#include "content/common/frame_messages.h"
+#include "content/common/navigation_client.mojom.h"
+#include "content/common/navigation_params.h"
 #include "content/common/navigation_params.mojom.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/common/impression.h"
+#include "content/public/common/previews_state.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/common/navigation/triggering_event_info.h"
+#include "third_party/blink/public/mojom/frame/navigation_initiator.mojom.h"
 #include "ui/base/window_open_disposition.h"
 
 class GURL;
@@ -34,31 +37,60 @@
 
 class FrameNavigationEntry;
 class FrameTreeNode;
+class NavigationControllerImpl;
+class NavigatorDelegate;
+class NavigationEntryImpl;
+class NavigationRequest;
 class PrefetchedSignedExchangeCache;
 class RenderFrameHostImpl;
 class WebBundleHandleTracker;
+struct LoadCommittedDetails;
 
-// Implementations of this interface are responsible for performing navigations
-// in a node of the FrameTree. Its lifetime is bound to all FrameTreeNode
-// objects that are using it and will be released once all nodes that use it are
-// freed. The Navigator is bound to a single frame tree and cannot be used by
-// multiple instances of FrameTree.
+// Navigator is responsible for performing navigations in a node of the
+// FrameTree. Its lifetime is bound to all FrameTreeNode objects that are using
+// it and will be released once all nodes that use it are freed. The Navigator
+// is bound to a single frame tree and cannot be used by multiple instances of
+// FrameTree.
 // TODO(nasko): Move all navigation methods, such as didStartProvisionalLoad
 // from WebContentsImpl to this interface.
 class CONTENT_EXPORT Navigator : public base::RefCounted<Navigator> {
  public:
+  Navigator(NavigationControllerImpl* navigation_controller,
+            NavigatorDelegate* delegate);
+
+  // This method verifies that a navigation to |url| doesn't commit into a WebUI
+  // process if it is not allowed to. Callers of this method should take one of
+  // two actions if the method returns false:
+  // * When called from browser process logic (e.g. NavigationRequest), this
+  //   indicates issues with the navigation logic and the browser process must
+  //   be terminated to avoid security issues.
+  // * If the codepath is processing an IPC message from a renderer process,
+  //   then the renderer process is misbehaving and must be terminated.
+  // TODO(nasko): Remove the is_renderer_initiated_check parameter when callers
+  // of this method are migrated to use CHECK instead of DumpWithoutCrashing.
+  static WARN_UNUSED_RESULT bool CheckWebUIRendererDoesNotDisplayNormalURL(
+      RenderFrameHostImpl* render_frame_host,
+      const GURL& url,
+      bool is_renderer_initiated_check);
+
+  static bool ShouldIgnoreIncomingRendererRequest(
+      const NavigationRequest* ongoing_navigation_request,
+      bool has_user_gesture);
+
   // Returns the delegate of this Navigator.
-  virtual NavigatorDelegate* GetDelegate();
+  NavigatorDelegate* GetDelegate();
 
   // Returns the NavigationController associated with this Navigator.
-  virtual NavigationController* GetController();
+  NavigationController* GetController();
+
+  base::TimeTicks GetCurrentLoadStart();
 
   // Notifications coming from the RenderFrameHosts ----------------------------
 
   // The RenderFrameHostImpl has failed to load the document.
-  virtual void DidFailLoadWithError(RenderFrameHostImpl* render_frame_host,
-                                    const GURL& url,
-                                    int error_code) {}
+  void DidFailLoadWithError(RenderFrameHostImpl* render_frame_host,
+                            const GURL& url,
+                            int error_code);
 
   // The RenderFrameHostImpl has committed a navigation. The Navigator is
   // responsible for resetting |navigation_request| at the end of this method
@@ -68,17 +100,16 @@
   // same-page navigation commits while another navigation is ongoing. The
   // Navigator should use the NavigationRequest provided by this method and not
   // attempt to access the RenderFrameHost's NavigationsRequests.
-  virtual void DidNavigate(
-      RenderFrameHostImpl* render_frame_host,
-      const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
-      std::unique_ptr<NavigationRequest> navigation_request,
-      bool was_within_same_document) {}
+  void DidNavigate(RenderFrameHostImpl* render_frame_host,
+                   const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
+                   std::unique_ptr<NavigationRequest> navigation_request,
+                   bool was_within_same_document);
 
   // Called on a newly created subframe during a history navigation. The browser
   // process looks up the corresponding FrameNavigationEntry for the new frame
   // navigates it in the correct process. Returns false if the
   // FrameNavigationEntry can't be found or the navigation fails.
-  virtual bool StartHistoryNavigationInNewSubframe(
+  bool StartHistoryNavigationInNewSubframe(
       RenderFrameHostImpl* render_frame_host,
       mojo::PendingAssociatedRemote<mojom::NavigationClient>*
           navigation_client);
@@ -89,15 +120,13 @@
   // |navigation_request|. The NavigationController should be called back with
   // RendererDidNavigate on success or DiscardPendingEntry on failure. The
   // callbacks should be called in a future iteration of the message loop.
-  virtual void Navigate(std::unique_ptr<NavigationRequest> request,
-                        ReloadType reload_type,
-                        RestoreType restore_type) {}
-
-  virtual base::TimeTicks GetCurrentLoadStart();
+  void Navigate(std::unique_ptr<NavigationRequest> request,
+                ReloadType reload_type,
+                RestoreType restore_type);
 
   // The RenderFrameHostImpl has received a request to open a URL with the
   // specified |disposition|.
-  virtual void RequestOpenURL(
+  void RequestOpenURL(
       RenderFrameHostImpl* render_frame_host,
       const GURL& url,
       const GlobalFrameRoutingId& initiator_routing_id,
@@ -111,12 +140,12 @@
       blink::TriggeringEventInfo triggering_event_info,
       const std::string& href_translate,
       scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
-      const base::Optional<Impression>& impression) {}
+      const base::Optional<Impression>& impression);
 
   // Called when a document requests a navigation in another document through a
   // RenderFrameProxy. If |method| is "POST", then |post_body| needs to specify
   // the request body, otherwise |post_body| should be null.
-  virtual void NavigateFromFrameProxy(
+  void NavigateFromFrameProxy(
       RenderFrameHostImpl* render_frame_host,
       const GURL& url,
       const GlobalFrameRoutingId& initiator_routing_id,
@@ -131,19 +160,19 @@
       const std::string& extra_headers,
       scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
       bool has_user_gesture,
-      const base::Optional<Impression>& impression) {}
+      const base::Optional<Impression>& impression);
 
   // Called after BeforeUnloadCompleted callback is invoked from the renderer.
   // If |frame_tree_node| has a NavigationRequest waiting for the renderer
   // response, then the request is either started or canceled, depending on the
   // value of |proceed|.
-  virtual void BeforeUnloadCompleted(FrameTreeNode* frame_tree_node,
-                                     bool proceed,
-                                     const base::TimeTicks& proceed_time) {}
+  void BeforeUnloadCompleted(FrameTreeNode* frame_tree_node,
+                             bool proceed,
+                             const base::TimeTicks& proceed_time);
 
   // Used to start a new renderer-initiated navigation, following a
   // BeginNavigation IPC from the renderer.
-  virtual void OnBeginNavigation(
+  void OnBeginNavigation(
       FrameTreeNode* frame_tree_node,
       mojom::CommonNavigationParamsPtr common_params,
       mojom::BeginNavigationParamsPtr begin_params,
@@ -157,29 +186,59 @@
 
   // Used to restart a navigation that was thought to be same-document in
   // cross-document mode.
-  virtual void RestartNavigationAsCrossDocument(
-      std::unique_ptr<NavigationRequest> navigation_request) {}
+  void RestartNavigationAsCrossDocument(
+      std::unique_ptr<NavigationRequest> navigation_request);
 
   // Cancel a NavigationRequest for |frame_tree_node|.
-  virtual void CancelNavigation(FrameTreeNode* frame_tree_node) {}
+  void CancelNavigation(FrameTreeNode* frame_tree_node);
 
   // Called when the network stack started handling the navigation request
   // so that the |timestamp| when it happened can be recorded into an histogram.
   // The |url| is used to verify we're tracking the correct navigation.
   // TODO(carlosk): Remove the URL parameter and rename this method to better
   // suit naming conventions.
-  virtual void LogResourceRequestTime(base::TimeTicks timestamp,
-                                      const GURL& url) {}
+  void LogResourceRequestTime(base::TimeTicks timestamp, const GURL& url);
 
   // Called to record the time it took to execute the before unload hook for the
   // current navigation.
-  virtual void LogBeforeUnloadTime(
+  void LogBeforeUnloadTime(
       const base::TimeTicks& renderer_before_unload_start_time,
-      const base::TimeTicks& renderer_before_unload_end_time) {}
+      const base::TimeTicks& renderer_before_unload_end_time);
 
- protected:
+ private:
   friend class base::RefCounted<Navigator>;
-  virtual ~Navigator() {}
+  friend class NavigatorTestWithBrowserSideNavigation;
+
+  // Holds data used to track browser side navigation metrics.
+  struct NavigationMetricsData;
+
+  ~Navigator();
+
+  void RecordNavigationMetrics(
+      const LoadCommittedDetails& details,
+      const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
+      SiteInstance* site_instance);
+
+  // Called when a renderer initiated navigation has started. Returns the
+  // pending NavigationEntry to be used. Either null or a new one owned
+  // NavigationController.
+  NavigationEntryImpl* GetNavigationEntryForRendererInitiatedNavigation(
+      const mojom::CommonNavigationParams& common_params,
+      FrameTreeNode* frame_tree_node);
+
+  // The NavigationController that will keep track of session history for all
+  // RenderFrameHost objects using this Navigator.
+  // TODO(nasko): Move ownership of the NavigationController from
+  // WebContentsImpl to this class.
+  NavigationControllerImpl* controller_;
+
+  // Used to notify the object embedding this Navigator about navigation
+  // events. Can be nullptr in tests.
+  NavigatorDelegate* delegate_;
+
+  std::unique_ptr<Navigator::NavigationMetricsData> navigation_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(Navigator);
 };
 
 }  // namespace content
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
deleted file mode 100644
index 1460e5e..0000000
--- a/content/browser/frame_host/navigator_impl.cc
+++ /dev/null
@@ -1,841 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/frame_host/navigator_impl.h"
-
-#include <utility>
-
-#include "base/check_op.h"
-#include "base/debug/dump_without_crashing.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/notreached.h"
-#include "base/strings/string_util.h"
-#include "base/time/time.h"
-#include "content/browser/child_process_security_policy_impl.h"
-#include "content/browser/frame_host/debug_urls.h"
-#include "content/browser/frame_host/frame_tree.h"
-#include "content/browser/frame_host/frame_tree_node.h"
-#include "content/browser/frame_host/navigation_controller_impl.h"
-#include "content/browser/frame_host/navigation_entry_impl.h"
-#include "content/browser/frame_host/navigation_request.h"
-#include "content/browser/frame_host/navigation_request_info.h"
-#include "content/browser/frame_host/navigator_delegate.h"
-#include "content/browser/frame_host/render_frame_host_impl.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/site_instance_impl.h"
-#include "content/browser/web_package/prefetched_signed_exchange_cache.h"
-#include "content/browser/web_package/web_bundle_handle_tracker.h"
-#include "content/browser/webui/web_ui_controller_factory_registry.h"
-#include "content/browser/webui/web_ui_impl.h"
-#include "content/common/frame_messages.h"
-#include "content/common/navigation_params.h"
-#include "content/common/navigation_params_utils.h"
-#include "content/common/page_messages.h"
-#include "content/common/view_messages.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/browser/global_request_id.h"
-#include "content/public/browser/invalidate_type.h"
-#include "content/public/browser/navigation_controller.h"
-#include "content/public/browser/navigation_details.h"
-#include "content/public/browser/page_navigator.h"
-#include "content/public/browser/render_view_host.h"
-#include "content/public/common/bindings_policy.h"
-#include "content/public/common/content_client.h"
-#include "content/public/common/content_constants.h"
-#include "content/public/common/navigation_policy.h"
-#include "content/public/common/url_utils.h"
-#include "net/base/net_errors.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
-#include "url/gurl.h"
-#include "url/url_util.h"
-
-namespace content {
-
-struct NavigatorImpl::NavigationMetricsData {
-  NavigationMetricsData(base::TimeTicks start_time,
-                        GURL url,
-                        RestoreType restore_type)
-      : start_time_(start_time), url_(url) {
-    is_restoring_from_last_session_ =
-        (restore_type == RestoreType::LAST_SESSION_EXITED_CLEANLY ||
-         restore_type == RestoreType::LAST_SESSION_CRASHED);
-  }
-
-  base::TimeTicks start_time_;
-  GURL url_;
-  bool is_restoring_from_last_session_;
-  base::TimeTicks url_job_start_time_;
-  base::TimeDelta before_unload_delay_;
-};
-
-NavigatorImpl::NavigatorImpl(NavigationControllerImpl* navigation_controller,
-                             NavigatorDelegate* delegate)
-    : controller_(navigation_controller), delegate_(delegate) {}
-
-NavigatorImpl::~NavigatorImpl() {}
-
-// static
-bool NavigatorImpl::CheckWebUIRendererDoesNotDisplayNormalURL(
-    RenderFrameHostImpl* render_frame_host,
-    const GURL& url,
-    bool is_renderer_initiated_check) {
-  // In single process mode, everything runs in the same process, so the checks
-  // below are irrelevant.
-  if (RenderProcessHost::run_renderer_in_process())
-    return true;
-
-  ChildProcessSecurityPolicyImpl* security_policy =
-      ChildProcessSecurityPolicyImpl::GetInstance();
-  GURL process_lock_url =
-      security_policy->GetOriginLock(render_frame_host->GetProcess()->GetID());
-
-  // In the case of error page process, any URL is allowed to commit.
-  if (process_lock_url == GURL(kUnreachableWebDataURL))
-    return true;
-
-  bool frame_has_bindings = ((render_frame_host->GetEnabledBindings() &
-                              kWebUIBindingsPolicyMask) != 0);
-  bool is_allowed_in_web_ui_renderer =
-      WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI(
-          render_frame_host->GetProcess()->GetBrowserContext(), url);
-
-  // Embedders might disable locking for WebUI URLs, which is bad idea, however
-  // this method should take this into account.
-  bool should_lock_to_origin = SiteInstanceImpl::ShouldLockToOrigin(
-      render_frame_host->GetSiteInstance()->GetIsolationContext(), url,
-      render_frame_host->GetSiteInstance()->IsGuest());
-
-  // If the |render_frame_host| has any WebUI bindings, disallow URLs that are
-  // not allowed in a WebUI renderer process.
-  if (frame_has_bindings) {
-    // The process itself must have WebUI bit in the security policy.
-    // Otherwise it indicates that there is a bug in browser process logic and
-    // the browser process must be terminated.
-    // TODO(nasko): Convert to CHECK() once it is confirmed this is not
-    // violated in reality.
-    if (!security_policy->HasWebUIBindings(
-            render_frame_host->GetProcess()->GetID())) {
-      base::debug::DumpWithoutCrashing();
-    }
-
-    // Check whether the process must be locked and if so that the process lock
-    // is indeed in place.
-    if (should_lock_to_origin && process_lock_url.is_empty())
-      return false;
-
-    // There must be a WebUI on the frame.
-    if (!render_frame_host->web_ui())
-      return false;
-
-    // The |url| must be allowed in a WebUI process if the frame has WebUI.
-    if (!is_allowed_in_web_ui_renderer) {
-      // If this method is called in response to IPC message from the renderer
-      // process, it should be terminated, otherwise it is a bug in the
-      // navigation logic and the browser process should be terminated to avoid
-      // exposing users to security issues.
-      if (is_renderer_initiated_check)
-        return false;
-
-      CHECK(false);
-    }
-  }
-
-  // If |url| is one that is allowed in WebUI renderer process, ensure that its
-  // origin is either opaque or matches the origin of the process lock.
-  if (is_allowed_in_web_ui_renderer) {
-    url::Origin url_origin = url::Origin::Create(url.GetOrigin());
-
-    // Verify |url| matches the origin of the process lock, if one is in place.
-    if (should_lock_to_origin) {
-      url::Origin process_lock_origin = url::Origin::Create(process_lock_url);
-      if (!url_origin.opaque() && process_lock_origin != url_origin)
-        return false;
-    }
-  }
-
-  return true;
-}
-
-// A renderer-initiated navigation should be ignored iff a) there is an ongoing
-// request b) which is browser initiated and c) the renderer request is not
-// user-initiated.
-// static
-bool NavigatorImpl::ShouldIgnoreIncomingRendererRequest(
-    const NavigationRequest* ongoing_navigation_request,
-    bool has_user_gesture) {
-  return ongoing_navigation_request &&
-         ongoing_navigation_request->browser_initiated() && !has_user_gesture;
-}
-
-NavigatorDelegate* NavigatorImpl::GetDelegate() {
-  return delegate_;
-}
-
-NavigationController* NavigatorImpl::GetController() {
-  return controller_;
-}
-
-void NavigatorImpl::DidFailLoadWithError(RenderFrameHostImpl* render_frame_host,
-                                         const GURL& url,
-                                         int error_code) {
-  if (delegate_) {
-    delegate_->DidFailLoadWithError(render_frame_host, url, error_code);
-  }
-}
-
-bool NavigatorImpl::StartHistoryNavigationInNewSubframe(
-    RenderFrameHostImpl* render_frame_host,
-    mojo::PendingAssociatedRemote<mojom::NavigationClient>* navigation_client) {
-  return controller_->StartHistoryNavigationInNewSubframe(render_frame_host,
-                                                          navigation_client);
-}
-
-void NavigatorImpl::DidNavigate(
-    RenderFrameHostImpl* render_frame_host,
-    const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
-    std::unique_ptr<NavigationRequest> navigation_request,
-    bool was_within_same_document) {
-  DCHECK(navigation_request);
-  FrameTreeNode* frame_tree_node = render_frame_host->frame_tree_node();
-  FrameTree* frame_tree = frame_tree_node->frame_tree();
-
-  bool is_same_document_navigation = controller_->IsURLSameDocumentNavigation(
-      params.url, params.origin, was_within_same_document, render_frame_host);
-
-  // If a frame claims the navigation was same-document, it must be the current
-  // frame, not a pending one.
-  if (is_same_document_navigation &&
-      render_frame_host !=
-          frame_tree_node->render_manager()->current_frame_host()) {
-    bad_message::ReceivedBadMessage(render_frame_host->GetProcess(),
-                                    bad_message::NI_IN_PAGE_NAVIGATION);
-    is_same_document_navigation = false;
-  }
-
-  if (ui::PageTransitionIsMainFrame(params.transition)) {
-    if (delegate_) {
-      // Run tasks that must execute just before the commit.
-      delegate_->DidNavigateMainFramePreCommit(is_same_document_navigation);
-    }
-  }
-
-  // For browser initiated navigation and same document navigation, frame policy
-  // in commit_params is nullopt and should use fallback value instead.
-  const blink::FramePolicy pending_frame_policy =
-      navigation_request->commit_params().frame_policy.value_or(
-          frame_tree_node->pending_frame_policy());
-
-  // DidNavigateFrame() must be called before replicating the new origin and
-  // other properties to proxies.  This is because it destroys the subframes of
-  // the frame we're navigating from, which might trigger those subframes to
-  // run unload handlers.  Those unload handlers should still see the old
-  // frame's origin.  See https://crbug.com/825283.
-  frame_tree_node->render_manager()->DidNavigateFrame(
-      render_frame_host, params.gesture == NavigationGestureUser,
-      is_same_document_navigation,
-      navigation_request
-          ->require_coop_browsing_instance_swap() /* clear_proxies_on_commit */,
-      pending_frame_policy);
-
-  // Save the new page's origin and other properties, and replicate them to
-  // proxies, including the proxy created in DidNavigateFrame() to replace the
-  // old frame in cross-process navigation cases.
-  frame_tree_node->SetCurrentOrigin(
-      params.origin, params.has_potentially_trustworthy_unique_origin);
-  frame_tree_node->SetInsecureRequestPolicy(params.insecure_request_policy);
-  frame_tree_node->SetInsecureNavigationsSet(params.insecure_navigations_set);
-
-  // Save the activation status of the previous page here before it gets reset
-  // in FrameTreeNode::ResetForNavigation.
-  bool previous_document_was_activated =
-      frame_tree->root()->HasStickyUserActivation();
-
-  if (!is_same_document_navigation) {
-    // Navigating to a new location means a new, fresh set of http headers
-    // and/or <meta> elements - we need to reset CSP and Feature Policy.
-    render_frame_host->ResetContentSecurityPolicies();
-    frame_tree_node->ResetForNavigation();
-
-    // Save the new document's embedding token and propagate to any parent
-    // document that embeds it. A token is only assigned to cross-process
-    // child frames.
-    render_frame_host->SetEmbeddingToken(params.embedding_token);
-  }
-
-  // Update the site of the SiteInstance if it doesn't have one yet, unless
-  // assigning a site is not necessary for this URL. In that case, the
-  // SiteInstance can still be considered unused until a navigation to a real
-  // page.
-  SiteInstanceImpl* site_instance = render_frame_host->GetSiteInstance();
-  if (!site_instance->HasSite() &&
-      SiteInstanceImpl::ShouldAssignSiteForURL(params.url)) {
-    site_instance->ConvertToDefaultOrSetSite(params.url);
-  }
-
-  // Need to update MIME type here because it's referred to in
-  // UpdateNavigationCommands() called by RendererDidNavigate() to
-  // determine whether or not to enable the encoding menu.
-  // It's updated only for the main frame. For a subframe,
-  // RenderView::UpdateURL does not set params.contents_mime_type.
-  // (see http://code.google.com/p/chromium/issues/detail?id=2929 )
-  // TODO(jungshik): Add a test for the encoding menu to avoid
-  // regressing it again.
-  // TODO(nasko): Verify the correctness of the above comment, since some of the
-  // code doesn't exist anymore. Also, move this code in the
-  // PageTransitionIsMainFrame code block above.
-  if (ui::PageTransitionIsMainFrame(params.transition) && delegate_)
-    delegate_->SetMainFrameMimeType(params.contents_mime_type);
-
-  int old_entry_count = controller_->GetEntryCount();
-  LoadCommittedDetails details;
-  bool did_navigate = controller_->RendererDidNavigate(
-      render_frame_host, params, &details, is_same_document_navigation,
-      previous_document_was_activated, navigation_request.get());
-
-  // If the history length and/or offset changed, update other renderers in the
-  // FrameTree.
-  if (old_entry_count != controller_->GetEntryCount() ||
-      details.previous_entry_index !=
-          controller_->GetLastCommittedEntryIndex()) {
-    frame_tree->root()->render_manager()->SendPageMessage(
-        new PageMsg_SetHistoryOffsetAndLength(
-            MSG_ROUTING_NONE, controller_->GetLastCommittedEntryIndex(),
-            controller_->GetEntryCount()),
-        site_instance);
-  }
-
-  render_frame_host->DidNavigate(params, is_same_document_navigation);
-
-  // Send notification about committed provisional loads. This notification is
-  // different from the NAV_ENTRY_COMMITTED notification which doesn't include
-  // the actual URL navigated to and isn't sent for AUTO_SUBFRAME navigations.
-  if (details.type != NAVIGATION_TYPE_NAV_IGNORE && delegate_) {
-    DCHECK_EQ(!render_frame_host->GetParent(),
-              did_navigate ? details.is_main_frame : false);
-    navigation_request->DidCommitNavigation(params, did_navigate,
-                                            details.did_replace_entry,
-                                            details.previous_url, details.type);
-    navigation_request.reset();
-  }
-
-  if (!did_navigate)
-    return;  // No navigation happened.
-
-  // DO NOT ADD MORE STUFF TO THIS FUNCTION! Your component should either listen
-  // for the appropriate notification (best) or you can add it to
-  // DidNavigateMainFramePostCommit / DidNavigateAnyFramePostCommit (only if
-  // necessary, please).
-
-  // TODO(carlosk): Move this out.
-  RecordNavigationMetrics(details, params, site_instance);
-
-  // Run post-commit tasks.
-  if (delegate_) {
-    if (details.is_main_frame) {
-      delegate_->DidNavigateMainFramePostCommit(render_frame_host, details,
-                                                params);
-    }
-
-    delegate_->DidNavigateAnyFramePostCommit(render_frame_host, details,
-                                             params);
-  }
-}
-
-void NavigatorImpl::Navigate(std::unique_ptr<NavigationRequest> request,
-                             ReloadType reload_type,
-                             RestoreType restore_type) {
-  TRACE_EVENT0("browser,navigation", "NavigatorImpl::Navigate");
-  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
-      "navigation,rail", "NavigationTiming navigationStart",
-      TRACE_EVENT_SCOPE_GLOBAL, request->common_params().navigation_start);
-
-  // Save destination url, as it is needed for
-  // DidStartNavigationToPendingEntry and request could be destroyed after
-  // BeginNavigation below.
-  GURL dest_url = request->common_params().url;
-  FrameTreeNode* frame_tree_node = request->frame_tree_node();
-
-  navigation_data_.reset(new NavigationMetricsData(
-      request->common_params().navigation_start, dest_url, restore_type));
-
-  // Check if the BeforeUnload event needs to execute before assigning the
-  // NavigationRequest to the FrameTreeNode. Assigning it to the FrameTreeNode
-  // has the side effect of initializing the current RenderFrameHost, which will
-  // return that it should execute the BeforeUnload event (even though we don't
-  // need to wait for it in the case of a brand new RenderFrameHost).
-  //
-  // We don't want to dispatch a beforeunload handler if
-  // is_history_navigation_in_new_child is true. This indicates a newly created
-  // child frame which does not have a beforeunload handler.
-  bool should_dispatch_beforeunload =
-      !NavigationTypeUtils::IsSameDocument(
-          request->common_params().navigation_type) &&
-      !request->common_params().is_history_navigation_in_new_child_frame &&
-      frame_tree_node->current_frame_host()->ShouldDispatchBeforeUnload(
-          false /* check_subframes_only */);
-
-  int nav_entry_id = request->nav_entry_id();
-  bool is_pending_entry =
-      controller_->GetPendingEntry() &&
-      (nav_entry_id == controller_->GetPendingEntry()->GetUniqueID());
-  frame_tree_node->CreatedNavigationRequest(std::move(request));
-  DCHECK(frame_tree_node->navigation_request());
-
-  // Have the current renderer execute its beforeunload event if needed. If it
-  // is not needed then NavigationRequest::BeginNavigation should be directly
-  // called instead.
-  if (should_dispatch_beforeunload) {
-    frame_tree_node->navigation_request()->SetWaitingForRendererResponse();
-    frame_tree_node->current_frame_host()->DispatchBeforeUnload(
-        RenderFrameHostImpl::BeforeUnloadType::BROWSER_INITIATED_NAVIGATION,
-        reload_type != ReloadType::NONE);
-  } else {
-    frame_tree_node->navigation_request()->BeginNavigation();
-    // WARNING: The NavigationRequest might have been destroyed in
-    // BeginNavigation(). Do not use |frame_tree_node->navigation_request()|
-    // after this point without null checking it first.
-  }
-
-  // Make sure no code called via RFH::Navigate clears the pending entry.
-  if (is_pending_entry)
-    CHECK_EQ(nav_entry_id, controller_->GetPendingEntry()->GetUniqueID());
-
-  // Notify observers about navigation.
-  if (delegate_ && is_pending_entry)
-    delegate_->DidStartNavigationToPendingEntry(dest_url, reload_type);
-}
-
-void NavigatorImpl::RequestOpenURL(
-    RenderFrameHostImpl* render_frame_host,
-    const GURL& url,
-    const GlobalFrameRoutingId& initiator_routing_id,
-    const base::Optional<url::Origin>& initiator_origin,
-    const scoped_refptr<network::ResourceRequestBody>& post_body,
-    const std::string& extra_headers,
-    const Referrer& referrer,
-    WindowOpenDisposition disposition,
-    bool should_replace_current_entry,
-    bool user_gesture,
-    blink::TriggeringEventInfo triggering_event_info,
-    const std::string& href_translate,
-    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
-    const base::Optional<Impression>& impression) {
-  // Note: This can be called for subframes (even when OOPIFs are not possible)
-  // if the disposition calls for a different window.
-
-  // Only the current RenderFrameHost should be sending an OpenURL request.
-  // Pending RenderFrameHost should know where it is navigating and pending
-  // deletion RenderFrameHost shouldn't be trying to navigate.
-  if (render_frame_host !=
-      render_frame_host->frame_tree_node()->current_frame_host()) {
-    return;
-  }
-
-  SiteInstance* current_site_instance = render_frame_host->GetSiteInstance();
-
-  // TODO(creis): Pass the redirect_chain into this method to support client
-  // redirects.  http://crbug.com/311721.
-  std::vector<GURL> redirect_chain;
-
-  int frame_tree_node_id = -1;
-
-  // Send the navigation to the current FrameTreeNode if it's destined for a
-  // subframe in the current tab.  We'll assume it's for the main frame
-  // (possibly of a new or different WebContents) otherwise.
-  if (disposition == WindowOpenDisposition::CURRENT_TAB &&
-      render_frame_host->GetParent()) {
-    frame_tree_node_id =
-        render_frame_host->frame_tree_node()->frame_tree_node_id();
-  }
-
-  OpenURLParams params(url, referrer, frame_tree_node_id, disposition,
-                       ui::PAGE_TRANSITION_LINK,
-                       true /* is_renderer_initiated */);
-  params.post_data = post_body;
-  params.extra_headers = extra_headers;
-  if (redirect_chain.size() > 0)
-    params.redirect_chain = redirect_chain;
-  params.should_replace_current_entry = should_replace_current_entry;
-  params.user_gesture = user_gesture;
-  params.triggering_event_info = triggering_event_info;
-  params.initiator_origin = initiator_origin;
-  params.initiator_routing_id = initiator_routing_id;
-
-  // RequestOpenURL is used only for local frames, so we can get here only if
-  // the navigation is initiated by a frame in the same SiteInstance as this
-  // frame.  Note that navigations on RenderFrameProxies do not use
-  // RequestOpenURL and go through NavigateFromFrameProxy instead.
-  params.source_site_instance = current_site_instance;
-
-  params.source_render_frame_id = render_frame_host->GetRoutingID();
-  params.source_render_process_id = render_frame_host->GetProcess()->GetID();
-
-  if (render_frame_host->web_ui()) {
-    // Note that we hide the referrer for Web UI pages. We don't really want
-    // web sites to see a referrer of "chrome://blah" (and some chrome: URLs
-    // might have search terms or other stuff we don't want to send to the
-    // site), so we send no referrer.
-    params.referrer = Referrer();
-
-    // Navigations in Web UI pages count as browser-initiated navigations.
-    params.is_renderer_initiated = false;
-  }
-
-  params.blob_url_loader_factory = std::move(blob_url_loader_factory);
-  params.href_translate = href_translate;
-  params.impression = impression;
-
-  if (delegate_)
-    delegate_->OpenURL(params);
-}
-
-void NavigatorImpl::NavigateFromFrameProxy(
-    RenderFrameHostImpl* render_frame_host,
-    const GURL& url,
-    const GlobalFrameRoutingId& initiator_routing_id,
-    const url::Origin& initiator_origin,
-    SiteInstance* source_site_instance,
-    const Referrer& referrer,
-    ui::PageTransition page_transition,
-    bool should_replace_current_entry,
-    NavigationDownloadPolicy download_policy,
-    const std::string& method,
-    scoped_refptr<network::ResourceRequestBody> post_body,
-    const std::string& extra_headers,
-    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
-    bool has_user_gesture,
-    const base::Optional<Impression>& impression) {
-  // |method != "POST"| should imply absence of |post_body|.
-  if (method != "POST" && post_body) {
-    NOTREACHED();
-    post_body = nullptr;
-  }
-
-  // Allow the delegate to cancel the transfer.
-  if (!delegate_->ShouldTransferNavigation(
-          render_frame_host->frame_tree_node()->IsMainFrame()))
-    return;
-
-  // TODO(creis): Determine if this transfer started as a browser-initiated
-  // navigation.  See https://crbug.com/495161.
-  bool is_renderer_initiated = true;
-  Referrer referrer_to_use(referrer);
-  if (render_frame_host->web_ui()) {
-    // Note that we hide the referrer for Web UI pages. We don't really want
-    // web sites to see a referrer of "chrome://blah" (and some chrome: URLs
-    // might have search terms or other stuff we don't want to send to the
-    // site), so we send no referrer.
-    referrer_to_use = Referrer();
-
-    // Navigations in Web UI pages count as browser-initiated navigations.
-    is_renderer_initiated = false;
-  }
-
-  if (is_renderer_initiated &&
-      ShouldIgnoreIncomingRendererRequest(
-          render_frame_host->frame_tree_node()->navigation_request(),
-          has_user_gesture)) {
-    return;
-  }
-
-  controller_->NavigateFromFrameProxy(
-      render_frame_host, url, initiator_routing_id, initiator_origin,
-      is_renderer_initiated, source_site_instance, referrer_to_use,
-      page_transition, should_replace_current_entry, download_policy, method,
-      post_body, extra_headers, std::move(blob_url_loader_factory), impression);
-}
-
-void NavigatorImpl::BeforeUnloadCompleted(FrameTreeNode* frame_tree_node,
-                                          bool proceed,
-                                          const base::TimeTicks& proceed_time) {
-  DCHECK(frame_tree_node);
-
-  NavigationRequest* navigation_request = frame_tree_node->navigation_request();
-
-  // The NavigationRequest may have been canceled while the renderer was
-  // executing the BeforeUnload event.
-  if (!navigation_request)
-    return;
-
-  // If the user chose not to proceed, cancel the ongoing navigation.
-  // Note: it might be a new navigation, and not the one that triggered the
-  // sending of the BeforeUnload IPC in the first place. However, the
-  // BeforeUnload where the user asked not to proceed will have taken place
-  // after the navigation started. The last user input shoud be respected, and
-  // the navigation cancelled anyway.
-  if (!proceed) {
-    CancelNavigation(frame_tree_node);
-    return;
-  }
-
-  // The browser-initiated NavigationRequest that triggered the sending of the
-  // BeforeUnload IPC might have been replaced by a renderer-initiated one while
-  // the BeforeUnload event executed in the renderer. In that case, the request
-  // will already have begun, so there is no need to start it again.
-  if (navigation_request->state() >
-      NavigationRequest::WAITING_FOR_RENDERER_RESPONSE) {
-    DCHECK(navigation_request->from_begin_navigation());
-    return;
-  }
-
-  // Update the navigation start: it should be when it was determined that the
-  // navigation will proceed.
-  navigation_request->set_navigation_start_time(proceed_time);
-
-  DCHECK_EQ(NavigationRequest::WAITING_FOR_RENDERER_RESPONSE,
-            navigation_request->state());
-
-  // Send the request to the IO thread.
-  navigation_request->BeginNavigation();
-  // DO NOT USE |navigation_request| BEYOND THIS POINT. It might have been
-  // destroyed in BeginNavigation().
-  // See https://crbug.com/770157.
-}
-
-void NavigatorImpl::OnBeginNavigation(
-    FrameTreeNode* frame_tree_node,
-    mojom::CommonNavigationParamsPtr common_params,
-    mojom::BeginNavigationParamsPtr begin_params,
-    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
-    mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,
-    mojo::PendingRemote<blink::mojom::NavigationInitiator> navigation_initiator,
-    scoped_refptr<PrefetchedSignedExchangeCache>
-        prefetched_signed_exchange_cache,
-    std::unique_ptr<WebBundleHandleTracker> web_bundle_handle_tracker) {
-  // TODO(clamy): the url sent by the renderer should be validated with
-  // FilterURL.
-  // This is a renderer-initiated navigation.
-  DCHECK(frame_tree_node);
-
-  if (common_params->is_history_navigation_in_new_child_frame) {
-    // Try to find a FrameNavigationEntry that matches this frame instead, based
-    // on the frame's unique name.  If this can't be found, fall back to the
-    // default path below.
-    if (frame_tree_node->navigator()->StartHistoryNavigationInNewSubframe(
-            frame_tree_node->current_frame_host(), &navigation_client)) {
-      return;
-    }
-  }
-
-  NavigationRequest* ongoing_navigation_request =
-      frame_tree_node->navigation_request();
-
-  // Client redirects during the initial history navigation of a child frame
-  // should take precedence over the history navigation (despite being renderer-
-  // initiated).  See https://crbug.com/348447 and https://crbug.com/691168.
-  if (ongoing_navigation_request &&
-      ongoing_navigation_request->common_params()
-          .is_history_navigation_in_new_child_frame) {
-    // Preemptively clear this local pointer before deleting the request.
-    ongoing_navigation_request = nullptr;
-    frame_tree_node->ResetNavigationRequest(false);
-  }
-
-  // Verify this navigation has precedence.
-  if (ShouldIgnoreIncomingRendererRequest(ongoing_navigation_request,
-                                          common_params->has_user_gesture)) {
-    return;
-  }
-
-  NavigationEntryImpl* navigation_entry =
-      GetNavigationEntryForRendererInitiatedNavigation(*common_params,
-                                                       frame_tree_node);
-  const bool override_user_agent =
-      delegate_ &&
-      delegate_->ShouldOverrideUserAgentForRendererInitiatedNavigation();
-  frame_tree_node->CreatedNavigationRequest(
-      NavigationRequest::CreateRendererInitiated(
-          frame_tree_node, navigation_entry, std::move(common_params),
-          std::move(begin_params), controller_->GetLastCommittedEntryIndex(),
-          controller_->GetEntryCount(), override_user_agent,
-          std::move(blob_url_loader_factory), std::move(navigation_client),
-          std::move(navigation_initiator),
-          std::move(prefetched_signed_exchange_cache),
-          std::move(web_bundle_handle_tracker)));
-  NavigationRequest* navigation_request = frame_tree_node->navigation_request();
-
-  // This frame has already run beforeunload before it sent this IPC.  See if
-  // any of its cross-process subframes also need to run beforeunload.  If so,
-  // delay the navigation until beforeunload completion callbacks are invoked on
-  // those frames.
-  DCHECK(!NavigationTypeUtils::IsSameDocument(
-      navigation_request->common_params().navigation_type));
-  bool should_dispatch_beforeunload =
-      frame_tree_node->current_frame_host()->ShouldDispatchBeforeUnload(
-          true /* check_subframes_only */);
-  if (should_dispatch_beforeunload) {
-    frame_tree_node->navigation_request()->SetWaitingForRendererResponse();
-    frame_tree_node->current_frame_host()->DispatchBeforeUnload(
-        RenderFrameHostImpl::BeforeUnloadType::RENDERER_INITIATED_NAVIGATION,
-        NavigationTypeUtils::IsReload(
-            navigation_request->common_params().navigation_type));
-    return;
-  }
-
-  // For main frames, NavigationHandle will be created after the call to
-  // |DidStartMainFrameNavigation|, so it receives the most up to date pending
-  // entry from the NavigationController.
-  navigation_request->BeginNavigation();
-  // DO NOT USE |navigation_request| BEYOND THIS POINT. It might have been
-  // destroyed in BeginNavigation().
-  // See https://crbug.com/770157.
-}
-
-void NavigatorImpl::RestartNavigationAsCrossDocument(
-    std::unique_ptr<NavigationRequest> navigation_request) {
-  FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node();
-  // Don't restart the navigation if there is already another ongoing navigation
-  // in the FrameTreeNode.
-  if (frame_tree_node->navigation_request())
-    return;
-
-  navigation_request->ResetForCrossDocumentRestart();
-  frame_tree_node->CreatedNavigationRequest(std::move(navigation_request));
-  frame_tree_node->navigation_request()->BeginNavigation();
-  // DO NOT USE THE NAVIGATION REQUEST BEYOND THIS POINT. It might have been
-  // destroyed in BeginNavigation().
-  // See https://crbug.com/770157.
-}
-
-void NavigatorImpl::CancelNavigation(FrameTreeNode* frame_tree_node) {
-  if (frame_tree_node->navigation_request())
-    frame_tree_node->navigation_request()->set_net_error(net::ERR_ABORTED);
-  frame_tree_node->ResetNavigationRequest(false);
-  if (frame_tree_node->IsMainFrame())
-    navigation_data_.reset();
-}
-
-void NavigatorImpl::LogResourceRequestTime(base::TimeTicks timestamp,
-                                           const GURL& url) {
-  if (navigation_data_ && navigation_data_->url_ == url) {
-    navigation_data_->url_job_start_time_ = timestamp;
-    UMA_HISTOGRAM_TIMES(
-        "Navigation.TimeToURLJobStart",
-        navigation_data_->url_job_start_time_ - navigation_data_->start_time_);
-  }
-}
-
-void NavigatorImpl::LogBeforeUnloadTime(
-    const base::TimeTicks& renderer_before_unload_start_time,
-    const base::TimeTicks& renderer_before_unload_end_time) {
-  // Only stores the beforeunload delay if we're tracking a browser initiated
-  // navigation and it happened later than the navigation request.
-  if (navigation_data_ &&
-      renderer_before_unload_start_time > navigation_data_->start_time_) {
-    navigation_data_->before_unload_delay_ =
-        renderer_before_unload_end_time - renderer_before_unload_start_time;
-  }
-}
-
-void NavigatorImpl::RecordNavigationMetrics(
-    const LoadCommittedDetails& details,
-    const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
-    SiteInstance* site_instance) {
-  DCHECK(site_instance->HasProcess());
-
-  if (!details.is_main_frame || !navigation_data_ ||
-      navigation_data_->url_job_start_time_.is_null() ||
-      navigation_data_->url_ != params.original_request_url) {
-    return;
-  }
-
-  base::TimeDelta time_to_commit =
-      base::TimeTicks::Now() - navigation_data_->start_time_;
-  UMA_HISTOGRAM_TIMES("Navigation.TimeToCommit", time_to_commit);
-
-  time_to_commit -= navigation_data_->before_unload_delay_;
-  base::TimeDelta time_to_network = navigation_data_->url_job_start_time_ -
-                                    navigation_data_->start_time_ -
-                                    navigation_data_->before_unload_delay_;
-  if (navigation_data_->is_restoring_from_last_session_) {
-    UMA_HISTOGRAM_TIMES(
-        "Navigation.TimeToCommit_SessionRestored_BeforeUnloadDiscounted",
-        time_to_commit);
-    UMA_HISTOGRAM_TIMES(
-        "Navigation.TimeToURLJobStart_SessionRestored_BeforeUnloadDiscounted",
-        time_to_network);
-    navigation_data_.reset();
-    return;
-  }
-  bool navigation_created_new_renderer_process =
-      site_instance->GetProcess()->GetInitTimeForNavigationMetrics() >
-      navigation_data_->start_time_;
-  if (navigation_created_new_renderer_process) {
-    UMA_HISTOGRAM_TIMES(
-        "Navigation.TimeToCommit_NewRenderer_BeforeUnloadDiscounted",
-        time_to_commit);
-    UMA_HISTOGRAM_TIMES(
-        "Navigation.TimeToURLJobStart_NewRenderer_BeforeUnloadDiscounted",
-        time_to_network);
-  } else {
-    UMA_HISTOGRAM_TIMES(
-        "Navigation.TimeToCommit_ExistingRenderer_BeforeUnloadDiscounted",
-        time_to_commit);
-    UMA_HISTOGRAM_TIMES(
-        "Navigation.TimeToURLJobStart_ExistingRenderer_BeforeUnloadDiscounted",
-        time_to_network);
-  }
-  navigation_data_.reset();
-}
-
-NavigationEntryImpl*
-NavigatorImpl::GetNavigationEntryForRendererInitiatedNavigation(
-    const mojom::CommonNavigationParams& common_params,
-    FrameTreeNode* frame_tree_node) {
-  if (!frame_tree_node->IsMainFrame())
-    return nullptr;
-
-  // If there is no browser-initiated pending entry for this navigation and it
-  // is not for the error URL, create a pending entry and ensure the address bar
-  // updates accordingly.  We don't know the referrer or extra headers at this
-  // point, but the referrer will be set properly upon commit.  This does not
-  // set the SiteInstance for the pending entry, because it may change
-  // before the URL commits.
-  NavigationEntryImpl* pending_entry = controller_->GetPendingEntry();
-  bool has_browser_initiated_pending_entry =
-      pending_entry && !pending_entry->is_renderer_initiated();
-  if (has_browser_initiated_pending_entry)
-    return nullptr;
-
-  // A pending navigation entry is created in OnBeginNavigation(). The renderer
-  // sends a provisional load notification after that. We don't want to create
-  // a duplicate navigation entry here.
-  bool renderer_provisional_load_to_pending_url =
-      pending_entry && pending_entry->is_renderer_initiated() &&
-      (pending_entry->GetURL() == common_params.url);
-  if (renderer_provisional_load_to_pending_url)
-    return nullptr;
-
-  // If there is a transient entry, creating a new pending entry will result
-  // in deleting it, which leads to inconsistent state.
-  bool has_transient_entry = !!controller_->GetTransientEntry();
-  if (has_transient_entry)
-    return nullptr;
-
-  // Since GetNavigationEntryForRendererInitiatedNavigation is called from
-  // OnBeginNavigation, we can assume that no frame proxies are involved and
-  // therefore that |current_site_instance| is also the |source_site_instance|.
-  SiteInstance* current_site_instance =
-      frame_tree_node->current_frame_host()->GetSiteInstance();
-  SiteInstance* source_site_instance = current_site_instance;
-
-  std::unique_ptr<NavigationEntryImpl> entry =
-      NavigationEntryImpl::FromNavigationEntry(
-          NavigationControllerImpl::CreateNavigationEntry(
-              common_params.url, content::Referrer(),
-              common_params.initiator_origin, source_site_instance,
-              ui::PAGE_TRANSITION_LINK, true /* is_renderer_initiated */,
-              std::string() /* extra_headers */,
-              controller_->GetBrowserContext(),
-              nullptr /* blob_url_loader_factory */));
-
-  controller_->SetPendingEntry(std::move(entry));
-  if (delegate_)
-    delegate_->NotifyChangedNavigationState(content::INVALIDATE_TYPE_URL);
-
-  return controller_->GetPendingEntry();
-}
-
-}  // namespace content
diff --git a/content/browser/frame_host/navigator_impl.h b/content/browser/frame_host/navigator_impl.h
deleted file mode 100644
index b5dbbba..0000000
--- a/content/browser/frame_host/navigator_impl.h
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_FRAME_HOST_NAVIGATOR_IMPL_H_
-#define CONTENT_BROWSER_FRAME_HOST_NAVIGATOR_IMPL_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/time/time.h"
-#include "content/browser/frame_host/navigation_controller_impl.h"
-#include "content/browser/frame_host/navigator.h"
-#include "content/common/content_export.h"
-#include "content/common/navigation_params.h"
-#include "content/common/navigation_params.mojom.h"
-#include "content/public/common/previews_state.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "url/gurl.h"
-
-class GURL;
-
-namespace content {
-
-class NavigationControllerImpl;
-class NavigatorDelegate;
-struct LoadCommittedDetails;
-
-// This class is an implementation of Navigator, responsible for managing
-// navigations in regular browser tabs.
-class CONTENT_EXPORT NavigatorImpl : public Navigator {
- public:
-  NavigatorImpl(NavigationControllerImpl* navigation_controller,
-                NavigatorDelegate* delegate);
-
-  // This method verifies that a navigation to |url| doesn't commit into a WebUI
-  // process if it is not allowed to. Callers of this method should take one of
-  // two actions if the method returns false:
-  // * When called from browser process logic (e.g. NavigationRequest), this
-  //   indicates issues with the navigation logic and the browser process must
-  //   be terminated to avoid security issues.
-  // * If the codepath is processing an IPC message from a renderer process,
-  //   then the renderer process is misbehaving and must be terminated.
-  // TODO(nasko): Remove the is_renderer_initiated_check parameter when callers
-  // of this method are migrated to use CHECK instead of DumpWithoutCrashing.
-  static WARN_UNUSED_RESULT bool CheckWebUIRendererDoesNotDisplayNormalURL(
-      RenderFrameHostImpl* render_frame_host,
-      const GURL& url,
-      bool is_renderer_initiated_check);
-
-  static bool ShouldIgnoreIncomingRendererRequest(
-      const NavigationRequest* ongoing_navigation_request,
-      bool has_user_gesture);
-
-  // Navigator implementation.
-  NavigatorDelegate* GetDelegate() override;
-  NavigationController* GetController() override;
-  void DidFailLoadWithError(RenderFrameHostImpl* render_frame_host,
-                            const GURL& url,
-                            int error_code) override;
-  void DidNavigate(RenderFrameHostImpl* render_frame_host,
-                   const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
-                   std::unique_ptr<NavigationRequest> navigation_request,
-                   bool was_within_same_document) override;
-  bool StartHistoryNavigationInNewSubframe(
-      RenderFrameHostImpl* render_frame_host,
-      mojo::PendingAssociatedRemote<mojom::NavigationClient>* navigation_client)
-      override;
-  void Navigate(std::unique_ptr<NavigationRequest> request,
-                ReloadType reload_type,
-                RestoreType restore_type) override;
-  void RequestOpenURL(
-      RenderFrameHostImpl* render_frame_host,
-      const GURL& url,
-      const GlobalFrameRoutingId& initiator_routing_id,
-      const base::Optional<url::Origin>& initiator_origin,
-      const scoped_refptr<network::ResourceRequestBody>& post_body,
-      const std::string& extra_headers,
-      const Referrer& referrer,
-      WindowOpenDisposition disposition,
-      bool should_replace_current_entry,
-      bool user_gesture,
-      blink::TriggeringEventInfo triggering_event_info,
-      const std::string& href_translate,
-      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
-      const base::Optional<Impression>& impression) override;
-  void NavigateFromFrameProxy(
-      RenderFrameHostImpl* render_frame_host,
-      const GURL& url,
-      const GlobalFrameRoutingId& initiator_routing_id,
-      const url::Origin& initiator_origin,
-      SiteInstance* source_site_instance,
-      const Referrer& referrer,
-      ui::PageTransition page_transition,
-      bool should_replace_current_entry,
-      NavigationDownloadPolicy download_policy,
-      const std::string& method,
-      scoped_refptr<network::ResourceRequestBody> post_body,
-      const std::string& extra_headers,
-      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
-      bool has_user_gesture,
-      const base::Optional<Impression>& impression) override;
-  void BeforeUnloadCompleted(FrameTreeNode* frame_tree_node,
-                             bool proceed,
-                             const base::TimeTicks& proceed_time) override;
-  void OnBeginNavigation(
-      FrameTreeNode* frame_tree_node,
-      mojom::CommonNavigationParamsPtr common_params,
-      mojom::BeginNavigationParamsPtr begin_params,
-      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
-      mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,
-      mojo::PendingRemote<blink::mojom::NavigationInitiator>
-          navigation_initiator,
-      scoped_refptr<PrefetchedSignedExchangeCache>
-          prefetched_signed_exchange_cache,
-      std::unique_ptr<WebBundleHandleTracker> web_bundle_handle_tracker)
-      override;
-  void RestartNavigationAsCrossDocument(
-      std::unique_ptr<NavigationRequest> navigation_request) override;
-  void LogResourceRequestTime(base::TimeTicks timestamp,
-                              const GURL& url) override;
-  void LogBeforeUnloadTime(
-      const base::TimeTicks& renderer_before_unload_start_time,
-      const base::TimeTicks& renderer_before_unload_end_time) override;
-  void CancelNavigation(FrameTreeNode* frame_tree_node) override;
-
- private:
-  // Holds data used to track browser side navigation metrics.
-  struct NavigationMetricsData;
-
-  friend class NavigatorTestWithBrowserSideNavigation;
-  ~NavigatorImpl() override;
-
-  void RecordNavigationMetrics(
-      const LoadCommittedDetails& details,
-      const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
-      SiteInstance* site_instance);
-
-  // Called when a renderer initiated navigation has started. Returns the
-  // pending NavigationEntry to be used. Either null or a new one owned
-  // NavigationController.
-  NavigationEntryImpl* GetNavigationEntryForRendererInitiatedNavigation(
-      const mojom::CommonNavigationParams& common_params,
-      FrameTreeNode* frame_tree_node);
-
-  // The NavigationController that will keep track of session history for all
-  // RenderFrameHost objects using this NavigatorImpl.
-  // TODO(nasko): Move ownership of the NavigationController from
-  // WebContentsImpl to this class.
-  NavigationControllerImpl* controller_;
-
-  // Used to notify the object embedding this Navigator about navigation
-  // events. Can be NULL in tests.
-  NavigatorDelegate* delegate_;
-
-  std::unique_ptr<NavigatorImpl::NavigationMetricsData> navigation_data_;
-
-  DISALLOW_COPY_AND_ASSIGN(NavigatorImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_FRAME_HOST_NAVIGATOR_IMPL_H_
diff --git a/content/browser/frame_host/navigator_impl_unittest.cc b/content/browser/frame_host/navigator_unittest.cc
similarity index 99%
rename from content/browser/frame_host/navigator_impl_unittest.cc
rename to content/browser/frame_host/navigator_unittest.cc
index 27a7fb9..0d819dc 100644
--- a/content/browser/frame_host/navigator_impl_unittest.cc
+++ b/content/browser/frame_host/navigator_unittest.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 "content/browser/frame_host/navigator_impl.h"
+#include "content/browser/frame_host/navigator.h"
 
 #include <stdint.h>
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index b37eba23..b021aa6 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -64,7 +64,6 @@
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
-#include "content/browser/frame_host/navigator_impl.h"
 #include "content/browser/frame_host/render_frame_host_delegate.h"
 #include "content/browser/frame_host/render_frame_proxy_host.h"
 #include "content/browser/generic_sensor/sensor_provider_proxy_impl.h"
@@ -256,6 +255,28 @@
 
 namespace content {
 
+struct RenderFrameHostOrProxy {
+  RenderFrameHostImpl* const frame;
+  RenderFrameProxyHost* const proxy;
+
+  RenderFrameHostOrProxy(RenderFrameHostImpl* frame,
+                         RenderFrameProxyHost* proxy)
+      : frame(frame), proxy(proxy) {
+    DCHECK(!frame || !proxy)
+        << "Both frame and proxy can't be non-null at the same time";
+  }
+
+  explicit operator bool() { return frame || proxy; }
+
+  FrameTreeNode* GetFrameTreeNode() {
+    if (frame)
+      return frame->frame_tree_node();
+    if (proxy)
+      return proxy->frame_tree_node();
+    return nullptr;
+  }
+};
+
 namespace {
 
 #if defined(OS_ANDROID)
@@ -414,28 +435,6 @@
   }
 }
 
-struct RenderFrameHostOrProxy {
-  RenderFrameHostImpl* const frame;
-  RenderFrameProxyHost* const proxy;
-
-  RenderFrameHostOrProxy(RenderFrameHostImpl* frame,
-                         RenderFrameProxyHost* proxy)
-      : frame(frame), proxy(proxy) {
-    DCHECK(!frame || !proxy)
-        << "Both frame and proxy can't be non-null at the same time";
-  }
-
-  explicit operator bool() { return frame || proxy; }
-
-  FrameTreeNode* GetFrameTreeNode() {
-    if (frame)
-      return frame->frame_tree_node();
-    if (proxy)
-      return proxy->frame_tree_node();
-    return nullptr;
-  }
-};
-
 RenderFrameHostOrProxy LookupRenderFrameHostOrProxy(int process_id,
                                                     int routing_id) {
   RenderFrameHostImpl* rfh =
@@ -3661,35 +3660,12 @@
   frame_tree_node()->SetInsecureNavigationsSet(set);
 }
 
-// TODO(https://crbug.com/1058038): Share the common code between the two
-// overloads below.
 RenderFrameHostImpl* RenderFrameHostImpl::FindAndVerifyChild(
     int32_t child_frame_routing_id,
     bad_message::BadMessageReason reason) {
   auto child_frame_or_proxy = LookupRenderFrameHostOrProxy(
       GetProcess()->GetID(), child_frame_routing_id);
-  // A race can result in |child| to be nullptr. Avoid killing the renderer in
-  // that case.
-  if (!child_frame_or_proxy)
-    return nullptr;
-
-  if (child_frame_or_proxy.GetFrameTreeNode()->frame_tree() !=
-      frame_tree_node()->frame_tree()) {
-    // Ignore the cases when the child lives in a different frame tree.
-    // This is possible when we create a proxy for inner WebContents (e.g.
-    // for portals) so the |child_frame_or_proxy| points to the root frame
-    // of the nested WebContents, which is in a different tree.
-    // TODO(altimin, lfg): Reconsider what the correct behaviour here should be.
-    return nullptr;
-  }
-  if (child_frame_or_proxy.GetFrameTreeNode()->parent() != this) {
-    bad_message::ReceivedBadMessage(GetProcess(), reason);
-    return nullptr;
-  }
-  return child_frame_or_proxy.proxy
-             ? child_frame_or_proxy.proxy->frame_tree_node()
-                   ->current_frame_host()
-             : child_frame_or_proxy.frame;
+  return FindAndVerifyChildInternal(child_frame_or_proxy, reason);
 }
 
 RenderFrameHostImpl* RenderFrameHostImpl::FindAndVerifyChild(
@@ -3697,6 +3673,12 @@
     bad_message::BadMessageReason reason) {
   auto child_frame_or_proxy =
       LookupRenderFrameHostOrProxy(GetProcess()->GetID(), child_frame_token);
+  return FindAndVerifyChildInternal(child_frame_or_proxy, reason);
+}
+
+RenderFrameHostImpl* RenderFrameHostImpl::FindAndVerifyChildInternal(
+    RenderFrameHostOrProxy child_frame_or_proxy,
+    bad_message::BadMessageReason reason) {
   // A race can result in |child| to be nullptr. Avoid killing the renderer in
   // that case.
   if (!child_frame_or_proxy)
@@ -5095,7 +5077,7 @@
 
   // Verify that if this RenderFrameHost is for a WebUI it is not committing a
   // URL which is not allowed in a WebUI process.
-  if (!NavigatorImpl::CheckWebUIRendererDoesNotDisplayNormalURL(
+  if (!Navigator::CheckWebUIRendererDoesNotDisplayNormalURL(
           this, url,
           /* is_renderer_initiated_check */ true)) {
     return CanCommitStatus::CANNOT_COMMIT_URL;
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index d84669d..7462933 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -215,6 +215,7 @@
 class WebBundleHandleTracker;
 struct UntrustworthyContextMenuParams;
 struct PendingNavigation;
+struct RenderFrameHostOrProxy;
 struct ResourceTimingInfo;
 struct SubresourceLoaderParams;
 
@@ -496,7 +497,7 @@
       blink::mojom::FrameOwnerElementType owner_type);
 
   // Update this frame's state at the appropriate time when a navigation
-  // commits. This is called by NavigatorImpl::DidNavigate as a helper, in the
+  // commits. This is called by Navigator::DidNavigate as a helper, in the
   // midst of a DidCommitProvisionalLoad call.
   void DidNavigate(const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
                    bool is_same_document_navigation);
@@ -1766,6 +1767,10 @@
 
   class DroppedInterfaceRequestLogger;
 
+  RenderFrameHostImpl* FindAndVerifyChildInternal(
+      RenderFrameHostOrProxy child_frame_or_proxy,
+      bad_message::BadMessageReason reason);
+
   // Update the RenderProcessHost priority when a navigation occurs.
   void UpdateRenderProcessHostFramePriorities();
 
diff --git a/content/browser/portal/portal.cc b/content/browser/portal/portal.cc
index 4babf8d..ce5f6fe7 100644
--- a/content/browser/portal/portal.cc
+++ b/content/browser/portal/portal.cc
@@ -11,8 +11,8 @@
 #include "base/memory/ptr_util.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
+#include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
-#include "content/browser/frame_host/navigator_impl.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/frame_host/render_frame_host_manager.h"
 #include "content/browser/frame_host/render_frame_proxy_host.h"
@@ -378,8 +378,8 @@
   if (owner_render_frame_host_->HasPendingCommitNavigation() ||
       (outer_navigation &&
        outer_navigation->state() >= NavigationRequest::WILL_PROCESS_RESPONSE) ||
-      NavigatorImpl::ShouldIgnoreIncomingRendererRequest(outer_navigation,
-                                                         has_user_gesture)) {
+      Navigator::ShouldIgnoreIncomingRendererRequest(outer_navigation,
+                                                     has_user_gesture)) {
     std::move(callback).Run(blink::mojom::PortalActivateResult::
                                 kRejectedDueToPredecessorNavigation);
     return;
diff --git a/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc b/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc
index 37ec5a3..e3ec39c 100644
--- a/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc
+++ b/content/browser/renderer_host/pepper/pepper_network_proxy_host.cc
@@ -46,9 +46,9 @@
   StoragePartition* storage_partition = BrowserContext::GetStoragePartition(
       site_instance->GetBrowserContext(), site_instance);
 
-  // TODO(https://crbug.com/1021661): Pass in a non-empty NetworkIsolationKey.
   storage_partition->GetNetworkContext()->LookUpProxyForURL(
-      url, net::NetworkIsolationKey::Todo(), std::move(proxy_lookup_client));
+      url, render_frame_host->GetNetworkIsolationKey(),
+      std::move(proxy_lookup_client));
   return true;
 }
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index e753093..b831bd2 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3271,6 +3271,7 @@
     switches::kDisablePermissionsAPI,
     switches::kDisablePresentationAPI,
     switches::kDisableRGBA4444Textures,
+    switches::kDisableAxSerializerForTesting,
     switches::kDisableRTCSmoothnessAlgorithm,
     switches::kDisableScrollToTextFragment,
     switches::kDisableSharedWorkers,
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index dd216d0..9c266f7 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -609,9 +609,6 @@
     IPC_MESSAGE_HANDLER(WidgetHostMsg_UpdateScreenRects_ACK,
                         OnUpdateScreenRectsAck)
     IPC_MESSAGE_HANDLER(WidgetHostMsg_RequestSetBounds, OnRequestSetBounds)
-    IPC_MESSAGE_HANDLER(WidgetHostMsg_AutoscrollStart, OnAutoscrollStart)
-    IPC_MESSAGE_HANDLER(WidgetHostMsg_AutoscrollFling, OnAutoscrollFling)
-    IPC_MESSAGE_HANDLER(WidgetHostMsg_AutoscrollEnd, OnAutoscrollEnd)
     IPC_MESSAGE_HANDLER(WidgetHostMsg_TextInputStateChanged,
                         OnTextInputStateChanged)
     IPC_MESSAGE_HANDLER(WidgetHostMsg_SelectionBoundsChanged,
@@ -2421,7 +2418,7 @@
              new_visual_properties.is_pinch_gesture_active;
 }
 
-void RenderWidgetHostImpl::OnAutoscrollStart(const gfx::PointF& position) {
+void RenderWidgetHostImpl::AutoscrollStart(const gfx::PointF& position) {
   GetView()->OnAutoscrollStart();
   sent_autoscroll_scroll_begin_ = false;
   autoscroll_in_progress_ = true;
@@ -2430,7 +2427,7 @@
   autoscroll_start_position_ = position;
 }
 
-void RenderWidgetHostImpl::OnAutoscrollFling(const gfx::Vector2dF& velocity) {
+void RenderWidgetHostImpl::AutoscrollFling(const gfx::Vector2dF& velocity) {
   DCHECK(autoscroll_in_progress_);
   if (!sent_autoscroll_scroll_begin_ && velocity != gfx::Vector2dF()) {
     // Send a GSB event with valid delta hints.
@@ -2457,7 +2454,7 @@
       event, ui::LatencyInfo(ui::SourceEventType::OTHER));
 }
 
-void RenderWidgetHostImpl::OnAutoscrollEnd() {
+void RenderWidgetHostImpl::AutoscrollEnd() {
   autoscroll_in_progress_ = false;
 
   delegate()->GetInputEventRouter()->SetAutoScrollInProgress(
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index ff93cdb0..7200b06 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -905,9 +905,6 @@
   void OnClose();
   void OnUpdateScreenRectsAck();
   void OnRequestSetBounds(const gfx::Rect& bounds);
-  void OnAutoscrollStart(const gfx::PointF& position);
-  void OnAutoscrollFling(const gfx::Vector2dF& velocity);
-  void OnAutoscrollEnd();
   void OnTextInputStateChanged(const TextInputState& params);
   void OnSelectionBoundsChanged(
       const WidgetHostMsg_SelectionBounds_Params& params);
@@ -929,6 +926,9 @@
   void SetHasTouchEventHandlers(bool has_handlers) override;
   void IntrinsicSizingInfoChanged(
       blink::mojom::IntrinsicSizingInfoPtr sizing_info) override;
+  void AutoscrollStart(const gfx::PointF& position) override;
+  void AutoscrollFling(const gfx::Vector2dF& velocity) override;
+  void AutoscrollEnd() override;
 
   // When the RenderWidget is destroyed and recreated, this resets states in the
   // browser to match the clean start for the renderer side.
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index aa4ffc5..fe9275c 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -36,6 +36,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/file_select_listener.h"
+#include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/resource_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/bindings_policy.h"
diff --git a/content/browser/service_worker/service_worker_client_utils.cc b/content/browser/service_worker/service_worker_client_utils.cc
index f79030b..52933cd 100644
--- a/content/browser/service_worker/service_worker_client_utils.cc
+++ b/content/browser/service_worker/service_worker_client_utils.cc
@@ -18,6 +18,7 @@
 #include "base/task/post_task.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/service_worker/service_worker_container_host.h"
@@ -247,7 +248,7 @@
     return;
   }
 
-  // The following code is a rough copy of NavigatorImpl::RequestOpenURL. That
+  // The following code is a rough copy of Navigator::RequestOpenURL. That
   // function can't be used directly since there is no render frame host yet
   // that the navigation will occur in.
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 02768a8..d3b464d 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -56,6 +56,7 @@
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/frame_host/render_frame_proxy_host.h"
diff --git a/content/browser/site_per_process_unload_browsertest.cc b/content/browser/site_per_process_unload_browsertest.cc
index ae037dd..64c47aa 100644
--- a/content/browser/site_per_process_unload_browsertest.cc
+++ b/content/browser/site_per_process_unload_browsertest.cc
@@ -26,6 +26,7 @@
 #include "content/browser/frame_host/cross_process_frame_connector.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/navigation_controller_impl.h"
+#include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 76ef418..5eef929 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -61,7 +61,7 @@
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
-#include "content/browser/frame_host/navigator_impl.h"
+#include "content/browser/frame_host/navigator.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/frame_host/render_frame_proxy_host.h"
 #include "content/browser/gpu/gpu_data_manager_impl.h"
@@ -568,11 +568,7 @@
       render_view_host_delegate_view_(nullptr),
       created_with_opener_(false),
       node_(this),
-      frame_tree_(new NavigatorImpl(&controller_, this),
-                  this,
-                  this,
-                  this,
-                  this),
+      frame_tree_(new Navigator(&controller_, this), this, this, this, this),
       is_load_to_different_document_(false),
       crashed_status_(base::TERMINATION_STATUS_STILL_RUNNING),
       crashed_error_code_(0),
diff --git a/content/common/input_messages.h b/content/common/input_messages.h
index 7a7e5e4..a17d699 100644
--- a/content/common/input_messages.h
+++ b/content/common/input_messages.h
@@ -54,10 +54,6 @@
     content::SyntheticPointerActionParams::Button::BUTTON_MAX)
 IPC_ENUM_TRAITS_MAX_VALUE(content::InputEventDispatchType,
                           content::InputEventDispatchType::DISPATCH_TYPE_MAX)
-IPC_ENUM_TRAITS_MAX_VALUE(
-    cc::OverscrollBehavior::OverscrollBehaviorType,
-    cc::OverscrollBehavior::OverscrollBehaviorType::kOverscrollBehaviorTypeMax)
-
 IPC_STRUCT_TRAITS_BEGIN(ui::DidOverscrollParams)
   IPC_STRUCT_TRAITS_MEMBER(accumulated_overscroll)
   IPC_STRUCT_TRAITS_MEMBER(latest_overscroll_delta)
@@ -66,11 +62,6 @@
   IPC_STRUCT_TRAITS_MEMBER(overscroll_behavior)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(cc::OverscrollBehavior)
-  IPC_STRUCT_TRAITS_MEMBER(x)
-  IPC_STRUCT_TRAITS_MEMBER(y)
-IPC_STRUCT_TRAITS_END()
-
 IPC_STRUCT_TRAITS_BEGIN(content::SyntheticGestureParams)
   IPC_STRUCT_TRAITS_MEMBER(gesture_source_type)
 IPC_STRUCT_TRAITS_END()
diff --git a/content/common/widget_messages.h b/content/common/widget_messages.h
index 76c5d32..0b7b6f8c4 100644
--- a/content/common/widget_messages.h
+++ b/content/common/widget_messages.h
@@ -185,15 +185,6 @@
 // throttle these messages.
 IPC_MESSAGE_ROUTED0(WidgetHostMsg_UpdateScreenRects_ACK)
 
-// Request a non-decelerating synthetic fling animation to be latched on the
-// scroller at the start point, and whose velocity can be changed over time by
-// sending multiple AutoscrollFling gestures.  Used for features like
-// middle-click autoscroll.
-IPC_MESSAGE_ROUTED1(WidgetHostMsg_AutoscrollStart, gfx::PointF /* start */)
-IPC_MESSAGE_ROUTED1(WidgetHostMsg_AutoscrollFling,
-                    gfx::Vector2dF /* velocity */)
-IPC_MESSAGE_ROUTED0(WidgetHostMsg_AutoscrollEnd)
-
 // Notifies the browser if the text input state has changed. Primarily useful
 // for IME as they need to know of all changes to update their interpretation
 // of the characters that have been input.
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 8e9eb36..a2692533 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -103,6 +103,10 @@
 const char kDisableAcceleratedVideoEncode[] =
     "disable-accelerated-video-encode";
 
+// Turns off the accessibility in the renderer.
+const char kDisableAxSerializerForTesting[] =
+    "disable-ax-serializer-for-testing";
+
 // Disable limits on the number of backing stores. Can prevent blinking for
 // users with many windows/tabs and lots of memory.
 const char kDisableBackingStoreLimit[]      = "disable-backing-store-limit";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 81c110d..62f71e50 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -40,6 +40,7 @@
 CONTENT_EXPORT extern const char kDisableYUVImageDecoding[];
 CONTENT_EXPORT extern const char kDisableAcceleratedVideoDecode[];
 CONTENT_EXPORT extern const char kDisableAcceleratedVideoEncode[];
+CONTENT_EXPORT extern const char kDisableAxSerializerForTesting[];
 extern const char kDisableBackingStoreLimit[];
 CONTENT_EXPORT extern const char
     kDisableBackgroundingOccludedWindowsForTesting[];
diff --git a/content/public/renderer/request_peer.h b/content/public/renderer/request_peer.h
index c461040..bea49370 100644
--- a/content/public/renderer/request_peer.h
+++ b/content/public/renderer/request_peer.h
@@ -39,12 +39,15 @@
   // note: only for requests with upload progress enabled.
   virtual void OnUploadProgress(uint64_t position, uint64_t size) = 0;
 
-  // Called when a redirect occurs.  The implementation may return false to
-  // suppress the redirect.  The URLResponseHead provides information about
+  // Called when a redirect occurs. The implementation may return false to
+  // suppress the redirect. The URLResponseHead provides information about
   // the redirect response and the RedirectInfo includes information about the
-  // request to be made if the method returns true.
-  virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
-                                  network::mojom::URLResponseHeadPtr head) = 0;
+  // request to be made if the method returns true. |removed_headers| outputs
+  // header field names that need to be removed.
+  virtual bool OnReceivedRedirect(
+      const net::RedirectInfo& redirect_info,
+      network::mojom::URLResponseHeadPtr head,
+      std::vector<std::string>* removed_headers) = 0;
 
   // Called when response headers are available (after all redirects have
   // been followed).
diff --git a/content/public/test/fake_render_widget_host.cc b/content/public/test/fake_render_widget_host.cc
index 5ff2f78..aadd508 100644
--- a/content/public/test/fake_render_widget_host.cc
+++ b/content/public/test/fake_render_widget_host.cc
@@ -50,4 +50,10 @@
     const base::string16& tooltip_text,
     base::i18n::TextDirection text_direction_hint) {}
 
+void FakeRenderWidgetHost::AutoscrollStart(const gfx::PointF& position) {}
+
+void FakeRenderWidgetHost::AutoscrollFling(const gfx::Vector2dF& position) {}
+
+void FakeRenderWidgetHost::AutoscrollEnd() {}
+
 }  // namespace content
diff --git a/content/public/test/fake_render_widget_host.h b/content/public/test/fake_render_widget_host.h
index d4ad6b6..0b04b32 100644
--- a/content/public/test/fake_render_widget_host.h
+++ b/content/public/test/fake_render_widget_host.h
@@ -36,6 +36,9 @@
   void SetHasTouchEventHandlers(bool has_handlers) override;
   void IntrinsicSizingInfoChanged(
       blink::mojom::IntrinsicSizingInfoPtr sizing_info) override;
+  void AutoscrollStart(const gfx::PointF& position) override;
+  void AutoscrollFling(const gfx::Vector2dF& position) override;
+  void AutoscrollEnd() override;
 
   // blink::mojom::WidgetHost overrides.
   void SetCursor(const ui::Cursor& cursor) override;
diff --git a/content/renderer/loader/resource_dispatcher.cc b/content/renderer/loader/resource_dispatcher.cc
index 952490a..4f6b3a9 100644
--- a/content/renderer/loader/resource_dispatcher.cc
+++ b/content/renderer/loader/resource_dispatcher.cc
@@ -43,6 +43,7 @@
 #include "services/network/public/cpp/url_loader_completion_status.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
+#include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/loader/resource_type_util.h"
 #include "third_party/blink/public/common/loader/throttling_url_loader.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
@@ -221,13 +222,18 @@
                                     redirect_info.new_url);
 
   ToLocalURLResponseHead(*request_info, *response_head);
-  if (request_info->peer->OnReceivedRedirect(redirect_info,
-                                             response_head.Clone())) {
+  std::vector<std::string> removed_headers;
+  if (request_info->peer->OnReceivedRedirect(
+          redirect_info, response_head.Clone(), &removed_headers)) {
     // Double-check if the request is still around. The call above could
     // potentially remove it.
     request_info = GetPendingRequestInfo(request_id);
     if (!request_info)
       return;
+    // TODO(yoav): If request_info doesn't change above, we could avoid this
+    // copy.
+    request_info->removed_headers = removed_headers;
+
     request_info->response_url = redirect_info.new_url;
     request_info->has_pending_redirect = true;
     NotifyResourceRedirectReceived(request_info->render_frame_id,
@@ -253,7 +259,7 @@
       request_info->url_loader->FollowRedirectForcingRestart();
     } else {
       request_info->url_loader->FollowRedirect(
-          {} /* removed_headers */, {} /* modified_headers */,
+          request_info->removed_headers, {} /* modified_headers */,
           {} /* modified_cors_exempt_headers */);
     }
   }
@@ -469,8 +475,9 @@
 
   while (response->context_for_redirect) {
     DCHECK(response->redirect_info);
-    bool follow_redirect = peer->OnReceivedRedirect(*response->redirect_info,
-                                                    response->head.Clone());
+    bool follow_redirect = peer->OnReceivedRedirect(
+        *response->redirect_info, response->head.Clone(),
+        nullptr /* removed_headers */);
     redirect_or_response_event.Reset();
     if (follow_redirect) {
       task_runner->PostTask(
diff --git a/content/renderer/loader/resource_dispatcher.h b/content/renderer/loader/resource_dispatcher.h
index 1158c634..949cc1e 100644
--- a/content/renderer/loader/resource_dispatcher.h
+++ b/content/renderer/loader/resource_dispatcher.h
@@ -213,6 +213,9 @@
     // For mojo loading.
     std::unique_ptr<blink::ThrottlingURLLoader> url_loader;
     std::unique_ptr<URLLoaderClientImpl> url_loader_client;
+
+    // The Client Hints headers that need to be removed from a redirect.
+    std::vector<std::string> removed_headers;
   };
   using PendingRequestMap = std::map<int, std::unique_ptr<PendingRequestInfo>>;
 
diff --git a/content/renderer/loader/resource_dispatcher_unittest.cc b/content/renderer/loader/resource_dispatcher_unittest.cc
index eb5e8f04..12b4870 100644
--- a/content/renderer/loader/resource_dispatcher_unittest.cc
+++ b/content/renderer/loader/resource_dispatcher_unittest.cc
@@ -200,7 +200,8 @@
     void OnUploadProgress(uint64_t position, uint64_t size) override {}
 
     bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
-                            network::mojom::URLResponseHeadPtr head) override {
+                            network::mojom::URLResponseHeadPtr head,
+                            std::vector<std::string>*) override {
       return false;
     }
 
diff --git a/content/renderer/loader/sync_load_context.cc b/content/renderer/loader/sync_load_context.cc
index 94016b3..ba16da6 100644
--- a/content/renderer/loader/sync_load_context.cc
+++ b/content/renderer/loader/sync_load_context.cc
@@ -18,6 +18,7 @@
 #include "net/url_request/redirect_info.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
+#include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 
 namespace content {
@@ -149,8 +150,16 @@
 
 bool SyncLoadContext::OnReceivedRedirect(
     const net::RedirectInfo& redirect_info,
-    network::mojom::URLResponseHeadPtr head) {
+    network::mojom::URLResponseHeadPtr head,
+    std::vector<std::string>* removed_headers) {
   DCHECK(!Completed());
+  if (removed_headers) {
+    // TODO(yoav): Get the actual FeaturePolicy here to support selective
+    // removal for sync XHR.
+    blink::FindClientHintsToRemove(nullptr /* feature_policy */,
+                                   redirect_info.new_url, removed_headers);
+  }
+
   response_->url = redirect_info.new_url;
   response_->head = std::move(head);
   response_->redirect_info = redirect_info;
diff --git a/content/renderer/loader/sync_load_context.h b/content/renderer/loader/sync_load_context.h
index 0c82bda..f7565e5d 100644
--- a/content/renderer/loader/sync_load_context.h
+++ b/content/renderer/loader/sync_load_context.h
@@ -90,7 +90,8 @@
   // RequestPeer implementation:
   void OnUploadProgress(uint64_t position, uint64_t size) override;
   bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
-                          network::mojom::URLResponseHeadPtr head) override;
+                          network::mojom::URLResponseHeadPtr head,
+                          std::vector<std::string>*) override;
   void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override;
   void OnStartLoadingResponseBody(
       mojo::ScopedDataPipeConsumerHandle body) override;
diff --git a/content/renderer/loader/test_request_peer.cc b/content/renderer/loader/test_request_peer.cc
index f345a4b..d453d67 100644
--- a/content/renderer/loader/test_request_peer.cc
+++ b/content/renderer/loader/test_request_peer.cc
@@ -24,7 +24,8 @@
 
 bool TestRequestPeer::OnReceivedRedirect(
     const net::RedirectInfo& redirect_info,
-    network::mojom::URLResponseHeadPtr head) {
+    network::mojom::URLResponseHeadPtr head,
+    std::vector<std::string>*) {
   EXPECT_FALSE(context_->cancelled);
   EXPECT_FALSE(context_->complete);
   ++context_->seen_redirects;
diff --git a/content/renderer/loader/test_request_peer.h b/content/renderer/loader/test_request_peer.h
index d61c1e8..f9a4f144 100644
--- a/content/renderer/loader/test_request_peer.h
+++ b/content/renderer/loader/test_request_peer.h
@@ -33,7 +33,8 @@
 
   void OnUploadProgress(uint64_t position, uint64_t size) override;
   bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
-                          network::mojom::URLResponseHeadPtr head) override;
+                          network::mojom::URLResponseHeadPtr head,
+                          std::vector<std::string>*) override;
   void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override;
   void OnStartLoadingResponseBody(
       mojo::ScopedDataPipeConsumerHandle body) override;
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 3a4ead0..a506772 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -382,7 +382,8 @@
 
   void OnUploadProgress(uint64_t position, uint64_t size);
   bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
-                          network::mojom::URLResponseHeadPtr head);
+                          network::mojom::URLResponseHeadPtr head,
+                          std::vector<std::string>* removed_headers);
   void OnReceivedResponse(network::mojom::URLResponseHeadPtr head);
   void OnStartLoadingResponseBody(mojo::ScopedDataPipeConsumerHandle body);
   void OnTransferSizeUpdated(int transfer_size_diff);
@@ -446,7 +447,8 @@
   // RequestPeer methods:
   void OnUploadProgress(uint64_t position, uint64_t size) override;
   bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
-                          network::mojom::URLResponseHeadPtr head) override;
+                          network::mojom::URLResponseHeadPtr head,
+                          std::vector<std::string>* removed_headers) override;
   void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override;
   void OnStartLoadingResponseBody(
       mojo::ScopedDataPipeConsumerHandle body) override;
@@ -475,7 +477,8 @@
   // RequestPeer implementation:
   void OnUploadProgress(uint64_t position, uint64_t size) override {}
   bool OnReceivedRedirect(const net::RedirectInfo& redirect_info,
-                          network::mojom::URLResponseHeadPtr head) override {
+                          network::mojom::URLResponseHeadPtr head,
+                          std::vector<std::string>*) override {
     return true;
   }
   void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override {}
@@ -724,7 +727,8 @@
 
 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
     const net::RedirectInfo& redirect_info,
-    network::mojom::URLResponseHeadPtr head) {
+    network::mojom::URLResponseHeadPtr head,
+    std::vector<std::string>* removed_headers) {
   if (!client_)
     return false;
 
@@ -742,7 +746,7 @@
       Referrer::NetReferrerPolicyToBlinkReferrerPolicy(
           redirect_info.new_referrer_policy),
       WebString::FromUTF8(redirect_info.new_method), response,
-      report_raw_headers_);
+      report_raw_headers_, removed_headers);
 }
 
 void WebURLLoaderImpl::Context::OnReceivedResponse(
@@ -847,8 +851,10 @@
 
 bool WebURLLoaderImpl::RequestPeerImpl::OnReceivedRedirect(
     const net::RedirectInfo& redirect_info,
-    network::mojom::URLResponseHeadPtr head) {
-  return context_->OnReceivedRedirect(redirect_info, std::move(head));
+    network::mojom::URLResponseHeadPtr head,
+    std::vector<std::string>* removed_headers) {
+  return context_->OnReceivedRedirect(redirect_info, std::move(head),
+                                      removed_headers);
 }
 
 void WebURLLoaderImpl::RequestPeerImpl::OnReceivedResponse(
diff --git a/content/renderer/loader/web_url_loader_impl_unittest.cc b/content/renderer/loader/web_url_loader_impl_unittest.cc
index 30a489e5..738a244 100644
--- a/content/renderer/loader/web_url_loader_impl_unittest.cc
+++ b/content/renderer/loader/web_url_loader_impl_unittest.cc
@@ -195,7 +195,8 @@
                           network::mojom::ReferrerPolicy new_referrer_policy,
                           const blink::WebString& new_method,
                           const blink::WebURLResponse& passed_redirect_response,
-                          bool& report_raw_headers) override {
+                          bool& report_raw_headers,
+                          std::vector<std::string>*) override {
     EXPECT_TRUE(loader_);
 
     // No test currently simulates mutiple redirects.
@@ -326,8 +327,10 @@
     redirect_info.new_url = GURL(kTestURL);
     redirect_info.new_site_for_cookies =
         net::SiteForCookies::FromUrl(GURL(kTestURL));
+    std::vector<std::string> removed_headers;
     peer()->OnReceivedRedirect(redirect_info,
-                               network::mojom::URLResponseHead::New());
+                               network::mojom::URLResponseHead::New(),
+                               &removed_headers);
     EXPECT_TRUE(client()->did_receive_redirect());
   }
 
@@ -340,7 +343,7 @@
     redirect_info.new_site_for_cookies =
         net::SiteForCookies::FromUrl(GURL(kTestHTTPSURL));
     peer()->OnReceivedRedirect(redirect_info,
-                               network::mojom::URLResponseHead::New());
+                               network::mojom::URLResponseHead::New(), nullptr);
     EXPECT_TRUE(client()->did_receive_redirect());
   }
 
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 5d686b0..1490b7e 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1570,18 +1570,6 @@
     GetWebWidget()->SetCursor(cursor);
 }
 
-void RenderWidget::AutoscrollStart(const gfx::PointF& point) {
-  Send(new WidgetHostMsg_AutoscrollStart(routing_id_, point));
-}
-
-void RenderWidget::AutoscrollFling(const gfx::Vector2dF& velocity) {
-  Send(new WidgetHostMsg_AutoscrollFling(routing_id_, velocity));
-}
-
-void RenderWidget::AutoscrollEnd() {
-  Send(new WidgetHostMsg_AutoscrollEnd(routing_id_));
-}
-
 // We are supposed to get a single call to Show for a newly created RenderWidget
 // that was created via RenderWidget::CreateWebView.  So, we wait until this
 // point to dispatch the ShowWidget message.
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 23b355b..d80e357 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -329,9 +329,6 @@
   void ScheduleAnimation() override;
   void DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) override;
   void DidChangeCursor(const ui::Cursor& cursor) override;
-  void AutoscrollStart(const gfx::PointF& point) override;
-  void AutoscrollFling(const gfx::Vector2dF& velocity) override;
-  void AutoscrollEnd() override;
   void ClosePopupWidgetSoon() override;
   void Show(blink::WebNavigationPolicy) override;
   blink::WebScreenInfo GetScreenInfo() override;
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.cc b/content/renderer/service_worker/service_worker_subresource_loader.cc
index 13db0dbf..59b2ded1 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader.cc
@@ -716,9 +716,8 @@
   // TODO(arthursonzogni, juncai): This seems to be correctly implemented, but
   // not used so far. Add tests and remove this DCHECK to support this feature
   // if needed. See https://crbug.com/845683.
-  DCHECK(removed_headers.empty() && modified_headers.IsEmpty() &&
-         modified_cors_exempt_headers.IsEmpty())
-      << "Redirect with removed or modified headers is not supported yet. See "
+  DCHECK(modified_headers.IsEmpty() && modified_cors_exempt_headers.IsEmpty())
+      << "Redirect with modified headers is not supported yet. See "
          "https://crbug.com/845683";
   DCHECK(!new_url.has_value()) << "Redirect with modified url was not "
                                   "supported yet. crbug.com/845683";
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index bd0a322..53def82d 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -23,6 +23,7 @@
 #include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/cors_exempt_headers.h"
 #include "content/public/browser/login_delegate.h"
+#include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/render_process_host.h"
@@ -314,6 +315,15 @@
           ->web_contents());
 }
 
+std::vector<std::unique_ptr<NavigationThrottle>>
+ShellContentBrowserClient::CreateThrottlesForNavigation(
+    NavigationHandle* navigation_handle) {
+  std::vector<std::unique_ptr<NavigationThrottle>> empty_throttles;
+  if (create_throttles_for_navigation_callback_)
+    return create_throttles_for_navigation_callback_.Run(navigation_handle);
+  return empty_throttles;
+}
+
 std::unique_ptr<LoginDelegate> ShellContentBrowserClient::CreateLoginDelegate(
     const net::AuthChallengeInfo& auth_info,
     content::WebContents* web_contents,
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h
index c79bc97..1af8045 100644
--- a/content/shell/browser/shell_content_browser_client.h
+++ b/content/shell/browser/shell_content_browser_client.h
@@ -78,6 +78,8 @@
   void OpenURL(SiteInstance* site_instance,
                const OpenURLParams& params,
                base::OnceCallback<void(WebContents*)> callback) override;
+  std::vector<std::unique_ptr<NavigationThrottle>> CreateThrottlesForNavigation(
+      NavigationHandle* navigation_handle) override;
   std::unique_ptr<LoginDelegate> CreateLoginDelegate(
       const net::AuthChallengeInfo& auth_info,
       content::WebContents* web_contents,
@@ -152,6 +154,12 @@
     register_browser_interface_binders_for_frame_callback_ =
         register_browser_interface_binders_for_frame_callback;
   }
+  void set_create_throttles_for_navigation_callback(
+      base::RepeatingCallback<std::vector<std::unique_ptr<NavigationThrottle>>(
+          NavigationHandle*)> create_throttles_for_navigation_callback) {
+    create_throttles_for_navigation_callback_ =
+        create_throttles_for_navigation_callback;
+  }
 
  protected:
   // Call this if CreateBrowserMainParts() is overridden in a subclass.
@@ -184,6 +192,9 @@
       RenderFrameHost* render_frame_host,
       mojo::BinderMapWithContext<RenderFrameHost*>* map)>
       register_browser_interface_binders_for_frame_callback_;
+  base::RepeatingCallback<std::vector<std::unique_ptr<NavigationThrottle>>(
+      NavigationHandle*)>
+      create_throttles_for_navigation_callback_;
 
   // Owned by content::BrowserMainLoop.
   ShellBrowserMainParts* shell_browser_main_parts_ = nullptr;
diff --git a/content/shell/renderer/web_test/web_view_test_proxy.cc b/content/shell/renderer/web_test/web_view_test_proxy.cc
index 857f001..04fef39 100644
--- a/content/shell/renderer/web_test/web_view_test_proxy.cc
+++ b/content/shell/renderer/web_test/web_view_test_proxy.cc
@@ -7,6 +7,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "base/command_line.h"
+#include "content/public/common/content_switches.h"
 #include "content/shell/common/web_test/web_test_string_util.h"
 #include "content/shell/renderer/web_test/blink_test_runner.h"
 #include "content/shell/renderer/web_test/mock_screen_orientation_client.h"
@@ -94,7 +96,15 @@
 }
 
 void WebViewTestProxy::Install(blink::WebLocalFrame* frame) {
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableAxSerializerForTesting)) {
+    RenderFrame* render_frame = RenderFrame::FromWebFrame(frame);
+    if (render_frame)
+      render_frame->SetAccessibilityModeForTest(ui::AXMode::kWebContents);
+  }
+
   accessibility_controller_.Install(frame);
+
   text_input_controller_.Install(frame);
 }
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index c86976a..53571390 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1651,7 +1651,7 @@
     "../browser/frame_host/navigation_entry_impl_unittest.cc",
     "../browser/frame_host/navigation_request_unittest.cc",
     "../browser/frame_host/navigation_throttle_runner_unittest.cc",
-    "../browser/frame_host/navigator_impl_unittest.cc",
+    "../browser/frame_host/navigator_unittest.cc",
     "../browser/frame_host/origin_policy_throttle_unittest.cc",
     "../browser/frame_host/render_frame_host_feature_policy_unittest.cc",
     "../browser/frame_host/render_frame_host_impl_unittest.cc",
diff --git a/content/test/ppapi/ppapi_browsertest.cc b/content/test/ppapi/ppapi_browsertest.cc
index 46e036d..69b45c0 100644
--- a/content/test/ppapi/ppapi_browsertest.cc
+++ b/content/test/ppapi/ppapi_browsertest.cc
@@ -123,8 +123,6 @@
 TEST_PPAPI_OUT_OF_PROCESS(MessageLoop_Basics)
 TEST_PPAPI_OUT_OF_PROCESS(MessageLoop_Post)
 
-TEST_PPAPI_OUT_OF_PROCESS(NetworkProxy)
-
 // TODO(danakj): http://crbug.com/115286
 TEST_PPAPI_IN_PROCESS(DISABLED_Scrollbar)
 // http://crbug.com/89961
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc
index 204bda7..fb7c96fa 100644
--- a/content/test/test_render_frame_host.cc
+++ b/content/test/test_render_frame_host.cc
@@ -13,7 +13,6 @@
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
-#include "content/browser/frame_host/navigator_impl.h"
 #include "content/browser/frame_host/render_frame_host_delegate.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/navigation_params.h"
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index bae8466..e0ea97b 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -15,7 +15,6 @@
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
-#include "content/browser/frame_host/navigator_impl.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/site_instance_impl.h"
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 07f80dd..bba4413 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -477,12 +477,7 @@
     "session_types": ["kiosk.autolaunched"]
   }, {
     "channel": "stable",
-    "extension_types": [
-      "extension",
-      "legacy_packaged_app",
-      "platform_app",
-      "login_screen_extension"
-    ],
+    "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
     "platforms": ["chromeos", "mac", "win", "linux"],
     "whitelist": [
       "0DE0F05680A4A056BCEC864ED8DDA84296F82B40",  // http://crbug.com/434651
@@ -508,15 +503,7 @@
       "61FF4757F9420B62B19BA5C96084649339DB31F5",  // http://crbug.com/731941
       "6FB7E1B6C0247B687AC14772E87A117F5F5E4497",  // http://crbug.com/731941
       "9834387FDA1F66A1B5CA06CB442137B556F12F2A",  // http://crbug.com/772346
-      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7",  // http://crbug.com/839189
-      // TODO(crbug.com/1075877, hendrich) remove this when
-      // enterprise.networkingAttributes has landed
-      "E219EE36A3B40612FD2A8CD6937B03EF0C97D3FE", // Imprivata (login screen) crbug.com/1075877
-      "4DBFC1C52D6660DD90791976DF7FEF7B3D360509", // Imprivata (login screen) crbug.com/1075877
-      "CDA6A10BE50CE65C59B766D0CE6A27E8E0A1533F", // Imprivata (login screen) crbug.com/1075877
-      "A24DE1B21A67E25FB62AC8491642038FE25DA75B", // Imprivata (in-session) crbug.com/1075877
-      "6B25164FFE2BADB5F1DBBD301CC022170267022D", // Imprivata (in-session) crbug.com/1075877
-      "4D15F9AFCF54E56F0A6E06D22DD15F133DCF0882"  // Imprivata (in-session) crbug.com/1075877
+      "A9A9FC0228ADF541F0334F22BEFB8F9C245B21D7"   // http://crbug.com/839189
     ]
   }],
   "networkingPrivate": {
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index d271d56..27f5d10 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -187,6 +187,7 @@
     "//ios/chrome/browser/url_loading",
     "//ios/chrome/browser/web:tab_id_tab_helper",
     "//ios/chrome/browser/web_state_list",
+    "//ios/chrome/browser/web_state_list:agents",
     "//ios/chrome/common/app_group:main_app",
     "//ios/net",
     "//ios/public/provider/chrome/browser",
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 4ca4745..7751b7d 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -55,6 +55,7 @@
 #import "ios/chrome/browser/ui/safe_mode/safe_mode_coordinator.h"
 #import "ios/chrome/browser/ui/util/multi_window_support.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h"
 #include "ios/net/cookies/cookie_store_ios.h"
 #include "ios/net/cookies/system_cookie_util.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
@@ -462,7 +463,10 @@
   UMA_HISTOGRAM_CUSTOM_TIMES("Session.TotalDurationMax1Day", duration,
                              base::TimeDelta::FromMilliseconds(1),
                              base::TimeDelta::FromHours(24), 50);
-  [currentInterface.tabModel recordSessionMetrics];
+  if (currentInterface.browser) {
+    WebStateListMetricsBrowserAgent::FromBrowser(currentInterface.browser)
+        ->RecordSessionMetrics();
+  }
 
   if (currentInterface.browserState) {
     IOSProfileSessionDurationsService* psdService =
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index 2acdb113..db96cd7 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -470,7 +470,6 @@
 TEST_F(AppStateNoFixtureTest, willResignActive) {
   // Setup.
   id tabModel = [OCMockObject mockForClass:[TabModel class]];
-  [[tabModel expect] recordSessionMetrics];
 
   StubBrowserInterfaceProvider* interfaceProvider =
       [[StubBrowserInterfaceProvider alloc] init];
diff --git a/ios/chrome/browser/main/browser_agent_util.mm b/ios/chrome/browser/main/browser_agent_util.mm
index e91fad58..b03800b 100644
--- a/ios/chrome/browser/main/browser_agent_util.mm
+++ b/ios/chrome/browser/main/browser_agent_util.mm
@@ -20,6 +20,7 @@
 #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h"
 #import "ios/chrome/browser/url_loading/url_loading_notifier_browser_agent.h"
 #import "ios/chrome/browser/web_state_list/tab_insertion_browser_agent.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_usage_enabler_browser_agent.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 
@@ -51,9 +52,11 @@
   SessionRestorationBrowserAgent::CreateForBrowser(
       browser, [SessionServiceIOS sharedService]);
 
-  // TabUsageRecorderBrowserAgent observes the SessionRestorationBrowserAgent,
-  // So it should be created after the the SessionRestorationBrowserAgent is
-  // created. Normal browser states are the only ones to get tab usage recorder.
+  // TabUsageRecorderBrowserAgent and WebStateListMetricsBrowserAgent observe
+  // the SessionRestorationBrowserAgent, so they should be created after the the
+  // SessionRestorationBrowserAgent is created.
+  WebStateListMetricsBrowserAgent::CreateForBrowser(browser);
+  // Normal browser states are the only ones to get tab usage recorder.
   if (!browser->GetBrowserState()->IsOffTheRecord())
     TabUsageRecorderBrowserAgent::CreateForBrowser(browser);
 
diff --git a/ios/chrome/browser/tabs/tab_model.h b/ios/chrome/browser/tabs/tab_model.h
index 0385829..92b4257 100644
--- a/ios/chrome/browser/tabs/tab_model.h
+++ b/ios/chrome/browser/tabs/tab_model.h
@@ -52,9 +52,6 @@
 // Closes ALL the tabs.
 - (void)closeAllTabs;
 
-// Records tab session metrics.
-- (void)recordSessionMetrics;
-
 // Tells the receiver to disconnect from the model object it depends on. This
 // should be called before destroying the browser state that the receiver was
 // initialized with.
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 0cb0d3971..66d1ea3 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -44,7 +44,6 @@
 #import "ios/chrome/browser/tabs/tab_parenting_observer.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
-#import "ios/chrome/browser/web_state_list/web_state_list_metrics_observer.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_usage_enabler_browser_agent.h"
 #include "ios/web/public/browser_state.h"
@@ -222,9 +221,6 @@
   // WebStateListObserverBridges.
   NSArray<id<WebStateListObserving>>* _retainedWebStateListObservers;
 
-  // Counters for metrics.
-  WebStateListMetricsObserver* _webStateListMetricsObserver;
-
   // Backs up property with the same name.
   TabUsageRecorderBrowserAgent* _tabUsageRecorder;
 
@@ -303,12 +299,6 @@
 
     _webStateListObservers.push_back(std::make_unique<TabParentingObserver>());
 
-    auto webStateListMetricsObserver =
-        std::make_unique<WebStateListMetricsObserver>();
-    _webStateListMetricsObserver = webStateListMetricsObserver.get();
-    _sessionRestorationBrowserAgent->AddObserver(_webStateListMetricsObserver);
-    _webStateListObservers.push_back(std::move(webStateListMetricsObserver));
-
     for (const auto& webStateListObserver : _webStateListObservers)
       _webStateList->AddObserver(webStateListObserver.get());
     _retainedWebStateListObservers = [retainedWebStateListObservers copy];
@@ -342,11 +332,6 @@
   _webStateList->CloseAllWebStates(WebStateList::CLOSE_USER_ACTION);
 }
 
-- (void)recordSessionMetrics {
-  if (_webStateListMetricsObserver)
-    _webStateListMetricsObserver->RecordSessionMetrics();
-}
-
 // NOTE: This can be called multiple times, so must be robust against that.
 - (void)disconnect {
   if (!_browserState)
@@ -355,15 +340,10 @@
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   TabModelList::UnregisterTabModelFromChromeBrowserState(_browserState, self);
 
-  _sessionRestorationBrowserAgent->RemoveObserver(_webStateListMetricsObserver);
-
   _sessionRestorationBrowserAgent = nullptr;
   _tabUsageRecorder = nullptr;
   _browserState = nullptr;
 
-  // Clear weak pointer to observers before destroying them.
-  _webStateListMetricsObserver = nullptr;
-
   // Close all tabs. Do this in an @autoreleasepool as WebStateList observers
   // will be notified (they are unregistered later). As some of them may be
   // implemented in Objective-C and unregister themselves in their -dealloc
diff --git a/ios/chrome/browser/translate/OWNERS b/ios/chrome/browser/translate/OWNERS
index 8f9a794..fd25432d 100644
--- a/ios/chrome/browser/translate/OWNERS
+++ b/ios/chrome/browser/translate/OWNERS
@@ -1,5 +1,6 @@
 file://components/translate/OWNERS
 mahmadi@chromium.org
+thegreenfrog@chromium.org
 
 # TEAM: ios-directory-owners@chromium.org
 # OS: iOS
diff --git a/ios/chrome/browser/web_state_list/BUILD.gn b/ios/chrome/browser/web_state_list/BUILD.gn
index a698a19..85d4b02 100644
--- a/ios/chrome/browser/web_state_list/BUILD.gn
+++ b/ios/chrome/browser/web_state_list/BUILD.gn
@@ -13,8 +13,6 @@
     "web_state_list_delegate.h",
     "web_state_list_favicon_driver_observer.h",
     "web_state_list_favicon_driver_observer.mm",
-    "web_state_list_metrics_observer.h",
-    "web_state_list_metrics_observer.mm",
     "web_state_list_observer.h",
     "web_state_list_observer.mm",
     "web_state_list_observer_bridge.h",
@@ -44,11 +42,15 @@
   sources = [
     "tab_insertion_browser_agent.h",
     "tab_insertion_browser_agent.mm",
+    "web_state_list_metrics_browser_agent.h",
+    "web_state_list_metrics_browser_agent.mm",
   ]
   deps = [
     ":web_state_list",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/main:public",
+    "//ios/chrome/browser/sessions:restoration_agent",
+    "//ios/chrome/browser/sessions:restoration_observer",
     "//ios/web/public",
   ]
   libs = [ "Foundation.framework" ]
diff --git a/ios/chrome/browser/web_state_list/web_state_list_metrics_observer.h b/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h
similarity index 66%
rename from ios/chrome/browser/web_state_list/web_state_list_metrics_observer.h
rename to ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h
index bf9c3b8..b5c9d20 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_metrics_observer.h
+++ b/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h
@@ -2,18 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_WEB_STATE_LIST_WEB_STATE_LIST_METRICS_OBSERVER_H_
-#define IOS_CHROME_BROWSER_WEB_STATE_LIST_WEB_STATE_LIST_METRICS_OBSERVER_H_
+#ifndef IOS_CHROME_BROWSER_WEB_STATE_LIST_WEB_STATE_LIST_METRICS_BROWSER_AGENT_H_
+#define IOS_CHROME_BROWSER_WEB_STATE_LIST_WEB_STATE_LIST_METRICS_BROWSER_AGENT_H_
 
 #include "base/macros.h"
+#import "ios/chrome/browser/main/browser_observer.h"
+#import "ios/chrome/browser/main/browser_user_data.h"
 #include "ios/chrome/browser/sessions/session_restoration_observer.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
 
-class WebStateListMetricsObserver : public WebStateListObserver,
-                                    public SessionRestorationObserver {
+class WebStateListMetricsBrowserAgent
+    : BrowserObserver,
+      public WebStateListObserver,
+      public SessionRestorationObserver,
+      public BrowserUserData<WebStateListMetricsBrowserAgent> {
  public:
-  WebStateListMetricsObserver();
-  ~WebStateListMetricsObserver() override;
+  WebStateListMetricsBrowserAgent();
+  ~WebStateListMetricsBrowserAgent() override;
 
   void RecordSessionMetrics();
 
@@ -32,11 +37,12 @@
                            ActiveWebStateChangeReason reason) override;
 
  private:
-  // Counters for metrics.
-  int inserted_web_state_counter_;
-  int detached_web_state_counter_;
-  int activated_web_state_counter_;
-  bool metric_collection_paused_;
+  explicit WebStateListMetricsBrowserAgent(Browser* browser);
+  friend class BrowserUserData<WebStateListMetricsBrowserAgent>;
+  BROWSER_USER_DATA_KEY_DECL();
+
+  // BrowserObserver methods
+  void BrowserDestroyed(Browser* browser) override;
 
   // Reset metrics counters.
   void ResetSessionMetrics();
@@ -46,7 +52,16 @@
   void SessionRestorationFinished(
       const std::vector<web::WebState*>& restored_web_states) override;
 
-  DISALLOW_COPY_AND_ASSIGN(WebStateListMetricsObserver);
+  // The WebStateList containing all the monitored tabs.
+  WebStateList* web_state_list_;  // weak
+
+  // Counters for metrics.
+  int inserted_web_state_counter_;
+  int detached_web_state_counter_;
+  int activated_web_state_counter_;
+  bool metric_collection_paused_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebStateListMetricsBrowserAgent);
 };
 
-#endif  // IOS_CHROME_BROWSER_WEB_STATE_LIST_WEB_STATE_LIST_METRICS_OBSERVER_H_
+#endif  // IOS_CHROME_BROWSER_WEB_STATE_LIST_WEB_STATE_LIST_METRICS_BROWSER_AGENT_H_
diff --git a/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.mm b/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.mm
new file mode 100644
index 0000000..297e35e
--- /dev/null
+++ b/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.mm
@@ -0,0 +1,109 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#import "ios/chrome/browser/sessions/session_restoration_browser_agent.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+BROWSER_USER_DATA_KEY_IMPL(WebStateListMetricsBrowserAgent)
+
+WebStateListMetricsBrowserAgent::WebStateListMetricsBrowserAgent(
+    Browser* browser)
+    : web_state_list_(browser->GetWebStateList()) {
+  browser->AddObserver(this);
+  DCHECK(web_state_list_);
+  web_state_list_->AddObserver(this);
+  SessionRestorationBrowserAgent* restoration_agent =
+      SessionRestorationBrowserAgent::FromBrowser(browser);
+  if (restoration_agent)
+    restoration_agent->AddObserver(this);
+}
+
+WebStateListMetricsBrowserAgent::WebStateListMetricsBrowserAgent() {
+  ResetSessionMetrics();
+}
+
+WebStateListMetricsBrowserAgent::~WebStateListMetricsBrowserAgent() = default;
+
+void WebStateListMetricsBrowserAgent::RecordSessionMetrics() {
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Session.ClosedTabCounts",
+                              detached_web_state_counter_, 1, 200, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Session.OpenedTabCounts",
+                              activated_web_state_counter_, 1, 200, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Session.NewTabCounts",
+                              inserted_web_state_counter_, 1, 200, 50);
+  ResetSessionMetrics();
+}
+
+void WebStateListMetricsBrowserAgent::WillStartSessionRestoration() {
+  metric_collection_paused_ = true;
+}
+
+void WebStateListMetricsBrowserAgent::SessionRestorationFinished(
+    const std::vector<web::WebState*>& restored_web_states) {
+  metric_collection_paused_ = false;
+}
+
+void WebStateListMetricsBrowserAgent::WebStateInsertedAt(
+    WebStateList* web_state_list,
+    web::WebState* web_state,
+    int index,
+    bool activating) {
+  if (metric_collection_paused_)
+    return;
+  base::RecordAction(base::UserMetricsAction("MobileNewTabOpened"));
+  ++inserted_web_state_counter_;
+}
+
+void WebStateListMetricsBrowserAgent::WebStateDetachedAt(
+    WebStateList* web_state_list,
+    web::WebState* web_state,
+    int index) {
+  if (metric_collection_paused_)
+    return;
+  base::RecordAction(base::UserMetricsAction("MobileTabClosed"));
+  ++detached_web_state_counter_;
+}
+
+void WebStateListMetricsBrowserAgent::WebStateActivatedAt(
+    WebStateList* web_state_list,
+    web::WebState* old_web_state,
+    web::WebState* new_web_state,
+    int active_index,
+    ActiveWebStateChangeReason reason) {
+  if (metric_collection_paused_)
+    return;
+  ++activated_web_state_counter_;
+  if (reason == ActiveWebStateChangeReason::Replaced)
+    return;
+
+  base::RecordAction(base::UserMetricsAction("MobileTabSwitched"));
+}
+
+void WebStateListMetricsBrowserAgent::ResetSessionMetrics() {
+  inserted_web_state_counter_ = 0;
+  detached_web_state_counter_ = 0;
+  activated_web_state_counter_ = 0;
+  metric_collection_paused_ = false;
+}
+
+void WebStateListMetricsBrowserAgent::BrowserDestroyed(Browser* browser) {
+  DCHECK_EQ(browser->GetWebStateList(), web_state_list_);
+
+  web_state_list_->RemoveObserver(this);
+  browser->RemoveObserver(this);
+  SessionRestorationBrowserAgent* restoration_agent =
+      SessionRestorationBrowserAgent::FromBrowser(browser);
+  if (restoration_agent)
+    restoration_agent->RemoveObserver(this);
+  web_state_list_ = nullptr;
+}
diff --git a/ios/chrome/browser/web_state_list/web_state_list_metrics_observer.mm b/ios/chrome/browser/web_state_list/web_state_list_metrics_observer.mm
deleted file mode 100644
index 076e9092..0000000
--- a/ios/chrome/browser/web_state_list/web_state_list_metrics_observer.mm
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/web_state_list/web_state_list_metrics_observer.h"
-
-#include "base/metrics/histogram_macros.h"
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-WebStateListMetricsObserver::WebStateListMetricsObserver() {
-  ResetSessionMetrics();
-}
-
-WebStateListMetricsObserver::~WebStateListMetricsObserver() = default;
-
-void WebStateListMetricsObserver::RecordSessionMetrics() {
-  UMA_HISTOGRAM_CUSTOM_COUNTS("Session.ClosedTabCounts",
-                              detached_web_state_counter_, 1, 200, 50);
-  UMA_HISTOGRAM_CUSTOM_COUNTS("Session.OpenedTabCounts",
-                              activated_web_state_counter_, 1, 200, 50);
-  UMA_HISTOGRAM_CUSTOM_COUNTS("Session.NewTabCounts",
-                              inserted_web_state_counter_, 1, 200, 50);
-  ResetSessionMetrics();
-}
-
-void WebStateListMetricsObserver::WillStartSessionRestoration() {
-  metric_collection_paused_ = true;
-}
-
-void WebStateListMetricsObserver::SessionRestorationFinished(
-    const std::vector<web::WebState*>& restored_web_states) {
-  metric_collection_paused_ = false;
-}
-
-void WebStateListMetricsObserver::WebStateInsertedAt(
-    WebStateList* web_state_list,
-    web::WebState* web_state,
-    int index,
-    bool activating) {
-  if (metric_collection_paused_)
-    return;
-  base::RecordAction(base::UserMetricsAction("MobileNewTabOpened"));
-  ++inserted_web_state_counter_;
-}
-
-void WebStateListMetricsObserver::WebStateDetachedAt(
-    WebStateList* web_state_list,
-    web::WebState* web_state,
-    int index) {
-  if (metric_collection_paused_)
-    return;
-  base::RecordAction(base::UserMetricsAction("MobileTabClosed"));
-  ++detached_web_state_counter_;
-}
-
-void WebStateListMetricsObserver::WebStateActivatedAt(
-    WebStateList* web_state_list,
-    web::WebState* old_web_state,
-    web::WebState* new_web_state,
-    int active_index,
-    ActiveWebStateChangeReason reason) {
-  if (metric_collection_paused_)
-    return;
-  ++activated_web_state_counter_;
-  if (reason == ActiveWebStateChangeReason::Replaced)
-    return;
-
-  base::RecordAction(base::UserMetricsAction("MobileTabSwitched"));
-}
-
-void WebStateListMetricsObserver::ResetSessionMetrics() {
-  inserted_web_state_counter_ = 0;
-  detached_web_state_counter_ = 0;
-  activated_web_state_counter_ = 0;
-  metric_collection_paused_ = false;
-}
diff --git a/ios/third_party/material_components_ios/BUILD.gn b/ios/third_party/material_components_ios/BUILD.gn
index f0fb742..53d5e054 100644
--- a/ios/third_party/material_components_ios/BUILD.gn
+++ b/ios/third_party/material_components_ios/BUILD.gn
@@ -288,6 +288,7 @@
   "src/components/ProgressView/src/MaterialProgressView.h",
   "src/components/ProgressView/src/Theming/MDCProgressView+MaterialTheming.h",
   "src/components/ProgressView/src/Theming/MaterialProgressView+Theming.h",
+  "src/components/ProgressView/src/private/MDCProgressGradientView.h",
   "src/components/ProgressView/src/private/MaterialProgressViewStrings.h",
   "src/components/ProgressView/src/private/MaterialProgressViewStrings_table.h",
   "src/components/Ripple/src/MDCRippleTouchController.h",
@@ -342,21 +343,21 @@
   "src/components/Snackbar/src/private/MDCSnackbarOverlayView.h",
   "src/components/Snackbar/src/private/MaterialSnackbarStrings.h",
   "src/components/Snackbar/src/private/MaterialSnackbarStrings_table.h",
+  "src/components/Tabs/src/ExtendedAlignment/MDCTabBarExtendedAlignment.h",
   "src/components/Tabs/src/MDCTabBar.h",
   "src/components/Tabs/src/MDCTabBarAlignment.h",
   "src/components/Tabs/src/MDCTabBarControllerDelegate.h",
   "src/components/Tabs/src/MDCTabBarDelegate.h",
   "src/components/Tabs/src/MDCTabBarDisplayDelegate.h",
-  "src/components/Tabs/src/MDCTabBarExtendedAlignment.h",
   "src/components/Tabs/src/MDCTabBarIndicatorAttributes.h",
   "src/components/Tabs/src/MDCTabBarIndicatorContext.h",
   "src/components/Tabs/src/MDCTabBarIndicatorTemplate.h",
   "src/components/Tabs/src/MDCTabBarItemAppearance.h",
-  "src/components/Tabs/src/MDCTabBarSizeClassDelegate.h",
   "src/components/Tabs/src/MDCTabBarTextTransform.h",
   "src/components/Tabs/src/MDCTabBarUnderlineIndicatorTemplate.h",
   "src/components/Tabs/src/MDCTabBarViewController.h",
   "src/components/Tabs/src/MaterialTabs.h",
+  "src/components/Tabs/src/SizeClassDelegate/MDCTabBarSizeClassDelegate.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarItem.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarItemCustomViewing.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarView.h",
@@ -636,6 +637,8 @@
   "src/components/Snackbar/src/TypographyThemer",
   "src/components/Snackbar/src/private",
   "src/components/Tabs/src",
+  "src/components/Tabs/src/ExtendedAlignment",
+  "src/components/Tabs/src/SizeClassDelegate",
   "src/components/Tabs/src/TabBarView",
   "src/components/Tabs/src/TabBarView/private",
   "src/components/Tabs/src/Theming",
@@ -1112,6 +1115,8 @@
   "src/components/ProgressView/src/Theming/MDCProgressView+MaterialTheming.h",
   "src/components/ProgressView/src/Theming/MDCProgressView+MaterialTheming.m",
   "src/components/ProgressView/src/Theming/MaterialProgressView+Theming.h",
+  "src/components/ProgressView/src/private/MDCProgressGradientView.h",
+  "src/components/ProgressView/src/private/MDCProgressGradientView.m",
   "src/components/ProgressView/src/private/MaterialProgressViewStrings.h",
   "src/components/ProgressView/src/private/MaterialProgressViewStrings_table.h",
   "src/components/Ripple/src/MDCRippleTouchController.h",
@@ -1194,25 +1199,25 @@
   "src/components/Snackbar/src/private/MDCSnackbarOverlayView.m",
   "src/components/Snackbar/src/private/MaterialSnackbarStrings.h",
   "src/components/Snackbar/src/private/MaterialSnackbarStrings_table.h",
+  "src/components/Tabs/src/ExtendedAlignment/MDCTabBarExtendedAlignment.h",
   "src/components/Tabs/src/MDCTabBar.h",
   "src/components/Tabs/src/MDCTabBar.m",
   "src/components/Tabs/src/MDCTabBarAlignment.h",
   "src/components/Tabs/src/MDCTabBarControllerDelegate.h",
   "src/components/Tabs/src/MDCTabBarDelegate.h",
   "src/components/Tabs/src/MDCTabBarDisplayDelegate.h",
-  "src/components/Tabs/src/MDCTabBarExtendedAlignment.h",
   "src/components/Tabs/src/MDCTabBarIndicatorAttributes.h",
   "src/components/Tabs/src/MDCTabBarIndicatorAttributes.m",
   "src/components/Tabs/src/MDCTabBarIndicatorContext.h",
   "src/components/Tabs/src/MDCTabBarIndicatorTemplate.h",
   "src/components/Tabs/src/MDCTabBarItemAppearance.h",
-  "src/components/Tabs/src/MDCTabBarSizeClassDelegate.h",
   "src/components/Tabs/src/MDCTabBarTextTransform.h",
   "src/components/Tabs/src/MDCTabBarUnderlineIndicatorTemplate.h",
   "src/components/Tabs/src/MDCTabBarUnderlineIndicatorTemplate.m",
   "src/components/Tabs/src/MDCTabBarViewController.h",
   "src/components/Tabs/src/MDCTabBarViewController.m",
   "src/components/Tabs/src/MaterialTabs.h",
+  "src/components/Tabs/src/SizeClassDelegate/MDCTabBarSizeClassDelegate.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarItem.h",
   "src/components/Tabs/src/TabBarView/MDCTabBarItem.m",
   "src/components/Tabs/src/TabBarView/MDCTabBarItemCustomViewing.h",
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index f959018..3d63acb 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -1492,6 +1492,7 @@
   }
 
   self.userInteractionState->ResetLastTransferTime();
+  self.webStateImpl->OnNavigationRedirected(context);
 }
 
 // WKNavigation objects are used as a weak key to store web::NavigationContext.
diff --git a/ios/web/public/test/fakes/test_web_state.h b/ios/web/public/test/fakes/test_web_state.h
index eedce5a..b954e9a 100644
--- a/ios/web/public/test/fakes/test_web_state.h
+++ b/ios/web/public/test/fakes/test_web_state.h
@@ -132,6 +132,7 @@
   // Notifier for tests.
   void OnPageLoaded(PageLoadCompletionStatus load_completion_status);
   void OnNavigationStarted(NavigationContext* navigation_context);
+  void OnNavigationRedirected(NavigationContext* context);
   void OnNavigationFinished(NavigationContext* navigation_context);
   void OnRenderProcessGone();
   void OnBackForwardStateChanged();
diff --git a/ios/web/public/test/fakes/test_web_state.mm b/ios/web/public/test/fakes/test_web_state.mm
index bba421ef..5d9fa7a0 100644
--- a/ios/web/public/test/fakes/test_web_state.mm
+++ b/ios/web/public/test/fakes/test_web_state.mm
@@ -312,6 +312,12 @@
     observer.DidStartNavigation(this, navigation_context);
 }
 
+void TestWebState::OnNavigationRedirected(
+    NavigationContext* navigation_context) {
+  for (auto& observer : observers_)
+    observer.DidRedirectNavigation(this, navigation_context);
+}
+
 void TestWebState::OnNavigationFinished(NavigationContext* navigation_context) {
   for (auto& observer : observers_)
     observer.DidFinishNavigation(this, navigation_context);
diff --git a/ios/web/public/web_state_observer.h b/ios/web/public/web_state_observer.h
index b39b9cf..d8d38bc 100644
--- a/ios/web/public/web_state_observer.h
+++ b/ios/web/public/web_state_observer.h
@@ -50,6 +50,14 @@
   virtual void DidStartNavigation(WebState* web_state,
                                   NavigationContext* navigation_context) {}
 
+  // Called when an in-progress main-frame navigation in |web_state| receives
+  // a server redirect to a different URL. At the point where this is called,
+  // |navigation_context|'s URL has already been updated, so calling GetUrl()
+  // on |navigation_context| will return the redirect URL rather than the
+  // original URL.
+  virtual void DidRedirectNavigation(WebState* web_state,
+                                     NavigationContext* navigation_context) {}
+
   // Called when a navigation finished in the WebState for the main frame. This
   // happens when a navigation is committed, aborted or replaced by a new one.
   // To know if the navigation has resulted in an error page, use
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index d286893..4807461 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -78,6 +78,9 @@
   // Notifies the observers that a navigation has started.
   void OnNavigationStarted(web::NavigationContextImpl* context);
 
+  // Notifies the observers that a navigation was redirected.
+  void OnNavigationRedirected(web::NavigationContextImpl* context);
+
   // Notifies the observers that a navigation has finished. For same-document
   // navigations notifies the observers about favicon URLs update using
   // candidates received in OnFaviconUrlUpdated.
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index ecb4a5a..3ccf5836 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -797,6 +797,11 @@
     observer.DidStartNavigation(this, context);
 }
 
+void WebStateImpl::OnNavigationRedirected(web::NavigationContextImpl* context) {
+  for (auto& observer : observers_)
+    observer.DidRedirectNavigation(this, context);
+}
+
 void WebStateImpl::OnNavigationFinished(web::NavigationContextImpl* context) {
   // Navigation manager loads internal URLs to restore session history and
   // create back-forward entries for WebUI. Do not trigger external callbacks.
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index 2e96260..8c42462c 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -731,6 +731,7 @@
   WebStateObserverMock() = default;
 
   MOCK_METHOD2(DidStartNavigation, void(WebState*, NavigationContext*));
+  MOCK_METHOD2(DidRedirectNavigation, void(WebState*, NavigationContext*));
   MOCK_METHOD2(DidFinishNavigation, void(WebState*, NavigationContext*));
   MOCK_METHOD1(DidStartLoading, void(WebState*));
   MOCK_METHOD1(DidStopLoading, void(WebState*));
@@ -1882,15 +1883,35 @@
           web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context,
           &nav_id));
 
-  // 5 calls on ShouldAllowRequest for redirections.
+  // 5 calls on ShouldAllowRequest and DidRedirectNavigation for redirections.
   WebStatePolicyDecider::RequestInfo expected_redirect_request_info(
       ui::PageTransition::PAGE_TRANSITION_CLIENT_REDIRECT,
       /*target_main_frame=*/true, /*has_user_gesture=*/false);
   EXPECT_CALL(
       *decider_,
       ShouldAllowRequest(_, RequestInfoMatch(expected_redirect_request_info)))
-      .Times(5)
-      .WillRepeatedly(Return(WebStatePolicyDecider::PolicyDecision::Allow()));
+      .WillOnce(Return(WebStatePolicyDecider::PolicyDecision::Allow()));
+  EXPECT_CALL(observer_, DidRedirectNavigation(web_state(), _));
+  EXPECT_CALL(
+      *decider_,
+      ShouldAllowRequest(_, RequestInfoMatch(expected_redirect_request_info)))
+      .WillOnce(Return(WebStatePolicyDecider::PolicyDecision::Allow()));
+  EXPECT_CALL(observer_, DidRedirectNavigation(web_state(), _));
+  EXPECT_CALL(
+      *decider_,
+      ShouldAllowRequest(_, RequestInfoMatch(expected_redirect_request_info)))
+      .WillOnce(Return(WebStatePolicyDecider::PolicyDecision::Allow()));
+  EXPECT_CALL(observer_, DidRedirectNavigation(web_state(), _));
+  EXPECT_CALL(
+      *decider_,
+      ShouldAllowRequest(_, RequestInfoMatch(expected_redirect_request_info)))
+      .WillOnce(Return(WebStatePolicyDecider::PolicyDecision::Allow()));
+  EXPECT_CALL(observer_, DidRedirectNavigation(web_state(), _));
+  EXPECT_CALL(
+      *decider_,
+      ShouldAllowRequest(_, RequestInfoMatch(expected_redirect_request_info)))
+      .WillOnce(Return(WebStatePolicyDecider::PolicyDecision::Allow()));
+  EXPECT_CALL(observer_, DidRedirectNavigation(web_state(), _));
 
   EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true, _))
       .WillOnce(
diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
index 6b4424d..b6cfb67 100644
--- a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java
@@ -292,7 +292,7 @@
         boolean canSeekForward = true;
         boolean canSeekBackward = true;
         try {
-            @SuppressLint("PrivateApi")
+            @SuppressLint({"DiscouragedPrivateApi", "PrivateApi"})
             Method getMetadata = player.getClass().getDeclaredMethod(
                     "getMetadata", boolean.class, boolean.class);
             getMetadata.setAccessible(true);
diff --git a/net/dns/public/doh_provider_list.cc b/net/dns/public/doh_provider_list.cc
index d68eb69..b5b2a50e 100644
--- a/net/dns/public/doh_provider_list.cc
+++ b/net/dns/public/doh_provider_list.cc
@@ -104,6 +104,12 @@
                        "" /* ui_name */, "" /* privacy_policy */,
                        false /* display_globally */,
                        {} /* display_countries */),
+      DohProviderEntry("Cznic", base::nullopt /* provider_id_for_histogram */,
+                       {"185.43.135.1", "2001:148f:fffe::1"},
+                       {"odvr.nic.cz"} /* dns_over_tls_hostnames */,
+                       "https://odvr.nic.cz/doh", "" /* ui_name */,
+                       "" /* privacy_policy */, false /* display_globally */,
+                       {} /* display_countries */),
       // Note: DNS.SB has separate entries for autoupgrade and settings UI to
       // allow the extra |no_ecs| parameter for autoupgrade. This parameter
       // disables EDNS Client Subnet (ECS) handling in order to match the
@@ -175,6 +181,13 @@
           "https://dns.quad9.net/dns-query", "Quad9 (9.9.9.9)" /* ui_name */,
           "https://www.quad9.net/home/privacy/" /* privacy_policy */,
           true /* display_globally */, {} /* display_countries */),
+      DohProviderEntry("Switch", base::nullopt /* provider_id_for_histogram */,
+                       {"130.59.31.251", "130.59.31.248", "2001:620:0:ff::2",
+                        "2001:620:0:ff::3"},
+                       {"dns.switch.ch"} /* dns_over_tls_hostnames */,
+                       "https://dns.switch.ch/dns-query", "" /* ui_name */,
+                       "" /* privacy_policy */, false /* display_globally */,
+                       {} /* display_countries */),
   }};
   return *providers;
 }
diff --git a/net/http/transport_security_persister.cc b/net/http/transport_security_persister.cc
index 5c5ef3a..ce625c78 100644
--- a/net/http/transport_security_persister.cc
+++ b/net/http/transport_security_persister.cc
@@ -213,7 +213,7 @@
 void OnWriteFinishedTask(scoped_refptr<base::SequencedTaskRunner> task_runner,
                          base::OnceClosure callback,
                          bool result) {
-  task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback)));
+  task_runner->PostTask(FROM_HERE, std::move(callback));
 }
 
 }  // namespace
diff --git a/ppapi/tests/test_network_proxy.cc b/ppapi/tests/test_network_proxy.cc
index 24ec9f1..de355730 100644
--- a/ppapi/tests/test_network_proxy.cc
+++ b/ppapi/tests/test_network_proxy.cc
@@ -33,15 +33,12 @@
   // Assume no one configures a proxy for localhost.
   ASSERT_EQ("DIRECT", callback.output().AsString());
 
-  callback.WaitForResult(
-      pp::NetworkProxy::GetProxyForURL(instance_,
-                                       pp::Var("http://www.google.com"),
-                                       callback.GetCallback()));
+  callback.WaitForResult(pp::NetworkProxy::GetProxyForURL(
+      instance_, pp::Var("https://use.proxy.test/"), callback.GetCallback()));
   CHECK_CALLBACK_BEHAVIOR(callback);
   ASSERT_EQ(PP_OK, callback.result());
   output = callback.output();
-  // Don't know what the proxy might be, but it should be a valid result.
-  ASSERT_TRUE(output.is_string());
+  ASSERT_EQ("PROXY proxy.test:80", callback.output().AsString());
 
   callback.WaitForResult(
       pp::NetworkProxy::GetProxyForURL(instance_,
diff --git a/third_party/blink/common/client_hints/client_hints.cc b/third_party/blink/common/client_hints/client_hints.cc
index 18403a4..f491de8f 100644
--- a/third_party/blink/common/client_hints/client_hints.cc
+++ b/third_party/blink/common/client_hints/client_hints.cc
@@ -11,6 +11,8 @@
 #include "base/stl_util.h"
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
+#include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "url/origin.h"
 
 namespace blink {
 
@@ -32,6 +34,24 @@
     "sec-ch-ua-platform-version",
 };
 
+const mojom::FeaturePolicyFeature kClientHintsFeaturePolicyMapping[] = {
+    mojom::FeaturePolicyFeature::kClientHintDeviceMemory,
+    mojom::FeaturePolicyFeature::kClientHintDPR,
+    mojom::FeaturePolicyFeature::kClientHintWidth,
+    mojom::FeaturePolicyFeature::kClientHintViewportWidth,
+    mojom::FeaturePolicyFeature::kClientHintRTT,
+    mojom::FeaturePolicyFeature::kClientHintDownlink,
+    mojom::FeaturePolicyFeature::kClientHintECT,
+    mojom::FeaturePolicyFeature::kClientHintLang,
+    mojom::FeaturePolicyFeature::kClientHintUA,
+    mojom::FeaturePolicyFeature::kClientHintUAArch,
+    mojom::FeaturePolicyFeature::kClientHintUAPlatform,
+    mojom::FeaturePolicyFeature::kClientHintUAModel,
+    mojom::FeaturePolicyFeature::kClientHintUAMobile,
+    mojom::FeaturePolicyFeature::kClientHintUAFullVersion,
+    mojom::FeaturePolicyFeature::kClientHintUAPlatformVersion,
+};
+
 const size_t kClientHintsMappingsCount = base::size(kClientHintsHeaderMapping);
 
 static_assert(
@@ -40,6 +60,11 @@
     "Client Hint name table size must match network::mojom::WebClientHintsType "
     "range");
 
+static_assert(base::size(kClientHintsFeaturePolicyMapping) ==
+                  kClientHintsMappingsCount,
+              "Client Hint table sizes must be identical between names and "
+              "feature policies");
+
 const char* const kWebEffectiveConnectionTypeMapping[] = {
     "4g" /* Unknown */, "4g" /* Offline */, "slow-2g" /* Slow 2G */,
     "2g" /* 2G */,      "3g" /* 3G */,      "4g" /* 4G */
@@ -94,4 +119,23 @@
   return base::make_optional(std::move(result));
 }
 
+// Add a list of Client Hints headers to be removed to the output vector, based
+// on FeaturePolicy and the url's origin.
+void FindClientHintsToRemove(const FeaturePolicy* feature_policy,
+                             const GURL& url,
+                             std::vector<std::string>* removed_headers) {
+  DCHECK(removed_headers);
+  url::Origin origin = url::Origin::Create(url);
+  for (size_t i = 0; i < blink::kClientHintsMappingsCount; ++i) {
+    // TODO(yoav): When FeaturePolicy is not present, we need to conserve the
+    // hints that are sent by default.
+    // TODO(yoav): We need to take legacy hints into account here.
+    if (!feature_policy ||
+        !feature_policy->IsFeatureEnabledForOrigin(
+            blink::kClientHintsFeaturePolicyMapping[i], origin)) {
+      removed_headers->push_back(blink::kClientHintsHeaderMapping[i]);
+    }
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/common/feature_policy/feature_policy.cc b/third_party/blink/common/feature_policy/feature_policy.cc
index 3f179e7..e87fd0d 100644
--- a/third_party/blink/common/feature_policy/feature_policy.cc
+++ b/third_party/blink/common/feature_policy/feature_policy.cc
@@ -414,6 +414,8 @@
         FeatureDefault(FeaturePolicy::FeatureDefault::EnableForSelf)},
        {mojom::FeaturePolicyFeature::kClientHintUAFullVersion,
         FeatureDefault(FeaturePolicy::FeatureDefault::EnableForSelf)},
+       {mojom::FeaturePolicyFeature::kClientHintUAPlatformVersion,
+        FeatureDefault(FeaturePolicy::FeatureDefault::EnableForSelf)},
        {mojom::FeaturePolicyFeature::kClientHintViewportWidth,
         FeatureDefault(FeaturePolicy::FeatureDefault::EnableForSelf)},
        {mojom::FeaturePolicyFeature::kClientHintWidth,
diff --git a/third_party/blink/public/common/client_hints/client_hints.h b/third_party/blink/public/common/client_hints/client_hints.h
index 560e5bb..36f95f57 100644
--- a/third_party/blink/public/common/client_hints/client_hints.h
+++ b/third_party/blink/public/common/client_hints/client_hints.h
@@ -11,13 +11,26 @@
 #include "base/optional.h"
 #include "services/network/public/mojom/web_client_hints_types.mojom-shared.h"
 #include "third_party/blink/public/common/common_export.h"
+#include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom.h"
+
+class GURL;
 
 namespace blink {
 
-// Mapping from WebClientHintsType to the hint's to the hint's outgoing header
+class FeaturePolicy;
+
+// Mapping from WebClientHintsType to the hint's outgoing header
 // (e.g. kLang => "sec-ch-lang"). The ordering matches the ordering of enums in
 // services/network/public/mojom/web_client_hints_types.mojom
 BLINK_COMMON_EXPORT extern const char* const kClientHintsHeaderMapping[];
+
+// Mapping from WebClientHintsType to the corresponding Feature-Policy (e.g.
+// kDpr => kClientHintsDPR). The order matches the header mapping and the enum
+// order in services/network/public/mojom/web_client_hints_types.mojom
+BLINK_COMMON_EXPORT extern const mojom::FeaturePolicyFeature
+    kClientHintsFeaturePolicyMapping[];
+
+// The size of the mapping arrays.
 BLINK_COMMON_EXPORT extern const size_t kClientHintsMappingsCount;
 
 // Mapping from WebEffectiveConnectionType to the header value. This value is
@@ -47,6 +60,13 @@
     bool permit_lang_hints,
     bool permit_ua_hints);
 
+// Add a list of Client Hints headers to be removed to the output vector, based
+// on FeaturePolicy and the url's origin.
+BLINK_COMMON_EXPORT void FindClientHintsToRemove(
+    const FeaturePolicy* feature_policy,
+    const GURL& url,
+    std::vector<std::string>* removed_headers);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_CLIENT_HINTS_CLIENT_HINTS_H_
diff --git a/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom b/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
index be56c82..adbeb99 100644
--- a/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
+++ b/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
@@ -128,7 +128,7 @@
   // Storage Access API
   kStorageAccessAPI = 70,
 
-  // Yet Another Client Hint
+  // Client Hint for the full browser version.
   kClientHintUAFullVersion = 71,
 
   // Trust Token API redemption and request signing operations
@@ -137,6 +137,9 @@
   // Controls use of Conversion Measurement API
   kConversionMeasurement = 73,
 
+  // Client Hint for UA platform version.
+  kClientHintUAPlatformVersion = 74,
+
   // Don't change assigned numbers of any item, and don't reuse removed slots.
   // Add new features at the end of the enum.
   // Also, run update_feature_policy_enum.py in
diff --git a/third_party/blink/public/mojom/page/widget.mojom b/third_party/blink/public/mojom/page/widget.mojom
index d80d2e2..fa790e6 100644
--- a/third_party/blink/public/mojom/page/widget.mojom
+++ b/third_party/blink/public/mojom/page/widget.mojom
@@ -59,6 +59,22 @@
   //
   // Also see blink::mojom::RemoteFrame::IntrinsicSizingInfoOfChildChanged.
   IntrinsicSizingInfoChanged(IntrinsicSizingInfo sizing_info);
+
+  // Requests a non-decelerating synthetic fling animation to be latched on the
+  // scroller at the start point, and whose velocity can be changed over time by
+  // sending multiple AutoscrollFling gestures. Used for features like
+  // middle-click autoscroll.
+  //
+  // Sent by a widget to the browser to notify the start point for the
+  // autoscroll.
+  AutoscrollStart(gfx.mojom.PointF position);
+
+  // Sent by a widget to the browser to notify the velocity for the autoscroll
+  // fling animation.
+  AutoscrollFling(gfx.mojom.Vector2dF velocity);
+
+  // Sent by a widget to the browser to notify the end of the autoscroll.
+  AutoscrollEnd();
 };
 
 // Implemented in Blink, this interface defines widget-specific methods that
diff --git a/third_party/blink/public/platform/web_url_loader_client.h b/third_party/blink/public/platform/web_url_loader_client.h
index c224915..55be6c1 100644
--- a/third_party/blink/public/platform/web_url_loader_client.h
+++ b/third_party/blink/public/platform/web_url_loader_client.h
@@ -57,6 +57,8 @@
   // Called when following a redirect. |new_.*| arguments contain the
   // information about the received redirect. When |report_raw_headers| is
   // updated it'll be used for filtering data of the next redirect or response.
+  // |removed_headers| outputs headers that need to be removed from the
+  // redirect request.
   //
   // Implementations should return true to instruct the loader to follow the
   // redirect, or false otherwise.
@@ -67,7 +69,8 @@
       network::mojom::ReferrerPolicy new_referrer_policy,
       const WebString& new_method,
       const WebURLResponse& passed_redirect_response,
-      bool& report_raw_headers) {
+      bool& report_raw_headers,
+      std::vector<std::string>* removed_headers) {
     return true;
   }
 
diff --git a/third_party/blink/public/web/web_widget_client.h b/third_party/blink/public/web/web_widget_client.h
index aefd993..7af0397 100644
--- a/third_party/blink/public/web/web_widget_client.h
+++ b/third_party/blink/public/web/web_widget_client.h
@@ -90,10 +90,6 @@
   // Called when the cursor for the widget changes.
   virtual void DidChangeCursor(const ui::Cursor&) {}
 
-  virtual void AutoscrollStart(const gfx::PointF&) {}
-  virtual void AutoscrollFling(const gfx::Vector2dF& velocity) {}
-  virtual void AutoscrollEnd() {}
-
   // Called to show the widget according to the given policy.
   virtual void Show(WebNavigationPolicy) {}
 
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 837f856..4537c6a 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -1196,6 +1196,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_video_track_writer.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_virtual_keyboard.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_virtual_keyboard.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_virtual_keyboard_overlay_geometry_change_event.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_virtual_keyboard_overlay_geometry_change_event.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_wake_lock.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_wake_lock.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_wake_lock_sentinel.cc",
@@ -1354,6 +1356,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_viewer_pose.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_viewport.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_viewport.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_webgl_binding.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_webgl_binding.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_webgl_layer.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_webgl_layer.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_xr_world_information.cc",
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py b/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py
index 086762c7..255c4b04 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/blink_v8_bridge.py
@@ -170,7 +170,8 @@
             or real_type.is_variadic):
         element_type = real_type.element_type
         element_type_info = blink_type_info(real_type.element_type)
-        if element_type.type_definition_object is not None:
+        if (element_type.type_definition_object is not None
+                and not element_type.is_enumeration):
             # In order to support recursive IDL data structures, we have to
             # avoid recursive C++ header inclusions and utilize C++ forward
             # declarations.  Since |VectorOf| requires complete type
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py b/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py
index 01d846e..ea908e8 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import itertools
+import multiprocessing
 import os.path
 
 import web_idl
@@ -37,8 +39,10 @@
 from .codegen_utils import make_header_include_directives
 from .codegen_utils import write_code_node_to_file
 from .mako_renderer import MakoRenderer
+from .package_initializer import package_initializer
 from .path_manager import PathManager
 
+
 _DICT_MEMBER_PRESENCE_PREDICATES = {
     "ScriptValue": "!{}.IsEmpty()",
     "ScriptPromise": "!{}.IsEmpty()",
@@ -710,9 +714,8 @@
         base_class_name=base_class_name)
 
     # Filepaths
-    basename = "dictionary_example"
-    header_path = path_manager.api_path(filename=basename, ext="h")
-    source_path = path_manager.api_path(filename=basename, ext="cc")
+    header_path = path_manager.api_path(ext="h")
+    source_path = path_manager.api_path(ext="cc")
 
     # Root nodes
     header_node = ListNode(tail="\n")
@@ -876,6 +879,31 @@
     write_code_node_to_file(source_node, path_manager.gen_path_to(source_path))
 
 
-def generate_dictionaries(web_idl_database):
-    dictionary = web_idl_database.find("RTCQuicStreamWriteParameters")
+def run_multiprocessing_task(args):
+    dictionary, package_initializer = args
+    package_initializer.init()
     generate_dictionary(dictionary)
+
+
+def generate_dictionaries(web_idl_database):
+    # More processes do not mean better performance.  The default size was
+    # chosen heuristically.
+    process_pool_size = 8
+    cpu_count = multiprocessing.cpu_count()
+    process_pool_size = max(1, min(cpu_count / 2, process_pool_size))
+
+    pool = multiprocessing.Pool(process_pool_size)
+    # Prior to Python3, Pool.map doesn't support user interrupts (e.g. Ctrl-C),
+    # although Pool.map_async(...).get(...) does.
+    timeout_in_sec = 3600  # Just enough long time
+    pool.map_async(
+        run_multiprocessing_task,
+        map(lambda dictionary: (dictionary, package_initializer()),
+            web_idl_database.dictionaries)).get(timeout_in_sec)
+
+    return
+
+    # When it is difficult to see errors in generator, use following loop
+    # instead of parallel runs above.
+    for dictionary in web_idl_database.dictionaries:
+        generate_dictionary(dictionary)
diff --git a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.cc.tmpl
index b902908..dff001f 100644
--- a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.cc.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.cc.tmpl
@@ -88,6 +88,11 @@
   return &GetCSSProperty{{property.surrogate_for.name.to_upper_camel_case()}}();
 }
   {% endif %}
+  {% if property.computed_value_comparable %}
+bool {{class_name}}::ComputedValuesEqual(const ComputedStyle& a, const ComputedStyle& b) const {
+  return a.{{property.getter}}() == b.{{property.getter}}();
+}
+  {% endif %}
   {% if property.direction_aware_options %}
     {% set options = property.direction_aware_options %}
     {% set resolver_name = options.resolver_name.to_upper_camel_case() %}
diff --git a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
index 822bc81..508538b 100644
--- a/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/properties/templates/css_properties.h.tmpl
@@ -49,6 +49,7 @@
   (property.font and 'kAffectsFont' or ''),
   (property.is_background and 'kBackground' or ''),
   (property.is_border and 'kBorder' or ''),
+  (property.computed_value_comparable and 'kComputedValueComparable' or ''),
 ] | reject('==', '') | join(' | ') %}
 {% set ctor_args = (not is_alias and [property_id, flags, separator] or []) %}
 // {{property.name}}
@@ -84,6 +85,9 @@
   {% for property_method in property.property_methods %}
   {{property_method.return_type}} {{property_method.name}}{{property_method.parameters}} const override;
   {% endfor %}
+  {% if property.computed_value_comparable %}
+  bool ComputedValuesEqual(const ComputedStyle& a, const ComputedStyle& b) const override;
+  {% endif %}
   {% if property.direction_aware_options %}
   const CSSProperty& ResolveDirectionAwareProperty(TextDirection, blink::WritingMode) const override;
   const CSSValue* CSSValueFromComputedStyleInternal(
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index c31362b4..1012d6d 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -675,6 +675,7 @@
     "resolver/match_result_test.cc",
     "resolver/selector_filter_parent_scope_test.cc",
     "resolver/style_adjuster_test.cc",
+    "resolver/style_builder_test.cc",
     "resolver/style_cascade_test.cc",
     "resolver/style_resolver_test.cc",
     "rule_feature_set_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 21ac5bf..1004043f 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -155,6 +155,14 @@
       valid_type: "bool",
     },
 
+    // - computed_value_comparable
+    //
+    // If true, a CSSProperty::ComputedValuesEqual function is generated.
+    computed_value_comparable: {
+      default: false,
+      valid_type: "bool",
+    },
+
     // - runtime_flag
     // The name of the flag on RuntimeEnabledFeatures
     // (e.g. "CSSOverscrollBehavior") that conditionally enables the
@@ -698,6 +706,7 @@
       style_builder_custom_functions: ["value"],
       priority: "High",
       valid_for_marker: true,
+      computed_value_comparable: true,
     },
     {
       name: "font-family",
@@ -954,7 +963,7 @@
       typedom_types: ["Keyword"],
       default_value: "mixed",
       getter: "GetTextOrientation",
-      style_builder_custom_functions: ["value"],
+      style_builder_custom_functions: ["initial", "inherit", "value"],
       priority: "High",
     },
     {
@@ -975,17 +984,19 @@
       typedom_types: ["Keyword"],
       default_value: "horizontal-tb",
       type_name: "WritingMode",
-      style_builder_custom_functions: ["value"],
+      style_builder_custom_functions: ["initial", "inherit", "value"],
       priority: "High",
+      computed_value_comparable: true,
     },
     {
       name: "-webkit-writing-mode",
       property_methods: ["CSSValueFromComputedStyleInternal"],
       inherited: true,
       type_name: "WritingMode",
-      style_builder_custom_functions: ["value"],
+      style_builder_custom_functions: ["initial", "inherit", "value"],
       priority: "High",
       surrogate_for: "writing-mode",
+      computed_value_comparable: true,
     },
     {
       name: "text-rendering",
@@ -1264,6 +1275,7 @@
       affected_by_forced_colors: true,
       valid_for_first_letter: true,
       is_border: true,
+      computed_value_comparable: true,
     },
     {
       name: "border-bottom-left-radius",
@@ -1410,6 +1422,7 @@
       affected_by_forced_colors: true,
       valid_for_first_letter: true,
       is_border: true,
+      computed_value_comparable: true,
     },
     {
       name: "border-left-style",
@@ -1459,6 +1472,7 @@
       affected_by_forced_colors: true,
       valid_for_first_letter: true,
       is_border: true,
+      computed_value_comparable: true,
     },
     {
       name: "border-right-style",
@@ -1508,6 +1522,7 @@
       affected_by_forced_colors: true,
       valid_for_first_letter: true,
       is_border: true,
+      computed_value_comparable: true,
     },
     {
       name: "border-top-left-radius",
@@ -1579,6 +1594,7 @@
       default_value: "Length()",
       typedom_types: ["Keyword", "Length", "Percentage"],
       converter: "ConvertLengthOrAuto",
+      computed_value_comparable: true,
     },
     {
       name: "box-shadow",
@@ -2253,6 +2269,7 @@
       default_value: "Length()",
       typedom_types: ["Keyword", "Length", "Percentage"],
       converter: "ConvertLengthOrAuto",
+      computed_value_comparable: true,
     },
     {
       name: "letter-spacing",
@@ -3004,6 +3021,7 @@
       default_value: "Length()",
       typedom_types: ["Keyword", "Length", "Percentage"],
       converter: "ConvertLengthOrAuto",
+      computed_value_comparable: true,
     },
     {
       name: "r",
@@ -3674,6 +3692,7 @@
       default_value: "Length()",
       typedom_types: ["Keyword", "Length", "Percentage"],
       converter: "ConvertLengthOrAuto",
+      computed_value_comparable: true,
     },
     {
       name: "touch-action",
diff --git a/third_party/blink/renderer/core/css/css_test_helpers.cc b/third_party/blink/renderer/core/css/css_test_helpers.cc
index fd7ff1e..e0e74d3 100644
--- a/third_party/blink/renderer/core/css/css_test_helpers.cc
+++ b/third_party/blink/renderer/core/css/css_test_helpers.cc
@@ -85,14 +85,16 @@
 void RegisterProperty(Document& document,
                       const String& name,
                       const String& syntax,
-                      const String& initial_value,
+                      const base::Optional<String>& initial_value,
                       bool is_inherited) {
+  DCHECK(!initial_value || !initial_value.value().IsNull());
   DummyExceptionStateForTesting exception_state;
   PropertyDefinition* property_definition = PropertyDefinition::Create();
   property_definition->setName(name);
   property_definition->setSyntax(syntax);
-  property_definition->setInitialValue(initial_value);
   property_definition->setInherits(is_inherited);
+  if (initial_value)
+    property_definition->setInitialValue(initial_value.value());
   PropertyRegistration::registerProperty(document.GetExecutionContext(),
                                          property_definition, exception_state);
   ASSERT_FALSE(exception_state.HadException());
diff --git a/third_party/blink/renderer/core/css/css_test_helpers.h b/third_party/blink/renderer/core/css/css_test_helpers.h
index df8aa45..04bb35f 100644
--- a/third_party/blink/renderer/core/css/css_test_helpers.h
+++ b/third_party/blink/renderer/core/css/css_test_helpers.h
@@ -55,7 +55,7 @@
 void RegisterProperty(Document& document,
                       const String& name,
                       const String& syntax,
-                      const String& initial_value,
+                      const base::Optional<String>& initial_value,
                       bool is_inherited);
 
 scoped_refptr<CSSVariableData> CreateVariableData(String);
diff --git a/third_party/blink/renderer/core/css/properties/css_property.h b/third_party/blink/renderer/core/css/properties/css_property.h
index 6679743..41d2da7 100644
--- a/third_party/blink/renderer/core/css/properties/css_property.h
+++ b/third_party/blink/renderer/core/css/properties/css_property.h
@@ -60,6 +60,9 @@
   bool AffectsFont() const { return flags_ & kAffectsFont; }
   bool IsBackground() const { return flags_ & kBackground; }
   bool IsBorder() const { return flags_ & kBorder; }
+  bool IsComputedValueComparable() const {
+    return flags_ & kComputedValueComparable;
+  }
 
   bool IsRepeated() const { return repetition_separator_ != '\0'; }
   char RepetitionSeparator() const { return repetition_separator_; }
@@ -73,6 +76,13 @@
     return false;
   }
 
+  virtual bool ComputedValuesEqual(const ComputedStyle&,
+                                   const ComputedStyle&) const {
+    // May only be called if IsComputedValueComparable() is true.
+    NOTREACHED();
+    return false;
+  }
+
   virtual const CSSValue* CSSValueFromComputedStyleInternal(
       const ComputedStyle&,
       const SVGComputedStyle&,
@@ -137,6 +147,8 @@
     // element, the native appearance must be disabled.
     kBackground = 1 << 16,
     kBorder = 1 << 17,
+    // Set if ComputedValuesEqual is implemented for the given CSSProperty.
+    kComputedValueComparable = 1 << 18,
   };
 
   constexpr CSSProperty(CSSPropertyID property_id,
diff --git a/third_party/blink/renderer/core/css/properties/css_property_test.cc b/third_party/blink/renderer/core/css/properties/css_property_test.cc
index c931da40..308e5ec 100644
--- a/third_party/blink/renderer/core/css/properties/css_property_test.cc
+++ b/third_party/blink/renderer/core/css/properties/css_property_test.cc
@@ -7,13 +7,35 @@
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/css/properties/css_property_instances.h"
+#include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
+#include "third_party/blink/renderer/core/css/resolver/style_builder.h"
+#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/style/data_equivalency.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 
 namespace blink {
 
-class CSSPropertyTest : public PageTestBase {};
+class CSSPropertyTest : public PageTestBase {
+ public:
+  const CSSValue* Parse(String name, String value) {
+    auto* set = css_test_helpers::ParseDeclarationBlock(name + ":" + value);
+    DCHECK(set);
+    if (set->PropertyCount() != 1)
+      return nullptr;
+    return &set->PropertyAt(0).Value();
+  }
+
+  scoped_refptr<ComputedStyle> ComputedStyleWithValue(
+      const CSSProperty& property,
+      const CSSValue& value) {
+    StyleResolverState state(GetDocument(), *GetDocument().body());
+    state.SetStyle(ComputedStyle::Create());
+    StyleBuilder::ApplyProperty(property, state, value);
+    return state.TakeStyle();
+  }
+};
 
 TEST_F(CSSPropertyTest, VisitedPropertiesAreNotWebExposed) {
   for (CSSPropertyID property_id : CSSPropertyIDList()) {
@@ -101,4 +123,101 @@
                                                   WritingMode::kHorizontalTb));
 }
 
+TEST_F(CSSPropertyTest, ComputedValuesEqualsSelf) {
+  scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
+
+  for (CSSPropertyID id : CSSPropertyIDList()) {
+    const CSSProperty& property = CSSProperty::Get(id);
+    if (!property.IsComputedValueComparable())
+      continue;
+    EXPECT_TRUE(property.ComputedValuesEqual(*style, *style));
+  }
+}
+
+namespace {
+
+// Examples must produce unique computed values. For example, it's not
+// allowed to list both 2px and calc(1px + 1px).
+const char* color_examples[] = {"red", "green", "#fef", "#faf", nullptr};
+const char* direction_examples[] = {"ltr", "rtl", nullptr};
+const char* length_or_auto_examples[] = {"auto", "1px", "2px", "5%", nullptr};
+const char* writing_mode_examples[] = {"horizontal-tb", "vertical-rl", nullptr};
+
+struct ComputedValuesEqualData {
+  const char* name;
+  const char** examples;
+} computed_values_equal_data[] = {
+    {"-webkit-writing-mode", writing_mode_examples},
+    {"border-bottom-color", color_examples},
+    {"border-left-color", color_examples},
+    {"border-right-color", color_examples},
+    {"border-top-color", color_examples},
+    {"bottom", length_or_auto_examples},
+    {"direction", direction_examples},
+    {"left", length_or_auto_examples},
+    {"right", length_or_auto_examples},
+    {"top", length_or_auto_examples},
+    {"writing-mode", writing_mode_examples},
+};
+
+}  // namespace
+
+TEST_F(CSSPropertyTest, ComparablePropertiesAreListed) {
+  HashSet<String> names;
+  for (const auto& data : computed_values_equal_data)
+    names.insert(data.name);
+
+  for (CSSPropertyID id : CSSPropertyIDList()) {
+    const CSSProperty& property = CSSProperty::Get(id);
+    EXPECT_TRUE(!property.IsComputedValueComparable() ||
+                names.Contains(property.GetPropertyNameString()))
+        << property.GetPropertyNameString() << " missing";
+  }
+}
+
+// This test verifies the correctness of CSSProperty::ComputedValuesEqual for
+// all properties that have the kComputedValueComparable flag.
+class ComputedValuesEqual
+    : public CSSPropertyTest,
+      public testing::WithParamInterface<ComputedValuesEqualData> {};
+
+INSTANTIATE_TEST_SUITE_P(CSSPropertyTest,
+                         ComputedValuesEqual,
+                         testing::ValuesIn(computed_values_equal_data));
+
+TEST_P(ComputedValuesEqual, Examples) {
+  auto data = GetParam();
+
+  CSSPropertyRef ref(data.name, GetDocument());
+  ASSERT_TRUE(ref.IsValid()) << data.name;
+  const CSSProperty& property = ref.GetProperty();
+  ASSERT_TRUE(property.IsComputedValueComparable()) << data.name;
+
+  // Convert const char* examples to CSSValues.
+  HeapVector<Member<const CSSValue>> values;
+  for (const char** example = data.examples; *example; ++example) {
+    const CSSValue* value = Parse(data.name, *example);
+    ASSERT_TRUE(value) << data.name << ":" << *example;
+    values.push_back(value);
+  }
+
+  for (const CSSValue* value_a : values) {
+    for (const CSSValue* value_b : values) {
+      auto style_a = ComputedStyleWithValue(property, *value_a);
+      auto style_b = ComputedStyleWithValue(property, *value_b);
+      if (value_a == value_b) {
+        EXPECT_TRUE(property.ComputedValuesEqual(*style_a, *style_b))
+            << property.GetPropertyNameString()
+            << ": expected equality between " << value_a->CssText() << " and "
+            << value_b->CssText();
+      } else {
+        EXPECT_FALSE(property.ComputedValuesEqual(*style_a, *style_b))
+            << property.GetPropertyNameString()
+            << ": expected non-equality between " << value_a->CssText()
+            << " and " << value_b->CssText();
+      }
+    }
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index 14e147e..a5e40f0 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -6241,6 +6241,15 @@
   return CSSIdentifierValue::Create(style.GetTextOrientation());
 }
 
+void TextOrientation::ApplyInitial(StyleResolverState& state) const {
+  state.SetTextOrientation(
+      ComputedStyleInitialValues::InitialTextOrientation());
+}
+
+void TextOrientation::ApplyInherit(StyleResolverState& state) const {
+  state.SetTextOrientation(state.ParentStyle()->GetTextOrientation());
+}
+
 void TextOrientation::ApplyValue(StyleResolverState& state,
                                  const CSSValue& value) const {
   state.SetTextOrientation(
@@ -7886,6 +7895,13 @@
   return CSSIdentifierValue::Create(style.GetWritingMode());
 }
 
+void WebkitWritingMode::ApplyInitial(StyleResolverState& state) const {
+  state.SetWritingMode(ComputedStyleInitialValues::InitialWritingMode());
+}
+void WebkitWritingMode::ApplyInherit(StyleResolverState& state) const {
+  state.SetWritingMode(state.ParentStyle()->GetWritingMode());
+}
+
 void WebkitWritingMode::ApplyValue(StyleResolverState& state,
                                    const CSSValue& value) const {
   state.SetWritingMode(
@@ -8089,6 +8105,14 @@
   return CSSIdentifierValue::Create(style.GetWritingMode());
 }
 
+void WritingMode::ApplyInitial(StyleResolverState& state) const {
+  state.SetWritingMode(ComputedStyleInitialValues::InitialWritingMode());
+}
+
+void WritingMode::ApplyInherit(StyleResolverState& state) const {
+  state.SetWritingMode(state.ParentStyle()->GetWritingMode());
+}
+
 void WritingMode::ApplyValue(StyleResolverState& state,
                              const CSSValue& value) const {
   state.SetWritingMode(
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_test.cc b/third_party/blink/renderer/core/css/resolver/style_builder_test.cc
new file mode 100644
index 0000000..c2d3085f
--- /dev/null
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_test.cc
@@ -0,0 +1,74 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/resolver/style_builder.h"
+#include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_inherited_value.h"
+#include "third_party/blink/renderer/core/css/css_initial_value.h"
+#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+
+namespace blink {
+
+class StyleBuilderTest : public PageTestBase {};
+
+TEST_F(StyleBuilderTest, WritingModeChangeDirtiesFont) {
+  const CSSProperty* properties[] = {
+      &GetCSSPropertyWritingMode(),
+      &GetCSSPropertyWebkitWritingMode(),
+  };
+
+  HeapVector<Member<const CSSValue>> values = {
+      CSSInitialValue::Create(),
+      CSSInheritedValue::Create(),
+      CSSIdentifierValue::Create(CSSValueID::kHorizontalTb),
+  };
+
+  for (const CSSProperty* property : properties) {
+    for (const CSSValue* value : values) {
+      auto parent_style = ComputedStyle::Create();
+      auto style = ComputedStyle::Create();
+      // This test assumes that initial 'writing-mode' is not 'vertical-lr'.
+      ASSERT_NE(WritingMode::kVerticalLr, style->GetWritingMode());
+      style->SetWritingMode(WritingMode::kVerticalLr);
+
+      StyleResolverState state(GetDocument(), *GetDocument().body(),
+                               parent_style.get(), parent_style.get());
+      state.SetStyle(style);
+
+      ASSERT_FALSE(state.GetFontBuilder().FontDirty());
+      StyleBuilder::ApplyProperty(*property, state, *value);
+      EXPECT_TRUE(state.GetFontBuilder().FontDirty());
+    }
+  }
+}
+
+TEST_F(StyleBuilderTest, TextOrientationChangeDirtiesFont) {
+  HeapVector<Member<const CSSValue>> values = {
+      CSSInitialValue::Create(),
+      CSSInheritedValue::Create(),
+      CSSIdentifierValue::Create(CSSValueID::kMixed),
+  };
+
+  for (const CSSValue* value : values) {
+    auto parent_style = ComputedStyle::Create();
+    auto style = ComputedStyle::Create();
+    // This test assumes that initial 'text-orientation' is not 'upright'.
+    ASSERT_NE(ETextOrientation::kUpright, style->GetTextOrientation());
+    style->SetTextOrientation(ETextOrientation::kUpright);
+
+    StyleResolverState state(GetDocument(), *GetDocument().body(),
+                             parent_style.get(), parent_style.get());
+    state.SetStyle(style);
+
+    ASSERT_FALSE(state.GetFontBuilder().FontDirty());
+    StyleBuilder::ApplyProperty(GetCSSPropertyTextOrientation(), state, *value);
+    EXPECT_TRUE(state.GetFontBuilder().FontDirty());
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
index 3543c16..1f57244 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
@@ -1728,7 +1728,7 @@
 }
 
 TEST_F(StyleCascadeTest, SubstituteRegisteredUniversalInvalid) {
-  RegisterProperty(GetDocument(), "--x", "*", g_null_atom, false);
+  RegisterProperty(GetDocument(), "--x", "*", base::nullopt, false);
 
   TestCascade cascade(GetDocument());
   cascade.Add("--y", " var(--x) ");
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 19f58cb..3e91e19 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -1326,7 +1326,7 @@
     const HeapHashSet<Member<RuleSet>>& rule_sets,
     InvalidationScope invalidation_scope) {
 #if DCHECK_IS_ON()
-  // Full scope recalcs should be handled while collecting the ruleSets before
+  // Full scope recalcs should be handled while collecting the rule sets before
   // calling this method.
   for (auto rule_set : rule_sets)
     DCHECK(!rule_set->Features().NeedsFullRecalcForRuleSetInvalidation());
@@ -1366,10 +1366,12 @@
       }
     }
 
-    if (element->GetStyleChangeType() < kSubtreeStyleChange)
+    if (element->GetStyleChangeType() < kSubtreeStyleChange &&
+        element->GetComputedStyle()) {
       element = ElementTraversal::Next(*element, stay_within);
-    else
+    } else {
       element = ElementTraversal::NextSkippingChildren(*element, stay_within);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc
index 88f00bf..cd29a29 100644
--- a/third_party/blink/renderer/core/exported/web_view_test.cc
+++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -4106,6 +4106,9 @@
   }
   void IntrinsicSizingInfoChanged(
       mojom::blink::IntrinsicSizingInfoPtr sizing_info) override {}
+  void AutoscrollStart(const gfx::PointF& position) override {}
+  void AutoscrollFling(const gfx::Vector2dF& position) override {}
+  void AutoscrollEnd() override {}
 
  private:
   mojo::AssociatedReceiver<mojom::blink::FrameWidgetHost>
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5 b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
index b0f9e46..39ecf95 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
@@ -104,6 +104,11 @@
       depends_on: ["FeaturePolicyForClientHints"],
     },
     {
+      name: "ClientHintUAPlatformVersion",
+      feature_policy_name: "ch-ua-platform-version",
+      depends_on: ["FeaturePolicyForClientHints"],
+    },
+    {
       name: "ClientHintViewportWidth",
       feature_policy_name: "ch-viewport-width",
       depends_on: ["FeaturePolicyForClientHints"],
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index e85f924a..9fde890 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -702,6 +702,18 @@
   widget_base_->SetCursor(cursor);
 }
 
+void WebFrameWidgetBase::AutoscrollStart(const gfx::PointF& position) {
+  GetAssociatedFrameWidgetHost()->AutoscrollStart(std::move(position));
+}
+
+void WebFrameWidgetBase::AutoscrollFling(const gfx::Vector2dF& velocity) {
+  GetAssociatedFrameWidgetHost()->AutoscrollFling(std::move(velocity));
+}
+
+void WebFrameWidgetBase::AutoscrollEnd() {
+  GetAssociatedFrameWidgetHost()->AutoscrollEnd();
+}
+
 void WebFrameWidgetBase::RequestAnimationAfterDelay(
     const base::TimeDelta& delay) {
   DCHECK(request_animation_after_delay_timer_.get());
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index 3fc9c7b7..5a5b5a05 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -80,6 +80,10 @@
   virtual void IntrinsicSizingInfoChanged(
       mojom::blink::IntrinsicSizingInfoPtr) {}
 
+  void AutoscrollStart(const gfx::PointF& position);
+  void AutoscrollFling(const gfx::Vector2dF& position);
+  void AutoscrollEnd();
+
   // Creates or returns cached mutator dispatcher. This usually requires a
   // round trip to the compositor. The returned WeakPtr must only be
   // dereferenced on the output |mutator_task_runner|.
diff --git a/third_party/blink/renderer/core/html/canvas/image_data.cc b/third_party/blink/renderer/core/html/canvas/image_data.cc
index a29d436..0995102 100644
--- a/third_party/blink/renderer/core/html/canvas/image_data.cc
+++ b/third_party/blink/renderer/core/html/canvas/image_data.cc
@@ -463,7 +463,7 @@
     NOTREACHED();
   }
 
-  if (storage_format_name != color_settings->storageFormat())
+  if (color_settings->storageFormat() != storage_format_name)
     color_settings->setStorageFormat(storage_format_name);
 
   if (!ImageData::ValidateConstructorArguments(
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
index 06dbf0ec..41165037 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
@@ -218,7 +218,7 @@
   // When writing the document, we ensure the ComputedStyle of the select
   // element's items (see AddElementStyle). This requires a style-clean tree.
   // See Element::EnsureComputedStyle for further explanation.
-  owner_element.GetDocument().UpdateStyleAndLayoutTree();
+  DCHECK(!owner_element.GetDocument().NeedsLayoutTreeUpdate());
   IntRect anchor_rect_in_screen = chrome_client_->ViewportToScreen(
       owner_element.VisibleBoundsInVisualViewport(),
       owner_element.GetDocument().View());
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc
index e74aa17..225ed07 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc
@@ -21,30 +21,6 @@
 // InternalPopupMenu::WriteDocument.
 #if !defined(OS_ANDROID)
 
-TEST(InternalPopupMenuTest, WriteDocumentInStyleDirtyTree) {
-  auto dummy_page_holder_ =
-      std::make_unique<DummyPageHolder>(IntSize(800, 600));
-  Document& document = dummy_page_holder_->GetDocument();
-  document.body()->setInnerHTML(R"HTML(
-    <select id="select">
-        <option value="foo">Foo</option>
-        <option value="bar" style="display:none">Bar</option>
-    </select>
-  )HTML");
-  document.View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest);
-  auto* select = To<HTMLSelectElement>(document.getElementById("select"));
-  ASSERT_TRUE(select);
-  auto* menu = MakeGarbageCollected<InternalPopupMenu>(
-      MakeGarbageCollected<EmptyChromeClient>(), *select);
-
-  document.body()->SetInlineStyleProperty(CSSPropertyID::kColor, "blue");
-
-  scoped_refptr<SharedBuffer> buffer = SharedBuffer::Create();
-
-  // Don't DCHECK in Element::EnsureComputedStyle.
-  static_cast<PagePopupClient*>(menu)->WriteDocument(buffer.get());
-}
-
 TEST(InternalPopupMenuTest, ShowSelectDisplayNone) {
   auto dummy_page_holder_ =
       std::make_unique<DummyPageHolder>(IntSize(800, 600));
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
index 9321cea..6d37d4d 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -536,7 +536,6 @@
   if (ShouldSendClientHint(
           ClientHintsMode::kLegacy, policy, resource_origin, is_1p_origin,
           network::mojom::blink::WebClientHintsType::kDeviceMemory,
-          mojom::blink::FeaturePolicyFeature::kClientHintDeviceMemory,
           hints_preferences, enabled_hints)) {
     request.SetHttpHeaderField(
         "Device-Memory",
@@ -548,7 +547,6 @@
   if (ShouldSendClientHint(ClientHintsMode::kLegacy, policy, resource_origin,
                            is_1p_origin,
                            network::mojom::blink::WebClientHintsType::kDpr,
-                           mojom::blink::FeaturePolicyFeature::kClientHintDPR,
                            hints_preferences, enabled_hints)) {
     request.SetHttpHeaderField("DPR", AtomicString(String::Number(dpr)));
   }
@@ -556,7 +554,6 @@
   if (ShouldSendClientHint(
           ClientHintsMode::kLegacy, policy, resource_origin, is_1p_origin,
           network::mojom::blink::WebClientHintsType::kViewportWidth,
-          mojom::blink::FeaturePolicyFeature::kClientHintViewportWidth,
           hints_preferences, enabled_hints) &&
       !GetResourceFetcherProperties().IsDetached() && GetFrame()->View()) {
     request.SetHttpHeaderField(
@@ -567,7 +564,6 @@
   if (ShouldSendClientHint(
           ClientHintsMode::kLegacy, policy, resource_origin, is_1p_origin,
           network::mojom::blink::WebClientHintsType::kResourceWidth,
-          mojom::blink::FeaturePolicyFeature::kClientHintWidth,
           hints_preferences, enabled_hints)) {
     if (resource_width.is_set) {
       float physical_width = resource_width.width * dpr;
@@ -579,7 +575,6 @@
   if (ShouldSendClientHint(ClientHintsMode::kStandard, policy, resource_origin,
                            is_1p_origin,
                            network::mojom::blink::WebClientHintsType::kRtt,
-                           mojom::blink::FeaturePolicyFeature::kClientHintRTT,
                            hints_preferences, enabled_hints)) {
     base::Optional<base::TimeDelta> http_rtt =
         GetNetworkStateNotifier().GetWebHoldbackHttpRtt();
@@ -598,7 +593,6 @@
   if (ShouldSendClientHint(
           ClientHintsMode::kStandard, policy, resource_origin, is_1p_origin,
           network::mojom::blink::WebClientHintsType::kDownlink,
-          mojom::blink::FeaturePolicyFeature::kClientHintDownlink,
           hints_preferences, enabled_hints)) {
     base::Optional<double> throughput_mbps =
         GetNetworkStateNotifier().GetWebHoldbackDownlinkThroughputMbps();
@@ -617,7 +611,6 @@
   if (ShouldSendClientHint(ClientHintsMode::kStandard, policy, resource_origin,
                            is_1p_origin,
                            network::mojom::blink::WebClientHintsType::kEct,
-                           mojom::blink::FeaturePolicyFeature::kClientHintECT,
                            hints_preferences, enabled_hints)) {
     base::Optional<WebEffectiveConnectionType> holdback_ect =
         GetNetworkStateNotifier().GetWebHoldbackEffectiveType();
@@ -634,7 +627,6 @@
   if (ShouldSendClientHint(ClientHintsMode::kStandard, policy, resource_origin,
                            is_1p_origin,
                            network::mojom::blink::WebClientHintsType::kLang,
-                           mojom::blink::FeaturePolicyFeature::kClientHintLang,
                            hints_preferences, enabled_hints)) {
     request.SetHttpHeaderField(
         blink::kClientHintsHeaderMapping[static_cast<size_t>(
@@ -649,7 +641,6 @@
       ShouldSendClientHint(
           ClientHintsMode::kStandard, policy, resource_origin, is_1p_origin,
           network::mojom::blink::WebClientHintsType::kUAArch,
-          mojom::blink::FeaturePolicyFeature::kClientHintUAArch,
           hints_preferences, enabled_hints)) {
     request.SetHttpHeaderField(
         blink::kClientHintsHeaderMapping[static_cast<size_t>(
@@ -661,7 +652,6 @@
       ShouldSendClientHint(
           ClientHintsMode::kStandard, policy, resource_origin, is_1p_origin,
           network::mojom::blink::WebClientHintsType::kUAPlatform,
-          mojom::blink::FeaturePolicyFeature::kClientHintUAPlatform,
           hints_preferences, enabled_hints)) {
     request.SetHttpHeaderField(
         blink::kClientHintsHeaderMapping[static_cast<size_t>(
@@ -673,7 +663,6 @@
       ShouldSendClientHint(
           ClientHintsMode::kStandard, policy, resource_origin, is_1p_origin,
           network::mojom::blink::WebClientHintsType::kUAPlatformVersion,
-          mojom::blink::FeaturePolicyFeature::kClientHintUAPlatform,
           hints_preferences, enabled_hints)) {
     request.SetHttpHeaderField(
         blink::kClientHintsHeaderMapping[static_cast<size_t>(
@@ -685,7 +674,6 @@
       ShouldSendClientHint(
           ClientHintsMode::kStandard, policy, resource_origin, is_1p_origin,
           network::mojom::blink::WebClientHintsType::kUAModel,
-          mojom::blink::FeaturePolicyFeature::kClientHintUAModel,
           hints_preferences, enabled_hints)) {
     request.SetHttpHeaderField(
         blink::kClientHintsHeaderMapping[static_cast<size_t>(
@@ -697,7 +685,6 @@
       ShouldSendClientHint(
           ClientHintsMode::kStandard, policy, resource_origin, is_1p_origin,
           network::mojom::blink::WebClientHintsType::kUAFullVersion,
-          mojom::blink::FeaturePolicyFeature::kClientHintUAFullVersion,
           hints_preferences, enabled_hints)) {
     request.SetHttpHeaderField(
         blink::kClientHintsHeaderMapping[static_cast<size_t>(
@@ -935,6 +922,11 @@
   return GetLocalFrameClient()->UserAgentMetadata();
 }
 
+const FeaturePolicy* FrameFetchContext::GetFeaturePolicy() const {
+  return document_ ? document_->GetSecurityContext().GetFeaturePolicy()
+                   : nullptr;
+}
+
 const ClientHintsPreferences FrameFetchContext::GetClientHintsPreferences()
     const {
   if (GetResourceFetcherProperties().IsDetached())
@@ -956,7 +948,6 @@
     const url::Origin& resource_origin,
     bool is_1p_origin,
     network::mojom::blink::WebClientHintsType type,
-    mojom::blink::FeaturePolicyFeature feature_policy_feature,
     const ClientHintsPreferences& hints_preferences,
     const WebEnabledClientHints& enabled_hints) const {
   bool origin_ok;
@@ -965,8 +956,10 @@
       base::FeatureList::IsEnabled(kAllowClientHintsToThirdParty)) {
     origin_ok = true;
   } else if (RuntimeEnabledFeatures::FeaturePolicyForClientHintsEnabled()) {
-    origin_ok = (policy && policy->IsFeatureEnabledForOrigin(
-                               feature_policy_feature, resource_origin));
+    origin_ok =
+        (policy && policy->IsFeatureEnabledForOrigin(
+                       kClientHintsFeaturePolicyMapping[static_cast<int>(type)],
+                       resource_origin));
   } else {
     origin_ok = is_1p_origin;
   }
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.h b/third_party/blink/renderer/core/loader/frame_fetch_context.h
index 8bea1d0..caf0421 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.h
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.h
@@ -167,6 +167,7 @@
   Settings* GetSettings() const;
   String GetUserAgent() const;
   base::Optional<UserAgentMetadata> GetUserAgentMetadata() const;
+  const FeaturePolicy* GetFeaturePolicy() const override;
   const ClientHintsPreferences GetClientHintsPreferences() const;
   float GetDevicePixelRatio() const;
 
@@ -176,7 +177,6 @@
                             const url::Origin& resource_origin,
                             bool is_1p_origin,
                             network::mojom::blink::WebClientHintsType,
-                            mojom::blink::FeaturePolicyFeature,
                             const ClientHintsPreferences&,
                             const WebEnabledClientHints&) const;
   void SetFirstPartyCookie(ResourceRequest&);
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 1907c89..7042aad 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -736,21 +736,24 @@
 void ChromeClientImpl::AutoscrollStart(const gfx::PointF& viewport_point,
                                        LocalFrame* local_frame) {
   // TODO(dcheng): Why is this null check necessary?
-  if (FrameWidget* widget = local_frame->GetWidgetForLocalRoot())
-    widget->Client()->AutoscrollStart(viewport_point);
+  if (WebFrameWidgetBase* widget =
+          WebLocalFrameImpl::FromFrame(local_frame)->LocalRootFrameWidget())
+    widget->AutoscrollStart(viewport_point);
 }
 
 void ChromeClientImpl::AutoscrollFling(const gfx::Vector2dF& velocity,
                                        LocalFrame* local_frame) {
   // TODO(dcheng): Why is this null check necessary?
-  if (FrameWidget* widget = local_frame->GetWidgetForLocalRoot())
-    widget->Client()->AutoscrollFling(velocity);
+  if (WebFrameWidgetBase* widget =
+          WebLocalFrameImpl::FromFrame(local_frame)->LocalRootFrameWidget())
+    widget->AutoscrollFling(velocity);
 }
 
 void ChromeClientImpl::AutoscrollEnd(LocalFrame* local_frame) {
   // TODO(dcheng): Why is this null check necessary?
-  if (FrameWidget* widget = local_frame->GetWidgetForLocalRoot())
-    widget->Client()->AutoscrollEnd();
+  if (WebFrameWidgetBase* widget =
+          WebLocalFrameImpl::FromFrame(local_frame)->LocalRootFrameWidget())
+    widget->AutoscrollEnd();
 }
 
 String ChromeClientImpl::AcceptLanguages() {
diff --git a/third_party/blink/renderer/core/testing/dictionary_test.cc b/third_party/blink/renderer/core/testing/dictionary_test.cc
index f2bf4d8..29e6eda 100644
--- a/third_party/blink/renderer/core/testing/dictionary_test.cc
+++ b/third_party/blink/renderer/core/testing/dictionary_test.cc
@@ -140,8 +140,10 @@
   boolean_member_ = base::nullopt;
   double_member_ = base::nullopt;
   unrestricted_double_member_ = base::nullopt;
-  string_member_ = String();
+  string_member_ = base::nullopt;
   string_member_with_default_ = String("Should not be returned");
+  byte_string_member_ = base::nullopt;
+  usv_string_member_ = base::nullopt;
   string_sequence_member_ = base::nullopt;
   string_sequence_member_with_default_.Fill("Should not be returned", 1);
   string_sequence_or_null_member_ = base::nullopt;
@@ -154,8 +156,9 @@
   object_or_null_member_with_default_ = ScriptValue();
   double_or_string_member_ = DoubleOrString();
   event_target_or_null_member_ = nullptr;
-  derived_string_member_ = String();
+  derived_string_member_ = base::nullopt;
   derived_string_member_with_default_ = String();
+  derived_derived_string_member_ = base::nullopt;
   required_boolean_member_ = false;
   dictionary_member_properties_ = base::nullopt;
   internal_enum_or_internal_enum_sequence_ =
@@ -188,10 +191,13 @@
     dict->setDoubleMember(double_member_.value());
   if (unrestricted_double_member_)
     dict->setUnrestrictedDoubleMember(unrestricted_double_member_.value());
-  dict->setStringMember(string_member_);
+  if (string_member_)
+    dict->setStringMember(string_member_.value());
   dict->setStringMemberWithDefault(string_member_with_default_);
-  dict->setByteStringMember(byte_string_member_);
-  dict->setUsvStringMember(usv_string_member_);
+  if (byte_string_member_)
+    dict->setByteStringMember(byte_string_member_.value());
+  if (usv_string_member_)
+    dict->setUsvStringMember(usv_string_member_.value());
   if (string_sequence_member_)
     dict->setStringSequenceMember(string_sequence_member_.value());
   dict->setStringSequenceMemberWithDefault(
@@ -219,13 +225,15 @@
   dict->setInternalEnumOrInternalEnumSequenceMember(
       internal_enum_or_internal_enum_sequence_);
   dict->setAnyMember(any_member_);
-  dict->setCallbackFunctionMember(callback_function_member_);
+  if (callback_function_member_)
+    dict->setCallbackFunctionMember(callback_function_member_);
 }
 
 void DictionaryTest::GetDerivedInternals(InternalDictionaryDerived* dict) {
   GetInternals(dict);
 
-  dict->setDerivedStringMember(derived_string_member_);
+  if (derived_string_member_)
+    dict->setDerivedStringMember(derived_string_member_.value());
   dict->setDerivedStringMemberWithDefault(derived_string_member_with_default_);
   dict->setRequiredBooleanMember(required_boolean_member_);
 }
@@ -234,7 +242,8 @@
     InternalDictionaryDerivedDerived* dict) {
   GetDerivedInternals(dict);
 
-  dict->setDerivedDerivedStringMember(derived_derived_string_member_);
+  if (derived_derived_string_member_)
+    dict->setDerivedDerivedStringMember(derived_derived_string_member_.value());
 }
 
 void DictionaryTest::Trace(Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/testing/dictionary_test.h b/third_party/blink/renderer/core/testing/dictionary_test.h
index 51c7fe77..8ed3ed8 100644
--- a/third_party/blink/renderer/core/testing/dictionary_test.h
+++ b/third_party/blink/renderer/core/testing/dictionary_test.h
@@ -64,10 +64,10 @@
   base::Optional<bool> boolean_member_;
   base::Optional<double> double_member_;
   base::Optional<double> unrestricted_double_member_;
-  String string_member_;
+  base::Optional<String> string_member_;
   String string_member_with_default_;
-  String byte_string_member_;
-  String usv_string_member_;
+  base::Optional<String> byte_string_member_;
+  base::Optional<String> usv_string_member_;
   base::Optional<Vector<String>> string_sequence_member_;
   Vector<String> string_sequence_member_with_default_;
   base::Optional<Vector<String>> string_sequence_or_null_member_;
@@ -81,9 +81,9 @@
   DoubleOrString double_or_string_member_;
   base::Optional<HeapVector<DoubleOrString>> double_or_string_sequence_member_;
   Member<EventTarget> event_target_or_null_member_;
-  String derived_string_member_;
+  base::Optional<String> derived_string_member_;
   String derived_string_member_with_default_;
-  String derived_derived_string_member_;
+  base::Optional<String> derived_derived_string_member_;
   bool required_boolean_member_;
   base::Optional<HashMap<String, String>> dictionary_member_properties_;
   InternalEnumOrInternalEnumSequence internal_enum_or_internal_enum_sequence_;
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_server.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_server.cc
index dba1c16..5e8bc96 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_server.cc
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_server.cc
@@ -27,6 +27,7 @@
 BluetoothRemoteGATTServer::BluetoothRemoteGATTServer(ExecutionContext* context,
                                                      BluetoothDevice* device)
     : ExecutionContextLifecycleObserver(context),
+      client_receivers_(this, context),
       device_(device),
       connected_(false) {}
 
@@ -79,12 +80,10 @@
 
 void BluetoothRemoteGATTServer::Dispose() {
   DisconnectIfConnected();
-  // The pipe to this object must be closed when is marked unreachable to
-  // prevent messages from being dispatched before lazy sweeping.
-  client_receivers_.Clear();
 }
 
 void BluetoothRemoteGATTServer::Trace(Visitor* visitor) {
+  visitor->Trace(client_receivers_);
   visitor->Trace(active_algorithms_);
   visitor->Trace(device_);
   ScriptWrappable::Trace(visitor);
@@ -116,7 +115,7 @@
   // See https://bit.ly/2S0zRAS for task types.
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI);
-  client_receivers_.Add(this, client.InitWithNewEndpointAndPassReceiver(),
+  client_receivers_.Add(client.InitWithNewEndpointAndPassReceiver(),
                         std::move(task_runner));
 
   service->RemoteServerConnect(
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_server.h b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_server.h
index 52b7f5e7..41d02aee 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_server.h
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_server.h
@@ -5,13 +5,14 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVER_H_
 
-#include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/modules/v8/string_or_unsigned_long.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/bluetooth/bluetooth_device.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
@@ -110,7 +111,9 @@
   // using this server’s connection.
   HeapHashSet<Member<ScriptPromiseResolver>> active_algorithms_;
 
-  mojo::AssociatedReceiverSet<mojom::blink::WebBluetoothServerClient>
+  HeapMojoAssociatedReceiverSet<mojom::blink::WebBluetoothServerClient,
+                                BluetoothRemoteGATTServer,
+                                HeapMojoWrapperMode::kWithoutContextObserver>
       client_receivers_;
 
   Member<BluetoothDevice> device_;
diff --git a/third_party/blink/renderer/modules/content_index/content_description_type_converter.cc b/third_party/blink/renderer/modules/content_index/content_description_type_converter.cc
index fbe9ec1..dc15839 100644
--- a/third_party/blink/renderer/modules/content_index/content_description_type_converter.cc
+++ b/third_party/blink/renderer/modules/content_index/content_description_type_converter.cc
@@ -76,11 +76,12 @@
 
   blink::HeapVector<blink::Member<blink::ContentIconDefinition>> blink_icons;
   for (const auto& icon : description->icons) {
-    auto* blink_icon =
-        blink::MakeGarbageCollected<blink::ContentIconDefinition>();
+    auto* blink_icon = blink::ContentIconDefinition::Create();
     blink_icon->setSrc(icon->src);
-    blink_icon->setSizes(icon->sizes);
-    blink_icon->setType(icon->type);
+    if (!icon->sizes.IsNull())
+      blink_icon->setSizes(icon->sizes);
+    if (!icon->type.IsNull())
+      blink_icon->setType(icon->type);
     blink_icons.push_back(blink_icon);
   }
   result->setIcons(blink_icons);
diff --git a/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.cc b/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.cc
index a50e312..ee31e24 100644
--- a/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.cc
+++ b/third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.cc
@@ -4,9 +4,9 @@
 
 #include "third_party/blink/renderer/modules/delegated_ink/delegated_ink_trail_presenter.h"
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ink_trail_style.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/events/pointer_event.h"
-#include "third_party/blink/renderer/modules/delegated_ink/ink_trail_style.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/event_target_modules_names.json5 b/third_party/blink/renderer/modules/event_target_modules_names.json5
index 373b645..4706fed5 100644
--- a/third_party/blink/renderer/modules/event_target_modules_names.json5
+++ b/third_party/blink/renderer/modules/event_target_modules_names.json5
@@ -71,6 +71,7 @@
     "MIDIPort",
     "VirtualKeyboard",
     "XR",
+    "XRLayer",
     "XRLightProbe",
     "XRSession",
     "XRSpace",
diff --git a/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc b/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc
index 1ccd8656..410a9a9 100644
--- a/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc
+++ b/third_party/blink/renderer/modules/installedapp/installed_app_controller.cc
@@ -92,11 +92,14 @@
     Vector<mojom::blink::RelatedApplicationPtr> result) {
   HeapVector<Member<RelatedApplication>> applications;
   for (const auto& res : result) {
-    auto* app = MakeGarbageCollected<RelatedApplication>();
+    auto* app = RelatedApplication::Create();
     app->setPlatform(res->platform);
-    app->setUrl(res->url);
-    app->setId(res->id);
-    app->setVersion(res->version);
+    if (!res->url.IsNull())
+      app->setUrl(res->url);
+    if (!res->id.IsNull())
+      app->setId(res->id);
+    if (!res->version.IsNull())
+      app->setVersion(res->version);
     applications.push_back(app);
   }
   callbacks->OnSuccess(applications);
diff --git a/third_party/blink/renderer/modules/notifications/notification_data.cc b/third_party/blink/renderer/modules/notifications/notification_data.cc
index 5a1eff9..5028807 100644
--- a/third_party/blink/renderer/modules/notifications/notification_data.cc
+++ b/third_party/blink/renderer/modules/notifications/notification_data.cc
@@ -126,7 +126,7 @@
     else
       NOTREACHED() << "Unknown action type: " << action->type();
 
-    if (action->hasPlaceholder() &&
+    if (!action->placeholder().IsNull() &&
         notification_action->type ==
             mojom::blink::NotificationActionType::BUTTON) {
       exception_state.ThrowTypeError(
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate.cc b/third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate.cc
index 5ea620f..64c42691 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate.cc
@@ -48,14 +48,13 @@
     ExecutionContext* context,
     const RTCIceCandidateInit* candidate_init,
     ExceptionState& exception_state) {
-  if (!candidate_init->hasSdpMid() && !candidate_init->hasSdpMLineIndex()) {
+  if (candidate_init->sdpMid().IsNull() &&
+      !candidate_init->hasSdpMLineIndex()) {
     exception_state.ThrowTypeError("sdpMid and sdpMLineIndex are both null.");
     return nullptr;
   }
 
-  String sdp_mid;
-  if (candidate_init->hasSdpMid())
-    sdp_mid = candidate_init->sdpMid();
+  String sdp_mid = candidate_init->sdpMid();
 
   base::Optional<uint16_t> sdp_m_line_index;
   if (candidate_init->hasSdpMLineIndex()) {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 354b09c..3dbef36 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -183,7 +183,7 @@
   if (candidate.IsRTCIceCandidateInit()) {
     const RTCIceCandidateInit* ice_candidate_init =
         candidate.GetAsRTCIceCandidateInit();
-    return !ice_candidate_init->hasSdpMid() &&
+    return ice_candidate_init->sdpMid().IsNull() &&
            !ice_candidate_init->hasSdpMLineIndex();
   }
 
@@ -1103,8 +1103,7 @@
 
 base::Optional<ComplexSdpCategory> RTCPeerConnection::CheckForComplexSdp(
     const RTCSessionDescriptionInit* session_description_init) const {
-  if (!session_description_init->hasType() ||
-      !session_description_init->hasSdp())
+  if (!session_description_init->hasType())
     return base::nullopt;
 
   base::Optional<SdpFormat> sdp_format = DeduceSdpFormat(
@@ -1345,8 +1344,7 @@
   }
 
   DCHECK(script_state->ContextIsValid());
-  if (session_description_init->type().IsNull() &&
-      session_description_init->sdp().IsNull()) {
+  if (!session_description_init->hasType()) {
     return setLocalDescription(script_state);
   }
   String sdp;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
index ec897f7e..e393162 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_test.cc
@@ -380,11 +380,11 @@
  public:
   RTCPeerConnection* CreatePC(
       V8TestingScope& scope,
-      const String& sdpSemantics = String(),
+      const String& sdp_semantics = String(),
       bool force_encoded_audio_insertable_streams = false,
       bool force_encoded_video_insertable_streams = false) {
     RTCConfiguration* config = RTCConfiguration::Create();
-    config->setSdpSemantics(sdpSemantics);
+    config->setSdpSemantics(sdp_semantics);
     config->setForceEncodedAudioInsertableStreams(
         force_encoded_audio_insertable_streams);
     config->setForceEncodedVideoInsertableStreams(
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_session_description_init.idl b/third_party/blink/renderer/modules/peerconnection/rtc_session_description_init.idl
index 4baebd3..a9ea75a 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_session_description_init.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_session_description_init.idl
@@ -7,5 +7,5 @@
 dictionary RTCSessionDescriptionInit {
     // TODO(foolip): |type| should be required. https://crbug.com/662898
     RTCSdpType type;
-    DOMString sdp;
+    DOMString sdp = "";
 };
diff --git a/third_party/blink/renderer/modules/webtransport/incoming_stream.cc b/third_party/blink/renderer/modules/webtransport/incoming_stream.cc
index cbd0fc6..9f7bdb03 100644
--- a/third_party/blink/renderer/modules/webtransport/incoming_stream.cc
+++ b/third_party/blink/renderer/modules/webtransport/incoming_stream.cc
@@ -289,8 +289,11 @@
 void IncomingStream::CloseAbortAndReset() {
   DVLOG(1) << "IncomingStream::CloseAbortAndReset() this=" << this;
 
-  controller_->Close();
-  controller_ = nullptr;
+  if (controller_) {
+    controller_->Close();
+    controller_ = nullptr;
+  }
+
   AbortAndReset();
 }
 
diff --git a/third_party/blink/renderer/modules/webtransport/incoming_stream_test.cc b/third_party/blink/renderer/modules/webtransport/incoming_stream_test.cc
index 5412406..81c1a619 100644
--- a/third_party/blink/renderer/modules/webtransport/incoming_stream_test.cc
+++ b/third_party/blink/renderer/modules/webtransport/incoming_stream_test.cc
@@ -166,6 +166,19 @@
   EXPECT_TRUE(result.done);
 }
 
+TEST_F(IncomingStreamTest, AbortReadingTwice) {
+  V8TestingScope scope;
+
+  auto* incoming_stream = CreateIncomingStream(scope);
+
+  EXPECT_CALL(mock_forget_stream_, Run());
+
+  incoming_stream->AbortReading(nullptr);
+
+  // The second call to AbortReading should be a no-op.
+  incoming_stream->AbortReading(nullptr);
+}
+
 TEST_F(IncomingStreamTest, ReadArrayBuffer) {
   V8TestingScope scope;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_layer.cc b/third_party/blink/renderer/modules/xr/xr_layer.cc
index 4d98c8b..f494854 100644
--- a/third_party/blink/renderer/modules/xr/xr_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_layer.cc
@@ -4,12 +4,21 @@
 
 #include "third_party/blink/renderer/modules/xr/xr_layer.h"
 
+#include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 
 namespace blink {
 
 XRLayer::XRLayer(XRSession* session) : session_(session) {}
 
+ExecutionContext* XRLayer::GetExecutionContext() const {
+  return session_->GetExecutionContext();
+}
+
+const AtomicString& XRLayer::InterfaceName() const {
+  return event_target_names::kXRLayer;
+}
+
 void XRLayer::Trace(Visitor* visitor) {
   visitor->Trace(session_);
   ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/xr/xr_layer.h b/third_party/blink/renderer/modules/xr/xr_layer.h
index fbf9e268..7433a689 100644
--- a/third_party/blink/renderer/modules/xr/xr_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_layer.h
@@ -5,13 +5,13 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_LAYER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_LAYER_H_
 
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
 
 namespace blink {
 
 class XRSession;
 
-class XRLayer : public ScriptWrappable {
+class XRLayer : public EventTargetWithInlineData {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -20,6 +20,10 @@
 
   XRSession* session() const { return session_; }
 
+  // EventTarget overrides.
+  ExecutionContext* GetExecutionContext() const override;
+  const AtomicString& InterfaceName() const override;
+
   void Trace(Visitor*) override;
 
  private:
diff --git a/third_party/blink/renderer/modules/xr/xr_layer.idl b/third_party/blink/renderer/modules/xr/xr_layer.idl
index f80b0c1e..165d50cb 100644
--- a/third_party/blink/renderer/modules/xr/xr_layer.idl
+++ b/third_party/blink/renderer/modules/xr/xr_layer.idl
@@ -4,4 +4,4 @@
 
 // https://immersive-web.github.io/webxr/#xrlayer-interface
 [SecureContext, Exposed=Window]
-interface XRLayer {};
\ No newline at end of file
+interface XRLayer : EventTarget {};
\ No newline at end of file
diff --git a/third_party/blink/renderer/platform/bindings/callback_function_base.cc b/third_party/blink/renderer/platform/bindings/callback_function_base.cc
index 108a35d..8cce68e 100644
--- a/third_party/blink/renderer/platform/bindings/callback_function_base.cc
+++ b/third_party/blink/renderer/platform/bindings/callback_function_base.cc
@@ -37,8 +37,8 @@
 }
 
 ScriptState* CallbackFunctionBase::CallbackRelevantScriptStateOrReportError(
-    const char* interface,
-    const char* operation) {
+    const char* interface_name,
+    const char* operation_name) {
   if (callback_relevant_script_state_)
     return callback_relevant_script_state_;
 
@@ -46,8 +46,9 @@
   ScriptState::Scope incumbent_scope(incumbent_script_state_);
   v8::TryCatch try_catch(GetIsolate());
   try_catch.SetVerbose(true);
-  ExceptionState exception_state(
-      GetIsolate(), ExceptionState::kExecutionContext, interface, operation);
+  ExceptionState exception_state(GetIsolate(),
+                                 ExceptionState::kExecutionContext,
+                                 interface_name, operation_name);
   exception_state.ThrowSecurityError(
       "An invocation of the provided callback failed due to cross origin "
       "access.");
@@ -55,15 +56,16 @@
 }
 
 ScriptState* CallbackFunctionBase::CallbackRelevantScriptStateOrThrowException(
-    const char* interface,
-    const char* operation) {
+    const char* interface_name,
+    const char* operation_name) {
   if (callback_relevant_script_state_)
     return callback_relevant_script_state_;
 
   // Throw a SecurityError due to a cross origin callback object.
   ScriptState::Scope incumbent_scope(incumbent_script_state_);
-  ExceptionState exception_state(
-      GetIsolate(), ExceptionState::kExecutionContext, interface, operation);
+  ExceptionState exception_state(GetIsolate(),
+                                 ExceptionState::kExecutionContext,
+                                 interface_name, operation_name);
   exception_state.ThrowSecurityError(
       "An invocation of the provided callback failed due to cross origin "
       "access.");
diff --git a/third_party/blink/renderer/platform/bindings/callback_function_base.h b/third_party/blink/renderer/platform/bindings/callback_function_base.h
index 942043b0..b5a1521 100644
--- a/third_party/blink/renderer/platform/bindings/callback_function_base.h
+++ b/third_party/blink/renderer/platform/bindings/callback_function_base.h
@@ -52,15 +52,16 @@
   // Returns the ScriptState of the relevant realm of the callback object iff
   // the callback is the same origin-domain. Otherwise, reports an error and
   // returns nullptr.
-  ScriptState* CallbackRelevantScriptStateOrReportError(const char* interface,
-                                                        const char* operation);
+  ScriptState* CallbackRelevantScriptStateOrReportError(
+      const char* interface_name,
+      const char* operation_name);
 
   // Returns the ScriptState of the relevant realm of the callback object iff
   // the callback is the same origin-domain. Otherwise, throws an exception and
   // returns nullptr.
   ScriptState* CallbackRelevantScriptStateOrThrowException(
-      const char* interface,
-      const char* operation);
+      const char* interface_name,
+      const char* operation_name);
 
   DOMWrapperWorld& GetWorld() const { return incumbent_script_state_->World(); }
 
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
index f51fdf80..bf9564a 100644
--- a/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
+++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.cc
@@ -39,8 +39,8 @@
 }
 
 ScriptState* CallbackInterfaceBase::CallbackRelevantScriptStateOrReportError(
-    const char* interface,
-    const char* operation) {
+    const char* interface_name,
+    const char* operation_name) {
   if (callback_relevant_script_state_)
     return callback_relevant_script_state_;
 
@@ -48,8 +48,9 @@
   ScriptState::Scope incumbent_scope(incumbent_script_state_);
   v8::TryCatch try_catch(GetIsolate());
   try_catch.SetVerbose(true);
-  ExceptionState exception_state(
-      GetIsolate(), ExceptionState::kExecutionContext, interface, operation);
+  ExceptionState exception_state(GetIsolate(),
+                                 ExceptionState::kExecutionContext,
+                                 interface_name, operation_name);
   exception_state.ThrowSecurityError(
       "An invocation of the provided callback failed due to cross origin "
       "access.");
@@ -57,15 +58,16 @@
 }
 
 ScriptState* CallbackInterfaceBase::CallbackRelevantScriptStateOrThrowException(
-    const char* interface,
-    const char* operation) {
+    const char* interface_name,
+    const char* operation_name) {
   if (callback_relevant_script_state_)
     return callback_relevant_script_state_;
 
   // Throw a SecurityError due to a cross origin callback object.
   ScriptState::Scope incumbent_scope(incumbent_script_state_);
-  ExceptionState exception_state(
-      GetIsolate(), ExceptionState::kExecutionContext, interface, operation);
+  ExceptionState exception_state(GetIsolate(),
+                                 ExceptionState::kExecutionContext,
+                                 interface_name, operation_name);
   exception_state.ThrowSecurityError(
       "An invocation of the provided callback failed due to cross origin "
       "access.");
diff --git a/third_party/blink/renderer/platform/bindings/callback_interface_base.h b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
index 6c30323..e32054f 100644
--- a/third_party/blink/renderer/platform/bindings/callback_interface_base.h
+++ b/third_party/blink/renderer/platform/bindings/callback_interface_base.h
@@ -63,15 +63,16 @@
   // Returns the ScriptState of the relevant realm of the callback object iff
   // the callback is the same origin-domain. Otherwise, reports an error and
   // returns nullptr.
-  ScriptState* CallbackRelevantScriptStateOrReportError(const char* interface,
-                                                        const char* operation);
+  ScriptState* CallbackRelevantScriptStateOrReportError(
+      const char* interface_name,
+      const char* operation_name);
 
   // Returns the ScriptState of the relevant realm of the callback object iff
   // the callback is the same origin-domain. Otherwise, throws an exception and
   // returns nullptr.
   ScriptState* CallbackRelevantScriptStateOrThrowException(
-      const char* interface,
-      const char* operation);
+      const char* interface_name,
+      const char* operation_name);
 
   DOMWrapperWorld& GetWorld() const { return incumbent_script_state_->World(); }
 
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
index d76dc22..43cf2969 100644
--- a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
@@ -99,12 +99,9 @@
                        uint32_t* rgba_dest) {
   *rgba_dest = SkPackARGB32NoCheck(
       alpha,
-      gfx::ToRoundedInt(base::ClampToRange(pixel.x(), 0.0f, 1.0f) *
-                        max_channel),
-      gfx::ToRoundedInt(base::ClampToRange(pixel.y(), 0.0f, 1.0f) *
-                        max_channel),
-      gfx::ToRoundedInt(base::ClampToRange(pixel.z(), 0.0f, 1.0f) *
-                        max_channel));
+      gfx::ToRoundedInt(base::ClampToRange(pixel.x(), 0.0f, 1.0f) * 255.0f),
+      gfx::ToRoundedInt(base::ClampToRange(pixel.y(), 0.0f, 1.0f) * 255.0f),
+      gfx::ToRoundedInt(base::ClampToRange(pixel.z(), 0.0f, 1.0f) * 255.0f));
 }
 
 inline void WritePixel(float max_channel,
@@ -437,10 +434,9 @@
   }
 
   uint32_t* rgba_8888 = buffer->GetAddr(0, 0);
-  if (ImageIsHighBitDepth() ||
-      // TODO(wtc): Figure out a way to check frame_cs == ~BT.2020 too since
-      // ConvertVideoFrameToRGBPixels() can handle that too.
-      frame_cs == gfx::ColorSpace::CreateREC709() ||
+  // TODO(wtc): Figure out a way to check frame_cs == ~BT.2020 too since
+  // ConvertVideoFrameToRGBPixels() can handle that too.
+  if (frame_cs == gfx::ColorSpace::CreateREC709() ||
       frame_cs == gfx::ColorSpace::CreateREC601() ||
       frame_cs == gfx::ColorSpace::CreateJpeg()) {
     // Create temporary frame wrapping the YUVA planes.
@@ -488,12 +484,22 @@
     DVLOG(1) << "Failed to update color transform...";
     return false;
   }
-  if (is_mono) {
-    YUVAToRGBA<ColorType::kMono, uint8_t>(image, color_transform_.get(),
-                                          rgba_8888);
+  if (ImageIsHighBitDepth()) {
+    if (is_mono) {
+      YUVAToRGBA<ColorType::kMono, uint16_t>(image, color_transform_.get(),
+                                             rgba_8888);
+    } else {
+      YUVAToRGBA<ColorType::kColor, uint16_t>(image, color_transform_.get(),
+                                              rgba_8888);
+    }
   } else {
-    YUVAToRGBA<ColorType::kColor, uint8_t>(image, color_transform_.get(),
-                                           rgba_8888);
+    if (is_mono) {
+      YUVAToRGBA<ColorType::kMono, uint8_t>(image, color_transform_.get(),
+                                            rgba_8888);
+    } else {
+      YUVAToRGBA<ColorType::kColor, uint8_t>(image, color_transform_.get(),
+                                             rgba_8888);
+    }
   }
   return true;
 }
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc
index b651427..a0a6e06 100644
--- a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder_test.cc
@@ -20,8 +20,6 @@
 #define FIXME_SUPPORT_ICC_PROFILE_NO_TRANSFORM 0
 #define FIXME_SUPPORT_ICC_PROFILE_TRANSFORM 0
 #define FIXME_DISTINGUISH_LOSSY_OR_LOSSLESS 0
-#define FIXME_SUPPORT_10BIT_FULL_RANGED_COLOR 0
-#define FIXME_SUPPORT_12BIT_FULL_RANGED_COLOR 0
 #define FIXME_CRASH_IF_COLOR_BEHAVIOR_IS_IGNORE 0
 
 namespace blink {
@@ -120,7 +118,7 @@
      ImageDecoder::kLosslessFormat,
      ImageDecoder::kAlphaNotPremultiplied,
      ColorBehavior::Tag(),
-     1,
+     0,
      {
          {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)},
          {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)},
@@ -241,20 +239,18 @@
      }},
 #endif
 #endif
-#if FIXME_SUPPORT_10BIT_FULL_RANGED_COLOR
     {"/images/resources/avif/red-full-ranged-10bpc.avif",
      10,
      ColorType::kRgb,
      ImageDecoder::kLosslessFormat,
      ImageDecoder::kAlphaNotPremultiplied,
      ColorBehavior::Tag(),
-     1,
+     0,
      {
          {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)},
          {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)},
          {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
      }},
-#endif
     {"/images/resources/avif/alpha-mask-limited-ranged-10bpc.avif",
      10,
      ColorType::kMono,
@@ -354,20 +350,18 @@
      }},
 #endif
 #endif
-#if FIXME_SUPPORT_12BIT_FULL_RANGED_COLOR
     {"/images/resources/avif/red-full-ranged-12bpc.avif",
      12,
      ColorType::kRgb,
      ImageDecoder::kLosslessFormat,
      ImageDecoder::kAlphaNotPremultiplied,
      ColorBehavior::Tag(),
-     1,
+     0,
      {
          {gfx::Point(0, 0), SkColorSetARGB(255, 255, 0, 0)},
          {gfx::Point(1, 1), SkColorSetARGB(255, 255, 0, 0)},
          {gfx::Point(2, 2), SkColorSetARGB(255, 255, 0, 0)},
      }},
-#endif
     {"/images/resources/avif/alpha-mask-limited-ranged-12bpc.avif",
      12,
      ColorType::kMono,
diff --git a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
index ea35331..1be8c54 100644
--- a/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
+++ b/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
@@ -54,6 +54,7 @@
 
 enum class ResourceType : uint8_t;
 class ClientHintsPreferences;
+class FeaturePolicy;
 class KURL;
 class ResourceTimingInfo;
 class WebScopedVirtualTimePauser;
@@ -142,6 +143,8 @@
     return MakeGarbageCollected<FetchContext>();
   }
 
+  virtual const FeaturePolicy* GetFeaturePolicy() const { return nullptr; }
+
   // Determine if the request is on behalf of an advertisement. If so, return
   // true.
   virtual bool CalculateIfAdSubresource(
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index faa244fc..cd2ec6c9 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -41,6 +41,7 @@
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/common/client_hints/client_hints.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
@@ -705,8 +706,13 @@
     network::mojom::ReferrerPolicy new_referrer_policy,
     const WebString& new_method,
     const WebURLResponse& passed_redirect_response,
-    bool& report_raw_headers) {
+    bool& report_raw_headers,
+    std::vector<std::string>* removed_headers) {
   DCHECK(!passed_redirect_response.IsNull());
+  if (removed_headers) {
+    FindClientHintsToRemove(Context().GetFeaturePolicy(),
+                            GURL(new_url.GetString().Utf8()), removed_headers);
+  }
 
   if (is_cache_aware_loading_activated_) {
     // Fail as cache miss if cached response is a redirect.
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
index ba73d21..2ecfb827 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
@@ -120,7 +120,8 @@
                           network::mojom::ReferrerPolicy new_referrer_policy,
                           const WebString& new_method,
                           const WebURLResponse& passed_redirect_response,
-                          bool& report_raw_headers) override;
+                          bool& report_raw_headers,
+                          std::vector<std::string>* removed_headers) override;
   void DidSendData(uint64_t bytes_sent,
                    uint64_t total_bytes_to_be_sent) override;
   void DidReceiveResponse(const WebURLResponse&) override;
diff --git a/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h b/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h
index d67fc2b..4a22d995 100644
--- a/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h
+++ b/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set.h
@@ -10,6 +10,7 @@
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "third_party/blink/renderer/platform/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 
 namespace blink {
 
@@ -20,7 +21,9 @@
 // context as a mandatory parameter. HeapMojoAssociatedReceiverSet resets the
 // mojo connection when 1) the owner object is garbage-collected or 2) the
 // associated ExecutionContext is detached.
-template <typename Interface, typename Owner>
+template <typename Interface,
+          typename Owner,
+          HeapMojoWrapperMode Mode = HeapMojoWrapperMode::kWithContextObserver>
 class HeapMojoAssociatedReceiverSet {
   DISALLOW_NEW();
 
@@ -91,7 +94,10 @@
     Owner* owner() { return owner_; }
 
     // ContextLifecycleObserver methods
-    void ContextDestroyed() override { associated_receiver_set_.Clear(); }
+    void ContextDestroyed() override {
+      if (Mode == HeapMojoWrapperMode::kWithContextObserver)
+        associated_receiver_set_.Clear();
+    }
 
    private:
     Member<Owner> owner_;
diff --git a/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc b/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc
index 11c2274..8068630 100644
--- a/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc
+++ b/third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver_set_test.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/platform/heap/heap_test_utilities.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/heap_observer_list.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 
 namespace blink {
 
@@ -53,15 +54,17 @@
   HeapObserverList<ContextLifecycleObserver> observers_;
 };
 
+template <HeapMojoWrapperMode Mode>
 class GCOwner;
 
+template <HeapMojoWrapperMode Mode>
 class HeapMojoAssociatedReceiverSetGCBaseTest : public TestSupportingGC {
  public:
   FakeContextNotifier* context() { return context_; }
   scoped_refptr<base::NullTaskRunner> task_runner() {
     return null_task_runner_;
   }
-  GCOwner* owner() { return owner_; }
+  GCOwner<Mode>* owner() { return owner_; }
   void set_is_owner_alive(bool alive) { is_owner_alive_ = alive; }
 
   void ClearOwner() { owner_ = nullptr; }
@@ -69,7 +72,7 @@
  protected:
   void SetUp() override {
     context_ = MakeGarbageCollected<FakeContextNotifier>();
-    owner_ = MakeGarbageCollected<GCOwner>(context(), this);
+    owner_ = MakeGarbageCollected<GCOwner<Mode>>(context(), this);
   }
   void TearDown() override {
     owner_ = nullptr;
@@ -77,24 +80,25 @@
   }
 
   Persistent<FakeContextNotifier> context_;
-  Persistent<GCOwner> owner_;
+  Persistent<GCOwner<Mode>> owner_;
   bool is_owner_alive_ = false;
   scoped_refptr<base::NullTaskRunner> null_task_runner_ =
       base::MakeRefCounted<base::NullTaskRunner>();
 };
 
-class GCOwner : public GarbageCollected<GCOwner>,
+template <HeapMojoWrapperMode Mode>
+class GCOwner : public GarbageCollected<GCOwner<Mode>>,
                 public sample::blink::Service {
  public:
   explicit GCOwner(FakeContextNotifier* context,
-                   HeapMojoAssociatedReceiverSetGCBaseTest* test)
+                   HeapMojoAssociatedReceiverSetGCBaseTest<Mode>* test)
       : associated_receiver_set_(this, context), test_(test) {
     test_->set_is_owner_alive(true);
   }
   void Dispose() { test_->set_is_owner_alive(false); }
   void Trace(Visitor* visitor) { visitor->Trace(associated_receiver_set_); }
 
-  HeapMojoAssociatedReceiverSet<sample::blink::Service, GCOwner>&
+  HeapMojoAssociatedReceiverSet<sample::blink::Service, GCOwner, Mode>&
   associated_receiver_set() {
     return associated_receiver_set_;
   }
@@ -106,22 +110,31 @@
   void GetPort(mojo::PendingReceiver<sample::blink::Port> receiver) override {}
 
  private:
-  HeapMojoAssociatedReceiverSet<sample::blink::Service, GCOwner>
+  HeapMojoAssociatedReceiverSet<sample::blink::Service, GCOwner, Mode>
       associated_receiver_set_;
-  HeapMojoAssociatedReceiverSetGCBaseTest* test_;
+  HeapMojoAssociatedReceiverSetGCBaseTest<Mode>* test_;
 };
 
 }  // namespace
 
+class HeapMojoAssociatedReceiverSetGCWithContextObserverTest
+    : public HeapMojoAssociatedReceiverSetGCBaseTest<
+          HeapMojoWrapperMode::kWithContextObserver> {};
+class HeapMojoAssociatedReceiverSetGCWithoutContextObserverTest
+    : public HeapMojoAssociatedReceiverSetGCBaseTest<
+          HeapMojoWrapperMode::kWithoutContextObserver> {};
+
 // Remove() a PendingAssociatedReceiver from HeapMojoAssociatedReceiverSet and
 // verify that the receiver is no longer part of the set.
-TEST_F(HeapMojoAssociatedReceiverSetGCBaseTest, RemovesReceiver) {
+TEST_F(HeapMojoAssociatedReceiverSetGCWithContextObserverTest,
+       RemovesReceiver) {
   auto& associated_receiver_set = owner()->associated_receiver_set();
-  mojo::AssociatedRemote<sample::blink::Service> remote;
-  auto receiver = remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+  mojo::AssociatedRemote<sample::blink::Service> associated_remote;
+  auto associated_receiver =
+      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
 
-  mojo::ReceiverId rid =
-      associated_receiver_set.Add(std::move(receiver), task_runner());
+  mojo::ReceiverId rid = associated_receiver_set.Add(
+      std::move(associated_receiver), task_runner());
   EXPECT_TRUE(associated_receiver_set.HasReceiver(rid));
 
   associated_receiver_set.Remove(rid);
@@ -129,14 +142,73 @@
   EXPECT_FALSE(associated_receiver_set.HasReceiver(rid));
 }
 
-// Clear() a HeapMojoAssociatedReceiverSet and verify that it is empty.
-TEST_F(HeapMojoAssociatedReceiverSetGCBaseTest, ClearLeavesSetEmpty) {
+// Same, without ContextObserver.
+TEST_F(HeapMojoAssociatedReceiverSetGCWithoutContextObserverTest,
+       RemovesReceiver) {
   auto& associated_receiver_set = owner()->associated_receiver_set();
-  mojo::AssociatedRemote<sample::blink::Service> remote;
-  auto receiver = remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+  mojo::AssociatedRemote<sample::blink::Service> associated_remote;
+  auto associated_receiver =
+      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
 
-  mojo::ReceiverId rid =
-      associated_receiver_set.Add(std::move(receiver), task_runner());
+  mojo::ReceiverId rid = associated_receiver_set.Add(
+      std::move(associated_receiver), task_runner());
+  EXPECT_TRUE(associated_receiver_set.HasReceiver(rid));
+
+  associated_receiver_set.Remove(rid);
+
+  EXPECT_FALSE(associated_receiver_set.HasReceiver(rid));
+}
+
+// Check that the wrapper does not outlive the owner when ConservativeGC finds
+// the wrapper.
+TEST_F(HeapMojoAssociatedReceiverSetGCWithContextObserverTest,
+       NoClearOnConservativeGC) {
+  auto* wrapper = owner_->associated_receiver_set().wrapper_.Get();
+
+  mojo::AssociatedRemote<sample::blink::Service> associated_remote;
+  auto associated_receiver =
+      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+
+  mojo::ReceiverId rid = owner()->associated_receiver_set().Add(
+      std::move(associated_receiver), task_runner());
+  EXPECT_TRUE(wrapper->associated_receiver_set().HasReceiver(rid));
+
+  ClearOwner();
+  EXPECT_TRUE(is_owner_alive_);
+
+  ConservativelyCollectGarbage();
+
+  EXPECT_TRUE(wrapper->associated_receiver_set().HasReceiver(rid));
+  EXPECT_TRUE(is_owner_alive_);
+}
+
+// Clear() a HeapMojoAssociatedReceiverSet and verify that it is empty.
+TEST_F(HeapMojoAssociatedReceiverSetGCWithContextObserverTest,
+       ClearLeavesSetEmpty) {
+  auto& associated_receiver_set = owner()->associated_receiver_set();
+  mojo::AssociatedRemote<sample::blink::Service> associated_remote;
+  auto associated_receiver =
+      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+
+  mojo::ReceiverId rid = associated_receiver_set.Add(
+      std::move(associated_receiver), task_runner());
+  EXPECT_TRUE(associated_receiver_set.HasReceiver(rid));
+
+  associated_receiver_set.Clear();
+
+  EXPECT_FALSE(associated_receiver_set.HasReceiver(rid));
+}
+
+// Same, without ContextObserver.
+TEST_F(HeapMojoAssociatedReceiverSetGCWithoutContextObserverTest,
+       ClearLeavesSetEmpty) {
+  auto& associated_receiver_set = owner()->associated_receiver_set();
+  mojo::AssociatedRemote<sample::blink::Service> associated_remote;
+  auto associated_receiver =
+      associated_remote.BindNewEndpointAndPassDedicatedReceiverForTesting();
+
+  mojo::ReceiverId rid = associated_receiver_set.Add(
+      std::move(associated_receiver), task_runner());
   EXPECT_TRUE(associated_receiver_set.HasReceiver(rid));
 
   associated_receiver_set.Clear();
diff --git a/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc b/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc
index a4bf13ce..c6258f6 100644
--- a/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc
+++ b/third_party/blink/renderer/platform/testing/weburl_loader_mock.cc
@@ -82,7 +82,7 @@
   bool follow = client_->WillFollowRedirect(
       redirect_url, net::SiteForCookies::FromUrl(redirect_url), WebString(),
       network::mojom::ReferrerPolicy::kDefault, method, redirect_response,
-      report_raw_headers);
+      report_raw_headers, nullptr /* removed_headers */);
   // |this| might be deleted in willFollowRedirect().
   if (!self)
     return redirect_url;
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
index 0dc0ada9..de86b04 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
@@ -165,9 +165,6 @@
     return cc::InputHandlerPointerResult();
   }
 
-  MOCK_CONST_METHOD1(IsCurrentlyScrollingLayerAt,
-                     bool(const gfx::Point& point));
-
   MOCK_CONST_METHOD1(
       GetEventListenerProperties,
       cc::EventListenerProperties(cc::EventListenerClass event_class));
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 12705b9..d904fde 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -71,11 +71,106 @@
 crbug.com/887140 virtual/hdr/color-profile-video.html [ Failure ]
 crbug.com/887140 virtual/hdr/video-canvas-alpha.html [ Failure ]
 
-crbug.com/1014421 external/wpt/css/cssom-view/MediaQueryList-addListener-handleEvent.html [ Timeout ]
+crbug.com/1014421 crbug.com/1075614 external/wpt/css/cssom-view/MediaQueryList-addListener-handleEvent.html [ Pass Failure Timeout ]
 
 # Tested by paint/background/root-element-background-transparency.html for now.
 external/wpt/css/compositing/root-element-background-transparency.html [ Failure ]
 
+# Now that the accessibility serializer is turned on for all web tests, it is
+# causing new failures, crashes and timeouts.
+# These failing tests are tested by virtual/disable-ax-serializer for now.
+# See https://crrev.com/c/2181915/ for more info.
+# TODO(aleventhal) Burn down this list.
+crbug.com/1075614 virtual/disable-ax-serializer/* [ Pass ]
+crbug.com/1045672 crbug.com/1006759 virtual/disable-ax-serializer/fast/forms/select/listbox-tap.html [ Failure ]
+crbug.com/1075614 accessibility/aom-click-action.html [ Crash Timeout ]
+crbug.com/1075614 accessibility/aom-virtual.html [ Crash Timeout ]
+crbug.com/1075614 accessibility/clickable.html [ Pass Failure ]
+crbug.com/1075614 external/wpt/css/css-text/text-transform/text-transform-upperlower-041.html [ Pass Failure Crash ]
+crbug.com/1075614 external/wpt/css/css-text/text-transform/text-transform-upperlower-043.html [ Pass Failure Crash ]
+crbug.com/1075614 external/wpt/css/css-text/text-transform/text-transform-upperlower-044.html [ Pass Failure Crash ]
+crbug.com/1075614 external/wpt/css/cssom-view/MediaQueryList-addListener-removeListener.html [ Pass Failure ]
+crbug.com/1075614 external/wpt/css/cssom-view/MediaQueryList-extends-EventTarget.html [ Pass Failure ]
+crbug.com/1075614 external/wpt/css/cssom-view/MediaQueryList-extends-EventTarget-interop.html [ Pass Failure ]
+crbug.com/1075614 external/wpt/css/cssom-view/MediaQueryListEvent.html [ Pass Failure ]
+crbug.com/1075614 external/wpt/dom/ranges/Range-mutations-replaceData.html [ Crash ]
+crbug.com/1075614 external/wpt/html/user-activation/consumption-crossorigin.sub.tentative.html [ Pass Timeout ]
+crbug.com/1075614 external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement.html [ Failure ]
+crbug.com/1075614 fast/forms/select_detached_textarea_crash.html [ Crash ]
+crbug.com/1075614 fast/forms/time-multiple-fields/time-multiple-fields-losing-renderer-on-click.html [ Pass Crash ]
+crbug.com/1075614 http/tests/devtools/console/console-context-selector.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/console/console-error-on-call-frame.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/debugger/fetch-breakpoints.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/elements/accessibility/autocomplete-attribute.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/elements/accessibility/edit-aria-attributes.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-async/async-await/async-pause-on-exception.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-async/async-callstack.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-async/async-callstack-in-console.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-navigation.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-breakpoints/debugger-breakpoints-not-activated-on-reload.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-breakpoints/dom-breakpoints-reload.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-breakpoints/dom-breakpoints.js [ Pass Failure Crash Timeout ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-breakpoints/provisional-breakpoints.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-breakpoints/restore-locations-for-breakpoint-with-broken-source-map.js [ Pass Failure Crash ]
+crbug.com/1075614 [ Release ] http/tests/devtools/sources/debugger-breakpoints/set-breakpoint.js [ Pass Failure Timeout ]
+crbug.com/1075614 [ Release ] http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js [ Pass Failure Timeout ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-breakpoints/set-logpoint.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-console/debug-console-command.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-frameworks/frameworks-dom-xhr-event-breakpoints.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-frameworks/frameworks-skip-step-in.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-frameworks/frameworks-sourcemap.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-frameworks/frameworks-step-into-skips-setTimeout.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-frameworks/frameworks-steppings.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-pause/debugger-pause-infinite-loop.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-pause/debugger-pause-on-promise-rejection.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-pause/pause-in-internal-script.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-step/debugger-step-into-custom-element-callbacks.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-step/debugger-step-into-event-listener.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-step/debugger-step-out-custom-element-callbacks.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-step/debugger-step-out-event-listener.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-step/debugger-step-through-promises.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-ui/custom-element-lifecycle-events.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-ui/debugger-inline-values.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-ui/debugger-inline-values-frames.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-ui/reveal-not-skipped.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger/debugger-proto-property.js [ Pass Failure Crash Timeout ]
+crbug.com/1075614 http/tests/devtools/sources/debugger/live-edit.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger/mutation-observer-suspend-while-paused.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger/source-frame-breakpoint-decorations.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/devtools/sources/debugger/source-frame-inline-breakpoint-decorations.js [ Pass Failure Crash ]
+crbug.com/1075614 http/tests/inspector-protocol/side-effects/evaluate-embedder-side-effect-free-attributes.js [ Timeout ]
+crbug.com/1075614 http/tests/misc/object-image-error.html [ Pass Failure Crash ]
+crbug.com/1075614 inspector-protocol/runtime/runtime-console-special-functions.js [ Skip ]
+crbug.com/1075614 plugins/focus.html [ Failure ]
+crbug.com/1075614 storage/indexeddb/cursor-continue-validity.html [ Pass Failure Timeout ]
+crbug.com/1075614 [ Release ] storage/indexeddb/index-cursor.html [ Pass Failure Timeout ]
+crbug.com/1075614 storage/indexeddb/mozilla/indexes.html [ Pass Failure Timeout ]
+crbug.com/1075614 storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Pass Failure Timeout ]
+crbug.com/1075614 storage/indexeddb/objectstore-cursor.html [ Pass Failure Timeout ]
+crbug.com/1075614 storage/indexeddb/mozilla/cursors.html [ Pass Failure Timeout ]
+crbug.com/1075614 storage/indexeddb/objectstore-keycursor.html [ Pass Failure Timeout ]
+crbug.com/1075614 virtual/controls-refresh/calendar-picker/date-picker-ax.html [ Failure ]
+crbug.com/1075614 virtual/disable-ax-serializer/http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-shifted-breakpoint.js [ Pass Failure Timeout ]
+crbug.com/1075614 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-basic-movement.html [ Timeout Failure ]
+crbug.com/1075614 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-dont-send-keyboard-events.html [ Timeout Failure ]
+crbug.com/1075614 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-enter-exit-focus.html [ Timeout Failure ]
+# crbug.com/1075614 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-enter-from-interest.html [ Timeout Failure ]
+crbug.com/1075614 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-does-not-submit.html [ Timeout Failure ]
+crbug.com/1075614 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-form-state-on-click.html [ Timeout Failure ]
+crbug.com/1075614 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-navigate-from-focus.html [ Timeout Failure ]
+crbug.com/1075614 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-video-on-click-a11y.html [ Timeout Failure ]
+crbug.com/1075614 virtual/text-antialias/text-transform-lower-turkic.html [ Crash ]
+crbug.com/1075614 virtual/text-antialias/text-transform-upper-lithuanian.html [ Crash ]
+crbug.com/1075614 wpt_internal/display-lock/css-content-visibility/content-visibility-026.html [ Failure ]
+crbug.com/1075614 virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/calendar-picker-mouse-operations.html [ Pass Timeout Crash ]
+crbug.com/1075614 virtual/form-controls-refresh-disabled/fast/forms/calendar-picker/week-picker-mouse-operations.html [ Skip ]
+# The following line causes a redundant warning in presubmit, but is necessary to avoid test breakage:
+crbug.com/1075614 virtual/not-site-per-process/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Pass Timeout ]
+crbug.com/1075614 virtual/scalefactor200/css3/filters/backdrop-filter-svg.html [ Skip ]
+crbug.com/1075614 virtual/web-components-v0-disabled/external/wpt/dom/ranges/Range-mutations-replaceData.html [ Skip ]
+crbug.com/963183 crbug.com/1075614 virtual/disable-ax-serializer/http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints.js [ Pass Failure ]
+
 # ====== Site Isolation failures from here ======
 # See also third_party/blink/web_tests/virtual/not-site-per-process/README.md
 #
@@ -264,7 +359,7 @@
 crbug.com/968791 crbug.com/1051044 virtual/scalefactor200/external/wpt/css/filter-effects/effect-reference-feimage-001.html [ Failure ]
 crbug.com/968791 crbug.com/1051044 virtual/scalefactor200/external/wpt/css/filter-effects/effect-reference-feimage-002.html [ Failure ]
 crbug.com/968791 crbug.com/1051044 virtual/scalefactor200/external/wpt/css/filter-effects/effect-reference-feimage-003.html [ Failure ]
-crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/filters-test-brightness-003.html [ Failure ]
+crbug.com/968791 virtual/scalefactor200/external/wpt/css/filter-effects/filters-test-brightness-003.html [ Pass Failure ]
 
 # Copying these from elsewhere in TestExpectations (for non-scalefactor200 virtual suite)
 crbug.com/591099 virtual/scalefactor200/external/wpt/css/filter-effects/filtered-inline-applies-to-float.html [ Failure ]
@@ -1466,7 +1561,7 @@
 crbug.com/1035582 [ Mac10.10 ] virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/select/select-multiple-appearance-basic.html [ Skip ]
 
 # Bug in FormControlsRefresh <select multiple> tap behavior:
-crbug.com/1045672 fast/forms/select/listbox-tap.html [ Failure ]
+crbug.com/1045672 fast/forms/select/listbox-tap.html [ Pass Failure ]
 
 ### sheriff 2019-07-16
 crbug.com/983799 [ Win ] http/tests/navigation/redirect-on-back-updates-history-item.html [ Timeout Pass ]
@@ -1800,7 +1895,7 @@
 crbug.com/1049641 [ Debug ] http/tests/devtools/sources/debugger/js-with-inline-stylesheets.js [ Pass Failure ]
 crbug.com/1049641 [ Debug ] http/tests/devtools/sources/debugger-breakpoints/set-breakpoint.js [ Pass Failure ]
 crbug.com/1049641 [ Debug ] http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js [ Pass Failure ]
-crbug.com/1049641 http/tests/devtools/sources/debugger/debugger-disable-enable.js [ Pass Timeout ]
+crbug.com/1049641 crbug.com/1075614 http/tests/devtools/sources/debugger/debugger-disable-enable.js [ Pass Timeout Crash ]
 
 # Sheriff 2019-08-19
 crbug.com/626703 [ Win7 ] external/wpt/xhr/responsexml-document-properties.htm [ Failure ]
@@ -1880,7 +1975,7 @@
 crbug.com/400829 virtual/stable/media/stable/video-object-fit-stable.html [ Failure ]
 
 # Source map asyncification requires some 3-way changes with the DevTools frontend.
-crbug.com/1032016 http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-shifted-breakpoint.js [ Pass Failure Timeout ]
+crbug.com/1032016 crbug.com/1075614 http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-shifted-breakpoint.js [ Pass Failure Timeout Crash ]
 
 # Renaming front_end/protocol to front_end/protocol_client requires a 3-way change. Temporarliy disable web tests that use |Protocol|
 crbug.com/1011811 http/tests/devtools/inspector-backend-commands.js [ Pass Failure ]
@@ -3057,7 +3152,7 @@
 crbug.com/626703 external/wpt/xhr/event-readystatechange-loaded.any.html [ Timeout Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-005.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/image-min-max-content-intrinsic-size-change-008.html [ Failure ]
-crbug.com/626703 external/wpt/css/css-animations/animationevent-marker-pseudoelement.html [ Timeout ]
+crbug.com/626703 external/wpt/css/css-animations/animationevent-marker-pseudoelement.html [ Pass Timeout ]
 crbug.com/626703 external/wpt/css/css-text/overflow-wrap/overflow-wrap-normal-keep-all-001.html [ Failure ]
 crbug.com/626703 external/wpt/html/semantics/embedded-content/media-elements/src_object_blob.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-lists/list-item-definition.html [ Failure ]
@@ -4080,7 +4175,7 @@
 
 # snav tests fail because of a DCHECK
 crbug.com/985520 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-enter-from-interest-a11y.html [ Crash ]
-crbug.com/985520 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-enter-from-interest.html [ Crash ]
+crbug.com/985520 virtual/focusless-spat-nav/fast/spatial-navigation/focusless/snav-focusless-enter-from-interest.html [ Crash Failure ]
 
 # User-Agent is not able to be set on XHR/fetch
 crbug.com/571722 external/wpt/xhr/preserve-ua-header-on-redirect.htm [ Failure ]
@@ -5515,7 +5610,7 @@
 crbug.com/1040270 http/tests/devtools/elements/styles-1/disable-property-workingcopy-update.js [ Pass Timeout ]
 
 # Broken in https://chromium-review.googlesource.com/c/chromium/src/+/1636716
-crbug.com/963183 http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints.js [ Pass Failure Timeout ]
+crbug.com/963183 crbug.com/1075614 http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints.js [ Pass Failure Timeout Crash ]
 
 crbug.com/836300 fast/css3-text/css3-text-decoration/text-decoration-skip-ink-links.html [ Pass Failure ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 88741e3..8e7640b 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -500,6 +500,7 @@
     "prefix": "focusless-spat-nav",
     "bases": [],
     "args": ["--enable-blink-features=FocuslessSpatialNavigation",
+             "--disable-ax-serializer",
              "--enable-spatial-navigation"]
   },
   {
@@ -761,5 +762,84 @@
     "prefix": "media-devtools",
     "bases": [ "inspector-protocol/media" ],
     "args": ["--enable-features=MediaInspectorLogging"]
+  },
+  {
+    "prefix": "disable-ax-serializer",
+    "bases": [
+      "accessibility/aom-click-action.html",
+      "accessibility/aom-virtual.html",
+      "accessibility/clickable.html",
+      "external/wpt/css/css-text/text-transform/text-transform-upperlower-041.html",
+      "external/wpt/css/css-text/text-transform/text-transform-upperlower-043.html",
+      "external/wpt/css/css-text/text-transform/text-transform-upperlower-044.html",
+      "external/wpt/css/cssom-view/MediaQueryList-addListener-removeListener.html",
+      "external/wpt/css/cssom-view/MediaQueryList-extends-EventTarget-interop.html",
+      "external/wpt/css/cssom-view/MediaQueryList-extends-EventTarget.html",
+      "external/wpt/css/cssom-view/MediaQueryListEvent.html",
+      "external/wpt/dom/ranges/Range-mutations-replaceData.html",
+      "external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement.html",
+      "fast/forms/select/listbox-tap.html",
+      "fast/forms/select_detached_textarea_crash.html",
+      "fast/forms/time-multiple-fields/time-multiple-fields-losing-renderer-on-click.html",
+      "http/tests/devtools/console/console-context-selector.js",
+      "http/tests/devtools/console/console-error-on-call-frame.js",
+      "http/tests/devtools/debugger/fetch-breakpoints.js",
+      "http/tests/devtools/elements/accessibility/autocomplete-attribute.js",
+      "http/tests/devtools/elements/accessibility/edit-aria-attributes.js",
+      "http/tests/devtools/sources/debugger-async/async-await/async-pause-on-exception.js",
+      "http/tests/devtools/sources/debugger-async/async-callstack-in-console.js",
+      "http/tests/devtools/sources/debugger-async/async-callstack.js",
+      "http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-navigation.js",
+      "http/tests/devtools/sources/debugger-breakpoints/breakpoints-ui-shifted-breakpoint.js",
+      "http/tests/devtools/sources/debugger-breakpoints/debugger-breakpoints-not-activated-on-reload.js",
+      "http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints.js",
+      "http/tests/devtools/sources/debugger-breakpoints/dom-breakpoints-reload.js",
+      "http/tests/devtools/sources/debugger-breakpoints/dom-breakpoints.js",
+      "http/tests/devtools/sources/debugger-breakpoints/provisional-breakpoints.js",
+      "http/tests/devtools/sources/debugger-breakpoints/restore-locations-for-breakpoint-with-broken-source-map.js",
+      "http/tests/devtools/sources/debugger-breakpoints/set-breakpoint.js",
+      "http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js",
+      "http/tests/devtools/sources/debugger-breakpoints/set-logpoint.js",
+      "http/tests/devtools/sources/debugger-console/debug-console-command.js",
+      "http/tests/devtools/sources/debugger-frameworks/frameworks-dom-xhr-event-breakpoints.js",
+      "http/tests/devtools/sources/debugger-frameworks/frameworks-skip-step-in.js",
+      "http/tests/devtools/sources/debugger-frameworks/frameworks-sourcemap.js",
+      "http/tests/devtools/sources/debugger-frameworks/frameworks-step-into-skips-setTimeout.js",
+      "http/tests/devtools/sources/debugger-frameworks/frameworks-steppings.js",
+      "http/tests/devtools/sources/debugger-pause/debugger-pause-infinite-loop.js",
+      "http/tests/devtools/sources/debugger-pause/debugger-pause-on-promise-rejection.js",
+      "http/tests/devtools/sources/debugger-pause/pause-in-internal-script.js",
+      "http/tests/devtools/sources/debugger-step/debugger-step-into-custom-element-callbacks.js",
+      "http/tests/devtools/sources/debugger-step/debugger-step-into-event-listener.js",
+      "http/tests/devtools/sources/debugger-step/debugger-step-out-custom-element-callbacks.js",
+      "http/tests/devtools/sources/debugger-step/debugger-step-out-event-listener.js",
+      "http/tests/devtools/sources/debugger-step/debugger-step-through-promises.js",
+      "http/tests/devtools/sources/debugger-ui/custom-element-lifecycle-events.js",
+      "http/tests/devtools/sources/debugger-ui/debugger-inline-values-frames.js",
+      "http/tests/devtools/sources/debugger-ui/debugger-inline-values.js",
+      "http/tests/devtools/sources/debugger-ui/reveal-not-skipped.js",
+      "http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js",
+      "http/tests/devtools/sources/debugger/debugger-disable-enable.js",
+      "http/tests/devtools/sources/debugger/debugger-proto-property.js",
+      "http/tests/devtools/sources/debugger/live-edit.js",
+      "http/tests/devtools/sources/debugger/mutation-observer-suspend-while-paused.js",
+      "http/tests/devtools/sources/debugger/source-frame-breakpoint-decorations.js",
+      "http/tests/devtools/sources/debugger/source-frame-inline-breakpoint-decorations.js",
+      "http/tests/inspector-protocol/side-effects/evaluate-embedder-side-effect-free-attributes.js",
+      "http/tests/misc/object-image-error.html",
+      "inspector-protocol/runtime/runtime-console-special-functions.js",
+      "plugins/focus.html",
+      "storage/indexeddb/cursor-continue-validity.html",
+      "storage/indexeddb/index-cursor.html",
+      "storage/indexeddb/mozilla/indexes.html",
+      "storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html",
+      "storage/indexeddb/objectstore-cursor.html",
+      "storage/indexeddb/objectstore-keycursor.html",
+      "virtual/controls-refresh/calendar-picker/date-picker-ax.html",
+      "virtual/text-antialias/text-transform-lower-turkic.html",
+      "virtual/text-antialias/text-transform-upper-lithuanian.html",
+      "wpt_internal/display-lock/css-content-visibility/content-visibility-026.html"
+    ],
+    "args": ["--disable-ax-serializer-for-testing"]
   }
 ]
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-iframe-redirect-with-fp-delegation.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-iframe-redirect-with-fp-delegation.https.html
new file mode 100644
index 0000000..a2bc66f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-iframe-redirect-with-fp-delegation.https.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Accept-CH Persistence test</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+// Make sure that cross origin iframes that gets redirected, and has Feature Policy delegation maintain their Client Hints.
+const test_name = "Iframe redirect with Feature Policy delegation";
+verify_iframe_state("resources/accept-ch-and-redir.py?url=" + get_host_info()["HTTPS_REMOTE_ORIGIN"] + "/client-hints/accept-ch-stickiness/resources/expect-received.py", test_name);
+
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-iframe-redirect-with-fp-delegation.https.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-iframe-redirect-with-fp-delegation.https.html.headers
new file mode 100644
index 0000000..b2bc98d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-iframe-redirect-with-fp-delegation.https.html.headers
@@ -0,0 +1,2 @@
+Accept-CH: DPR, Device-Memory
+Feature-Policy: ch-dpr *; ch-device-memory *
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-navigation-redirect.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-navigation-redirect.https.html
new file mode 100644
index 0000000..14ba51a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-navigation-redirect.https.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Accept-CH Persistence test</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+// Make sure a cross origin navigation that gets redirected  doesn't keep the initial request's Client Hints.
+const test_name = "cross-origin redirect on navigation";
+verify_navigation_state("resources/accept-ch-and-redir.py?url=" + get_host_info()["HTTPS_REMOTE_ORIGIN"] + "/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py", test_name);
+
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-navigation-redirect.https.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-navigation-redirect.https.html.headers
new file mode 100644
index 0000000..e157e45
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-navigation-redirect.https.html.headers
@@ -0,0 +1 @@
+Accept-CH: DPR, Device-Memory
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect-with-fp-delegation.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect-with-fp-delegation.https.html
new file mode 100644
index 0000000..8e94bba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect-with-fp-delegation.https.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Accept-CH Persistence test</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+// Make sure a cross origin subresource that gets redirected with Feature Policy delegation keeps the initial request's Client Hints.
+const test_name = "cross-origin subresource redirect with Feature Policy delegaation";
+verify_subresource_state("resources/accept-ch-and-redir.py?url=" + get_host_info()["HTTPS_REMOTE_ORIGIN"] + "/client-hints/accept-ch-stickiness/resources/expect-received.py", test_name);
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect-with-fp-delegation.https.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect-with-fp-delegation.https.html.headers
new file mode 100644
index 0000000..5515b8a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect-with-fp-delegation.https.html.headers
@@ -0,0 +1,2 @@
+Accept-CH: DPR, Device-Memory
+Feature-Policy: ch-dpr *;ch-device-memory *
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect.https.html
new file mode 100644
index 0000000..31334543
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect.https.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Accept-CH Persistence test</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+// Make sure a cross origin subresource that gets redirected  doesn't keep the initial request's Client Hints.
+const test_name = "cross-origin subresource redirect";
+verify_subresource_state("resources/accept-ch-and-redir.py?url=" + get_host_info()["HTTPS_REMOTE_ORIGIN"] + "/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py", test_name);
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect.https.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect.https.html.headers
new file mode 100644
index 0000000..e157e45
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect.https.html.headers
@@ -0,0 +1 @@
+Accept-CH: DPR, Device-Memory
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-syncxhr-redirect.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-syncxhr-redirect.https.html
new file mode 100644
index 0000000..1cce664
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-syncxhr-redirect.https.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Accept-CH Persistence test</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+// Make sure a cross origin syn XHR that gets redirected doesn't keep the initial request's Client Hints.
+const test_name = "cross-origin sync XHR redirect";
+verify_syncxhr_state("resources/accept-ch-and-redir.py?url=" + get_host_info()["HTTPS_REMOTE_ORIGIN"] + "/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py", test_name);
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-syncxhr-redirect.https.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-syncxhr-redirect.https.html.headers
new file mode 100644
index 0000000..e157e45
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/cross-origin-syncxhr-redirect.https.html.headers
@@ -0,0 +1 @@
+Accept-CH: DPR, Device-Memory
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-and-redir-to-do-not-expect.py b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-and-redir-to-do-not-expect.py
deleted file mode 100644
index eaca03fe76..0000000
--- a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-and-redir-to-do-not-expect.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def main(request, response):
-    return 301, [('Location', 'do-not-expect-received.py'),('Accept-CH', 'device-memory, DPR')], ''
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-and-redir-to-expect.py b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-and-redir-to-expect.py
deleted file mode 100644
index 835ef396..0000000
--- a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-and-redir-to-expect.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def main(request, response):
-    return 301, [('Location', 'expect-received.py'),('Accept-CH', 'device-memory, DPR')], ''
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-and-redir.py b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-and-redir.py
new file mode 100644
index 0000000..706983c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-and-redir.py
@@ -0,0 +1,5 @@
+def main(request, response):
+    url = ''
+    if 'url' in request.GET:
+        url = request.GET['url']
+    return 301, [('Location', url),('Accept-CH', 'device-memory, DPR')], ''
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-test.js b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-test.js
index 87974570..a088436 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-test.js
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/accept-ch-test.js
@@ -21,6 +21,20 @@
     "hints preferences cached");
 }
 
+function verify_iframe_state(expect_url, test_name) {
+  promise_test(t => {
+    return new Promise(resolve => {
+      window.addEventListener('message', t.step_func(function(e) {
+        assert_equals(e.data, "PASS", "message from opened frame");
+        fetch("/client-hints/accept-ch-stickiness/resources/clear-site-data.html").then(resolve);
+      }));
+      const iframe = document.createElement("iframe");
+      iframe.src = expect_url;
+      document.body.appendChild(iframe);
+    });
+  }, test_name + " got client hints according to expectations.");
+}
+
 function verify_navigation_state(expect_url, test_name) {
   promise_test(t => {
     return new Promise(resolve => {
@@ -40,14 +54,27 @@
 function verify_subresource_state(expect_url, test_name) {
   promise_test(t => {
     return new Promise(resolve => {
-      let win;
-      window.addEventListener('message', t.step_func(function(e) {
-        win.close();
-        assert_equals(e.data, "PASS", "message from opened page");
+      fetch(expect_url).then(response => response.text()).then(t.step_func(text => {
+        assert_true(text.includes("PASS"));
         fetch("/client-hints/accept-ch-stickiness/resources/clear-site-data.html").then(resolve);
       }));
-      // Open expect_url as a subresource.
-      fetch(expect_url).then(resolve);
+    });
+  }, test_name + " got client hints according to expectations.");
+}
+
+function verify_syncxhr_state(expect_url, test_name) {
+  promise_test(t => {
+    return new Promise(resolve => {
+      const xhr = new XMLHttpRequest();
+      xhr.onreadystatechange = t.step_func(() => {
+        if (xhr.readyState != XMLHttpRequest.DONE) {
+          return;
+        }
+        assert_true(xhr.responseText.includes("PASS"));
+        fetch("/client-hints/accept-ch-stickiness/resources/clear-site-data.html").then(resolve);
+      });
+      xhr.open("GET", expect_url, false /* async */);
+      xhr.send();
     });
   }, test_name + " got client hints according to expectations.");
 }
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py
index 6b0e61a..ba647ed 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/do-not-expect-received.py
@@ -15,5 +15,5 @@
   window.opener.postMessage("%s" , "*");
 </script>
 ''' % (result)
-    headers = [("Content-Type", "text/html")]
+    headers = [("Content-Type", "text/html"), ("Access-Control-Allow-Origin", "*")]
     return 200, headers, content
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/expect-received.py b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/expect-received.py
index 5aab578..7a2874e 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/expect-received.py
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/resources/expect-received.py
@@ -12,8 +12,9 @@
 
     content = '''
 <script>
-  window.opener.postMessage("%s" , "*");
+  let messagee = window.opener || window.parent;
+  messagee.postMessage("%s" , "*");
 </script>
 ''' % (result)
-    headers = [("Content-Type", "text/html")]
+    headers = [("Content-Type", "text/html"), ("Access-Control-Allow-Origin", "*")]
     return 200, headers, content
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-navigation-redirect.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-navigation-redirect.https.html
index 2ab128f..69fc55e8 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-navigation-redirect.https.html
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-navigation-redirect.https.html
@@ -12,7 +12,7 @@
 // header here are a single step, connected via redirect.
 const test_name = "redirect on navigation";
 verify_initial_state(echo, test_name);
-verify_navigation_state("resources/accept-ch-and-redir-to-expect.py", test_name);
+verify_navigation_state("resources/accept-ch-and-redir.py?url=expect-received.py", test_name);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect-opted-in.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect-opted-in.https.html
new file mode 100644
index 0000000..66c0e57
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect-opted-in.https.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<title>Accept-CH Persistence test</title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/accept-ch-test.js"></script>
+
+<script>
+// Make sure a same origin subresource that gets redirected keeps the initial request's Client Hints.
+const test_name = "same-origin subresource redirect with opt-in";
+verify_subresource_state("resources/accept-ch-and-redir.py?url=expect-received.py", test_name);
+</script>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect-opted-in.https.html.headers b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect-opted-in.https.html.headers
new file mode 100644
index 0000000..e157e45
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect-opted-in.https.html.headers
@@ -0,0 +1 @@
+Accept-CH: DPR, Device-Memory
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect.https.html b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect.https.html
index 4a8233b..8e687b5 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect.https.html
+++ b/third_party/blink/web_tests/external/wpt/client-hints/accept-ch-stickiness/same-origin-subresource-redirect.https.html
@@ -8,11 +8,10 @@
 <script src="resources/accept-ch-test.js"></script>
 
 <script>
-// This is similar to accept-ch-test.js tests, except setting and checking
-// header here are a single step, connected via redirect.
-const test_name = "redirect on navigation";
+// Make sure a same origin subresource without an opt-in that gets redirected doesn't contain Client Hints.
+const test_name = "same-origin subresource redirect with no opt-in";
 verify_initial_state(echo, test_name);
-verify_subresource_state("resources/accept-ch-and-redir-to-do-not-expect.py", test_name);
+verify_subresource_state("resources/accept-ch-and-redir.py?url=do-not-expect-received.py", test_name);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/fullscreen-crash.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/fullscreen-crash.html
new file mode 100644
index 0000000..545f291
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/fullscreen-crash.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="test-wait">
+<link rel="help" href="https://drafts.csswg.org/css-scroll-anchoring/">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=823150">
+
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<style>
+
+#a { height: 700px; }
+#b { border: 4px solid #ccc; }
+
+</style>
+<div id="a"><div id="b"></div></div>
+<script>
+
+onload = () => {
+  test_driver.bless("requestFullscreen", step2);
+};
+step2 = () => {
+  b.requestFullscreen();
+  b.addEventListener('fullscreenchange', step3);
+};
+step3 = () => {
+  document.designMode = "on";
+  document.execCommand("selectAll");
+  document.execCommand("formatBlock", false, "p");
+  document.documentElement.classList.remove('test-wait');
+};
+
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/text-orientation-initial-ref.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/text-orientation-initial-ref.html
new file mode 100644
index 0000000..322dde3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/text-orientation-initial-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<div style="writing-mode:vertical-rl">
+  <div>
+    vertical
+  </div>
+</div>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/text-orientation-initial.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/text-orientation-initial.html
new file mode 100644
index 0000000..434e14c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/text-orientation-initial.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel=help href="https://drafts.csswg.org/css-writing-modes-4/#text-orientation">
+<link rel=help href="https://crbug.com/1081659">
+<link rel="match" href="text-orientation-initial-ref.html">
+<div style="writing-mode:vertical-rl;text-orientation:upright">
+  <div style="text-orientation:initial">
+    vertical
+  </div>
+</div>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/writing-mode-initial-ref.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/writing-mode-initial-ref.html
new file mode 100644
index 0000000..6f7b84f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/writing-mode-initial-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<div>
+  <div>
+    horizontal
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/writing-mode-initial.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/writing-mode-initial.html
new file mode 100644
index 0000000..2e7c9272
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/writing-mode-initial.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel=help href="https://drafts.csswg.org/css-writing-modes-4/#block-flow">
+<link rel=help href="https://crbug.com/1081659">
+<link rel="match" href="writing-mode-initial-ref.html">
+<div style="writing-mode:vertical-rl">
+  <div style="writing-mode:initial">
+    horizontal
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/cross-origin-status-codes.html b/third_party/blink/web_tests/external/wpt/resource-timing/cross-origin-status-codes.html
index 4b36986..e8ece5ff 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/cross-origin-status-codes.html
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/cross-origin-status-codes.html
@@ -31,7 +31,6 @@
       if (!entry.name.includes("status-code"))
         return;
 
-      assert_equals(nameMap[entry.name], undefined, `Found duplicate entry for ${entry.name}`);
       nameMap[entry.name] = true;
       if (!firstEntry) {
         firstEntry = entry;
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/status-codes-create-entry.html b/third_party/blink/web_tests/external/wpt/resource-timing/status-codes-create-entry.html
index 430a7fd..c31505a 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/status-codes-create-entry.html
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/status-codes-create-entry.html
@@ -18,13 +18,13 @@
   window.addEventListener("load", t.step_func_done(() => {
     const images = document.getElementsByTagName("img");
     for (let img of images) {
-      assert_equals(performance.getEntriesByName(img.src).length, 1, img.src);
+      assert_greater_than(performance.getEntriesByName(img.src).length, 0, img.src);
     }
     const scripts = document.getElementsByTagName("script");
     let noSrcScriptFound = false;
     for (let script of scripts) {
       if (script.src) {
-        assert_equals(performance.getEntriesByName(script.src).length, 1, script.src);
+        assert_greater_than(performance.getEntriesByName(script.src).length, 0, script.src);
       } else {
         // Ignore this script, which has no src value. There should only be one such script.
         assert_false(noSrcScriptFound);
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
index a416ef5..f468542 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 496 tests; 474 PASS, 22 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 496 tests; 476 PASS, 20 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Test driver for asyncInitCertificate
@@ -142,9 +142,9 @@
 PASS RTCSessionDescription must be primary interface of new RTCSessionDescription({ type: 'offer' })
 PASS Stringification of new RTCSessionDescription({ type: 'offer' })
 PASS RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "type" with the proper type
-FAIL RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "sdp" with the proper type assert_equals: expected "string" but got "object"
+PASS RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "sdp" with the proper type
 PASS RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "toJSON()" with the proper type
-FAIL RTCSessionDescription interface: default toJSON operation on new RTCSessionDescription({ type: 'offer' }) assert_equals: expected "string" but got "object"
+PASS RTCSessionDescription interface: default toJSON operation on new RTCSessionDescription({ type: 'offer' })
 PASS RTCIceCandidate interface: existence and properties of interface object
 PASS RTCIceCandidate interface object length
 PASS RTCIceCandidate interface object name
diff --git a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
index 6df2129..7d6e243 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 277 tests; 266 PASS, 11 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 277 tests; 268 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Partial interface Navigator: original interface defined
@@ -197,10 +197,10 @@
 PASS XRInputSourceArray must be primary interface of xrInputSourceArray
 PASS Stringification of xrInputSourceArray
 PASS XRInputSourceArray interface: xrInputSourceArray must inherit property "length" with the proper type
-FAIL XRLayer interface: existence and properties of interface object assert_equals: prototype of XRLayer is not EventTarget expected function "function EventTarget() { [native code] }" but got function "function () { [native code] }"
+PASS XRLayer interface: existence and properties of interface object
 PASS XRLayer interface object length
 PASS XRLayer interface object name
-FAIL XRLayer interface: existence and properties of interface prototype object assert_equals: prototype of XRLayer.prototype is not EventTarget.prototype expected object "[object EventTarget]" but got object "[object Object]"
+PASS XRLayer interface: existence and properties of interface prototype object
 PASS XRLayer interface: existence and properties of interface prototype object's "constructor" property
 PASS XRLayer interface: existence and properties of interface prototype object's @@unscopables property
 PASS XRWebGLLayer interface: existence and properties of interface object
diff --git a/third_party/blink/web_tests/fast/layout/scroll-anchoring/fullscreen-crash.html b/third_party/blink/web_tests/fast/layout/scroll-anchoring/fullscreen-crash.html
deleted file mode 100644
index f20408fb5..0000000
--- a/third_party/blink/web_tests/fast/layout/scroll-anchoring/fullscreen-crash.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/run-after-layout-and-paint.js"></script>
-<style>
-
-#a { height: 700px; }
-#b { border: 4px solid #ccc; }
-
-</style>
-<div id="a"><div id="b"></div></div>
-<script>
-
-// Based on ClusterFuzz test case in crbug.com/823150.
-t = async_test("This test passes if it does not crash.");
-onload = () => {
-  addEventListener("keypress", step2);
-  eventSender.keyDown(" ", []);
-};
-step2 = () => {
-  b.webkitRequestFullScreen();
-  runAfterLayoutAndPaint(() => { runAfterLayoutAndPaint(step3); });
-};
-step3 = () => {
-  document.designMode = "on";
-  document.execCommand("selectAll");
-  document.execCommand("formatBlock", false, "p");
-  t.done();
-};
-
-</script>
diff --git a/third_party/blink/web_tests/virtual/disable-ax-serializer/README.md b/third_party/blink/web_tests/virtual/disable-ax-serializer/README.md
new file mode 100644
index 0000000..1c0048f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/disable-ax-serializer/README.md
@@ -0,0 +1 @@
+This directory contains tests that will only pass when accessibility serialization is turned off.
diff --git a/third_party/blink/web_tests/virtual/legacy-client-hints-no-fp-delegation/external/wpt/client-hints/accept-ch-stickiness/cross-origin-iframe-redirect-with-fp-delegation.https-expected.txt b/third_party/blink/web_tests/virtual/legacy-client-hints-no-fp-delegation/external/wpt/client-hints/accept-ch-stickiness/cross-origin-iframe-redirect-with-fp-delegation.https-expected.txt
new file mode 100644
index 0000000..166cdfce
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/legacy-client-hints-no-fp-delegation/external/wpt/client-hints/accept-ch-stickiness/cross-origin-iframe-redirect-with-fp-delegation.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Iframe redirect with Feature Policy delegation got client hints according to expectations. assert_equals: message from opened frame expected "PASS" but got "FAIL"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/legacy-client-hints-no-fp-delegation/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect-with-fp-delegation.https-expected.txt b/third_party/blink/web_tests/virtual/legacy-client-hints-no-fp-delegation/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect-with-fp-delegation.https-expected.txt
new file mode 100644
index 0000000..a59069e3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/legacy-client-hints-no-fp-delegation/external/wpt/client-hints/accept-ch-stickiness/cross-origin-subresource-redirect-with-fp-delegation.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL cross-origin subresource redirect with Feature Policy delegaation got client hints according to expectations. assert_true: expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index b9d78c7..13a0ee3 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -9132,7 +9132,7 @@
     getter removed
     getter session
     method constructor
-interface XRLayer
+interface XRLayer : EventTarget
     attribute @@toStringTag
     method constructor
 interface XRPose
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
index e0bf55a..b23b4bc 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 496 tests; 407 PASS, 89 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 496 tests; 409 PASS, 87 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Test driver for asyncInitCertificate
@@ -142,9 +142,9 @@
 PASS RTCSessionDescription must be primary interface of new RTCSessionDescription({ type: 'offer' })
 PASS Stringification of new RTCSessionDescription({ type: 'offer' })
 PASS RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "type" with the proper type
-FAIL RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "sdp" with the proper type assert_equals: expected "string" but got "object"
+PASS RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "sdp" with the proper type
 PASS RTCSessionDescription interface: new RTCSessionDescription({ type: 'offer' }) must inherit property "toJSON()" with the proper type
-FAIL RTCSessionDescription interface: default toJSON operation on new RTCSessionDescription({ type: 'offer' }) assert_equals: expected "string" but got "object"
+PASS RTCSessionDescription interface: default toJSON operation on new RTCSessionDescription({ type: 'offer' })
 PASS RTCIceCandidate interface: existence and properties of interface object
 PASS RTCIceCandidate interface object length
 PASS RTCIceCandidate interface object name
diff --git a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
index 755c4dd8..f25d43f 100644
--- a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
@@ -17,6 +17,7 @@
 ch-ua-mobile
 ch-ua-model
 ch-ua-platform
+ch-ua-platform-version
 ch-viewport-width
 ch-width
 conversion-measurement
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 9fb965f2..4e66d1b 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -11360,7 +11360,7 @@
     getter removed
     getter session
     method constructor
-interface XRLayer
+interface XRLayer : EventTarget
     attribute @@toStringTag
     method constructor
 interface XRLightEstimate
diff --git a/tools/android/memconsumer/BUILD.gn b/tools/android/memconsumer/BUILD.gn
index 91863142..f490d8a 100644
--- a/tools/android/memconsumer/BUILD.gn
+++ b/tools/android/memconsumer/BUILD.gn
@@ -15,8 +15,6 @@
 android_apk("memconsumer_apk") {
   apk_name = "MemConsumer"
   android_manifest = "java/AndroidManifest.xml"
-  min_sdk_version = 16
-  target_sdk_version = 16
   sources = [
     "java/src/org/chromium/memconsumer/MemConsumer.java",
     "java/src/org/chromium/memconsumer/ResidentService.java",
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 822344c3..79b57e2 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -27406,6 +27406,7 @@
   <int value="71" label="ClientHintUAFullVersion"/>
   <int value="72" label="TrustTokenRedemption"/>
   <int value="73" label="ConversionMeasurement"/>
+  <int value="74" label="ClientHintUAPlatformVersion"/>
 </enum>
 
 <enum name="FeaturePolicyImageCompressionFormat">
@@ -39765,6 +39766,7 @@
   <int value="-361948582" label="material-security-verbose"/>
   <int value="-360453785" label="LeftToRightUrls:disabled"/>
   <int value="-360038744" label="invert-viewport-scroll-order"/>
+  <int value="-357464687" label="SharingPreferVapid:enabled"/>
   <int value="-355278724" label="CorbAllowlistAlsoAppliesToOorCors:disabled"/>
   <int value="-354783358" label="NTPSaveToOffline:disabled"/>
   <int value="-353182790" label="ConsistentOmniboxGeolocation:disabled"/>
@@ -39998,6 +40000,7 @@
   <int value="-79327236" label="ModeSpecificPowerButton:enabled"/>
   <int value="-78035185" label="custom_summary"/>
   <int value="-77872983" label="BookmarkAppsMac:disabled"/>
+  <int value="-77789682" label="SharingPreferVapid:disabled"/>
   <int value="-77084779" label="ExperimentalFlingAnimation:enabled"/>
   <int value="-76631048" label="disable-offline-auto-reload-visible-only"/>
   <int value="-76445689" label="WasmCodeCache:enabled"/>
@@ -65710,6 +65713,7 @@
   <int value="12" label="Settings button for notifications."/>
   <int value="13" label="Announcement notification ack button."/>
   <int value="14" label="Announcement notification open button."/>
+  <int value="15" label="TWA notification 'Got it' button."/>
 </enum>
 
 <enum name="SystemNotificationType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index cbd8626..a101443 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -102872,9 +102872,10 @@
 </histogram>
 
 <histogram name="NewTabPage.BackgroundService.Collections.RequestLatency"
-    units="ms" expires_after="M90">
-  <owner>dbeam@chromium.org</owner>
+    units="ms" expires_after="2021-01-01">
+  <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
+  <owner>chrome-desktop-ntp@google.com</owner>
   <summary>
     The time it took until a request from the New Tab Page for Backdrop
     Collections was served.
@@ -102882,9 +102883,10 @@
 </histogram>
 
 <histogram name="NewTabPage.BackgroundService.Images.RequestLatency" units="ms"
-    expires_after="M90">
-  <owner>dbeam@chromium.org</owner>
+    expires_after="2021-01-01">
+  <owner>tiborg@chromium.org</owner>
   <owner>yyushkina@chromium.org</owner>
+  <owner>chrome-desktop-ntp@google.com</owner>
   <summary>
     The time it took until a request from the New Tab Page for Backdrop Images
     was served.
@@ -192591,6 +192593,7 @@
   <suffix name="CleanBrowsingSecure" label="CleanBrowsing secure filter."/>
   <suffix name="Cloudflare" label="Cloudflare."/>
   <suffix name="Comcast" label="Comcast."/>
+  <suffix name="Cznic" label="CZ.NIC ODVR."/>
   <suffix name="Dnssb" label="DNS.SB."/>
   <suffix name="DnssbUserSelected" label="DNS.SB (user-selected)."/>
   <suffix name="Google" label="Google."/>
@@ -192601,6 +192604,7 @@
   <suffix name="Quad9Cdn" label="Quad9 cdn service."/>
   <suffix name="Quad9Insecure" label="Quad9 insecure service."/>
   <suffix name="Quad9Secure" label="Quad9 secure service."/>
+  <suffix name="Switch" label="SWITCH Public DNS."/>
   <affected-histogram name="Net.DNS.DnsTransaction.Insecure.FailureTime"/>
   <affected-histogram name="Net.DNS.DnsTransaction.Insecure.SuccessTime"/>
   <affected-histogram
diff --git a/tools/perf/core/perfetto_binary_roller/upload_trace_processor.py b/tools/perf/core/perfetto_binary_roller/upload_trace_processor.py
index fdf3591..3725661 100755
--- a/tools/perf/core/perfetto_binary_roller/upload_trace_processor.py
+++ b/tools/perf/core/perfetto_binary_roller/upload_trace_processor.py
@@ -38,7 +38,7 @@
   parser.add_argument(
       '--revision',
       help=('Perfetto revision. '
-            'If not supplied, will try to infer from //third_party/perfetto.'))
+            'If not supplied, will try to infer from DEPS file.'))
 
   # When this script is invoked on a CI bot, there are some extra arguments
   # that we have to ignore.
@@ -49,6 +49,9 @@
   binary_deps_manager.UploadHostBinary(trace_processor.TP_BINARY_NAME,
                                        args.path, revision)
 
+  # CI bot expects a valid JSON object as script output.
+  sys.stdout.write('{}\n')
+
 
 if __name__ == '__main__':
   sys.exit(main(sys.argv[1:]))
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
index c11fc87..afab037 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev.cc
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
@@ -35,6 +35,7 @@
 #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
 #include "ui/events/ozone/evdev/touch_evdev_types.h"
 #include "ui/events/ozone/evdev/touch_filter/false_touch_finder.h"
+#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
 #include "ui/events/ozone/evdev/touch_filter/palm_detection_filter.h"
 #include "ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h"
 #include "ui/events/ozone/features.h"
@@ -123,6 +124,14 @@
           base::FeatureList::IsEnabled(kEnablePalmOnMaxTouchMajor)),
       palm_on_tool_type_palm_(
           base::FeatureList::IsEnabled(kEnablePalmOnToolTypePalm)) {
+  if (base::FeatureList::IsEnabled(kEnableNeuralPalmDetectionFilter) &&
+      NeuralStylusPalmDetectionFilter::
+          CompatibleWithNeuralStylusPalmDetectionFilter(devinfo)) {
+    // When a neural net palm detector is enabled, we do not look at tool_type
+    // nor the max size of the touch as indicators of palm, merely the NN
+    // system.
+    palm_on_tool_type_palm_ = palm_on_touch_major_max_ = false;
+  }
   touch_evdev_debug_buffer_.Initialize(devinfo);
 }
 
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev.h b/ui/events/ozone/evdev/touch_event_converter_evdev.h
index e23c365..82da9b0 100644
--- a/ui/events/ozone/evdev/touch_event_converter_evdev.h
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev.h
@@ -190,10 +190,10 @@
   base::RepeatingCallback<void(bool)> enable_palm_suppression_callback_;
 
   // Do we mark a touch as palm when touch_major is the max?
-  const bool palm_on_touch_major_max_;
+  bool palm_on_touch_major_max_;
 
   // Do we mark a touch as palm when the tool type is marked as TOOL_TYPE_PALM ?
-  const bool palm_on_tool_type_palm_;
+  bool palm_on_tool_type_palm_;
 
   DISALLOW_COPY_AND_ASSIGN(TouchEventConverterEvdev);
 };
diff --git a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.cc b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.cc
index bfef46c..2449b05 100644
--- a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.cc
+++ b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.cc
@@ -399,6 +399,10 @@
       !code_check(ABS_MT_TOUCH_MINOR)) {
     return false;
   }
+  // Only work with internal touchscreens.
+  if (devinfo.device_type() != INPUT_DEVICE_INTERNAL) {
+    return false;
+  }
   return true;
 }
 
diff --git a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_unittest.cc b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_unittest.cc
index d2954a1..0b5ba7f 100644
--- a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_unittest.cc
+++ b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_unittest.cc
@@ -74,7 +74,9 @@
       {kEveTouchScreen, true},
       {kLinkTouchscreen, true},  // No ABS_MT_TOOL_TYPE
       {kNocturneStylus, false},
-      {kKohakuTouchscreen, true}};
+      {kKohakuTouchscreen, true},
+      // The Wacom Intuos is external.
+      {kWacomIntuosPtS_Finger, false}};
   for (const auto& it : devices) {
     EXPECT_TRUE(CapabilitiesToDeviceInfo(it.first, &devinfo));
     EXPECT_EQ(it.second,
diff --git a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc
index 888e4b0..9e90f03 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc
+++ b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.cc
@@ -10,6 +10,7 @@
 #include "base/feature_list.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
+#include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "ui/events/ozone/evdev/event_device_info.h"
 #include "ui/events/ozone/evdev/touch_filter/heuristic_stylus_palm_detection_filter.h"
@@ -40,14 +41,25 @@
   return return_value;
 }
 
-}  // namespace internal
-
-namespace {
 std::string FetchNeuralPalmRadiusPolynomial(const EventDeviceInfo& devinfo,
                                             const std::string param_string) {
   if (!param_string.empty()) {
     return param_string;
   }
+
+#if defined(OS_CHROMEOS)
+  // We should really only be running in chromeos anyway; We do a check here
+  // temporarily for hatch and reef.  These numbers should live in config on
+  // chromeos side but for now during experiment are hard-coded here.
+  // TODO(robsc): Investigate a better way of doing this configuration.
+  std::string release_board = base::SysInfo::GetLsbReleaseBoard();
+  if ("hatch" == release_board) {
+    return "0.090477715, 3.9225964";
+  } else if ("reef" == release_board) {
+    return "0.17889799, 4.22584412";
+  }
+#endif
+
   // Basking. Does not report vendor_id / product_id
   if (devinfo.name() == "Elan Touchscreen") {
     return "0.17889799,4.22584412";
@@ -56,7 +68,7 @@
   // By default, return the original.
   return param_string;
 }
-}  // namespace
+}  // namespace internal
 
 std::unique_ptr<PalmDetectionFilter> CreatePalmDetectionFilter(
     const EventDeviceInfo& devinfo,
@@ -64,8 +76,8 @@
   if (base::FeatureList::IsEnabled(kEnableNeuralPalmDetectionFilter) &&
       NeuralStylusPalmDetectionFilter::
           CompatibleWithNeuralStylusPalmDetectionFilter(devinfo)) {
-    std::vector<float> radius_polynomial =
-        internal::ParseRadiusPolynomial(FetchNeuralPalmRadiusPolynomial(
+    std::vector<float> radius_polynomial = internal::ParseRadiusPolynomial(
+        internal::FetchNeuralPalmRadiusPolynomial(
             devinfo, kNeuralPalmRadiusPolynomial.Get()));
     // Theres only one model right now.
     std::unique_ptr<NeuralStylusPalmDetectionFilterModel> model =
diff --git a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h
index 6972fe0..4dc9a3d 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h
+++ b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory.h
@@ -28,6 +28,14 @@
 
 COMPONENT_EXPORT(EVDEV)
 std::vector<float> ParseRadiusPolynomial(const std::string& radius_string);
+
+// Returns either an empty string or a comma separated floating point numbers,
+// used in transforming touch_major/touch_minor radius. The parsed numbers are
+// used as described in:
+// https://source.chromium.org/chromium/chromium/src/+/master:ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_model.h;l=63
+COMPONENT_EXPORT(EVDEV)
+std::string FetchNeuralPalmRadiusPolynomial(const EventDeviceInfo& devinfo,
+                                            const std::string param_string);
 }  // namespace internal
 
 }  // namespace ui
diff --git a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc
index 3d0d7eb..793ab1c 100644
--- a/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc
+++ b/ui/events/ozone/evdev/touch_filter/palm_detection_filter_factory_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <linux/input.h>
 
+#include "base/system/sys_info.h"
 #include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/ozone/evdev/event_device_info.h"
@@ -32,13 +33,16 @@
     EXPECT_TRUE(
         CapabilitiesToDeviceInfo(kNocturneStylus, &nocturne_stylus_info_));
     EXPECT_TRUE(CapabilitiesToDeviceInfo(kEveStylus, &eve_stylus_info_));
+    EXPECT_TRUE(CapabilitiesToDeviceInfo(kKohakuTouchscreen,
+                                         &kohaku_touchscreen_info_));
     scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
   }
 
  protected:
   std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
   EventDeviceInfo eve_touchscreen_info_, eve_stylus_info_,
-      nocturne_touchscreen_info_, nocturne_stylus_info_;
+      nocturne_touchscreen_info_, nocturne_stylus_info_,
+      kohaku_touchscreen_info_;
   SharedPalmDetectionFilterState shared_palm_state_;
 
   DISALLOW_COPY_AND_ASSIGN(PalmDetectionFilterFactoryTest);
@@ -47,6 +51,25 @@
 class PalmDetectionFilterFactoryDeathTest
     : public PalmDetectionFilterFactoryTest {};
 
+#if defined(OS_CHROMEOS)
+TEST_F(PalmDetectionFilterFactoryTest, Radiuses) {
+  std::string lsb_release = "CHROMEOS_RELEASE_BOARD=hatch\n";
+  base::SysInfo::SetChromeOSVersionInfoForTest(lsb_release, base::Time());
+  EXPECT_EQ("0.090477715, 3.9225964", internal::FetchNeuralPalmRadiusPolynomial(
+                                          kohaku_touchscreen_info_, ""));
+
+  lsb_release = "CHROMEOS_RELEASE_BOARD=reef\n";
+  base::SysInfo::SetChromeOSVersionInfoForTest(lsb_release, base::Time());
+  EXPECT_EQ("0.17889799, 4.22584412", internal::FetchNeuralPalmRadiusPolynomial(
+                                          kohaku_touchscreen_info_, ""));
+
+  lsb_release = "CHROMEOS_RELEASE_BOARD=octopus\n";
+  base::SysInfo::SetChromeOSVersionInfoForTest(lsb_release, base::Time());
+  EXPECT_EQ("", internal::FetchNeuralPalmRadiusPolynomial(
+                    kohaku_touchscreen_info_, ""));
+}
+#endif
+
 TEST_F(PalmDetectionFilterFactoryTest, AllDisabled) {
   scoped_feature_list_->InitWithFeatures(
       {}, {ui::kEnableHeuristicPalmDetectionFilter,
@@ -55,7 +78,6 @@
       CreatePalmDetectionFilter(eve_touchscreen_info_, &shared_palm_state_);
   EXPECT_EQ(OpenPalmDetectionFilter::kFilterName,
             palm_filter->FilterNameForTesting());
-
   palm_filter = CreatePalmDetectionFilter(nocturne_touchscreen_info_,
                                           &shared_palm_state_);
   EXPECT_EQ(OpenPalmDetectionFilter::kFilterName,
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.cc b/ui/ozone/platform/wayland/host/wayland_screen.cc
index 1670b7d..81af627 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen.cc
+++ b/ui/ozone/platform/wayland/host/wayland_screen.cc
@@ -4,11 +4,15 @@
 
 #include "ui/ozone/platform/wayland/host/wayland_screen.h"
 
+#include <set>
+#include <vector>
+
+#include "base/stl_util.h"
 #include "ui/display/display.h"
 #include "ui/display/display_finder.h"
 #include "ui/display/display_observer.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
 #include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
 #include "ui/ozone/platform/wayland/host/wayland_window.h"
@@ -172,6 +176,14 @@
   return gfx::kNullAcceleratedWidget;
 }
 
+gfx::AcceleratedWidget WaylandScreen::GetLocalProcessWidgetAtPoint(
+    const gfx::Point& point,
+    const std::set<gfx::AcceleratedWidget>& ignore) const {
+  auto widget = GetAcceleratedWidgetAtScreenPoint(point);
+  return !widget || base::Contains(ignore, widget) ? gfx::kNullAcceleratedWidget
+                                                   : widget;
+}
+
 display::Display WaylandScreen::GetDisplayNearestPoint(
     const gfx::Point& point) const {
   return *FindDisplayNearestPoint(display_list_.displays(), point);
diff --git a/ui/ozone/platform/wayland/host/wayland_screen.h b/ui/ozone/platform/wayland/host/wayland_screen.h
index c81b47a3..8d65dd81 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen.h
+++ b/ui/ozone/platform/wayland/host/wayland_screen.h
@@ -5,14 +5,13 @@
 #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SCREEN_H_
 #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SCREEN_H_
 
+#include <set>
 #include <vector>
 
-#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "ui/display/display_list.h"
-#include "ui/ozone/platform/wayland/host/wayland_output.h"
-#include "ui/ozone/public/ozone_platform.h"
+#include "ui/gfx/geometry/point.h"
 #include "ui/ozone/public/platform_screen.h"
 
 namespace ui {
@@ -23,6 +22,8 @@
 class WaylandScreen : public PlatformScreen {
  public:
   explicit WaylandScreen(WaylandConnection* connection);
+  WaylandScreen(const WaylandScreen&) = delete;
+  WaylandScreen& operator=(const WaylandScreen&) = delete;
   ~WaylandScreen() override;
 
   void OnOutputAdded(uint32_t output_id);
@@ -33,7 +34,7 @@
 
   base::WeakPtr<WaylandScreen> GetWeakPtr();
 
-  // display::Screen implementation.
+  // PlatformScreen overrides:
   const std::vector<display::Display>& GetAllDisplays() const override;
   display::Display GetPrimaryDisplay() const override;
   display::Display GetDisplayForAcceleratedWidget(
@@ -41,6 +42,9 @@
   gfx::Point GetCursorScreenPoint() const override;
   gfx::AcceleratedWidget GetAcceleratedWidgetAtScreenPoint(
       const gfx::Point& point) const override;
+  gfx::AcceleratedWidget GetLocalProcessWidgetAtPoint(
+      const gfx::Point& point,
+      const std::set<gfx::AcceleratedWidget>& ignore) const override;
   display::Display GetDisplayNearestPoint(
       const gfx::Point& point) const override;
   display::Display GetDisplayMatching(
@@ -56,8 +60,6 @@
   base::ObserverList<display::DisplayObserver> observers_;
 
   base::WeakPtrFactory<WaylandScreen> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(WaylandScreen);
 };
 
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
index 9198aa88..899fff6 100644
--- a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
@@ -10,6 +10,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display_observer.h"
 #include "ui/display/display_switches.h"
+#include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
 #include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
 #include "ui/ozone/platform/wayland/host/wayland_screen.h"
@@ -292,6 +293,28 @@
   window_->SetPointerFocus(false);
 }
 
+TEST_P(WaylandScreenTest, GetLocalProcessWidgetAtPoint) {
+  gfx::Point point(10, 10);
+  EXPECT_EQ(platform_screen_->GetLocalProcessWidgetAtPoint(point, {}),
+            gfx::kNullAcceleratedWidget);
+
+  // Set a focus to the main window. Now, that focused window must be returned.
+  window_->SetPointerFocus(true);
+  EXPECT_EQ(platform_screen_->GetLocalProcessWidgetAtPoint(point, {}),
+            window_->GetWidget());
+
+  // Null widget must be returned when the focused window is part of the
+  // |ignore| list.
+  gfx::AcceleratedWidget w = window_->GetWidget();
+  EXPECT_EQ(
+      platform_screen_->GetLocalProcessWidgetAtPoint(point, {w - 1, w, w + 1}),
+      gfx::kNullAcceleratedWidget);
+
+  // Reset the focus to avoid crash on dtor as long as there is no real pointer
+  // object.
+  window_->SetPointerFocus(false);
+}
+
 TEST_P(WaylandScreenTest, GetDisplayMatching) {
   TestDisplayObserver observer;
   platform_screen_->AddObserver(&observer);
diff --git a/weblayer/browser/errorpage_browsertest.cc b/weblayer/browser/errorpage_browsertest.cc
index 80e6e89..ea080df7 100644
--- a/weblayer/browser/errorpage_browsertest.cc
+++ b/weblayer/browser/errorpage_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "content/public/test/url_loader_interceptor.h"
+#include "net/base/mock_network_change_notifier.h"
 #include "net/test/url_request/url_request_failed_job.h"
 #include "weblayer/common/features.h"
 #include "weblayer/shell/browser/shell.h"
@@ -56,18 +57,12 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-class MockNetworkChangeNotifier : public net::NetworkChangeNotifier {
- public:
-  ConnectionType GetCurrentConnectionType() const override {
-    return net::NetworkChangeNotifier::CONNECTION_4G;
-  }
-};
-
 IN_PROC_BROWSER_TEST_F(ErrorPageReloadBrowserTest, ReloadOnNetworkChanged) {
   // Make sure the renderer thinks it's online, since that is a necessary
   // condition for the reload.
-  net::NetworkChangeNotifier::DisableForTest disable;
-  MockNetworkChangeNotifier mock_notifier;
+  net::test::ScopedMockNetworkChangeNotifier mock_network_change_notifier;
+  mock_network_change_notifier.mock_network_change_notifier()
+      ->SetConnectionType(net::NetworkChangeNotifier::CONNECTION_4G);
 
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url = embedded_test_server()->GetURL("/error_page");