diff --git a/DEPS b/DEPS
index 0d7b5ca..fdc6a366 100644
--- a/DEPS
+++ b/DEPS
@@ -200,11 +200,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': 'b69a9d48bf176ba802a22c1bc0002fe09045b458',
+  'skia_revision': 'd90024d4982937c249bbbff8f02f46bfce71bb97',
   # 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': '8a682e08571d84f1d93ef73a07cfac7a089676c9',
+  'v8_revision': '22f2e5ef66ed5c820532f27b141096d3ddb1966e',
   # 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.
@@ -212,11 +212,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'c4ca12e32e6aef9f5f8eb06c4b361264bec19f8d',
+  'angle_revision': 'e51c9068a84f14e42157c097eb764ec3009e0e54',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'aeb3616301c8a2988ede07e4c34e8975da4d5fa0',
+  'swiftshader_revision': '6d612051c083238db89541be4fbb2d624a9baef4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -267,7 +267,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '11b40137016bc78282f346fe45333676b3ac75fb',
+  'catapult_revision': '91c1a7c2dc0a8c9442d3d5786c424593eef45cae',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -275,7 +275,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': '361d02972a3d37e6e7ebe25e2df51e781da6c0d0',
+  'devtools_frontend_revision': '068400262b841bb557d3322f296406a6272be029',
   # 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.
@@ -1369,7 +1369,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'gt2DKWmtJU6vqOju1UcBB-_Nthud81s3cnZkERzzSEUC'
+              'version': 'XF2EE8yaR8BB2gKWbGt1VaQYsdZ57CVGOdgEPj2CcaQC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1551,7 +1551,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'afaca889d46b2c493de34a8f0771c89dea17bb1a',
+    Var('webrtc_git') + '/src.git' + '@' + '7d75f2ca782f38006bb65bc67533e582190454e8',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1623,7 +1623,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a08962107ef6052993af0b1aca418ec6e5bd921a',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2ee6d70254071c9ed67cfe550cc406a68f008952',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/gfx/hardware_renderer_viz.cc b/android_webview/browser/gfx/hardware_renderer_viz.cc
index 5e08a918..f6e08458 100644
--- a/android_webview/browser/gfx/hardware_renderer_viz.cc
+++ b/android_webview/browser/gfx/hardware_renderer_viz.cc
@@ -159,7 +159,8 @@
 
   if (child_frame->frame) {
     DCHECK(!viz_frame_submission_);
-    without_gpu_->SubmitChildCompositorFrame(child_frame);
+    without_gpu_->SubmitChildCompositorFrame(child_id.local_surface_id(),
+                                             child_frame);
   }
 
   gfx::Size frame_size = without_gpu_->GetChildFrameSize();
@@ -275,7 +276,9 @@
     RenderThreadManager* state,
     RootFrameSinkGetter root_frame_sink_getter,
     AwVulkanContextProvider* context_provider)
-    : HardwareRenderer(state), output_surface_provider_(context_provider) {
+    : HardwareRenderer(state),
+      viz_frame_submission_(features::IsUsingVizFrameSubmissionForWebView()),
+      output_surface_provider_(context_provider) {
   DCHECK_CALLED_ON_VALID_THREAD(render_thread_checker_);
   DCHECK(output_surface_provider_.renderer_settings().use_skia_renderer);
 
@@ -336,10 +339,43 @@
   if (!child_frame_)
     return;
 
-  viz::SurfaceId child_surface_id = child_frame_->GetSurfaceId();
+  // Two problems currently can cause the content-generated LocalSurfaceId
+  // to remain the same even if a frame of a new size is submitted:
+  // * The content LocalSurfaceId is currently copied from browser, not
+  //   the renderer when the renderer submits a frame
+  // * Synchronous compositor can resize the viewport in LTHI::OnDraw before
+  //   a new LocalSurfaceId is committed.
+  // Therefore we do not use the LocalSurfaceId allocated by content, and
+  // instead generate our own LocalSurfaceId here for the root renderer frame.
+  if (!renderer_root_local_surface_id_allocator_ ||
+      child_frame_->frame_sink_id != surface_id_.frame_sink_id() ||
+      layer_tree_frame_sink_id_ != child_frame_->layer_tree_frame_sink_id) {
+    renderer_root_local_surface_id_allocator_ =
+        std::make_unique<viz::ParentLocalSurfaceIdAllocator>();
+    layer_tree_frame_sink_id_ = child_frame_->layer_tree_frame_sink_id;
+    renderer_root_local_surface_id_ = viz::LocalSurfaceId();
+  }
+
+  if (!viz_frame_submission_ && child_frame_->frame) {
+    if (!renderer_root_local_surface_id_.is_valid() ||
+        child_frame_->frame->size_in_pixels() != frame_size_ ||
+        child_frame_->frame->device_scale_factor() != device_scale_factor_) {
+      renderer_root_local_surface_id_allocator_->GenerateId();
+      renderer_root_local_surface_id_ =
+          renderer_root_local_surface_id_allocator_->GetCurrentLocalSurfaceId();
+      frame_size_ = child_frame_->frame->size_in_pixels();
+      device_scale_factor_ = child_frame_->frame->device_scale_factor();
+    }
+  }
+
+  viz::SurfaceId child_surface_id =
+      viz_frame_submission_ ? child_frame_->GetSurfaceId()
+                            : viz::SurfaceId(child_frame_->frame_sink_id,
+                                             renderer_root_local_surface_id_);
   if (child_surface_id.is_valid() && child_surface_id != surface_id_) {
     surface_id_ = child_surface_id;
-    device_scale_factor_ = child_frame_->device_scale_factor;
+    if (viz_frame_submission_)
+      device_scale_factor_ = child_frame_->device_scale_factor;
   }
 
   if (!surface_id_.is_valid())
diff --git a/android_webview/browser/gfx/hardware_renderer_viz.h b/android_webview/browser/gfx/hardware_renderer_viz.h
index 539912b..7b97e6da 100644
--- a/android_webview/browser/gfx/hardware_renderer_viz.h
+++ b/android_webview/browser/gfx/hardware_renderer_viz.h
@@ -35,14 +35,19 @@
   void DestroyOnViz();
   bool IsUsingVulkan() const;
 
-  // Information about last delegated frame.
-  float device_scale_factor_ = 0;
-
+  const bool viz_frame_submission_;
   viz::SurfaceId surface_id_;
 
   // Used to create viz::OutputSurface and gl::GLSurface
   OutputSurfaceProviderWebview output_surface_provider_;
 
+  std::unique_ptr<viz::ParentLocalSurfaceIdAllocator>
+      renderer_root_local_surface_id_allocator_;
+  uint32_t layer_tree_frame_sink_id_ = 0u;
+  viz::LocalSurfaceId renderer_root_local_surface_id_;
+  float device_scale_factor_ = 1.0f;
+  gfx::Size frame_size_;
+
   // These are accessed on the viz thread.
   std::unique_ptr<OnViz> on_viz_;
 
diff --git a/android_webview/browser/gfx/root_frame_sink.cc b/android_webview/browser/gfx/root_frame_sink.cc
index 12fbe1b..4eac7a7e 100644
--- a/android_webview/browser/gfx/root_frame_sink.cc
+++ b/android_webview/browser/gfx/root_frame_sink.cc
@@ -195,7 +195,9 @@
   client_ = nullptr;
 }
 
-void RootFrameSink::SubmitChildCompositorFrame(ChildFrame* child_frame) {
+void RootFrameSink::SubmitChildCompositorFrame(
+    const viz::LocalSurfaceId& local_surface_id,
+    ChildFrame* child_frame) {
   DCHECK(child_frame->frame);
   if (!child_sink_support_ ||
       child_sink_support_->frame_sink_id() != child_frame->frame_sink_id ||
@@ -209,7 +211,7 @@
   }
 
   child_sink_support_->SubmitCompositorFrame(
-      child_frame->local_surface_id, std::move(*child_frame->frame),
+      local_surface_id, std::move(*child_frame->frame),
       std::move(child_frame->hit_test_region_list));
   child_frame->frame.reset();
 }
diff --git a/android_webview/browser/gfx/root_frame_sink.h b/android_webview/browser/gfx/root_frame_sink.h
index c607602..afa412f 100644
--- a/android_webview/browser/gfx/root_frame_sink.h
+++ b/android_webview/browser/gfx/root_frame_sink.h
@@ -58,7 +58,8 @@
   bool IsChildSurface(const viz::FrameSinkId& frame_sink_id);
   void DettachClient();
 
-  void SubmitChildCompositorFrame(ChildFrame* child_frame);
+  void SubmitChildCompositorFrame(const viz::LocalSurfaceId& local_surface_id,
+                                  ChildFrame* child_frame);
   viz::FrameTimingDetailsMap TakeChildFrameTimingDetailsMap();
   gfx::Size GetChildFrameSize();
 
diff --git a/apps/load_and_launch_browsertest.cc b/apps/load_and_launch_browsertest.cc
index 065d900..02c7168 100644
--- a/apps/load_and_launch_browsertest.cc
+++ b/apps/load_and_launch_browsertest.cc
@@ -38,6 +38,12 @@
 
 namespace {
 
+constexpr char kTestExtensionId[] = "behllobkkfkfnphdnhnkndlbkcpglgmj";
+
+// Lacros doesn't support launching with chrome already running. See the header
+// comment for InProcessBrowserTest::GetCommandLineForRelaunch().
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
+
 const char* kSwitchesToCopy[] = {
     sandbox::policy::switches::kNoSandbox,
     switches::kUserDataDir,
@@ -56,10 +62,6 @@
     switches::kDisableFeatures,
 };
 
-constexpr char kTestExtensionId[] = "behllobkkfkfnphdnhnkndlbkcpglgmj";
-
-}  // namespace
-
 // TODO(jackhou): Enable this test once it works on OSX. It currently does not
 // work for the same reason --app-id doesn't. See http://crbug.com/148465
 #if defined(OS_MAC)
@@ -99,7 +101,7 @@
 }
 
 // TODO(jackhou): Enable this test once it works on OSX. It currently does not
-// work for the same reason --app-id doesn't. See http://crbug.com/148465
+// work for the same reason --app-id doesn't. See http://crbug.com/148465.
 #if defined(OS_MAC)
 #define MAYBE_LoadAndLaunchAppWithFile DISABLED_LoadAndLaunchAppWithFile
 #else
@@ -140,7 +142,7 @@
   ASSERT_EQ(0, exit_code);
 }
 
-namespace {
+#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
 
 // TestFixture that appends --load-and-launch-app with an app before calling
 // BrowserMain.
@@ -200,8 +202,6 @@
   }
 };
 
-}  // namespace
-
 // Case where Chrome is not running.
 IN_PROC_BROWSER_TEST_F(LoadAndLaunchPlatformAppBrowserTest,
                        LoadAndLaunchAppChromeNotRunning) {
@@ -237,4 +237,5 @@
                 kTestExtensionId, extensions::ExtensionRegistry::EVERYTHING));
 }
 
+}  // namespace
 }  // namespace apps
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index b634ba06..d9ef55e3 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -323,6 +323,8 @@
     "clipboard/scoped_clipboard_history_pause_impl.h",
     "clipboard/views/clipboard_history_bitmap_item_view.cc",
     "clipboard/views/clipboard_history_bitmap_item_view.h",
+    "clipboard/views/clipboard_history_file_item_view.cc",
+    "clipboard/views/clipboard_history_file_item_view.h",
     "clipboard/views/clipboard_history_item_view.cc",
     "clipboard/views/clipboard_history_item_view.h",
     "clipboard/views/clipboard_history_label.cc",
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index 56608873..56c23a80 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -512,9 +512,15 @@
       ->context_factory()
       ->GetHostFrameSinkManager()
       ->CreateVideoCapturer(video_capturer.InitWithNewPipeAndPassReceiver());
+
+  // We bind the audio stream factory only if audio recording is enabled. This
+  // is ok since the |audio_stream_factory| parameter in the recording service
+  // APIs is optional, and can be not bound.
   mojo::PendingRemote<audio::mojom::StreamFactory> audio_stream_factory;
-  delegate_->BindAudioStreamFactory(
-      audio_stream_factory.InitWithNewPipeAndPassReceiver());
+  if (enable_audio_recording_) {
+    delegate_->BindAudioStreamFactory(
+        audio_stream_factory.InitWithNewPipeAndPassReceiver());
+  }
 
   auto frame_sink_id = capture_params.window->GetFrameSinkId();
   if (!frame_sink_id.is_valid()) {
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h
index 234d4b1..81e8417 100644
--- a/ash/capture_mode/capture_mode_controller.h
+++ b/ash/capture_mode/capture_mode_controller.h
@@ -252,6 +252,9 @@
 
   std::unique_ptr<CaptureModeSession> capture_mode_session_;
 
+  // Whether the service should record audio.
+  bool enable_audio_recording_ = true;
+
   // True when video recording is in progress.
   bool is_recording_in_progress_ = false;
 
diff --git a/ash/capture_mode/capture_mode_test_api.cc b/ash/capture_mode/capture_mode_test_api.cc
index 474a7f2..0123998 100644
--- a/ash/capture_mode/capture_mode_test_api.cc
+++ b/ash/capture_mode/capture_mode_test_api.cc
@@ -62,6 +62,11 @@
   controller_->on_file_saved_callback_ = std::move(callback);
 }
 
+void CaptureModeTestApi::SetAudioRecordingEnabled(bool enabled) {
+  DCHECK(!controller_->is_recording_in_progress());
+  controller_->enable_audio_recording_ = enabled;
+}
+
 void CaptureModeTestApi::SetType(bool for_video) {
   controller_->SetType(for_video ? CaptureModeType::kVideo
                                  : CaptureModeType::kImage);
diff --git a/ash/clipboard/views/clipboard_history_file_item_view.cc b/ash/clipboard/views/clipboard_history_file_item_view.cc
new file mode 100644
index 0000000..7f9efcb6
--- /dev/null
+++ b/ash/clipboard/views/clipboard_history_file_item_view.cc
@@ -0,0 +1,51 @@
+// 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 "ash/clipboard/views/clipboard_history_file_item_view.h"
+
+#include "ash/public/cpp/file_icon_util.h"
+#include "base/files/file_path.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/view_class_properties.h"
+
+namespace {
+
+// The file icon's preferred size.
+constexpr gfx::Size kIconSize(20, 20);
+
+// The file icon's margin.
+constexpr gfx::Insets kIconMargin(/*top=*/0,
+                                  /*left=*/0,
+                                  /*bottom=*/0,
+                                  /*right=*/12);
+}  // namespace
+
+namespace ash {
+
+ClipboardHistoryFileItemView::ClipboardHistoryFileItemView(
+    const ClipboardHistoryItem* clipboard_history_item,
+    views::MenuItemView* container)
+    : ClipboardHistoryTextItemView(clipboard_history_item, container) {}
+ClipboardHistoryFileItemView::~ClipboardHistoryFileItemView() = default;
+
+std::unique_ptr<ClipboardHistoryFileItemView::ContentsView>
+ClipboardHistoryFileItemView::CreateContentsView() {
+  auto file_icon = std::make_unique<views::ImageView>();
+  const std::string copied_file_name = base::UTF16ToUTF8(text());
+  file_icon->SetImage(GetIconForPath(base::FilePath(copied_file_name)));
+  file_icon->SetImageSize(kIconSize);
+  file_icon->SetProperty(views::kMarginsKey, kIconMargin);
+  auto contents_view = ClipboardHistoryTextItemView::CreateContentsView();
+
+  // `file_icon` should be `contents_view`'s first child.
+  contents_view->AddChildViewAt(std::move(file_icon), /*index=*/0);
+
+  return contents_view;
+}
+
+const char* ClipboardHistoryFileItemView::GetClassName() const {
+  return "ClipboardHistoryFileItemView";
+}
+
+}  // namespace ash
diff --git a/ash/clipboard/views/clipboard_history_file_item_view.h b/ash/clipboard/views/clipboard_history_file_item_view.h
new file mode 100644
index 0000000..52dc2c3
--- /dev/null
+++ b/ash/clipboard/views/clipboard_history_file_item_view.h
@@ -0,0 +1,36 @@
+// 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 ASH_CLIPBOARD_VIEWS_CLIPBOARD_HISTORY_FILE_ITEM_VIEW_H_
+#define ASH_CLIPBOARD_VIEWS_CLIPBOARD_HISTORY_FILE_ITEM_VIEW_H_
+
+#include "ash/clipboard/views/clipboard_history_text_item_view.h"
+
+namespace views {
+class MenuItemView;
+}
+
+namespace ash {
+
+// The menu item showing the copied file.
+class ClipboardHistoryFileItemView : public ClipboardHistoryTextItemView {
+ public:
+  ClipboardHistoryFileItemView(
+      const ClipboardHistoryItem* clipboard_history_item,
+      views::MenuItemView* container);
+  ClipboardHistoryFileItemView(const ClipboardHistoryFileItemView& rhs) =
+      delete;
+  ClipboardHistoryFileItemView& operator=(
+      const ClipboardHistoryFileItemView& rhs) = delete;
+  ~ClipboardHistoryFileItemView() override;
+
+ private:
+  // ClipboardHistoryTextItemView:
+  std::unique_ptr<ContentsView> CreateContentsView() override;
+  const char* GetClassName() const override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_CLIPBOARD_VIEWS_CLIPBOARD_HISTORY_FILE_ITEM_VIEW_H_
diff --git a/ash/clipboard/views/clipboard_history_item_view.cc b/ash/clipboard/views/clipboard_history_item_view.cc
index 31735ec..de029f9 100644
--- a/ash/clipboard/views/clipboard_history_item_view.cc
+++ b/ash/clipboard/views/clipboard_history_item_view.cc
@@ -8,6 +8,7 @@
 #include "ash/clipboard/clipboard_history_resource_manager.h"
 #include "ash/clipboard/clipboard_history_util.h"
 #include "ash/clipboard/views/clipboard_history_bitmap_item_view.h"
+#include "ash/clipboard/views/clipboard_history_file_item_view.h"
 #include "ash/clipboard/views/clipboard_history_text_item_view.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/style/ash_color_provider.h"
@@ -188,7 +189,7 @@
       return std::make_unique<ClipboardHistoryBitmapItemView>(
           &item, resource_manager, container);
     case ClipboardHistoryUtil::ClipboardHistoryDisplayFormat::kFile:
-      return std::make_unique<ClipboardHistoryTextItemView>(&item, container);
+      return std::make_unique<ClipboardHistoryFileItemView>(&item, container);
   }
 }
 
diff --git a/ash/clipboard/views/clipboard_history_text_item_view.h b/ash/clipboard/views/clipboard_history_text_item_view.h
index fd660c0..05b0281a 100644
--- a/ash/clipboard/views/clipboard_history_text_item_view.h
+++ b/ash/clipboard/views/clipboard_history_text_item_view.h
@@ -25,11 +25,16 @@
       delete;
   ~ClipboardHistoryTextItemView() override;
 
+ protected:
+  const base::string16& text() const { return text_; }
+
+  // ClipboardHistoryItemView:
+  std::unique_ptr<ContentsView> CreateContentsView() override;
+
  private:
   class TextContentsView;
 
   // ClipboardHistoryItemView:
-  std::unique_ptr<ContentsView> CreateContentsView() override;
   base::string16 GetAccessibleName() const override;
   const char* GetClassName() const override;
 
diff --git a/ash/public/cpp/capture_mode_test_api.h b/ash/public/cpp/capture_mode_test_api.h
index e9f16cd3..95706b1 100644
--- a/ash/public/cpp/capture_mode_test_api.h
+++ b/ash/public/cpp/capture_mode_test_api.h
@@ -54,6 +54,10 @@
   using OnFileSavedCallback = base::OnceCallback<void(const base::FilePath&)>;
   void SetOnCaptureFileSavedCallback(OnFileSavedCallback callback);
 
+  // Sets whether or not audio will be recorded when capturing a video. Should
+  // only be called before recording starts, otherwise it has no effect.
+  void SetAudioRecordingEnabled(bool enabled);
+
  private:
   // Sets the capture mode type to a video capture if |for_video| is true, or
   // image capture otherwise.
diff --git a/ash/public/cpp/holding_space/holding_space_image.cc b/ash/public/cpp/holding_space/holding_space_image.cc
index cf96ae3..8453f69b 100644
--- a/ash/public/cpp/holding_space/holding_space_image.cc
+++ b/ash/public/cpp/holding_space/holding_space_image.cc
@@ -37,7 +37,7 @@
 
     // When missing the cache, asynchronously resolve the bitmap for `scale`.
     async_bitmap_resolver_.Run(
-        gfx::ScaleToCeiledSize(placeholder_.size(), scale),
+        gfx::ScaleToCeiledSize(placeholder_.size(), scale), scale,
         base::BindOnce(&ImageSkiaSource::CacheImageForScale,
                        weak_factory_.GetWeakPtr(), scale));
 
diff --git a/ash/public/cpp/holding_space/holding_space_image.h b/ash/public/cpp/holding_space/holding_space_image.h
index f778b42..e614d82 100644
--- a/ash/public/cpp/holding_space/holding_space_image.h
+++ b/ash/public/cpp/holding_space/holding_space_image.h
@@ -24,8 +24,8 @@
   using BitmapCallback = base::OnceCallback<void(const SkBitmap*)>;
 
   // Returns a bitmap asynchronously for a given size.
-  using AsyncBitmapResolver =
-      base::RepeatingCallback<void(const gfx::Size&, BitmapCallback)>;
+  using AsyncBitmapResolver = base::RepeatingCallback<
+      void(const gfx::Size&, float scale_factor, BitmapCallback)>;
 
   HoldingSpaceImage(const gfx::ImageSkia& placeholder,
                     AsyncBitmapResolver async_bitmap_resolver);
diff --git a/ash/services/recording/public/mojom/recording_service.mojom b/ash/services/recording/public/mojom/recording_service.mojom
index 00de7abc..f53cbea 100644
--- a/ash/services/recording/public/mojom/recording_service.mojom
+++ b/ash/services/recording/public/mojom/recording_service.mojom
@@ -37,9 +37,10 @@
 // Note that a maximum of one screen recording can be done at any time.
 interface RecordingService {
   // All the below Record*() interfaces, take a pending remote to a client (e.g.
-  // Ash) to which it will send the muxed video chunks, and two other pending
-  // remotes bound to the video capturer on Viz on the GPU process, and to the
-  // audio stream factory on the Audio Service respectively.
+  // Ash) to which it will send the muxed video chunks, a pending remote bound
+  // to the video capturer on Viz on the GPU process, and another *optional*
+  // pending remote to the audio stream factory on the Audio Service, which if
+  // not provided, the service will not record audio.
 
   // Starts a fullscreen recording of a root window which has the given
   // |frame_sink_id|. The resulting video will have a resolution equal to the
@@ -50,7 +51,7 @@
   RecordFullscreen(
       pending_remote<RecordingServiceClient> client,
       pending_remote<viz.mojom.FrameSinkVideoCapturer> video_capturer,
-      pending_remote<audio.mojom.StreamFactory> audio_stream_factory,
+      pending_remote<audio.mojom.StreamFactory>? audio_stream_factory,
       viz.mojom.FrameSinkId frame_sink_id,
       gfx.mojom.Size video_size);
 
@@ -65,7 +66,7 @@
   RecordWindow(
       pending_remote<RecordingServiceClient> client,
       pending_remote<viz.mojom.FrameSinkVideoCapturer> video_capturer,
-      pending_remote<audio.mojom.StreamFactory> audio_stream_factory,
+      pending_remote<audio.mojom.StreamFactory>? audio_stream_factory,
       viz.mojom.FrameSinkId frame_sink_id,
       gfx.mojom.Size initial_video_size,
       gfx.mojom.Size max_video_size);
@@ -78,7 +79,7 @@
   RecordRegion(
       pending_remote<RecordingServiceClient> client,
       pending_remote<viz.mojom.FrameSinkVideoCapturer> video_capturer,
-      pending_remote<audio.mojom.StreamFactory> audio_stream_factory,
+      pending_remote<audio.mojom.StreamFactory>? audio_stream_factory,
       viz.mojom.FrameSinkId frame_sink_id,
       gfx.mojom.Size full_capture_size,
       gfx.mojom.Rect crop_region);
diff --git a/ash/services/recording/recording_encoder_muxer.cc b/ash/services/recording/recording_encoder_muxer.cc
index 10b97d7..109bf36 100644
--- a/ash/services/recording/recording_encoder_muxer.cc
+++ b/ash/services/recording/recording_encoder_muxer.cc
@@ -36,7 +36,7 @@
 base::SequenceBound<RecordingEncoderMuxer> RecordingEncoderMuxer::Create(
     scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
     const media::VideoEncoder::Options& video_encoder_options,
-    const media::AudioParameters& audio_input_params,
+    const media::AudioParameters* audio_input_params,
     media::WebmMuxer::WriteDataCB muxer_output_callback,
     FailureCallback on_failure_callback) {
   return base::SequenceBound<RecordingEncoderMuxer>(
@@ -70,9 +70,10 @@
     std::unique_ptr<media::AudioBus> audio_bus,
     base::TimeTicks capture_time) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(audio_encoder_);
 
   if (!did_failure_occur())
-    audio_encoder_.EncodeAudio(*audio_bus, capture_time);
+    audio_encoder_->EncodeAudio(*audio_bus, capture_time);
 }
 
 void RecordingEncoderMuxer::FlushAndFinalize(base::OnceClosure on_done) {
@@ -82,7 +83,8 @@
   // it will result in OnAudioEncoded() being called directly (if any audio
   // frames were still buffered and not processed). The video encoder responds
   // asynchronously.
-  audio_encoder_.Flush();
+  if (audio_encoder_)
+    audio_encoder_->Flush();
   video_encoder_.Flush(
       base::BindOnce(&RecordingEncoderMuxer::OnVideoEncoderFlushed,
                      base::Unretained(this), std::move(on_done)));
@@ -90,21 +92,24 @@
 
 RecordingEncoderMuxer::RecordingEncoderMuxer(
     const media::VideoEncoder::Options& video_encoder_options,
-    const media::AudioParameters& audio_input_params,
+    const media::AudioParameters* audio_input_params,
     media::WebmMuxer::WriteDataCB muxer_output_callback,
     FailureCallback on_failure_callback)
     : audio_encoder_(
-          audio_input_params,
-          base::BindRepeating(&RecordingEncoderMuxer::OnAudioEncoded,
-                              base::Unretained(this)),
-          base::BindRepeating(&RecordingEncoderMuxer::OnEncoderStatus,
-                              base::Unretained(this),
-                              /*for_video=*/false),
-          // 0 means the encoder picks bitrate automatically.
-          /*bits_per_second=*/0),
+          !audio_input_params
+              ? nullptr
+              : std::make_unique<media::AudioOpusEncoder>(
+                    *audio_input_params,
+                    base::BindRepeating(&RecordingEncoderMuxer::OnAudioEncoded,
+                                        base::Unretained(this)),
+                    base::BindRepeating(&RecordingEncoderMuxer::OnEncoderStatus,
+                                        base::Unretained(this),
+                                        /*for_video=*/false),
+                    // 0 means the encoder picks bitrate automatically.
+                    /*bits_per_second=*/0)),
       webm_muxer_(media::kCodecOpus,
                   /*has_video_=*/true,
-                  /*has_audio_=*/true,
+                  /*has_audio_=*/!!audio_input_params,
                   muxer_output_callback),
       on_failure_callback_(std::move(on_failure_callback)) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -175,6 +180,7 @@
 void RecordingEncoderMuxer::OnAudioEncoded(
     media::EncodedAudioBuffer encoded_audio) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(audio_encoder_);
 
   // TODO(crbug.com/1143798): Explore changing the WebmMuxer so it doesn't work
   // with strings, to avoid copying the encoded data.
diff --git a/ash/services/recording/recording_encoder_muxer.h b/ash/services/recording/recording_encoder_muxer.h
index 211e771..9c714a6 100644
--- a/ash/services/recording/recording_encoder_muxer.h
+++ b/ash/services/recording/recording_encoder_muxer.h
@@ -5,6 +5,8 @@
 #ifndef ASH_SERVICES_RECORDING_RECORDING_ENCODER_MUXER_H_
 #define ASH_SERVICES_RECORDING_RECORDING_ENCODER_MUXER_H_
 
+#include <memory>
+
 #include "base/callback_forward.h"
 #include "base/containers/circular_deque.h"
 #include "base/containers/queue.h"
@@ -56,6 +58,8 @@
   // |blocking_task_runner| on which all operations as well as destruction will
   // happen. |video_encoder_options| and |audio_input_params| will be used to
   // initialize the video and audio encoders respectively.
+  // If |audio_input_params| is nullptr, then the service is not recording
+  // audio, and the muxer will be initialized accordingly.
   // |muxer_output_callback| will be called on the same sequence of
   // |blocking_task_runner| to provide the muxer output chunks ready to be sent
   // to the recording service client.
@@ -65,7 +69,7 @@
   static base::SequenceBound<RecordingEncoderMuxer> Create(
       scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
       const media::VideoEncoder::Options& video_encoder_options,
-      const media::AudioParameters& audio_input_params,
+      const media::AudioParameters* audio_input_params,
       media::WebmMuxer::WriteDataCB muxer_output_callback,
       FailureCallback on_failure_callback);
 
@@ -95,7 +99,7 @@
 
   RecordingEncoderMuxer(
       const media::VideoEncoder::Options& video_encoder_options,
-      const media::AudioParameters& audio_input_params,
+      const media::AudioParameters* audio_input_params,
       media::WebmMuxer::WriteDataCB muxer_output_callback,
       FailureCallback on_failure_callback);
   ~RecordingEncoderMuxer();
@@ -136,7 +140,8 @@
 
   media::VpxVideoEncoder video_encoder_ GUARDED_BY_CONTEXT(sequence_checker_);
 
-  media::AudioOpusEncoder audio_encoder_ GUARDED_BY_CONTEXT(sequence_checker_);
+  std::unique_ptr<media::AudioOpusEncoder> audio_encoder_
+      GUARDED_BY_CONTEXT(sequence_checker_);
 
   media::WebmMuxer webm_muxer_ GUARDED_BY_CONTEXT(sequence_checker_);
 
diff --git a/ash/services/recording/recording_service.cc b/ash/services/recording/recording_service.cc
index fac3f24..e92da981 100644
--- a/ash/services/recording/recording_service.cc
+++ b/ash/services/recording/recording_service.cc
@@ -16,7 +16,6 @@
 #include "base/time/time.h"
 #include "media/audio/audio_device_description.h"
 #include "media/base/audio_codecs.h"
-#include "media/base/audio_parameters.h"
 #include "media/base/status.h"
 #include "media/base/video_frame.h"
 #include "media/capture/mojom/video_capture_types.mojom.h"
@@ -63,7 +62,8 @@
 
 RecordingService::RecordingService(
     mojo::PendingReceiver<mojom::RecordingService> receiver)
-    : receiver_(this, std::move(receiver)),
+    : audio_parameters_(GetAudioParameters()),
+      receiver_(this, std::move(receiver)),
       consumer_receiver_(this),
       main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       encoding_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
@@ -126,7 +126,8 @@
 void RecordingService::StopRecording() {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   video_capturer_remote_->Stop();
-  audio_capturer_->Stop();
+  if (audio_capturer_)
+    audio_capturer_->Stop();
   audio_capturer_.reset();
 }
 
@@ -260,9 +261,11 @@
   // a keyframe if one has not been coded in the last keyframe_interval frames.
   video_encoder_options.keyframe_interval = 100;
 
-  const auto audio_params = GetAudioParameters();
+  const bool should_record_audio = audio_stream_factory.is_valid();
+
   encoder_muxer_ = RecordingEncoderMuxer::Create(
-      encoding_task_runner_, video_encoder_options, audio_params,
+      encoding_task_runner_, video_encoder_options,
+      should_record_audio ? &audio_parameters_ : nullptr,
       base::BindRepeating(&RecordingService::OnMuxerWrite,
                           base::Unretained(this)),
       base::BindOnce(&RecordingService::OnEncodingFailure,
@@ -270,12 +273,15 @@
 
   ConnectAndStartVideoCapturer(std::move(video_capturer));
 
+  if (!should_record_audio)
+    return;
+
   audio_capturer_ = audio::CreateInputDevice(
       std::move(audio_stream_factory),
       std::string(media::AudioDeviceDescription::kDefaultDeviceId),
       audio::DeadStreamDetection::kEnabled);
   DCHECK(audio_capturer_);
-  audio_capturer_->Initialize(audio_params, this);
+  audio_capturer_->Initialize(audio_parameters_, this);
   audio_capturer_->Start();
 }
 
@@ -316,7 +322,8 @@
   // capturer. We will stop the recording and flush whatever video chunks we
   // currently have.
   did_failure_occur_ = true;
-  audio_capturer_->Stop();
+  if (audio_capturer_)
+    audio_capturer_->Stop();
   audio_capturer_.reset();
   TerminateRecording(/*success=*/false);
 }
@@ -326,6 +333,7 @@
     base::TimeTicks audio_capture_time) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   DCHECK(encoder_muxer_);
+  DCHECK(audio_capturer_);
 
   // We ignore any subsequent frames after a failure.
   if (did_failure_occur_)
diff --git a/ash/services/recording/recording_service.h b/ash/services/recording/recording_service.h
index 0857274a..79f8dc6e7 100644
--- a/ash/services/recording/recording_service.h
+++ b/ash/services/recording/recording_service.h
@@ -18,7 +18,9 @@
 #include "base/thread_annotations.h"
 #include "base/threading/sequence_bound.h"
 #include "base/threading/thread_checker.h"
+#include "media/base/audio_bus.h"
 #include "media/base/audio_capturer_source.h"
+#include "media/base/audio_parameters.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -144,6 +146,9 @@
   // reset the |number_of_buffered_chunks_| back to 0.
   void FlushBufferedChunks();
 
+  // The audio parameters that will be used when recording audio.
+  const media::AudioParameters audio_parameters_;
+
   // The mojo receiving end of the service.
   mojo::Receiver<mojom::RecordingService> receiver_;
 
@@ -179,7 +184,8 @@
   mojo::Remote<viz::mojom::FrameSinkVideoCapturer> video_capturer_remote_
       GUARDED_BY_CONTEXT(main_thread_checker_);
 
-  // The audio capturer instance.
+  // The audio capturer instance. It is created only if the service is requested
+  // to record audio along side the video.
   scoped_refptr<media::AudioCapturerSource> audio_capturer_
       GUARDED_BY_CONTEXT(main_thread_checker_);
 
diff --git a/ash/system/dark_mode/dark_mode_detailed_view.cc b/ash/system/dark_mode/dark_mode_detailed_view.cc
index a03eec95..d6b95297 100644
--- a/ash/system/dark_mode/dark_mode_detailed_view.cc
+++ b/ash/system/dark_mode/dark_mode_detailed_view.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/system/dark_mode/dark_mode_detailed_view.h"
+#include <cstddef>
 
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -120,8 +121,10 @@
 
 void DarkModeDetailedView::OnThemeChanged() {
   TrayDetailedView::OnThemeChanged();
-  themed_label_->SetEnabledColor(GetLabelColor());
-  neutral_label_->SetEnabledColor(GetLabelColor());
+  TrayPopupUtils::SetLabelFontList(themed_label_,
+                                   TrayPopupUtils::FontStyle::kSystemInfo);
+  TrayPopupUtils::SetLabelFontList(neutral_label_,
+                                   TrayPopupUtils::FontStyle::kSystemInfo);
   TrayPopupUtils::UpdateToggleButtonColors(toggle_);
 }
 
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 88b7c46..b4d4c60 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -135,7 +135,7 @@
     title_label->SetEnabledColor(color_provider->GetContentLayerColor(
         AshColorProvider::ContentLayerType::kTextColorPrimary));
     TrayPopupUtils::SetLabelFontList(title_label,
-                                     TrayPopupUtils::FontStyle::kSubHeader);
+                                     TrayPopupUtils::FontStyle::kPodMenuHeader);
     layout_ptr->SetFlexForView(title_label, 1);
 
     settings_button_ = AddChildView(std::make_unique<TopShortcutButton>(
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 7e92c32..03ad6bd 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -105,7 +105,7 @@
     title_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
         AshColorProvider::ContentLayerType::kTextColorPrimary));
     TrayPopupUtils::SetLabelFontList(title_label,
-                                     TrayPopupUtils::FontStyle::kSmallTitle);
+                                     TrayPopupUtils::FontStyle::kPodMenuHeader);
     layout_ptr->SetFlexForView(title_label, 1);
     help_button_ = AddChildView(std::make_unique<TopShortcutButton>(
         base::BindRepeating(
diff --git a/ash/system/tray/tray_popup_utils.cc b/ash/system/tray/tray_popup_utils.cc
index 9640a4a..94bfe55 100644
--- a/ash/system/tray/tray_popup_utils.cc
+++ b/ash/system/tray/tray_popup_utils.cc
@@ -369,21 +369,30 @@
 
 void TrayPopupUtils::SetLabelFontList(views::Label* label, FontStyle style) {
   label->SetAutoColorReadabilityEnabled(false);
-  const gfx::FontList& base_font_list = views::Label::GetDefaultFontList();
+  const gfx::FontList google_sans_font_list({"Google Sans"}, gfx::Font::NORMAL,
+                                            16, gfx::Font::Weight::MEDIUM);
+  const gfx::FontList roboto_font_list({"Roboto"}, gfx::Font::NORMAL, 16,
+                                       gfx::Font::Weight::MEDIUM);
+
   switch (style) {
     case FontStyle::kTitle:
-      label->SetFontList(base_font_list.Derive(8, gfx::Font::NORMAL,
-                                               gfx::Font::Weight::MEDIUM));
+      label->SetFontList(google_sans_font_list);
+      break;
+    case FontStyle::kPodMenuHeader:
+      label->SetFontList(roboto_font_list);
       break;
     case FontStyle::kSubHeader:
-      label->SetFontList(base_font_list.Derive(4, gfx::Font::NORMAL,
-                                               gfx::Font::Weight::MEDIUM));
+      label->SetFontList(roboto_font_list.Derive(-1, gfx::Font::NORMAL,
+                                                 gfx::Font::Weight::MEDIUM));
       break;
     case FontStyle::kSmallTitle:
+      label->SetFontList(roboto_font_list.Derive(-3, gfx::Font::NORMAL,
+                                                 gfx::Font::Weight::MEDIUM));
+      break;
     case FontStyle::kDetailedViewLabel:
     case FontStyle::kSystemInfo:
-      label->SetFontList(base_font_list.Derive(1, gfx::Font::NORMAL,
-                                               gfx::Font::Weight::NORMAL));
+      label->SetFontList(roboto_font_list.Derive(-4, gfx::Font::NORMAL,
+                                                 gfx::Font::Weight::NORMAL));
       break;
   }
 }
diff --git a/ash/system/tray/tray_popup_utils.h b/ash/system/tray/tray_popup_utils.h
index 8f8d3c79..a0a62bd 100644
--- a/ash/system/tray/tray_popup_utils.h
+++ b/ash/system/tray/tray_popup_utils.h
@@ -38,7 +38,9 @@
   enum class FontStyle {
     // Topmost header rows for default view and detailed view.
     kTitle,
-    // Topmost header rows for secondary tray bubbles.
+    // Topmost header for secondary tray bubbles
+    kPodMenuHeader,
+    // Small title used for selections in tray bubbles.
     kSmallTitle,
     // Text in sub-section header rows in detailed views.
     kSubHeader,
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 2d04c66d..3a8aa58 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -1215,6 +1215,7 @@
     params.name = "OverviewNoWindowsLabel";
     params.horizontal_padding = kNoItemsIndicatorHorizontalPaddingDp;
     params.vertical_padding = kNoItemsIndicatorVerticalPaddingDp;
+    params.rounding_dp = kNoItemsIndicatorRoundingDp;
     auto* color_provider = AshColorProvider::Get();
     params.background_color = color_provider->GetBaseLayerColor(
         AshColorProvider::BaseLayerType::kTransparent80);
@@ -1227,10 +1228,6 @@
     params.hide_in_mini_view = true;
     no_windows_widget_ = std::make_unique<RoundedLabelWidget>();
     no_windows_widget_->Init(std::move(params));
-    no_windows_widget_->GetLayer()->SetRoundedCornerRadius(
-        gfx::RoundedCornersF(kNoItemsIndicatorRoundingDp));
-    no_windows_widget_->GetLayer()->SetBackgroundBlur(
-        static_cast<float>(AshColorProvider::LayerBlurSigma::kBlurDefault));
 
     aura::Window* widget_window = no_windows_widget_->GetNativeWindow();
     widget_window->parent()->StackChildAtBottom(widget_window);
diff --git a/ash/wm/overview/rounded_label_widget.cc b/ash/wm/overview/rounded_label_widget.cc
index 31fa3615..e229b6bb 100644
--- a/ash/wm/overview/rounded_label_widget.cc
+++ b/ash/wm/overview/rounded_label_widget.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/public/cpp/window_properties.h"
+#include "ash/style/ash_color_provider.h"
 #include "ash/wm/overview/scoped_overview_animation_settings.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -42,7 +43,8 @@
     SetBackgroundColor(background_color);
     SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
-
+    layer()->SetBackgroundBlur(
+        static_cast<float>(AshColorProvider::LayerBlurSigma::kBlurDefault));
     const gfx::RoundedCornersF radii(rounding_dp);
     layer()->SetRoundedCornerRadius(radii);
     layer()->SetIsFastRoundedCorner(true);
diff --git a/base/system/sys_info.h b/base/system/sys_info.h
index 2884804..61df0adf 100644
--- a/base/system/sys_info.h
+++ b/base/system/sys_info.h
@@ -161,10 +161,14 @@
   // Returns true when actually running in a Chrome OS environment.
   static bool IsRunningOnChromeOS();
 
-  // Test method to force re-parsing of lsb-release.
+  // Overrides |lsb_release| and |lsb_release_time|. Overrides cannot be nested.
+  // Call ResetChromeOSVersionInfoForTest() to restore the previous values.
   static void SetChromeOSVersionInfoForTest(const std::string& lsb_release,
                                             const Time& lsb_release_time);
 
+  // Undoes the function above.
+  static void ResetChromeOSVersionInfoForTest();
+
   // Returns the kernel version of the host operating system.
   static std::string KernelVersion();
 
diff --git a/base/system/sys_info_chromeos.cc b/base/system/sys_info_chromeos.cc
index e8d2b48..aefbb33 100644
--- a/base/system/sys_info_chromeos.cc
+++ b/base/system/sys_info_chromeos.cc
@@ -12,7 +12,7 @@
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
-#include "base/lazy_instance.h"
+#include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -156,11 +156,18 @@
   bool is_running_on_chromeos_;
 };
 
-static LazyInstance<ChromeOSVersionInfo>::Leaky g_chrome_os_version_info =
-    LAZY_INSTANCE_INITIALIZER;
+bool g_use_chromeos_version_info_for_test = false;
 
 ChromeOSVersionInfo& GetChromeOSVersionInfo() {
-  return g_chrome_os_version_info.Get();
+  // ChromeOSVersionInfo only stores the parsed lsb-release values. We use a
+  // second instance for overrides in tests so we can cleanly restore the
+  // original lsb-release.
+  if (g_use_chromeos_version_info_for_test) {
+    static base::NoDestructor<ChromeOSVersionInfo> version_info_for_test;
+    return *version_info_for_test;
+  }
+  static base::NoDestructor<ChromeOSVersionInfo> version_info;
+  return *version_info;
 }
 
 }  // namespace
@@ -229,10 +236,18 @@
 // static
 void SysInfo::SetChromeOSVersionInfoForTest(const std::string& lsb_release,
                                             const Time& lsb_release_time) {
+  DCHECK(!g_use_chromeos_version_info_for_test) << "Nesting is not allowed";
+  g_use_chromeos_version_info_for_test = true;
   std::unique_ptr<Environment> env(Environment::Create());
   env->SetVar(kLsbReleaseKey, lsb_release);
   env->SetVar(kLsbReleaseTimeKey, NumberToString(lsb_release_time.ToDoubleT()));
-  g_chrome_os_version_info.Get().Parse();
+  GetChromeOSVersionInfo().Parse();
+}
+
+// static
+void SysInfo::ResetChromeOSVersionInfoForTest() {
+  DCHECK(g_use_chromeos_version_info_for_test);
+  g_use_chromeos_version_info_for_test = false;
 }
 
 // static
diff --git a/base/system/sys_info_unittest.cc b/base/system/sys_info_unittest.cc
index 1604b54..e44745c 100644
--- a/base/system/sys_info_unittest.cc
+++ b/base/system/sys_info_unittest.cc
@@ -17,6 +17,8 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
+#include "base/test/scoped_chromeos_version_info.h"
+#include "base/test/scoped_running_on_chromeos.h"
 #include "base/test/task_environment.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
@@ -223,7 +225,7 @@
   const char kLsbRelease[] =
       "FOO=1234123.34.5\n"
       "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
-  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, Time());
+  test::ScopedChromeOSVersionInfo version(kLsbRelease, Time());
   SysInfo::OperatingSystemVersionNumbers(&os_major_version, &os_minor_version,
                                          &os_bugfix_version);
   EXPECT_EQ(1, os_major_version);
@@ -238,7 +240,7 @@
   const char kLsbRelease[] =
       "CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
       "FOO=1234123.34.5\n";
-  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, Time());
+  test::ScopedChromeOSVersionInfo version(kLsbRelease, Time());
   SysInfo::OperatingSystemVersionNumbers(&os_major_version, &os_minor_version,
                                          &os_bugfix_version);
   EXPECT_EQ(1, os_major_version);
@@ -251,7 +253,7 @@
   int32_t os_minor_version = -1;
   int32_t os_bugfix_version = -1;
   const char kLsbRelease[] = "FOO=1234123.34.5\n";
-  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, Time());
+  test::ScopedChromeOSVersionInfo version(kLsbRelease, Time());
   SysInfo::OperatingSystemVersionNumbers(&os_major_version, &os_minor_version,
                                          &os_bugfix_version);
   EXPECT_EQ(0, os_major_version);
@@ -263,61 +265,74 @@
   const char kLsbRelease[] = "CHROMEOS_RELEASE_VERSION=1.2.3.4";
   // Use a fake time that can be safely displayed as a string.
   const Time lsb_release_time(Time::FromDoubleT(12345.6));
-  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, lsb_release_time);
+  test::ScopedChromeOSVersionInfo version(kLsbRelease, lsb_release_time);
   Time parsed_lsb_release_time = SysInfo::GetLsbReleaseTime();
   EXPECT_DOUBLE_EQ(lsb_release_time.ToDoubleT(),
                    parsed_lsb_release_time.ToDoubleT());
 }
 
 TEST_F(SysInfoTest, IsRunningOnChromeOS) {
-  SysInfo::SetChromeOSVersionInfoForTest("", Time());
-  EXPECT_FALSE(SysInfo::IsRunningOnChromeOS());
-
-  const char kLsbRelease1[] =
-      "CHROMEOS_RELEASE_NAME=Non Chrome OS\n"
-      "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
-  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease1, Time());
-  EXPECT_FALSE(SysInfo::IsRunningOnChromeOS());
-
-  const char kLsbRelease2[] =
-      "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-      "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
-  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease2, Time());
-  EXPECT_TRUE(SysInfo::IsRunningOnChromeOS());
-
-  const char kLsbRelease3[] = "CHROMEOS_RELEASE_NAME=Chromium OS\n";
-  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease3, Time());
-  EXPECT_TRUE(SysInfo::IsRunningOnChromeOS());
+  {
+    const char kLsbRelease1[] =
+        "CHROMEOS_RELEASE_NAME=Non Chrome OS\n"
+        "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
+    test::ScopedChromeOSVersionInfo version(kLsbRelease1, Time());
+    EXPECT_FALSE(SysInfo::IsRunningOnChromeOS());
+  }
+  {
+    const char kLsbRelease2[] =
+        "CHROMEOS_RELEASE_NAME=Chrome OS\n"
+        "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
+    test::ScopedChromeOSVersionInfo version(kLsbRelease2, Time());
+    EXPECT_TRUE(SysInfo::IsRunningOnChromeOS());
+  }
+  {
+    const char kLsbRelease3[] = "CHROMEOS_RELEASE_NAME=Chromium OS\n";
+    test::ScopedChromeOSVersionInfo version(kLsbRelease3, Time());
+    EXPECT_TRUE(SysInfo::IsRunningOnChromeOS());
+  }
 }
 
 TEST_F(SysInfoTest, CrashOnBaseImage) {
-  const char kLsbRelease2[] =
+  const char kLsbRelease[] =
       "CHROMEOS_RELEASE_NAME=Chrome OS\n"
       "CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
       "CHROMEOS_RELEASE_TRACK=stable-channel\n";
-  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease2, Time());
+  test::ScopedChromeOSVersionInfo version(kLsbRelease, Time());
   EXPECT_TRUE(SysInfo::IsRunningOnChromeOS());
   EXPECT_DEATH_IF_SUPPORTED({ SysInfo::CrashIfChromeOSNonTestImage(); }, "");
 }
 
 TEST_F(SysInfoTest, NoCrashOnTestImage) {
-  const char kLsbRelease2[] =
+  const char kLsbRelease[] =
       "CHROMEOS_RELEASE_NAME=Chrome OS\n"
       "CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
       "CHROMEOS_RELEASE_TRACK=testimage-channel\n";
-  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease2, Time());
+  test::ScopedChromeOSVersionInfo version(kLsbRelease, Time());
   EXPECT_TRUE(SysInfo::IsRunningOnChromeOS());
   // Should not crash.
   SysInfo::CrashIfChromeOSNonTestImage();
 }
 
 TEST_F(SysInfoTest, NoCrashOnLinuxBuild) {
-  SysInfo::SetChromeOSVersionInfoForTest("", Time());
+  test::ScopedChromeOSVersionInfo version("", Time());
   EXPECT_FALSE(SysInfo::IsRunningOnChromeOS());
   // Should not crash.
   SysInfo::CrashIfChromeOSNonTestImage();
 }
 
+TEST_F(SysInfoTest, ScopedRunningOnChromeOS) {
+  // base_unittests run both on linux-chromeos and actual devices, so the
+  // initial state of IsRunningOnChromeOS may vary.
+  bool was_running = SysInfo::IsRunningOnChromeOS();
+  {
+    test::ScopedRunningOnChromeOS running_on_chromeos;
+    EXPECT_TRUE(SysInfo::IsRunningOnChromeOS());
+  }
+  // Previous value restored.
+  EXPECT_EQ(was_running, SysInfo::IsRunningOnChromeOS());
+}
+
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
 }  // namespace base
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index c395665..b823030 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/compiled_action.gni")
+import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/nacl/config.gni")
 import("//build/config/ui.gni")
 import("//build_overrides/build.gni")
@@ -182,6 +183,15 @@
     ]
   }
 
+  if (is_chromeos_ash || is_chromeos_lacros) {
+    sources += [
+      "scoped_chromeos_version_info.cc",
+      "scoped_chromeos_version_info.h",
+      "scoped_running_on_chromeos.cc",
+      "scoped_running_on_chromeos.h",
+    ]
+  }
+
   if (is_linux || is_chromeos) {
     sources += [ "test_file_util_linux.cc" ]
     public_deps += [ ":fontconfig_util_linux" ]
diff --git a/base/test/scoped_chromeos_version_info.cc b/base/test/scoped_chromeos_version_info.cc
new file mode 100644
index 0000000..35ef9faf
--- /dev/null
+++ b/base/test/scoped_chromeos_version_info.cc
@@ -0,0 +1,23 @@
+// 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 "base/test/scoped_chromeos_version_info.h"
+
+#include "base/system/sys_info.h"
+
+namespace base {
+namespace test {
+
+ScopedChromeOSVersionInfo::ScopedChromeOSVersionInfo(StringPiece lsb_release,
+                                                     Time lsb_release_time) {
+  SysInfo::SetChromeOSVersionInfoForTest(lsb_release.as_string(),
+                                         lsb_release_time);
+}
+
+ScopedChromeOSVersionInfo::~ScopedChromeOSVersionInfo() {
+  SysInfo::ResetChromeOSVersionInfoForTest();
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/scoped_chromeos_version_info.h b/base/test/scoped_chromeos_version_info.h
new file mode 100644
index 0000000..c392c38
--- /dev/null
+++ b/base/test/scoped_chromeos_version_info.h
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_SCOPED_CHROMEOS_VERSION_INFO_H_
+#define BASE_TEST_SCOPED_CHROMEOS_VERSION_INFO_H_
+
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+
+namespace base {
+namespace test {
+
+// Test helper that temporarily overrides the cached lsb-release data.
+// NOTE: Must be created on the main thread before any other threads are
+// started. Cannot be nested.
+class ScopedChromeOSVersionInfo {
+ public:
+  // Overrides |lsb_release| and |lsb_release_time|. For example, can be used to
+  // simulate a specific OS version. Note that |lsb_release| must contain
+  // CHROMEOS_RELEASE_NAME to make base::SysInfo::IsRunningOnChromeOS() return
+  // true.
+  ScopedChromeOSVersionInfo(StringPiece lsb_release, Time lsb_release_time);
+  ScopedChromeOSVersionInfo(const ScopedChromeOSVersionInfo&) = delete;
+  ScopedChromeOSVersionInfo& operator=(const ScopedChromeOSVersionInfo&) =
+      delete;
+  ~ScopedChromeOSVersionInfo();
+};
+
+}  // namespace test
+}  // namespace base
+
+#endif  //  BASE_TEST_SCOPED_CHROMEOS_VERSION_INFO_H_
diff --git a/base/test/scoped_running_on_chromeos.cc b/base/test/scoped_running_on_chromeos.cc
new file mode 100644
index 0000000..d171d23
--- /dev/null
+++ b/base/test/scoped_running_on_chromeos.cc
@@ -0,0 +1,31 @@
+// 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 "base/test/scoped_running_on_chromeos.h"
+
+#include "base/system/sys_info.h"
+#include "base/time/time.h"
+
+namespace base {
+namespace test {
+namespace {
+
+// Chrome OS /etc/lsb-release values that make SysInfo::IsRunningOnChromeOS()
+// return true.
+const char kLsbRelease[] =
+    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
+    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
+
+}  // namespace
+
+ScopedRunningOnChromeOS::ScopedRunningOnChromeOS() {
+  SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, Time());
+}
+
+ScopedRunningOnChromeOS::~ScopedRunningOnChromeOS() {
+  SysInfo::ResetChromeOSVersionInfoForTest();
+}
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/scoped_running_on_chromeos.h b/base/test/scoped_running_on_chromeos.h
new file mode 100644
index 0000000..1b4da3847
--- /dev/null
+++ b/base/test/scoped_running_on_chromeos.h
@@ -0,0 +1,25 @@
+// 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 BASE_TEST_SCOPED_RUNNING_ON_CHROMEOS_H_
+#define BASE_TEST_SCOPED_RUNNING_ON_CHROMEOS_H_
+
+namespace base {
+namespace test {
+
+// Test helper that forces base::SysInfo::IsRunningOnChromeOS() to return true.
+// NOTE: Must be created on the main thread before any other threads are
+// started. Cannot be nested.
+class ScopedRunningOnChromeOS {
+ public:
+  ScopedRunningOnChromeOS();
+  ScopedRunningOnChromeOS(const ScopedRunningOnChromeOS&) = delete;
+  ScopedRunningOnChromeOS& operator=(const ScopedRunningOnChromeOS&) = delete;
+  ~ScopedRunningOnChromeOS();
+};
+
+}  // namespace test
+}  // namespace base
+
+#endif  //  BASE_TEST_SCOPED_RUNNING_ON_CHROMEOS_H_
diff --git a/build/android/docs/lint.md b/build/android/docs/lint.md
index 7e68e52..4ba13d7 100644
--- a/build/android/docs/lint.md
+++ b/build/android/docs/lint.md
@@ -86,6 +86,11 @@
 [build/android/gyp/lint.py](https://source.chromium.org/chromium/chromium/src/+/master:build/android/gyp/lint.py).
 Disabling globally makes lint a bit faster.
 
+The exception to the above rule is for warnings that affect multiple languages.
+Feel free to suppress those in lint-suppressions.xml files since it is not
+practical to suppress them in each language file and it is a lot of extra bloat
+to list out every language for every violation in lint-baseline.xml files.
+
 Here is an example of how to structure a suppressions XML file:
 
 ```xml
diff --git a/build/android/gyp/desugar.py b/build/android/gyp/desugar.py
index 6d2a234..1df7bf7e 100755
--- a/build/android/gyp/desugar.py
+++ b/build/android/gyp/desugar.py
@@ -21,7 +21,9 @@
                       help='Jar input path to include .class files from.')
   parser.add_argument('--output-jar', required=True,
                       help='Jar output path.')
-  parser.add_argument('--classpath', required=True,
+  parser.add_argument('--classpath',
+                      action='append',
+                      required=True,
                       help='Classpath.')
   parser.add_argument('--bootclasspath', required=True,
                       help='Path to javac bootclasspath interface jar.')
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 15707eb..ea43332 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -2050,6 +2050,11 @@
             "--depfile",
             rebase_path(depfile, root_build_dir),
           ]
+          if (defined(invoker.desugar_jars_paths)) {
+            _rebased_desugar_jars_paths =
+                rebase_path(invoker.desugar_jars_paths, root_build_dir)
+            args += [ "--classpath=${_rebased_desugar_jars_paths}" ]
+          }
           if (treat_warnings_as_errors) {
             args += [ "--warnings-as-errors" ]
           }
@@ -3447,6 +3452,9 @@
   #  input_jars_paths: Optional list of additional .jar file paths, which will
   #    be added to the compile-time classpath when building this target (but
   #    not to the runtime classpath).
+  #  desugar_jars_paths: Optional list of additional .jar file paths, which will
+  #    be added to the desugar classpath when building this target (but not to
+  #    any other classpath). This is only used to break dependency cycles.
   #  gradle_treat_as_prebuilt: Cause generate_gradle.py to reference this
   #    library via its built .jar rather than including its .java sources.
   #  proguard_enabled: Optional. True to enable ProGuard obfuscation.
@@ -4063,6 +4071,9 @@
           enable_desugar = _enable_desugar && enable_bazel_desugar
           if (enable_desugar) {
             classpath_deps = _full_classpath_deps
+            if (defined(invoker.desugar_jars_paths)) {
+              desugar_jars_paths = invoker.desugar_jars_paths
+            }
           }
         }
         if (defined(invoker.enable_jetify) && invoker.enable_jetify) {
diff --git a/build/config/coverage/OWNERS b/build/config/coverage/OWNERS
index 3583b2a8..0fc481f 100644
--- a/build/config/coverage/OWNERS
+++ b/build/config/coverage/OWNERS
@@ -1,4 +1,3 @@
 inferno@chromium.org
 liaoyuke@chromium.org
-mmoroz@chromium.org
 ochang@chromium.org
diff --git a/build/config/sanitizers/OWNERS b/build/config/sanitizers/OWNERS
index 0a25a01..f6a122b5 100644
--- a/build/config/sanitizers/OWNERS
+++ b/build/config/sanitizers/OWNERS
@@ -1,4 +1,3 @@
 inferno@chromium.org
 metzman@chromium.org
-mmoroz@chromium.org
 ochang@chromium.org
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 5df48ad..66fdedfc 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20201120.1.1
+0.20201120.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 5df48ad..66fdedfc 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20201120.1.1
+0.20201120.3.1
diff --git a/build/fuchsia/symbolizer.py b/build/fuchsia/symbolizer.py
index e9732c6..7db9fc9f 100644
--- a/build/fuchsia/symbolizer.py
+++ b/build/fuchsia/symbolizer.py
@@ -4,19 +4,48 @@
 
 import logging
 import os
+import re
 import subprocess
 
 from common import SDK_ROOT
 from common import GetHostArchFromPlatform
 from common import GetHostToolPathFromPlatform
 
-# TODO(crbug.com/1131647): Change 'llvm-3.8' to 'llvm' after docker image is
-# updated.
-ARM64_DOCKER_LLVM_SYMBOLIZER_PATH = os.path.join('/', 'usr', 'lib', 'llvm-3.8',
+# Paths to the llvm-symbolizer executable in different test hosts.
+X64_LLVM_SYMBOLIZER_PATH = os.path.join(SDK_ROOT, os.pardir, os.pardir,
+                                        'llvm-build', 'Release+Asserts', 'bin',
+                                        'llvm-symbolizer')
+ARM64_XENIAL_LLVM_SYMBOLIZER_PATH = os.path.join('/', 'usr', 'lib', 'llvm-3.8',
+                                                 'bin', 'llvm-symbolizer')
+ARM64_BIONIC_LLVM_SYMBOLIZER_PATH = os.path.join('/', 'usr', 'lib', 'llvm-6.0',
                                                  'bin', 'llvm-symbolizer')
 
+
+def _GetLLVMSymbolizerPath():
+  """Determines the path to the LLVM symbolizer executable based on test host
+  architecture and Ubuntu distro."""
+
+  if GetHostArchFromPlatform() == 'x64':
+    return X64_LLVM_SYMBOLIZER_PATH
+
+  # Get distro codename from /etc/os-release.
+  with open(os.path.join('/', 'etc', 'os-release')) as os_release_file:
+    os_release_text = os_release_file.read()
+  version_codename_re = r'^VERSION_CODENAME=(?P<codename>[\w.-]+)$'
+  match = re.search(version_codename_re, os_release_text, re.MULTILINE)
+  codename = match.group('codename') if match else None
+
+  if codename == 'xenial':
+    return ARM64_XENIAL_LLVM_SYMBOLIZER_PATH
+  elif codename == 'bionic':
+    return ARM64_BIONIC_LLVM_SYMBOLIZER_PATH
+  else:
+    raise Exception('Unknown Ubuntu release "%s"' % codename)
+
+
 def BuildIdsPaths(package_paths):
-  """Generate build ids paths for symbolizer processes."""
+  """Generates build ids paths for symbolizer processes."""
+
   build_ids_paths = map(
       lambda package_path: os.path.join(
           os.path.dirname(package_path), 'ids.txt'),
@@ -33,15 +62,8 @@
                   unstripped binaries on the filesystem.
   Returns a Popen object for the started process."""
 
-  if (GetHostArchFromPlatform() == 'arm64' and
-      os.path.isfile(ARM64_DOCKER_LLVM_SYMBOLIZER_PATH)):
-    llvm_symbolizer_path = ARM64_DOCKER_LLVM_SYMBOLIZER_PATH
-  else:
-    llvm_symbolizer_path = os.path.join(SDK_ROOT, os.pardir, os.pardir,
-                                        'llvm-build', 'Release+Asserts', 'bin',
-                                        'llvm-symbolizer')
-
   symbolizer = GetHostToolPathFromPlatform('symbolize')
+  llvm_symbolizer_path = _GetLLVMSymbolizerPath()
   symbolizer_cmd = [symbolizer,
                     '-ids-rel', '-llvm-symbolizer', llvm_symbolizer_path,
                     '-build-id-dir', os.path.join(SDK_ROOT, '.build-id')]
diff --git a/build/sanitizers/OWNERS b/build/sanitizers/OWNERS
index e9a248c..85a810a 100644
--- a/build/sanitizers/OWNERS
+++ b/build/sanitizers/OWNERS
@@ -4,7 +4,6 @@
 inferno@chromium.org
 mbarbella@chromium.org
 metzman@chromium.org
-mmoroz@chromium.org
 rnk@chromium.org
 per-file tsan_suppressions.cc=*
 per-file lsan_suppressions.cc=*
diff --git a/build/toolchain/OWNERS b/build/toolchain/OWNERS
index 39669b33..d7012d39 100644
--- a/build/toolchain/OWNERS
+++ b/build/toolchain/OWNERS
@@ -1,9 +1,5 @@
 dpranke@google.com
 scottmg@chromium.org
 
-# Clang Static Analyzer.
-per-file clang_static_analyzer*=mmoroz@chromium.org
-
 # Code Coverage.
-per-file *code_coverage*=mmoroz@chromium.org
 per-file *code_coverage*=liaoyuke@chromium.org
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index 5dfca52..0f76c1a8 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -45,78 +45,6 @@
 namespace cc {
 namespace {
 
-class ScopedSkSurfaceForUnpremultiplyAndDither {
- public:
-  ScopedSkSurfaceForUnpremultiplyAndDither(
-      viz::RasterContextProvider* context_provider,
-      sk_sp<SkColorSpace> color_space,
-      const gfx::Rect& playback_rect,
-      const gfx::Rect& raster_full_rect,
-      const gfx::Size& max_tile_size,
-      GLuint texture_id,
-      const gfx::Size& texture_size,
-      bool can_use_lcd_text,
-      int msaa_sample_count)
-      : context_provider_(context_provider),
-        texture_id_(texture_id),
-        offset_(playback_rect.OffsetFromOrigin() -
-                raster_full_rect.OffsetFromOrigin()),
-        size_(playback_rect.size()) {
-    // Determine the |intermediate_size| to use for our 32-bit texture. If we
-    // know the max tile size, use that. This prevents GPU cache explosion due
-    // to using lots of different 32-bit texture sizes. Otherwise just use the
-    // exact size of the target texture.
-    gfx::Size intermediate_size;
-    if (!max_tile_size.IsEmpty()) {
-      DCHECK_GE(max_tile_size.width(), texture_size.width());
-      DCHECK_GE(max_tile_size.height(), texture_size.height());
-      intermediate_size = max_tile_size;
-    } else {
-      intermediate_size = texture_size;
-    }
-
-    // Allocate a 32-bit surface for raster. We will copy from that into our
-    // actual surface in destruction.
-    SkImageInfo n32Info = SkImageInfo::MakeN32Premul(intermediate_size.width(),
-                                                     intermediate_size.height(),
-                                                     std::move(color_space));
-    SkSurfaceProps surface_props =
-        skia::LegacyDisplayGlobals::ComputeSurfaceProps(can_use_lcd_text);
-    surface_ = SkSurface::MakeRenderTarget(
-        context_provider->GrContext(), SkBudgeted::kNo, n32Info,
-        msaa_sample_count, kTopLeft_GrSurfaceOrigin, &surface_props);
-  }
-
-  ~ScopedSkSurfaceForUnpremultiplyAndDither() {
-    // In lost-context cases, |surface_| may be null and there's nothing
-    // meaningful to do here.
-    if (!surface_)
-      return;
-
-    GrBackendTexture backend_texture =
-        surface_->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess);
-    if (!backend_texture.isValid()) {
-      return;
-    }
-    GrGLTextureInfo info;
-    if (!backend_texture.getGLTextureInfo(&info)) {
-      return;
-    }
-    context_provider_->ContextGL()->UnpremultiplyAndDitherCopyCHROMIUM(
-        info.fID, texture_id_, offset_.x(), offset_.y(), size_.width(),
-        size_.height());
-  }
-
-  SkSurface* surface() { return surface_.get(); }
-
- private:
-  viz::RasterContextProvider* context_provider_;
-  GLuint texture_id_;
-  gfx::Vector2d offset_;
-  gfx::Size size_;
-  sk_sp<SkSurface> surface_;
-};
-
 static void RasterizeSourceOOP(
     const RasterSource* raster_source,
     bool resource_has_previous_content,
@@ -186,7 +114,6 @@
     const gfx::AxisTransform2d& transform,
     const RasterSource::PlaybackSettings& playback_settings,
     viz::RasterContextProvider* context_provider,
-    bool unpremultiply_and_dither,
     const gfx::Size& max_tile_size) {
   gpu::raster::RasterInterface* ri = context_provider->RasterInterface();
   if (mailbox->IsZero()) {
@@ -211,26 +138,15 @@
       texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
   {
     ScopedGrContextAccess gr_context_access(context_provider);
-    base::Optional<viz::ClientResourceProvider::ScopedSkSurface> scoped_surface;
-    base::Optional<ScopedSkSurfaceForUnpremultiplyAndDither>
-        scoped_dither_surface;
     SkSurface* surface;
     sk_sp<SkColorSpace> sk_color_space = color_space.ToSkColorSpace();
-    if (!unpremultiply_and_dither) {
-      scoped_surface.emplace(context_provider->GrContext(), sk_color_space,
-                             texture_id, texture_target, resource_size,
-                             resource_format,
-                             skia::LegacyDisplayGlobals::ComputeSurfaceProps(
-                                 playback_settings.use_lcd_text),
-                             playback_settings.msaa_sample_count);
-      surface = scoped_surface->surface();
-    } else {
-      scoped_dither_surface.emplace(
-          context_provider, sk_color_space, playback_rect, raster_full_rect,
-          max_tile_size, texture_id, resource_size,
-          playback_settings.use_lcd_text, playback_settings.msaa_sample_count);
-      surface = scoped_dither_surface->surface();
-    }
+    viz::ClientResourceProvider::ScopedSkSurface scoped_surface(
+        context_provider->GrContext(), sk_color_space, texture_id,
+        texture_target, resource_size, resource_format,
+        skia::LegacyDisplayGlobals::ComputeSurfaceProps(
+            playback_settings.use_lcd_text),
+        playback_settings.msaa_sample_count);
+    surface = scoped_surface.surface();
 
     // Allocating an SkSurface will fail after a lost context.  Pretend we
     // rasterized, as the contents of the resource don't matter anymore.
@@ -379,8 +295,6 @@
       use_gpu_memory_buffer_resources_(use_gpu_memory_buffer_resources),
       tile_format_(tile_format),
       max_tile_size_(max_tile_size),
-      unpremultiply_and_dither_low_bit_depth_tiles_(
-          unpremultiply_and_dither_low_bit_depth_tiles),
       enable_oop_rasterization_(enable_oop_rasterization),
       pending_raster_queries_(pending_raster_queries),
       random_generator_(static_cast<uint32_t>(base::RandUint64())),
@@ -599,7 +513,6 @@
                       resource_size, resource_format, color_space,
                       raster_full_rect, playback_rect, transform,
                       playback_settings, worker_context_provider_,
-                      ShouldUnpremultiplyAndDitherResource(resource_format),
                       max_tile_size_);
     }
     if (measure_raster_metric) {
@@ -614,12 +527,8 @@
 
 bool GpuRasterBufferProvider::ShouldUnpremultiplyAndDitherResource(
     viz::ResourceFormat format) const {
-  switch (format) {
-    case viz::RGBA_4444:
-      return unpremultiply_and_dither_low_bit_depth_tiles_;
-    default:
-      return false;
-  }
+  // TODO(crbug.com/1151490): Re-enable for OOPR.
+  return false;
 }
 
 }  // namespace cc
diff --git a/cc/raster/gpu_raster_buffer_provider.h b/cc/raster/gpu_raster_buffer_provider.h
index d3cc322..2980c4c3b 100644
--- a/cc/raster/gpu_raster_buffer_provider.h
+++ b/cc/raster/gpu_raster_buffer_provider.h
@@ -164,7 +164,6 @@
   const bool use_gpu_memory_buffer_resources_;
   const viz::ResourceFormat tile_format_;
   const gfx::Size max_tile_size_;
-  const bool unpremultiply_and_dither_low_bit_depth_tiles_;
   const bool enable_oop_rasterization_;
 
   RasterQueryQueue* const pending_raster_queries_;
diff --git a/cc/trees/layer_tree_host_pixeltest_tiles.cc b/cc/trees/layer_tree_host_pixeltest_tiles.cc
index d837df9..65911d4 100644
--- a/cc/trees/layer_tree_host_pixeltest_tiles.cc
+++ b/cc/trees/layer_tree_host_pixeltest_tiles.cc
@@ -353,7 +353,8 @@
 
 // This test doesn't work on Vulkan because on our hardware we can't render to
 // RGBA4444 format using either SwiftShader or native Vulkan. See
-// crbug.com/987278 for details
+// crbug.com/987278 for details.
+// TODO(crbug.com/1151490) : Re-enable after this is supported for OOPR.
 #if BUILDFLAG(ENABLE_GL_BACKEND_TESTS)
 class LayerTreeHostTilesTestPartialInvalidationLowBitDepth
     : public LayerTreeHostTilesTestPartialInvalidation {
@@ -373,14 +374,16 @@
         RasterTestConfig{viz::RendererType::kGL, TestRasterType::kGpu}),
     ::testing::PrintToStringParamName());
 
-TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth, PartialRaster) {
+TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth,
+       DISABLED_PartialRaster) {
   use_partial_raster_ = true;
   RunSingleThreadedPixelTest(picture_layer_,
                              base::FilePath(FILE_PATH_LITERAL(
                                  "blue_yellow_partial_flipped_dither.png")));
 }
 
-TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth, FullRaster) {
+TEST_P(LayerTreeHostTilesTestPartialInvalidationLowBitDepth,
+       DISABLED_FullRaster) {
   RunSingleThreadedPixelTest(
       picture_layer_,
       base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped_dither.png")));
diff --git a/chrome/VERSION b/chrome/VERSION
index 39c467e..e2adb3c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=89
 MINOR=0
-BUILD=4332
+BUILD=4333
 PATCH=0
diff --git a/chrome/android/expectations/lint-baseline.xml b/chrome/android/expectations/lint-baseline.xml
index c810c5a2..f23aa2a 100644
--- a/chrome/android/expectations/lint-baseline.xml
+++ b/chrome/android/expectations/lint-baseline.xml
@@ -173,7 +173,7 @@
         errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java"
-            line="463"
+            line="422"
             column="30"/>
     </issue>
 
@@ -195,7 +195,7 @@
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../android_webview/java/src/org/chromium/android_webview/AwContents.java"
-            line="1347"
+            line="1348"
             column="9"/>
     </issue>
 
@@ -206,7 +206,7 @@
         errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../android_webview/java/src/org/chromium/android_webview/AwContents.java"
-            line="1348"
+            line="1349"
             column="17"/>
     </issue>
 
@@ -217,7 +217,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../android_webview/java/src/org/chromium/android_webview/AwContents.java"
-            line="1844"
+            line="1845"
             column="46"/>
     </issue>
 
@@ -228,7 +228,7 @@
         errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../android_webview/java/src/org/chromium/android_webview/AwContents.java"
-            line="2843"
+            line="2840"
             column="22"/>
     </issue>
 
@@ -305,7 +305,7 @@
         errorLine2="                                   ~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java"
-            line="135"
+            line="144"
             column="36"/>
     </issue>
 
@@ -316,7 +316,7 @@
         errorLine2="                                                      ~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java"
-            line="224"
+            line="236"
             column="55"/>
     </issue>
 
@@ -327,7 +327,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java"
-            line="228"
+            line="240"
             column="48"/>
     </issue>
 
@@ -338,7 +338,7 @@
         errorLine2="                                                        ~~~~~~~~~~~~">
         <location
             file="../../weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java"
-            line="305"
+            line="300"
             column="57"/>
     </issue>
 
@@ -349,7 +349,7 @@
         errorLine2="                                                                            ~~~~~~~~~~~~">
         <location
             file="../../weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java"
-            line="416"
+            line="411"
             column="77"/>
     </issue>
 
@@ -360,7 +360,7 @@
         errorLine2="                                                                                   ~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java"
-            line="367"
+            line="364"
             column="84"/>
     </issue>
 
@@ -371,7 +371,7 @@
         errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java"
-            line="757"
+            line="754"
             column="23"/>
     </issue>
 
@@ -393,7 +393,7 @@
         errorLine2="                                                                  ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java"
-            line="791"
+            line="797"
             column="67"/>
     </issue>
 
@@ -437,7 +437,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManagerImpl.java"
-            line="137"
+            line="138"
             column="46"/>
     </issue>
 
@@ -448,7 +448,7 @@
         errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManagerImpl.java"
-            line="143"
+            line="144"
             column="46"/>
     </issue>
 
@@ -470,7 +470,7 @@
         errorLine2="                                          ^">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java"
-            line="491"
+            line="504"
             column="43"/>
     </issue>
 
@@ -481,7 +481,7 @@
         errorLine2="                                         ~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java"
-            line="495"
+            line="508"
             column="42"/>
     </issue>
 
@@ -547,7 +547,7 @@
         errorLine2="                   ~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java"
-            line="112"
+            line="111"
             column="20"/>
     </issue>
 
@@ -558,7 +558,7 @@
         errorLine2="                                         ~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java"
-            line="131"
+            line="130"
             column="42"/>
     </issue>
 
@@ -569,7 +569,7 @@
         errorLine2="                   ~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java"
-            line="145"
+            line="144"
             column="20"/>
     </issue>
 
@@ -653,39 +653,6 @@
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="            dropdown = new OmniboxSuggestionsRecyclerView(context);"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownFactory.java"
-            line="38"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="        dropdown.setAdapter(adapter);"
-        errorLine2="                 ~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownFactory.java"
-            line="42"
-            column="18"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            dropdown = new OmniboxSuggestionsList(context);"
-        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownFactory.java"
-            line="50"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="                            .setAnchorView(mToolbarLayout.getOptionalButtonView())"
         errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -701,7 +668,7 @@
         errorLine2="                     ~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java"
-            line="515"
+            line="516"
             column="22"/>
     </issue>
 
@@ -745,7 +712,7 @@
         errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java"
-            line="477"
+            line="470"
             column="52"/>
     </issue>
 
@@ -778,7 +745,7 @@
         errorLine2="                   ~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java"
-            line="327"
+            line="334"
             column="20"/>
     </issue>
 
@@ -954,7 +921,7 @@
         errorLine2="                                                                         ~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java"
-            line="1535"
+            line="1534"
             column="74"/>
     </issue>
 
@@ -965,7 +932,7 @@
         errorLine2="                              ~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java"
-            line="626"
+            line="590"
             column="31"/>
     </issue>
 
@@ -1064,7 +1031,7 @@
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../components/messages/android/java/res/layout/message_banner_view.xml"
-            line="27"
+            line="31"
             column="9"/>
     </issue>
 
@@ -1075,7 +1042,7 @@
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../components/messages/android/java/res/layout/message_banner_view.xml"
-            line="56"
+            line="60"
             column="9"/>
     </issue>
 
@@ -1448,6 +1415,17 @@
         errorLine1="&lt;FrameLayout"
         errorLine2="^">
         <location
+            file="../../chrome/android/feed/core/java/resv1/layout/feed_more_button.xml"
+            line="7"
+            column="1"/>
+    </issue>
+
+    <issue
+        id="MergeRootFrame"
+        message="This `&lt;FrameLayout>` can be replaced with a `&lt;merge>` tag"
+        errorLine1="&lt;FrameLayout"
+        errorLine2="^">
+        <location
             file="../../chrome/android/java/res/layout/signin_activity.xml"
             line="5"
             column="1"/>
@@ -1460,11 +1438,55 @@
         errorLine2="                                ~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/DropdownItemViewInfoListManager.java"
-            line="34"
+            line="35"
             column="33"/>
     </issue>
 
     <issue
+        id="UsableSpace"
+        message="Consider also using `StorageManager#getAllocatableBytes` and `allocateBytes` which will consider clearable cached data"
+        errorLine1="                    dir.getAbsolutePath(), dir.getUsableSpace(), dir.getTotalSpace(), type);"
+        errorLine2="                                               ~~~~~~~~~~~~~~">
+        <location
+            file="../../chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DownloadDirectoryProvider.java"
+            line="152"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="UsableSpace"
+        message="Consider also using `StorageManager#getAllocatableBytes` and `allocateBytes` which will consider clearable cached data"
+        errorLine1="            mFreeSpace = Environment.getExternalStorageDirectory().getUsableSpace();"
+        errorLine2="                                                                   ~~~~~~~~~~~~~~">
+        <location
+            file="../../chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java"
+            line="317"
+            column="68"/>
+    </issue>
+
+    <issue
+        id="UsableSpace"
+        message="Consider also using `StorageManager#getAllocatableBytes` and `allocateBytes` which will consider clearable cached data"
+        errorLine1="        return Environment.getDataDirectory().getUsableSpace();"
+        errorLine2="                                              ~~~~~~~~~~~~~~">
+        <location
+            file="../../chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtils.java"
+            line="240"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="UsableSpace"
+        message="Consider also using `StorageManager#getAllocatableBytes` and `allocateBytes` which will consider clearable cached data"
+        errorLine1="                        defaultDownloadDir.getAbsolutePath(), defaultDownloadDir.getUsableSpace(),"
+        errorLine2="                                                                                 ~~~~~~~~~~~~~~">
+        <location
+            file="../../chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageSummaryProvider.java"
+            line="86"
+            column="82"/>
+    </issue>
+
+    <issue
         id="Autofill"
         message="Missing `autofillHints` attribute"
         errorLine1="        &lt;EditText"
@@ -1504,7 +1526,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/accessibility_tab_switcher/AccessibilityTabModelListItem.java"
-            line="459"
+            line="458"
             column="20"/>
     </issue>
 
@@ -1515,7 +1537,7 @@
         errorLine2="                                             ^">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java"
-            line="193"
+            line="216"
             column="46"/>
     </issue>
 
@@ -1526,7 +1548,7 @@
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java"
-            line="270"
+            line="283"
             column="9"/>
     </issue>
 
@@ -1537,7 +1559,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="../../components/browser_ui/android/bottomsheet/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheet.java"
-            line="275"
+            line="278"
             column="20"/>
     </issue>
 
@@ -1592,7 +1614,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java"
-            line="660"
+            line="661"
             column="20"/>
     </issue>
 
@@ -1713,7 +1735,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java"
-            line="111"
+            line="139"
             column="20"/>
     </issue>
 
@@ -1823,7 +1845,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarControlContainer.java"
-            line="264"
+            line="261"
             column="20"/>
     </issue>
 
@@ -1834,7 +1856,7 @@
         errorLine2="                   ~~~~~~~~~~~~">
         <location
             file="../../chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java"
-            line="488"
+            line="487"
             column="20"/>
     </issue>
 
@@ -1907,94 +1929,6 @@
     <issue
         id="KeyboardInaccessibleWidget"
         message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
-        errorLine1="    android:clickable=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/layout/download_manager_card_footer.xml"
-            line="14"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="KeyboardInaccessibleWidget"
-        message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
-        errorLine1="    android:clickable=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/layout/download_manager_generic_item.xml"
-            line="14"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="KeyboardInaccessibleWidget"
-        message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
-        errorLine1="    android:clickable=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/layout/download_manager_image_item.xml"
-            line="13"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="KeyboardInaccessibleWidget"
-        message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
-        errorLine1="    android:clickable=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/layout/download_manager_in_progress_image_item.xml"
-            line="13"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="KeyboardInaccessibleWidget"
-        message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
-        errorLine1="    android:clickable=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/layout/download_manager_in_progress_item.xml"
-            line="13"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="KeyboardInaccessibleWidget"
-        message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
-        errorLine1="    android:clickable=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/layout/download_manager_in_progress_video_item.xml"
-            line="12"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="KeyboardInaccessibleWidget"
-        message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
-        errorLine1="    android:clickable=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/layout/download_manager_prefetch_article.xml"
-            line="14"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="KeyboardInaccessibleWidget"
-        message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
-        errorLine1="    android:clickable=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/layout/download_manager_video_item.xml"
-            line="13"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="KeyboardInaccessibleWidget"
-        message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
         errorLine1="        android:clickable=&quot;true&quot;"
         errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -2031,17 +1965,6 @@
         errorLine1="    android:clickable=&quot;true&quot;"
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="../../chrome/android/java/res/layout/share_sheet_item.xml"
-            line="9"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="KeyboardInaccessibleWidget"
-        message="&apos;clickable&apos; attribute found, please also add &apos;focusable&apos;"
-        errorLine1="    android:clickable=&quot;true&quot;"
-        errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
             file="../../chrome/android/java/res/layout/start_top_toolbar.xml"
             line="13"
             column="5"/>
diff --git a/chrome/android/expectations/lint-suppressions.xml b/chrome/android/expectations/lint-suppressions.xml
index c7bed24..38615bc 100644
--- a/chrome/android/expectations/lint-suppressions.xml
+++ b/chrome/android/expectations/lint-suppressions.xml
@@ -54,10 +54,6 @@
   <issue id="InvalidVectorPath" severity="ignore"/>
   <issue id="LogConditional" severity="ignore"/>
   <issue id="LongLogTag" severity="ignore"/>
-  <issue id="MergeRootFrame">
-    <!-- TODO(crbug.com/1039415): Remove suppression after fixing bug. -->
-    <ignore regexp="chrome/android/feed/core/java/resv1/layout/feed_more_button.xml"/>
-  </issue>
   <issue id="MissingClass" severity="ignore"/>
   <issue id="MissingDefaultResource">
     <!-- Only used by ToolbarControlContainer guarded by tablet form-factor. -->
@@ -68,10 +64,6 @@
   <issue id="MissingPermission" severity="ignore"/>
   <issue id="MissingQuantity" severity="ignore"/>
   <issue id="MissingRegistered" severity="ignore"/>
-  <issue id="MissingSuperCall">
-    <!-- TODO(wnwen): File bug to fix -->
-    <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectionToolbar.java"/>
-  </issue>
   <issue id="MissingTranslation">
     <ignore regexp="restriction_values.xml.*"/>
   </issue>
@@ -82,8 +74,6 @@
     <ignore regexp="Static interface  method requires API level 24"/>
     <!-- 1: TaskInfo is refactored at API 29. -->
     <ignore regexp="Field requires API level .*`android.app.TaskInfo"/>
-    <!-- 1: TODO(crbug.com/1085410): Fix -->
-    <ignore regexp="components/content_capture/android/java/src/org/chromium/components/content_capture"/>
     <!-- Endnote: Please specify number of suppressions when adding more -->
   </issue>
   <!-- This warning just adds a lot of false positives. -->
@@ -255,25 +245,17 @@
     <ignore regexp="components/page_info/android/java/res/drawable-hdpi/pageinfo_*"/>
     <!--TODO(crbug.com/1052375): Remove this suppression once PermissionParamsListBuilder moves to components.-->
     <ignore regexp="The resource `R.string.page_info_permission_ads_subtitle` appears to be unused"/>
-    <!--TODO(crbug.com/1069186): The following 14 are found when we switched to linting the entire app. -->
+    <!--TODO(crbug.com/1069186): The following 10 are found when we switched to linting the entire app. -->
     <ignore regexp="The resource `R.string.download_manager_ui_documents` appears to be unused"/>
     <ignore regexp="The resource `R.string.download_manager_offline_home` appears to be unused"/>
     <ignore regexp="The resource `R.string.ntp_learn_more_about_suggested_content` appears to be unused"/>
-    <ignore regexp="The resource `R.string.ntp_feed_menu_iph` appears to be unused"/>
     <ignore regexp="The resource `R.string.tab_switcher_button_label` appears to be unused"/>
-    <ignore regexp="The resource `R.string.prefs_autofill_assistant_switch` appears to be unused"/>
     <ignore regexp="The resource `R.string.storage_clear_dialog_text` appears to be unused"/>
     <ignore regexp="The resource `R.string.website_settings_category_notifications_block` appears to be unused"/>
     <ignore regexp="The resource `R.string.autofill_cc_google_issued` appears to be unused"/>
     <ignore regexp="The resource `R.string.notification_manage_button` appears to be unused"/>
     <ignore regexp="The resource `R.string.app_banner_add` appears to be unused"/>
     <ignore regexp="The resource `R.string.notification_category_permission_requests` appears to be unused"/>
-    <ignore regexp="The resource `R.drawable.ic_launcher_background` appears to be unused"/>
-    <ignore regexp="The resource `R.string.combined_notification_text` appears to be unused"/>
-    <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.string.app_banner_install` appears to be unused"/>
-    <ignore regexp="The resource `R.string.iph_download_indicator_text` appears to be unused"/>
     <!--TODO(crbug.com/1106109): Remove this suppression once Most Visited Tiles header is implemented.-->
     <ignore regexp="The resource `R.string.most_visited_tiles_header` appears to be unused"/>
     <ignore regexp="The resource `R.string.accessibility_omnibox_showing_suggestions_for_website` appears to be unused"/>
@@ -296,13 +278,6 @@
     <ignore regexp="The resource `R.string.price_drop_spotted_show_me` 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/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/storage/StorageSummaryProvider.java"/>
-  </issue>
   <issue id="VectorPath" severity="ignore"/>
   <!-- These constructors are useful for layout editors which we currently do not support. -->
   <issue id="ViewConstructor" severity="ignore"/>
diff --git a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
index 08d48a4..c1bb9789 100644
--- a/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle.AndroidManifest.expected
@@ -599,9 +599,10 @@
       </intent-filter>  # DIFF-ANCHOR: 9c5197e9
     </activity>  # DIFF-ANCHOR: aea75380
     <activity  # DIFF-ANCHOR: a1fac31f
-        android:exported="false"
+        android:exported="true"
         android:label="@string/cablev2_activity_title"
         android:name="org.chromium.chrome.browser.webauth.authenticator.CableAuthenticatorActivity"
+        android:permission="com.google.android.gms.auth.cryptauth.permission.CABLEV2_SERVER_LINK"
         android:theme="@style/Theme.Chromium.Activity.Fullscreen">
       <intent-filter>  # DIFF-ANCHOR: 5873407a
         <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
diff --git a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
index f5921ef..cb6797ad 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle.AndroidManifest.expected
@@ -572,9 +572,10 @@
       </intent-filter>  # DIFF-ANCHOR: 9c5197e9
     </activity>  # DIFF-ANCHOR: aea75380
     <activity  # DIFF-ANCHOR: a1fac31f
-        android:exported="false"
+        android:exported="true"
         android:label="@string/cablev2_activity_title"
         android:name="org.chromium.chrome.browser.webauth.authenticator.CableAuthenticatorActivity"
+        android:permission="com.google.android.gms.auth.cryptauth.permission.CABLEV2_SERVER_LINK"
         android:theme="@style/Theme.Chromium.Activity.Fullscreen">
       <intent-filter>  # DIFF-ANCHOR: 5873407a
         <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
diff --git a/chrome/android/features/cablev2_authenticator/BUILD.gn b/chrome/android/features/cablev2_authenticator/BUILD.gn
index 9e9d7d00..6eee7d9 100644
--- a/chrome/android/features/cablev2_authenticator/BUILD.gn
+++ b/chrome/android/features/cablev2_authenticator/BUILD.gn
@@ -48,6 +48,7 @@
     "java/res/layout/cablev2_fcm.xml",
     "java/res/layout/cablev2_qr_dialog.xml",
     "java/res/layout/cablev2_qr_scan.xml",
+    "java/res/layout/cablev2_serverlink.xml",
     "java/res/layout/cablev2_usb_attached.xml",
     "java/res/values/attrs.xml",
     "java/res/values/styles.xml",
diff --git a/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_serverlink.xml b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_serverlink.xml
new file mode 100644
index 0000000..fb83e19
--- /dev/null
+++ b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_serverlink.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-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. -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <TextView
+        xmlns:tools="http://schemas.android.com/tools"
+        tools:ignore="HardcodedText"
+        style="@style/TextAppearance.TextLarge.Primary"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:text="Connecting to your computer" />
+    <TextView
+        xmlns:tools="http://schemas.android.com/tools"
+        style="@style/TextAppearance.TextMedium.Secondary"
+        android:id="@+id/status_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center" />
+</LinearLayout>
diff --git a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
index 34f74588..c8f601af 100644
--- a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
+++ b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
@@ -96,7 +96,7 @@
 
     public CableAuthenticator(Context context, CableAuthenticatorUI ui, long networkContext,
             long registration, String activityClassName, boolean isFcmNotification,
-            UsbAccessory accessory) {
+            UsbAccessory accessory, byte[] serverLink) {
         mContext = context;
         mUi = ui;
 
@@ -118,6 +118,10 @@
             CableAuthenticatorJni.get().onInteractionReady(this);
         }
 
+        if (serverLink != null) {
+            CableAuthenticatorJni.get().startServerLink(this, serverLink);
+        }
+
         // Otherwise wait for a QR scan.
     }
 
@@ -464,6 +468,10 @@
     }
 
     static String getName() {
+        final String name = BluetoothAdapter.getDefaultAdapter().getName();
+        if (name != null && name.length() > 0) {
+            return name;
+        }
         return Build.MANUFACTURER + " " + Build.MODEL;
     }
 
@@ -569,6 +577,12 @@
                 String qrUrl, boolean link);
 
         /**
+         * Called to instruct the C++ code to start a new transaction based on the given link
+         * information which has been provided by the server.
+         */
+        boolean startServerLink(CableAuthenticator cableAuthenticator, byte[] serverLinkData);
+
+        /**
          * unlink causes the root secret to be rotated and the FCM token to be rotated. This
          * prevents all previously linked devices from being able to contact this device in the
          * future -- they'll have to go via the QR-scanning path again. It returns the updated state
diff --git a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
index 7eab9b6..11246b3 100644
--- a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
+++ b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorUI.java
@@ -23,11 +23,13 @@
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.appcompat.app.AlertDialog;
 import androidx.core.content.res.ResourcesCompat;
 import androidx.fragment.app.Fragment;
 
+import org.chromium.base.Log;
 import org.chromium.ui.base.ActivityAndroidPermissionDelegate;
 import org.chromium.ui.base.AndroidPermissionDelegate;
 import org.chromium.ui.widget.Toast;
@@ -39,14 +41,27 @@
  */
 public class CableAuthenticatorUI
         extends Fragment implements OnClickListener, QRScanDialog.Callback {
+    private static final String TAG = "CableAuthenticatorUI";
+
     // ENABLE_BLUETOOTH_REQUEST_CODE is a random int used to identify responses
     // to a request to enable Bluetooth. (Request codes can only be 16-bit.)
     private static final int ENABLE_BLUETOOTH_REQUEST_CODE = 64907;
 
+    private static final String ACTIVITY_CLASS_NAME_EXTRA =
+            "org.chromium.chrome.modules.cablev2_authenticator.ActivityClassName";
+    private static final String FCM_EXTRA = "org.chromium.chrome.modules.cablev2_authenticator.FCM";
+    private static final String NETWORK_CONTEXT_EXTRA =
+            "org.chromium.chrome.modules.cablev2_authenticator.NetworkContext";
+    private static final String REGISTRATION_EXTRA =
+            "org.chromium.chrome.modules.cablev2_authenticator.Registration";
+    private static final String SERVER_LINK_EXTRA =
+            "org.chromium.chrome.browser.webauth.authenticator.ServerLink";
+
     private enum Mode {
         QR, // Triggered from Settings; can scan QR code to start handshake.
         FCM, // Triggered by user selecting notification; handshake already running.
         USB, // Triggered by connecting via USB.
+        SERVER_LINK, // Triggered by GMSCore forwarding from GAIA.
     }
     private Mode mMode;
     private AndroidPermissionDelegate mPermissionDelegate;
@@ -54,6 +69,7 @@
     private LinearLayout mQRButton;
     private LinearLayout mUnlinkButton;
     private ImageView mHeader;
+    private TextView mStatusText;
 
     // The following two members store a pending QR-scan result while Bluetooth
     // is enabled.
@@ -68,25 +84,27 @@
         Bundle arguments = getArguments();
         final UsbAccessory accessory =
                 (UsbAccessory) arguments.getParcelable(UsbManager.EXTRA_ACCESSORY);
+        final byte[] serverLink = arguments.getByteArray(SERVER_LINK_EXTRA);
         if (accessory != null) {
             mMode = Mode.USB;
-        } else if (arguments.getBoolean("org.chromium.chrome.modules.cablev2_authenticator.FCM")) {
+        } else if (arguments.getBoolean(FCM_EXTRA)) {
             mMode = Mode.FCM;
+        } else if (serverLink != null) {
+            mMode = Mode.SERVER_LINK;
         } else {
             mMode = Mode.QR;
         }
 
-        final long networkContext = arguments.getLong(
-                "org.chromium.chrome.modules.cablev2_authenticator.NetworkContext");
-        final long registration =
-                arguments.getLong("org.chromium.chrome.modules.cablev2_authenticator.Registration");
-        final String activityClassName = arguments.getString(
-                "org.chromium.chrome.modules.cablev2_authenticator.ActivityClassName");
+        Log.i(TAG, "Starting in mode " + mMode.toString());
+
+        final long networkContext = arguments.getLong(NETWORK_CONTEXT_EXTRA);
+        final long registration = arguments.getLong(REGISTRATION_EXTRA);
+        final String activityClassName = arguments.getString(ACTIVITY_CLASS_NAME_EXTRA);
 
         mPermissionDelegate = new ActivityAndroidPermissionDelegate(
                 new WeakReference<Activity>((Activity) context));
         mAuthenticator = new CableAuthenticator(getContext(), this, networkContext, registration,
-                activityClassName, mMode == Mode.FCM, accessory);
+                activityClassName, mMode == Mode.FCM, accessory, serverLink);
     }
 
     @Override
@@ -102,13 +120,18 @@
             case FCM:
                 return inflater.inflate(R.layout.cablev2_fcm, container, false);
 
+            case SERVER_LINK:
+                View v = inflater.inflate(R.layout.cablev2_serverlink, container, false);
+                mStatusText = v.findViewById(R.id.status_text);
+                return v;
+
             case QR:
                 // TODO: should check FEATURE_BLUETOOTH with
                 // https://developer.android.com/reference/android/content/pm/PackageManager.html#hasSystemFeature(java.lang.String)
                 // TODO: strings should be translated but this will be replaced during
                 // the UI process.
 
-                View v = inflater.inflate(R.layout.cablev2_qr_scan, container, false);
+                v = inflater.inflate(R.layout.cablev2_qr_scan, container, false);
                 mQRButton = v.findViewById(R.id.qr_scan);
                 mQRButton.setOnClickListener(this);
 
@@ -208,19 +231,26 @@
     }
 
     void onStatus(int code) {
-        if (mMode != Mode.QR) {
-            // In FCM mode, the handshake is done before the UI appears. For
-            // USB everything should happen immediately.
-            return;
-        }
+        switch (mMode) {
+            case QR:
+                // These values must match up with the Status enum in v2_authenticator.h
+                if (code == 1) {
+                    setHeader(R.style.step2);
+                } else if (code == 2) {
+                    setHeader(R.style.step3);
+                } else if (code == 3) {
+                    setHeader(R.style.step4);
+                }
+                break;
 
-        // These values must match up with the Status enum in v2_authenticator.h
-        if (code == 1) {
-            setHeader(R.style.step2);
-        } else if (code == 2) {
-            setHeader(R.style.step3);
-        } else if (code == 3) {
-            setHeader(R.style.step4);
+            case SERVER_LINK:
+                mStatusText.setText(String.valueOf(code));
+                break;
+
+            case FCM:
+            case USB:
+                // In FCM mode, the handshake is done before the UI appears. For
+                // USB everything should happen immediately.
         }
     }
 
diff --git a/chrome/android/features/cablev2_authenticator/native/cablev2_authenticator_android.cc b/chrome/android/features/cablev2_authenticator/native/cablev2_authenticator_android.cc
index 208299f..1bdd90d 100644
--- a/chrome/android/features/cablev2_authenticator/native/cablev2_authenticator_android.cc
+++ b/chrome/android/features/cablev2_authenticator/native/cablev2_authenticator_android.cc
@@ -87,6 +87,29 @@
   return base::as_bytes(base::make_span(data_bytes, data_len));
 }
 
+// JavaByteArrayToFixedSpan returns a span that aliases |data|, or |nullopt| if
+// the span is not of the correct length. Be aware that the reference for |data|
+// must outlive the span.
+template <size_t N>
+base::Optional<base::span<const uint8_t, N>> JavaByteArrayToFixedSpan(
+    JNIEnv* env,
+    const JavaParamRef<jbyteArray>& data) {
+  static_assert(N != 0,
+                "Zero case is different from JavaByteArrayToSpan as null "
+                "inputs will always be rejected here.");
+
+  if (data.is_null()) {
+    return base::nullopt;
+  }
+
+  const size_t data_len = env->GetArrayLength(data);
+  if (data_len != N) {
+    return base::nullopt;
+  }
+  const jbyte* data_bytes = env->GetByteArrayElements(data, /*iscopy=*/nullptr);
+  return base::as_bytes(base::make_span<N>(data_bytes, data_len));
+}
+
 // GlobalData holds all the state for ongoing security key operations. Since
 // there are ultimately only one human user, concurrent requests are not
 // supported.
@@ -512,6 +535,36 @@
   return true;
 }
 
+static jboolean JNI_CableAuthenticator_StartServerLink(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& cable_authenticator,
+    const JavaParamRef<jbyteArray>& server_link_data_java) {
+  constexpr size_t kDataSize =
+      device::kP256X962Length + device::cablev2::kQRSecretSize;
+  const base::Optional<base::span<const uint8_t, kDataSize>> server_link_data =
+      JavaByteArrayToFixedSpan<kDataSize>(env, server_link_data_java);
+
+  if (!server_link_data) {
+    FIDO_LOG(ERROR) << "Bad length server-link data length";
+    return false;
+  }
+
+  // Sending pairing information is disabled when doing a server-linked
+  // connection, thus the root secret and authenticator name will not be used.
+  std::array<uint8_t, device::cablev2::kRootSecretSize> dummy_root_secret = {0};
+  std::string dummy_authenticator_name = "";
+  GlobalData& global_data = GetGlobalData();
+  global_data
+      .current_transaction = device::cablev2::authenticator::TransactFromQRCode(
+      std::make_unique<AndroidPlatform>(env, cable_authenticator),
+      global_data.network_context, dummy_root_secret, dummy_authenticator_name,
+      server_link_data
+          ->subspan<device::kP256X962Length, device::cablev2::kQRSecretSize>(),
+      server_link_data->subspan<0, device::kP256X962Length>(), base::nullopt);
+
+  return true;
+}
+
 static ScopedJavaLocalRef<jbyteArray> JNI_CableAuthenticator_Unlink(
     JNIEnv* env) {
   std::vector<uint8_t> serialized_state;
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 4f73e753..3fefe052 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -853,12 +853,11 @@
         <activity android:name="org.chromium.chrome.browser.webauth.authenticator.CableAuthenticatorActivity"
             android:theme="@style/Theme.Chromium.Activity.Fullscreen"
             android:label="@string/cablev2_activity_title"
-            android:exported="false">
-            <!-- This has an intent filter but is not exported. This works
-            because the intent is sent by the Android system, not another
-            application. We want to limit this activity to only be invoked by
-            Chromium or the OS because otherwise applications could fool
-            Chromium into communicating with a fake USB device. -->
+            android:permission="com.google.android.gms.auth.cryptauth.permission.CABLEV2_SERVER_LINK"
+            android:exported="true">
+            <!-- This activity can be started by GMSCore, and is thus exported
+            with a permission set, or can be started by the Android system in
+            the case that a USB device is attached. -->
             <intent-filter>
                  <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
             </intent-filter>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index 6690b9a..b126a5a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -1505,6 +1505,9 @@
             return;
         }
 
+        TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile())
+                .notifyEvent(EventConstants.APP_MENU_BOOKMARK_STAR_ICON_PRESSED);
+
         // Note we get user bookmark ID over just a bookmark ID here: Managed bookmarks can't be
         // edited. If the current URL is only bookmarked by managed bookmarks, this will return
         // INVALID_ID, so the code below will fall back on adding a new bookmark instead.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
index 773d07474..5fe23ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
@@ -365,6 +365,8 @@
             // Adds reading list as the first top level folder.
             if (bookmarkId.getType() == BookmarkType.READING_LIST) {
                 topLevelFolders.add(bookmarkId);
+                TrackerFactory.getTrackerForProfile(Profile.getLastUsedRegularProfile())
+                        .notifyEvent(EventConstants.READ_LATER_BOTTOM_SHEET_FOLDER_SEEN);
                 continue;
             }
             BookmarkId parent = bookmarkModel.getBookmarkById(bookmarkId).getParentId();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeader.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeader.java
index 7f12d473..4d838107 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeader.java
@@ -15,16 +15,16 @@
 /**
  * Helper class to manage all the logic and UI behind adding the reading list section headers in the
  * bookmark content UI.
- * TODO(crbug/1147787): Add integration tests.
  */
 class ReadingListSectionHeader {
     /**
      * Sorts the reading list and adds section headers if the list is a reading list.
      * Noop, if the list isn't a reading list. The layout rows are shown in the following order :
-     * 1 - Section header with title "Unread"
-     * 2 - Unread reading list articles.
-     * 3 - Section header with title "Read"
-     * 4 - Read reading list articles.
+     * 1 - Any promo header
+     * 2 - Section header with title "Unread"
+     * 3 - Unread reading list articles.
+     * 4 - Section header with title "Read"
+     * 5 - Read reading list articles.
      * @param listItems The list of bookmark items to be shown in the UI.
      * @param context The associated activity context.
      */
@@ -41,10 +41,11 @@
             readingListStartIndex++;
         }
 
-        assert readingListStartIndex < listItems.size();
+        // If we have no read/unread elements, exit.
+        if (readingListStartIndex == listItems.size()) return;
         sort(listItems, readingListStartIndex);
 
-        // Add a section header at the top.
+        // Add a section header at the top. If it is for read, exit right away.
         assert listItems.get(readingListStartIndex).getBookmarkItem().getId().getType()
                 == BookmarkType.READING_LIST;
         boolean isRead = listItems.get(readingListStartIndex).getBookmarkItem().isRead();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediator.java
index e915a6cfe..af49c6f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediator.java
@@ -16,10 +16,12 @@
 import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabBrowserControlsConstraintsHelper;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.components.messages.ManagedMessageDispatcher;
 import org.chromium.components.messages.MessageQueueDelegate;
+import org.chromium.content_public.common.BrowserControlsState;
 import org.chromium.ui.util.TokenHolder;
 
 /**
@@ -129,7 +131,9 @@
         mBrowserControlsToken =
                 mBrowserControlsManager.getBrowserVisibilityDelegate().showControlsPersistent();
         mContainerCoordinator.showMessageContainer();
-        if (BrowserControlsUtils.areBrowserControlsFullyVisible(mBrowserControlsManager)) {
+        final Tab tab = mTabModelSelector != null ? mTabModelSelector.getCurrentTab() : null;
+        if (TabBrowserControlsConstraintsHelper.getConstraints(tab) == BrowserControlsState.HIDDEN
+                || BrowserControlsUtils.areBrowserControlsFullyVisible(mBrowserControlsManager)) {
             runnable.run();
         } else {
             mBrowserControlsObserver.setOneTimeRunnableOnControlsFullyVisible(runnable);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
index f98735d..b53d791 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
@@ -54,14 +54,20 @@
     }
 
     /**
+     * If there are no browser controls visible, the {@link MessageContainer} view should be laid
+     * out for this method to return a meaningful value.
+     *
      * @return The maximum translation Y value the message banner can have as a result of the
      *         animations or the gestures. Positive values mean the message banner can be translated
      *         upward from the top of the MessagesContainer.
      */
     public int getMessageMaxTranslation() {
-        // TODO(sinansahin): We need to account for other scenarios where there are no browser
-        // controls visible (e.g. PWAs).
-        return getContainerTopOffset();
+        final int containerTopOffset = getContainerTopOffset();
+        if (containerTopOffset == 0) {
+            return mContainer.getHeight();
+        }
+
+        return containerTopOffset;
     }
 
     @Override
@@ -77,8 +83,9 @@
 
     /** @return Offset of the message container from the top of the screen. */
     private int getContainerTopOffset() {
+        if (mControlsManager.getContentOffset() == 0) return 0;
         final Resources res = mContainer.getResources();
-        return mControlsManager.getTopControlsHeight()
+        return mControlsManager.getContentOffset()
                 - res.getDimensionPixelOffset(R.dimen.message_bubble_inset)
                 - res.getDimensionPixelOffset(R.dimen.message_shadow_top_margin);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
index 23ef7b0..ffdf0de 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -155,6 +155,7 @@
             mSubCoordinator.destroy();
             mSubCoordinator = null;
         }
+        mUrlCoordinator.destroy();
         mUrlCoordinator = null;
         mLocationBarLayout.removeUrlFocusChangeListener(mAutocompleteCoordinator);
         mAutocompleteCoordinator.destroy();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 0356a73..41f8b688 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -669,7 +669,8 @@
      * need to be updated.
      */
     /* package */ void updateMicButtonState() {
-        mVoiceSearchEnabled = mVoiceRecognitionHandler.isVoiceSearchEnabled();
+        mVoiceSearchEnabled =
+                mVoiceRecognitionHandler != null && mVoiceRecognitionHandler.isVoiceSearchEnabled();
         updateButtonVisibility();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index 73144f4e..2410e742 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -222,12 +222,14 @@
                 new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
                     @Override
                     public void onLongPress(MotionEvent e) {
+                        if (mUrlBarDelegate == null) return;
                         mUrlBarDelegate.gestureDetected(true);
                         performLongClick();
                     }
 
                     @Override
                     public boolean onSingleTapUp(MotionEvent e) {
+                        if (mUrlBarDelegate == null) return true;
                         requestFocus();
                         mUrlBarDelegate.gestureDetected(false);
                         return true;
@@ -244,6 +246,15 @@
         ApiCompatibilityUtils.disableSmartSelectionTextClassifier(this);
     }
 
+    public void destroy() {
+        setAllowFocus(false);
+        mUrlBarDelegate = null;
+        setOnFocusChangeListener(null);
+        mTextContextMenuDelegate = null;
+        mUrlTextChangeListener = null;
+        mTextChangedListener = null;
+    }
+
     /**
      * Initialize the delegate that allows interaction with the Window.
      */
@@ -340,7 +351,8 @@
 
     @Override
     public View focusSearch(int direction) {
-        if (direction == View.FOCUS_BACKWARD && mUrlBarDelegate.getViewForUrlBackFocus() != null) {
+        if (mUrlBarDelegate != null && direction == View.FOCUS_BACKWARD
+                && mUrlBarDelegate.getViewForUrlBackFocus() != null) {
             return mUrlBarDelegate.getViewForUrlBackFocus();
         } else {
             return super.focusSearch(direction);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
index 5d27d66..ff684e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarCoordinator.java
@@ -37,8 +37,8 @@
         int SELECT_END = 1;
     }
 
-    private final UrlBar mUrlBar;
-    private final UrlBarMediator mMediator;
+    private UrlBar mUrlBar;
+    private UrlBarMediator mMediator;
 
     /**
      * Constructs a coordinator for the given UrlBar view.
@@ -67,6 +67,13 @@
         mMediator = new UrlBarMediator(model, focusChangeCallback);
     }
 
+    public void destroy() {
+        mMediator.destroy();
+        mMediator = null;
+        mUrlBar.destroy();
+        mUrlBar = null;
+    }
+
     /** @see UrlBarMediator#addUrlTextChangeListener(UrlTextChangeListener) */
     public void addUrlTextChangeListener(UrlTextChangeListener listener) {
         mMediator.addUrlTextChangeListener(listener);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
index dee3561b..c069513 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBarMediator.java
@@ -79,6 +79,12 @@
         setUseDarkTextColors(true);
     }
 
+    public void destroy() {
+        mUrlTextChangeListeners.clear();
+        mTextChangedListeners.clear();
+        mOnFocusChangeCallback = (unused) -> {};
+    }
+
     /** Adds a listener for url text changes. */
     public void addUrlTextChangeListener(UrlTextChangeListener listener) {
         mUrlTextChangeListeners.add(listener);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
index 5f964c2..f4bff20 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteController.java
@@ -198,7 +198,7 @@
      * {@link #start(Profile,String, String, boolean)}.
      *
      * <p>
-     * Calling this method with {@code false}, will result in
+     * Calling this method with {@code true}, will result in
      * {@link #onSuggestionsReceived(AutocompleteResult, String, long)} being called with an empty
      * result set.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index fe50fab..feda225 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -223,7 +223,7 @@
     }
 
     public void destroy() {
-        stopAutocomplete(true);
+        stopAutocomplete(false);
         mDropdownViewInfoListBuilder.destroy();
         if (mTabObserver != null) {
             mTabObserver.destroy();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java
index 0387e03..e4bcdf3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownAdapter.java
@@ -63,7 +63,7 @@
 
     /** Ensures selection is reset to beginning of the list. */
     void resetSelection() {
-        setSelectedViewIndex(0);
+        setSelectedViewIndex(RecyclerView.NO_POSITION);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorActivity.java
index c1d07ce8..4a5d8dc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticatorActivity.java
@@ -12,10 +12,12 @@
 import android.hardware.usb.UsbAccessory;
 import android.hardware.usb.UsbManager;
 import android.os.Bundle;
+import android.util.Base64;
 
 import androidx.fragment.app.Fragment;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Log;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeBaseAppCompatActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -35,10 +37,13 @@
  * pulls in the dynamic feature module containing the needed code.
  */
 public class CableAuthenticatorActivity extends ChromeBaseAppCompatActivity {
+    private static final String TAG = "CableAuthenticatorActivity";
     static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = "show_fragment_args";
     // See https://developer.android.com/guide/topics/connectivity/usb/accessory#java
     static final String USB_ACCESSORY_ATTACHED =
             "android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
+    static final String SERVER_LINK_EXTRA =
+            "org.chromium.chrome.browser.webauth.authenticator.ServerLink";
 
     @Override
     @SuppressLint("SetTextI18n") // TODO(BUG=1002262): translate
@@ -71,10 +76,19 @@
             // is untrusted.
             arguments = new Bundle();
             arguments.putParcelable(UsbManager.EXTRA_ACCESSORY, accessory);
+        } else if (intent.hasExtra(SERVER_LINK_EXTRA)) {
+            // This Intent comes from GMSCore when it's triggering a server-linked connection.
+            final String serverLinkBase64 = intent.getStringExtra(SERVER_LINK_EXTRA);
+            arguments = new Bundle();
+            try {
+                final byte[] serverLink = Base64.decode(serverLinkBase64, Base64.DEFAULT);
+                arguments.putByteArray(SERVER_LINK_EXTRA, serverLink);
+            } catch (IllegalArgumentException e) {
+                Log.i(TAG, "Invalid base64 in ServerLink argument");
+            }
         } else {
-            // Since this Activity is not exported, this only happens when a
-            // notification is tapped and |EXTRA_SHOW_FRAGMENT_ARGUMENTS| thus
-            // comes from our own PendingIntent.
+            // Since this Activity is not otherwise exported, this only happens when a notification
+            // is tapped and |EXTRA_SHOW_FRAGMENT_ARGUMENTS| thus comes from our own PendingIntent.
             arguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
         }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesTest.java
index e41abafa..31ce50d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/mostvisited/MostVisitedTilesTest.java
@@ -210,6 +210,7 @@
             throws InterruptedException {
         // Skip past the 'what-you-typed' suggestion.
         sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         checkUrlBarTextIs(mTile1.url.getSpec());
 
         sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
@@ -230,6 +231,7 @@
             throws InterruptedException {
         // Skip past the 'what-you-typed' suggestion.
         sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         checkUrlBarTextIs(mTile1.url.getSpec());
 
         sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
@@ -250,6 +252,7 @@
             throws InterruptedException {
         // Skip past the 'what-you-typed' suggestion.
         sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+        sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
         checkUrlBarTextIs(mTile1.url.getSpec());
 
         sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java
index ed09a9a..00e81949 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeaderTest.java
@@ -27,7 +27,7 @@
 @Config(manifest = Config.NONE)
 public class ReadingListSectionHeaderTest {
     @Test
-    public void testAddReadingListSectionHeaders() {
+    public void testListWithReadUnreadItems() {
         Context context = ContextUtils.getApplicationContext();
         String titleRead = context.getString(org.chromium.chrome.R.string.reading_list_read);
         String titleUnread = context.getString(org.chromium.chrome.R.string.reading_list_unread);
@@ -53,6 +53,80 @@
                 "Expected a different item", 2, listItems.get(4).getBookmarkItem().getId().getId());
     }
 
+    @Test
+    public void testListWithReadUnreadAndPromoItems() {
+        Context context = ContextUtils.getApplicationContext();
+        String titleRead = context.getString(org.chromium.chrome.R.string.reading_list_read);
+
+        List<BookmarkListEntry> listItems = new ArrayList<>();
+        listItems.add(BookmarkListEntry.createSyncPromoHeader(ViewType.PERSONALIZED_SIGNIN_PROMO));
+        listItems.add(createReadingListEntry(1, true));
+        listItems.add(createReadingListEntry(2, true));
+        ReadingListSectionHeader.maybeSortAndInsertSectionHeaders(listItems, context);
+
+        assertEquals("Incorrect number of items in the adapter", 4, listItems.size());
+        assertEquals("Expected promo section header", ViewType.PERSONALIZED_SIGNIN_PROMO,
+                listItems.get(0).getViewType());
+        assertEquals("Expected read section header", ViewType.SECTION_HEADER,
+                listItems.get(1).getViewType());
+        assertEquals("Expected read title text", titleRead, listItems.get(1).getHeaderTitle());
+        assertEquals(
+                "Expected a different item", 1, listItems.get(2).getBookmarkItem().getId().getId());
+        assertEquals(
+                "Expected a different item", 2, listItems.get(3).getBookmarkItem().getId().getId());
+    }
+
+    @Test
+    public void testEmptyList() {
+        Context context = ContextUtils.getApplicationContext();
+        List<BookmarkListEntry> listItems = new ArrayList<>();
+        ReadingListSectionHeader.maybeSortAndInsertSectionHeaders(listItems, context);
+        assertEquals("Incorrect number of items in the adapter", 0, listItems.size());
+    }
+
+    @Test
+    public void testNoReadUnreadItems() {
+        Context context = ContextUtils.getApplicationContext();
+        List<BookmarkListEntry> listItems = new ArrayList<>();
+        listItems.add(BookmarkListEntry.createSyncPromoHeader(ViewType.PERSONALIZED_SIGNIN_PROMO));
+        ReadingListSectionHeader.maybeSortAndInsertSectionHeaders(listItems, context);
+
+        assertEquals("Incorrect number of items in the adapter", 1, listItems.size());
+        assertEquals("Expected promo section header", ViewType.PERSONALIZED_SIGNIN_PROMO,
+                listItems.get(0).getViewType());
+    }
+
+    @Test
+    public void testUnreadItemsOnly() {
+        Context context = ContextUtils.getApplicationContext();
+        String titleUnread = context.getString(org.chromium.chrome.R.string.reading_list_unread);
+
+        List<BookmarkListEntry> listItems = new ArrayList<>();
+        listItems.add(createReadingListEntry(1, false));
+        listItems.add(createReadingListEntry(2, false));
+        ReadingListSectionHeader.maybeSortAndInsertSectionHeaders(listItems, context);
+
+        assertEquals("Incorrect number of items in the adapter", 3, listItems.size());
+        assertEquals(
+                "Expected section header", ViewType.SECTION_HEADER, listItems.get(0).getViewType());
+        assertEquals("Expected unread title text", titleUnread, listItems.get(0).getHeaderTitle());
+    }
+
+    @Test
+    public void testReadItemsOnly() {
+        Context context = ContextUtils.getApplicationContext();
+        String titleRead = context.getString(org.chromium.chrome.R.string.reading_list_read);
+
+        List<BookmarkListEntry> listItems = new ArrayList<>();
+        listItems.add(createReadingListEntry(1, true));
+        ReadingListSectionHeader.maybeSortAndInsertSectionHeaders(listItems, context);
+
+        assertEquals("Incorrect number of items in the adapter", 2, listItems.size());
+        assertEquals(
+                "Expected section header", ViewType.SECTION_HEADER, listItems.get(0).getViewType());
+        assertEquals("Expected read title text", titleRead, listItems.get(0).getHeaderTitle());
+    }
+
     private BookmarkListEntry createReadingListEntry(long id, boolean read) {
         BookmarkId bookmarkId = new BookmarkId(id, BookmarkType.READING_LIST);
         BookmarkItem bookmarkItem =
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 4217c74..07d2843 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10231,6 +10231,22 @@
       </message>
     </if>
 
+    <!-- Font Access chooser -->
+    <if expr="not is_android">
+      <message name="IDS_FONT_ACCESS_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce Local Font Access chooser details to the user in a popup when it is from a website.">
+        <ph name="Origin">$1<ex>www.google.com</ex></ph> wants to use local fonts
+      </message>
+      <message name="IDS_FONT_ACCESS_CHOOSER_NO_FONTS_FOUND_PROMPT" desc="The label shown to the user to inform them that no fonts were found matching the requirements that the application provided.">
+        No matching font found.
+      </message>
+      <message name="IDS_FONT_ACCESS_CHOOSER_IMPORT_BUTTON_TEXT" desc="Label on the button that closes the Local Font Access chooser popup and returns the selected fonts.">
+        Import
+      </message>
+      <message name="IDS_FONT_ACCESS_CHOOSER_CANCEL_BUTTON_TEXT" desc="Label on the button that closes the chooser popup without selecting an option.">
+        Cancel
+      </message>
+    </if>
+
     <!-- Chrome IME API activated bubble. -->
     <message name="IDS_IME_API_ACTIVATED_WARNING" desc="The Warning info when an IME extension is trying to activate.">
       <ph name="EXTENSION_NAME">$1<ex>Gmail Checker</ex></ph> extension may collect all the text you type, including personal data like passwords and credit card numbers. Do you want to use this extension?
diff --git a/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_CANCEL_BUTTON_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_CANCEL_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..5addd5c9
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_CANCEL_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+166abf67c1e1b3d3bf23f40c5dba3536d60340bd
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_IMPORT_BUTTON_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_IMPORT_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..5addd5c9
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_IMPORT_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+166abf67c1e1b3d3bf23f40c5dba3536d60340bd
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_NO_FONTS_FOUND_PROMPT.png.sha1 b/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_NO_FONTS_FOUND_PROMPT.png.sha1
new file mode 100644
index 0000000..a15cd98
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_NO_FONTS_FOUND_PROMPT.png.sha1
@@ -0,0 +1 @@
+7ce296e147531e4214d2e2a5c7f4896236cd83aa
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_PROMPT_ORIGIN.png.sha1 b/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_PROMPT_ORIGIN.png.sha1
new file mode 100644
index 0000000..5addd5c9
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_FONT_ACCESS_CHOOSER_PROMPT_ORIGIN.png.sha1
@@ -0,0 +1 @@
+166abf67c1e1b3d3bf23f40c5dba3536d60340bd
\ No newline at end of file
diff --git a/chrome/app/nearby_share_strings.grdp b/chrome/app/nearby_share_strings.grdp
index f47a8b1..c600af7 100644
--- a/chrome/app/nearby_share_strings.grdp
+++ b/chrome/app/nearby_share_strings.grdp
@@ -122,6 +122,9 @@
   <message name="IDS_NEARBY_ERROR_NO_RESPONSE" desc="Notify user that the file share failed because the receiver neither accepted nor declined the share.">
     The device you're sharing with didn't respond
   </message>
+  <message name="IDS_NEARBY_ERROR_NOT_ENOUGH_SPACE" desc="Notify user that the file can't be shared because there isn't enough disk space to store it. Displayed as a subtitle under a more generic file-couldn't-be-shared message.">
+    Not enough disk space
+  </message>
   <message name="IDS_NEARBY_ERROR_REJECTED" desc="Notify sender that receiver declined the file share.">
     The device you’re trying to share with did not accept
   </message>
diff --git a/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_ERROR_NOT_ENOUGH_SPACE.png.sha1 b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_ERROR_NOT_ENOUGH_SPACE.png.sha1
new file mode 100644
index 0000000..356d0d20
--- /dev/null
+++ b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_ERROR_NOT_ENOUGH_SPACE.png.sha1
@@ -0,0 +1 @@
+7df1723c66d444204194927a80ea52f6a28d85bc
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c7b3e2a..2afad46 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3553,6 +3553,8 @@
       "first_run/first_run.h",
       "first_run/first_run_dialog.h",
       "first_run/first_run_internal.h",
+      "font_access/chrome_font_access_delegate.cc",
+      "font_access/chrome_font_access_delegate.h",
       "font_family_cache.cc",
       "font_family_cache.h",
       "hid/chrome_hid_delegate.cc",
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 3a7cf44..7fc8b2c 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -63,6 +63,7 @@
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/federated_learning/floc_id_provider.h"
 #include "chrome/browser/federated_learning/floc_id_provider_factory.h"
+#include "chrome/browser/font_access/chrome_font_access_delegate.h"
 #include "chrome/browser/font_family_cache.h"
 #include "chrome/browser/gpu/chrome_browser_main_extra_parts_gpu.h"
 #include "chrome/browser/hid/chrome_hid_delegate.h"
@@ -291,6 +292,7 @@
 #include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/client_certificate_delegate.h"
 #include "content/public/browser/file_url_loader.h"
+#include "content/public/browser/font_access_delegate.h"
 #include "content/public/browser/gpu_data_manager.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/navigation_throttle.h"
@@ -4966,6 +4968,13 @@
   return hid_delegate_.get();
 }
 
+content::FontAccessDelegate*
+ChromeContentBrowserClient::GetFontAccessDelegate() {
+  if (!font_access_delegate_)
+    font_access_delegate_ = std::make_unique<ChromeFontAccessDelegate>();
+  return static_cast<content::FontAccessDelegate*>(font_access_delegate_.get());
+}
+
 std::unique_ptr<content::AuthenticatorRequestClientDelegate>
 ChromeContentBrowserClient::GetWebAuthenticationRequestDelegate(
     content::RenderFrameHost* render_frame_host) {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index f4800d0..2ffb17e 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -53,6 +53,7 @@
 
 namespace content {
 class BrowserContext;
+class FontAccessDelegate;
 class QuotaPermissionContext;
 }  // namespace content
 
@@ -92,6 +93,7 @@
 }
 
 class ChromeBluetoothDelegate;
+class ChromeFontAccessDelegate;
 class ChromeHidDelegate;
 class ChromeSerialDelegate;
 
@@ -538,6 +540,7 @@
 #if !defined(OS_ANDROID)
   content::SerialDelegate* GetSerialDelegate() override;
   content::HidDelegate* GetHidDelegate() override;
+  content::FontAccessDelegate* GetFontAccessDelegate() override;
   std::unique_ptr<content::AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
       content::RenderFrameHost* render_frame_host) override;
@@ -789,6 +792,7 @@
 #if !defined(OS_ANDROID)
   std::unique_ptr<ChromeSerialDelegate> serial_delegate_;
   std::unique_ptr<ChromeHidDelegate> hid_delegate_;
+  std::unique_ptr<ChromeFontAccessDelegate> font_access_delegate_;
 #endif
   std::unique_ptr<ChromeBluetoothDelegate> bluetooth_delegate_;
 
diff --git a/chrome/browser/chrome_main_browsertest.cc b/chrome/browser/chrome_main_browsertest.cc
index b15aed2..2ee85087 100644
--- a/chrome/browser/chrome_main_browsertest.cc
+++ b/chrome/browser/chrome_main_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/command_line.h"
 #include "base/process/launch.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -19,9 +20,11 @@
 #include "content/public/test/browser_test.h"
 #include "net/base/filename_util.h"
 
-// These tests don't apply to the Mac version; see GetCommandLineForRelaunch
+// These tests don't apply to Mac or Lacros; see GetCommandLineForRelaunch
 // for details.
-#if !defined(OS_MAC)
+#if defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#error Not supported on this platform.
+#endif
 
 class ChromeMainTest : public InProcessBrowserTest {
  public:
@@ -112,5 +115,3 @@
   ASSERT_EQ(2u, chrome::GetTotalBrowserCount());
   ASSERT_EQ(1u, chrome::GetTabbedBrowserCount(profile));
 }
-
-#endif  // !OS_MAC
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 6414c65c..20f674b 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -3198,8 +3198,6 @@
     "policy/minimum_version_policy_test_helpers.h",
     "printing/printing_stubs.cc",
     "printing/printing_stubs.h",
-    "scoped_set_running_on_chromeos_for_testing.cc",
-    "scoped_set_running_on_chromeos_for_testing.h",
     "secure_channel/fake_nearby_connection_broker.cc",
     "secure_channel/fake_nearby_connection_broker.h",
     "secure_channel/fake_nearby_endpoint_finder.cc",
diff --git a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
index 171012f..ad2ca59 100644
--- a/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/bluetooth/arc_bluetooth_bridge_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
+#include "base/test/scoped_chromeos_version_info.h"
 #include "base/test/task_environment.h"
 #include "components/arc/bluetooth/bluetooth_type_converters.h"
 #include "components/arc/mojom/bluetooth.mojom.h"
@@ -120,8 +121,6 @@
     fake_bluetooth_instance_ = std::make_unique<FakeBluetoothInstance>();
     arc_bridge_service_->bluetooth()->SetInstance(
         fake_bluetooth_instance_.get(), 17);
-    base::SysInfo::SetChromeOSVersionInfoForTest(
-        "CHROMEOS_ARC_ANDROID_SDK_VERSION=28", base::Time::Now());
     WaitForInstanceReady(arc_bridge_service_->bluetooth());
 
     device::BluetoothAdapterFactory::Get()->GetAdapter(base::BindOnce(
@@ -279,6 +278,9 @@
 // Invoke OnDiscoveryStarted to send cached device to BT instance,
 // and check correctness of the Advertising data sent via arc bridge.
 TEST_F(ArcBluetoothBridgeTest, LEDeviceFound) {
+  base::test::ScopedChromeOSVersionInfo version(
+      "CHROMEOS_ARC_ANDROID_SDK_VERSION=28", base::Time::Now());
+
   EXPECT_EQ(0u, fake_bluetooth_instance_->le_device_found_data().size());
   AddTestDevice();
   EXPECT_EQ(3u, fake_bluetooth_instance_->le_device_found_data().size());
@@ -300,8 +302,9 @@
 }
 
 TEST_F(ArcBluetoothBridgeTest, LEDeviceFoundForN) {
-  base::SysInfo::SetChromeOSVersionInfoForTest(
+  base::test::ScopedChromeOSVersionInfo version(
       "CHROMEOS_ARC_ANDROID_SDK_VERSION=27", base::Time::Now());
+
   EXPECT_EQ(0u, fake_bluetooth_instance_->le_device_found_data().size());
   AddTestDevice();
   EXPECT_EQ(3u, fake_bluetooth_instance_->le_device_found_data().size());
diff --git a/chrome/browser/chromeos/crosapi/browser_util.cc b/chrome/browser/chromeos/crosapi/browser_util.cc
index 428c6ca..9acf05d 100644
--- a/chrome/browser/chromeos/crosapi/browser_util.cc
+++ b/chrome/browser/chromeos/crosapi/browser_util.cc
@@ -83,6 +83,7 @@
   params->session_type = environment_provider->GetSessionType();
   params->device_mode = environment_provider->GetDeviceMode();
   params->interface_versions = GetInterfaceVersions();
+  params->default_paths = environment_provider->GetDefaultPaths();
 
   return params;
 }
diff --git a/chrome/browser/chromeos/crosapi/environment_provider.cc b/chrome/browser/chromeos/crosapi/environment_provider.cc
index 28d1721..e0e1f27a 100644
--- a/chrome/browser/chromeos/crosapi/environment_provider.cc
+++ b/chrome/browser/chromeos/crosapi/environment_provider.cc
@@ -4,9 +4,13 @@
 
 #include "chrome/browser/chromeos/crosapi/environment_provider.h"
 
+#include "base/files/file_util.h"
+#include "base/system/sys_info.h"
+#include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profiles_state.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
 #include "chromeos/tpm/install_attributes.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/user_manager/user.h"
@@ -55,4 +59,29 @@
   }
 }
 
+mojom::DefaultPathsPtr EnvironmentProvider::GetDefaultPaths() {
+  mojom::DefaultPathsPtr default_paths = mojom::DefaultPaths::New();
+  // The default paths belong to ash's primary user profile. Lacros does not
+  // support multi-signin.
+  const user_manager::User* user =
+      user_manager::UserManager::Get()->GetPrimaryUser();
+  Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user);
+  if (base::SysInfo::IsRunningOnChromeOS()) {
+    // Typically /home/chronos/u-<hash>/MyFiles.
+    default_paths->documents =
+        file_manager::util::GetMyFilesFolderForProfile(profile);
+    // Typically /home/chronos/u-<hash>/MyFiles/Downloads.
+    default_paths->downloads =
+        file_manager::util::GetDownloadsFolderForProfile(profile);
+  } else {
+    // On developer linux workstations the above functions do path mangling to
+    // support multi-signin which gets undone later in ash-specific code. This
+    // is awkward for Lacros development, so just provide some defaults.
+    base::FilePath home = base::GetHomeDir();
+    default_paths->documents = home.Append("Documents");
+    default_paths->downloads = home.Append("Downloads");
+  }
+  return default_paths;
+}
+
 }  // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/environment_provider.h b/chrome/browser/chromeos/crosapi/environment_provider.h
index c90bb548..d90609b 100644
--- a/chrome/browser/chromeos/crosapi/environment_provider.h
+++ b/chrome/browser/chromeos/crosapi/environment_provider.h
@@ -20,6 +20,11 @@
   // Virtual for tests.
   virtual crosapi::mojom::SessionType GetSessionType();
   virtual crosapi::mojom::DeviceMode GetDeviceMode();
+
+  // Returns the default paths, such as Downloads and Documents (MyFiles).
+  // These are provided by ash because they are part of the device account,
+  // not the Lacros profile.
+  virtual crosapi::mojom::DefaultPathsPtr GetDefaultPaths();
 };
 
 }  // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/file_manager_ash.cc b/chrome/browser/chromeos/crosapi/file_manager_ash.cc
index 2889cf1..7312bfb 100644
--- a/chrome/browser/chromeos/crosapi/file_manager_ash.cc
+++ b/chrome/browser/chromeos/crosapi/file_manager_ash.cc
@@ -20,6 +20,8 @@
 // Lacros does not support multi-signin. Lacros uses /home/chronos/user as the
 // base for all system-level directories but the file manager expects the raw
 // profile path with the /home/chronos/u-{hash} prefix. Clean up the path.
+// TODO(https://crbug.com/1150702): Delete this function after all Lacros
+// clients are on M89. Lacros is switching to use the raw profile path.
 base::FilePath ExpandPath(Profile* primary_profile,
                           const base::FilePath& path) {
   return file_manager::util::ReplacePathPrefix(
diff --git a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc
index 99bcc8e..06a79c2 100644
--- a/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc
+++ b/chrome/browser/chromeos/crosapi/test_mojo_connection_manager.cc
@@ -9,11 +9,13 @@
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
+#include "base/path_service.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h"
 #include "chrome/browser/chromeos/crosapi/browser_util.h"
+#include "chrome/common/chrome_paths.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
@@ -31,6 +33,12 @@
   mojom::DeviceMode GetDeviceMode() override {
     return crosapi::mojom::DeviceMode::kConsumer;
   }
+  mojom::DefaultPathsPtr GetDefaultPaths() override {
+    mojom::DefaultPathsPtr paths = mojom::DefaultPaths::New();
+    base::PathService::Get(chrome::DIR_USER_DOCUMENTS, &paths->documents);
+    base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &paths->downloads);
+    return paths;
+  }
 };
 
 // TODO(crbug.com/1124494): Refactor the code to share with ARC.
diff --git a/chrome/browser/chromeos/file_manager/path_util_unittest.cc b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
index 6d83ac6..9e77c9e4 100644
--- a/chrome/browser/chromeos/file_manager/path_util_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/ptr_util.h"
 #include "base/system/sys_info.h"
+#include "base/test/scoped_running_on_chromeos.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
@@ -21,7 +22,6 @@
 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -49,10 +49,6 @@
 namespace util {
 namespace {
 
-const char kLsbRelease[] =
-    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
-
 class FileManagerPathUtilTest : public testing::Test {
  public:
   FileManagerPathUtilTest() = default;
@@ -93,8 +89,7 @@
             GetDownloadsFolderForProfile(profile_.get()));
 
   // When running inside ChromeOS, it should return /home/u-{hash}/MyFiles.
-  chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
-                                                              base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
   EXPECT_EQ("/home/chronos/u-0123456789abcdef/MyFiles",
             GetMyFilesFolderForProfile(profile_.get()).value());
   EXPECT_EQ("/home/chronos/u-0123456789abcdef/MyFiles/Downloads",
@@ -208,8 +203,7 @@
   base::FilePath myfilesDownloadsFile =
       home.Append("MyFiles/Downloads/file.txt");
   base::FilePath other("/some/other/path");
-  chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
-                                                              base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
   // MyFilesVolume enabled, migrate paths under Downloads.
   EXPECT_TRUE(
       MigrateFromDownloadsToMyFiles(profile_.get(), downloads, &result));
@@ -230,8 +224,7 @@
 TEST_F(FileManagerPathUtilTest, MultiProfileDownloadsFolderMigration) {
   // MigratePathFromOldFormat is explicitly disabled on Linux build.
   // So we need to fake that this is real ChromeOS system.
-  chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
-                                                              base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
 
   // /home/chronos/u-${HASH}/MyFiles/Downloads
   const FilePath kDownloadsFolder =
@@ -688,8 +681,7 @@
 }
 
 TEST_F(FileManagerPathUtilConvertUrlTest, ConvertPathToArcUrl_MyFiles) {
-  chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
-                                                              base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
   GURL url;
   const base::FilePath myfiles = GetMyFilesFolderForProfile(
       chromeos::ProfileHelper::Get()->GetProfileByUserIdHashForTest(
@@ -789,8 +781,7 @@
 }
 
 TEST_F(FileManagerPathUtilConvertUrlTest, ConvertToContentUrls_MyFiles) {
-  chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
-                                                              base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
   const base::FilePath myfiles = GetMyFilesFolderForProfile(
       chromeos::ProfileHelper::Get()->GetProfileByUserIdHashForTest(
           "user@gmail.com-hash"));
diff --git a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
index 18106b4..8e0c783 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
@@ -17,13 +17,13 @@
 #include "base/memory/weak_ptr.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_running_on_chromeos.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #include "chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h"
 #include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
 #include "chrome/browser/chromeos/file_system_provider/fake_extension_provider.h"
 #include "chrome/browser/chromeos/file_system_provider/service.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -44,9 +44,6 @@
 
 namespace file_manager {
 namespace {
-const char kLsbRelease[] =
-    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
 
 class LoggingObserver : public VolumeManagerObserver {
  public:
@@ -1007,8 +1004,7 @@
 
 TEST_F(VolumeManagerTest, VolumeManagerInitializeMyFilesVolume) {
   // Emulate running inside ChromeOS.
-  chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
-                                                              base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
   volume_manager()->Initialize();  // Adds "Downloads"
   std::vector<base::WeakPtr<Volume>> volume_list =
       volume_manager()->GetVolumeList();
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index 617e613..9a1477a 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -901,14 +901,6 @@
   input_view_url_overridden = false;
 }
 
-void InputMethodManagerImpl::StateImpl::ConnectMojoManager(
-    mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager> receiver) {
-  if (!ime_service_connector_) {
-    ime_service_connector_ = std::make_unique<ImeServiceConnector>(profile);
-  }
-  ime_service_connector_->SetupImeService(std::move(receiver));
-}
-
 // ------------------------ InputMethodManagerImpl
 bool InputMethodManagerImpl::IsLoginKeyboard(
     const std::string& layout) const {
@@ -1197,7 +1189,15 @@
 void InputMethodManagerImpl::ConnectInputEngineManager(
     mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager> receiver) {
   DCHECK(state_);
-  state_->ConnectMojoManager(std::move(receiver));
+  ImeServiceConnectorMap::iterator iter =
+      ime_service_connectors_.find(state_->profile);
+  if (iter == ime_service_connectors_.end()) {
+    auto connector_ = std::make_unique<ImeServiceConnector>(state_->profile);
+    iter = ime_service_connectors_
+               .insert(std::make_pair(state_->profile, std::move(connector_)))
+               .first;
+  }
+  iter->second->SetupImeService(std::move(receiver));
 }
 
 bool InputMethodManagerImpl::IsISOLevel5ShiftUsedByCurrentInputMethod() const {
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.h b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
index 64fee58..897291f 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.h
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.h
@@ -129,11 +129,6 @@
     // Reset the input view URL to the default url of the current input method.
     void ResetInputViewUrl();
 
-    // Connect to an InputEngineManager instance in an IME Mojo service.
-    void ConnectMojoManager(
-        mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager>
-            receiver);
-
     // ------------------------- Data members.
     Profile* const profile;
 
@@ -188,8 +183,6 @@
     InputMethodManager::UIStyle ui_style_ =
         InputMethodManager::UIStyle::kNormal;
 
-    std::unique_ptr<ImeServiceConnector> ime_service_connector_;
-
     // Do not forget to update StateImpl::InitFrom(const StateImpl& other) and
     // StateImpl::Dump() when adding new data members!!!
   };
@@ -355,6 +348,12 @@
   using ProfileEngineMap = std::map<Profile*, EngineMap, ProfileCompare>;
   ProfileEngineMap engine_map_;
 
+  // Map a profile to the IME service connector.
+  typedef std::
+      map<Profile*, std::unique_ptr<ImeServiceConnector>, ProfileCompare>
+          ImeServiceConnectorMap;
+  ImeServiceConnectorMap ime_service_connectors_;
+
   content::NotificationRegistrar notification_registrar_;
 
   DISALLOW_COPY_AND_ASSIGN(InputMethodManagerImpl);
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_mode_detector_unittest.cc b/chrome/browser/chromeos/login/demo_mode/demo_mode_detector_unittest.cc
index 1e3e9ef..40ff08599 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_mode_detector_unittest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_mode_detector_unittest.cc
@@ -8,12 +8,12 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_chromeos_version_info.h"
 #include "base/test/scoped_command_line.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/login/ui/mock_login_display_host.h"
-#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/constants/chromeos_switches.h"
@@ -207,7 +207,7 @@
   EXPECT_TRUE(
       base::Time::FromString("Wed, 24 Oct 2018 12:00:00 PDT", &release_time));
 
-  ScopedSetRunningOnChromeOSForTesting version_info(lsb_release, release_time);
+  base::test::ScopedChromeOSVersionInfo version_info(lsb_release, release_time);
 
   ExpectDemoModeWillNotLaunch();
 
diff --git a/chrome/browser/chromeos/login/hwid_checker_unittest.cc b/chrome/browser/chromeos/login/hwid_checker_unittest.cc
index 8d13cf7e..d4d744e4 100644
--- a/chrome/browser/chromeos/login/hwid_checker_unittest.cc
+++ b/chrome/browser/chromeos/login/hwid_checker_unittest.cc
@@ -6,9 +6,9 @@
 
 #include "base/system/sys_info.h"
 #include "base/test/scoped_command_line.h"
+#include "base/test/scoped_running_on_chromeos.h"
 #include "base/time/time.h"
 #include "build/branding_buildflags.h"
-#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
 #include "chromeos/system/fake_statistics_provider.h"
 #include "content/public/common/content_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -139,9 +139,6 @@
 }
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-const char kLsbRelease[] =
-    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
 
 // Test logic for command line "test-type" switch.
 TEST(MachineHWIDCheckerTest, TestSwitch) {
@@ -152,7 +149,7 @@
 
   // THEN IsMachineHWIDCorrect() is always true.
   EXPECT_TRUE(IsMachineHWIDCorrect());
-  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
   EXPECT_TRUE(IsMachineHWIDCorrect());
 
   system::ScopedFakeStatisticsProvider fake_statistics_provider;
@@ -166,7 +163,7 @@
 // Test logic when not running on Chrome OS.
 TEST(MachineHWIDCheckerTest, NotOnChromeOS) {
   // GIVEN the OS is not Chrome OS.
-  ScopedSetRunningOnChromeOSForTesting fake_release("", base::Time());
+  ASSERT_FALSE(base::SysInfo::IsRunningOnChromeOS());
 
   // THEN IsMachineHWIDCorrect() is always true.
   EXPECT_TRUE(IsMachineHWIDCorrect());
@@ -182,7 +179,7 @@
 // Test logic when running on Chrome OS but the HWID is not present.
 TEST(MachineHWIDCheckerTest, OnCrosNoHWID) {
   // GIVEN the OS is Chrome OS.
-  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
 
   // GIVEN the HWID is not present.
   system::ScopedFakeStatisticsProvider fake_statistics_provider;
@@ -210,7 +207,7 @@
                                                "DELL HORIZON MAGENTA DVT 4770");
 
   // THEN IsMachineHWIDCorrect() is always true.
-  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
   EXPECT_TRUE(IsMachineHWIDCorrect());
   fake_statistics_provider.SetMachineStatistic(system::kIsVmKey,
                                                system::kIsVmValueFalse);
@@ -228,7 +225,7 @@
                                                system::kIsVmValueTrue);
 
   // GIVEN the OS is Chrome OS.
-  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
   // THEN IsMachineHWIDCorrect() is always true.
   fake_statistics_provider.SetMachineStatistic(system::kHardwareClassKey,
                                                "INVALID_HWID");
@@ -245,7 +242,7 @@
 // Test logic when HWID is invalid and we're not in a VM.
 TEST(MachineHWIDCheckerTest, InvalidHWIDInVMNotTrue) {
   // GIVEN the OS is Chrome OS.
-  ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease, base::Time());
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
 
   // GIVEN the HWID is invalid.
   system::ScopedFakeStatisticsProvider fake_statistics_provider;
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc
index 08cf22e..5f5dc81 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_files_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/files/file_util.h"
 #include "base/test/bind.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_running_on_chromeos.h"
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/guest_os/guest_os_registry_service.h"
@@ -16,7 +17,6 @@
 #include "chrome/browser/chromeos/plugin_vm/mock_plugin_vm_manager.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager_factory.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
-#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
 #include "chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/test/base/testing_profile.h"
@@ -35,10 +35,6 @@
     testing::StrictMock<base::MockCallback<
         base::OnceCallback<void(const base::FilePath& dir, bool result)>>>;
 
-const char kLsbRelease[] =
-    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
-
 class PluginVmFilesTest : public testing::Test {
  protected:
   void SetUp() override {
@@ -85,7 +81,7 @@
   content::BrowserTaskEnvironment task_environment_;
   TestingProfile profile_;
   FakePluginVmFeatures fake_plugin_vm_features_;
-  chromeos::ScopedSetRunningOnChromeOSForTesting fake_release_{kLsbRelease, {}};
+  base::test::ScopedRunningOnChromeOS running_on_chromeos_;
   std::string app_id_;
   storage::ExternalMountPoints* mount_points_;
   std::string mount_name_;
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.cc b/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.cc
index d737669..300acf6 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.cc
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.cc
@@ -7,13 +7,13 @@
 #include "ash/public/cpp/shelf_item_delegate.h"
 #include "ash/public/cpp/shelf_model.h"
 #include "base/system/sys_info.h"
+#include "base/test/scoped_running_on_chromeos.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/login/users/mock_user_manager.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_features.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h"
 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/common/chrome_features.h"
@@ -32,9 +32,6 @@
 const char kPluginVmLicenseKey[] = "LICENSE_KEY";
 const char kDomain[] = "example.com";
 const char kDeviceId[] = "device_id";
-const char kLsbRelease[] =
-    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
 
 // For adding a fake shelf item without requiring opening an actual window.
 class FakeShelfItemDelegate : public ash::ShelfItemDelegate {
@@ -124,9 +121,8 @@
       account_id, true, user_manager::USER_TYPE_REGULAR);
   scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
       std::move(mock_user_manager));
-  fake_release_ =
-      std::make_unique<chromeos::ScopedSetRunningOnChromeOSForTesting>(
-          kLsbRelease, base::Time::Now());
+  running_on_chromeos_ =
+      std::make_unique<base::test::ScopedRunningOnChromeOS>();
 }
 
 void PluginVmTestHelper::EnablePluginVmFeature() {
diff --git a/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.h b/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.h
index a738d16ec7..b011c9c 100644
--- a/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.h
+++ b/chrome/browser/chromeos/plugin_vm/plugin_vm_test_helper.h
@@ -10,9 +10,11 @@
 
 class TestingProfile;
 
-namespace chromeos {
-class ScopedSetRunningOnChromeOSForTesting;
-}  // namespace chromeos
+namespace base {
+namespace test {
+class ScopedRunningOnChromeOS;
+}  // namespace test
+}  // namespace base
 
 namespace user_manager {
 class ScopedUserManager;
@@ -51,7 +53,7 @@
   TestingProfile* testing_profile_;
   std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  std::unique_ptr<chromeos::ScopedSetRunningOnChromeOSForTesting> fake_release_;
+  std::unique_ptr<base::test::ScopedRunningOnChromeOS> running_on_chromeos_;
 
   DISALLOW_COPY_AND_ASSIGN(PluginVmTestHelper);
 };
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
index 3cf252f..2894a87 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
@@ -6,12 +6,14 @@
 
 #include <vector>
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/privacy_screen_dlp_helper.h"
 #include "base/bind.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_notification_helper.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
+#include "chrome/browser/ui/ash/chrome_capture_mode_delegate.h"
 #include "content/public/browser/visibility.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
@@ -113,19 +115,18 @@
   return false;
 }
 
-void DlpContentManager::OnVideoCaptureStarted(const ScreenshotArea& area,
-                                              base::OnceClosure stop_callback) {
+void DlpContentManager::OnVideoCaptureStarted(const ScreenshotArea& area) {
   if (IsVideoCaptureRestricted(area)) {
-    std::move(stop_callback).Run();
+    if (ash::features::IsCaptureModeEnabled())
+      ChromeCaptureModeDelegate::Get()->InterruptVideoRecordingIfAny();
     return;
   }
-  DCHECK(!running_video_capture_.has_value());
-  running_video_capture_.emplace(
-      std::make_pair(area, std::move(stop_callback)));
+  DCHECK(!running_video_capture_area_.has_value());
+  running_video_capture_area_.emplace(area);
 }
 
 void DlpContentManager::OnVideoCaptureStopped() {
-  running_video_capture_.reset();
+  running_video_capture_area_.reset();
 }
 
 bool DlpContentManager::IsCaptureModeInitRestricted() const {
@@ -367,13 +368,13 @@
 }
 
 void DlpContentManager::CheckRunningVideoCapture() {
-  if (!running_video_capture_.has_value())
+  if (!running_video_capture_area_.has_value())
     return;
-  const auto& area = running_video_capture_->first;
-  auto& stop_callback = running_video_capture_->second;
-  if (IsAreaRestricted(area, DlpContentRestriction::kVideoCapture)) {
-    std::move(stop_callback).Run();
-    running_video_capture_.reset();
+  if (IsAreaRestricted(*running_video_capture_area_,
+                       DlpContentRestriction::kVideoCapture)) {
+    if (ash::features::IsCaptureModeEnabled())
+      ChromeCaptureModeDelegate::Get()->InterruptVideoRecordingIfAny();
+    running_video_capture_area_.reset();
   }
 }
 
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
index 95d6a418..99200b38 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
@@ -70,9 +70,7 @@
       const content::DesktopMediaID& media_id) const;
 
   // Called when video capturing for |area| is started.
-  // |stop_callback| will be called when restricted content will appear there.
-  void OnVideoCaptureStarted(const ScreenshotArea& area,
-                             base::OnceClosure stop_callback);
+  void OnVideoCaptureStarted(const ScreenshotArea& area);
 
   // Called when video capturing is stopped.
   void OnVideoCaptureStopped();
@@ -203,9 +201,8 @@
   // Set of restriction applied to the currently visible content.
   DlpContentRestrictionSet on_screen_restrictions_;
 
-  // The currently running video capture are and callback to stop, if any.
-  base::Optional<std::pair<ScreenshotArea, base::OnceClosure>>
-      running_video_capture_;
+  // The currently running video capture area if any.
+  base::Optional<ScreenshotArea> running_video_capture_area_;
 
   // List of the currently running screen captures.
   std::vector<ScreenCaptureInfo> running_screen_captures_;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_browsertest.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_browsertest.cc
index 2375204..7e936b31 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_browsertest.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_browsertest.cc
@@ -4,11 +4,14 @@
 
 #include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
 
+#include "ash/public/cpp/ash_features.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_test_utils.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/ui/ash/chrome_capture_mode_delegate.h"
 #include "chrome/browser/ui/ash/screenshot_area.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -45,7 +48,17 @@
 
 class DlpContentManagerBrowserTest : public InProcessBrowserTest {
  public:
-  DlpContentManagerBrowserTest() {}
+  DlpContentManagerBrowserTest() = default;
+  ~DlpContentManagerBrowserTest() override = default;
+
+  // InProcessBrowserTest:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(ash::features::kCaptureMode);
+    InProcessBrowserTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(DlpContentManagerBrowserTest, ScreenshotsRestricted) {
@@ -104,8 +117,6 @@
   DlpContentManager* manager = DlpContentManager::Get();
   aura::Window* root_window =
       browser()->window()->GetNativeWindow()->GetRootWindow();
-  ScreenshotArea fullscreen = ScreenshotArea::CreateForPartialWindow(
-      root_window, root_window->bounds());
 
   // Open first browser window.
   Browser* browser1 = browser();
@@ -130,7 +141,9 @@
 
   // Start capture of the whole screen.
   base::RunLoop run_loop;
-  manager->OnVideoCaptureStarted(fullscreen, run_loop.QuitClosure());
+  auto* capture_mode_delegate = ChromeCaptureModeDelegate::Get();
+  capture_mode_delegate->StartObservingRestrictedContent(
+      root_window, root_window->bounds(), run_loop.QuitClosure());
 
   // Move first window with confidential content to make it visible.
   browser1->window()->SetBounds(gfx::Rect(100, 100, 700, 700));
@@ -138,7 +151,7 @@
   // Check that capture was requested to be stopped via callback.
   run_loop.Run();
 
-  manager->OnVideoCaptureStopped();
+  capture_mode_delegate->StopObservingRestrictedContent();
   browser2->window()->Close();
 }
 
@@ -147,8 +160,6 @@
   DlpContentManager* manager = DlpContentManager::Get();
   aura::Window* root_window =
       browser()->window()->GetNativeWindow()->GetRootWindow();
-  ScreenshotArea fullscreen = ScreenshotArea::CreateForPartialWindow(
-      root_window, root_window->bounds());
 
   // Open first browser window.
   Browser* browser1 = browser();
@@ -173,7 +184,9 @@
 
   // Start capture of the whole screen.
   base::RunLoop run_loop;
-  manager->OnVideoCaptureStarted(fullscreen, run_loop.QuitClosure());
+  auto* capture_mode_delegate = ChromeCaptureModeDelegate::Get();
+  capture_mode_delegate->StartObservingRestrictedContent(
+      root_window, root_window->bounds(), run_loop.QuitClosure());
 
   // Move second window to make first window with confidential content visible.
   browser2->window()->SetBounds(gfx::Rect(150, 150, 700, 700));
@@ -181,7 +194,7 @@
   // Check that capture was requested to be stopped via callback.
   run_loop.Run();
 
-  manager->OnVideoCaptureStopped();
+  capture_mode_delegate->StopObservingRestrictedContent();
   browser2->window()->Close();
 }
 
@@ -190,8 +203,6 @@
   DlpContentManager* manager = DlpContentManager::Get();
   aura::Window* root_window =
       browser()->window()->GetNativeWindow()->GetRootWindow();
-  ScreenshotArea fullscreen = ScreenshotArea::CreateForPartialWindow(
-      root_window, root_window->bounds());
 
   // Open first browser window.
   Browser* browser1 = browser();
@@ -216,8 +227,9 @@
 
   // Start capture of the whole screen.
   base::RunLoop run_loop;
-  manager->OnVideoCaptureStarted(
-      fullscreen, base::BindOnce([] {
+  auto* capture_mode_delegate = ChromeCaptureModeDelegate::Get();
+  capture_mode_delegate->StartObservingRestrictedContent(
+      root_window, root_window->bounds(), base::BindOnce([] {
         FAIL() << "Video capture stop callback shouldn't be called";
       }));
 
@@ -225,8 +237,8 @@
   browser1->window()->SetBounds(gfx::Rect(150, 150, 500, 500));
 
   // Check that capture was not requested to be stopped via callback.
-  manager->OnVideoCaptureStopped();
   run_loop.RunUntilIdle();
+  capture_mode_delegate->StopObservingRestrictedContent();
 
   browser2->window()->Close();
 }
diff --git a/chrome/browser/chromeos/printing/history/print_job_info_proto_conversions_unittest.cc b/chrome/browser/chromeos/printing/history/print_job_info_proto_conversions_unittest.cc
index c1ebe5a9..f76d123 100644
--- a/chrome/browser/chromeos/printing/history/print_job_info_proto_conversions_unittest.cc
+++ b/chrome/browser/chromeos/printing/history/print_job_info_proto_conversions_unittest.cc
@@ -20,7 +20,7 @@
 constexpr char kVendorId[] = "iso_a3_297x420mm";
 
 constexpr char kName[] = "name";
-constexpr char kUri[] = "ipp://192.168.1.5";
+constexpr char kUri[] = "ipp://192.168.1.5:631";
 
 constexpr char kTitle[] = "title";
 constexpr char kId[] = "id";
diff --git a/chrome/browser/chromeos/printing/print_management/print_job_info_mojom_conversions_unittest.cc b/chrome/browser/chromeos/printing/print_management/print_job_info_mojom_conversions_unittest.cc
index 97a2510..ffe299c 100644
--- a/chrome/browser/chromeos/printing/print_management/print_job_info_mojom_conversions_unittest.cc
+++ b/chrome/browser/chromeos/printing/print_management/print_job_info_mojom_conversions_unittest.cc
@@ -24,7 +24,7 @@
 namespace {
 
 constexpr char kName[] = "name";
-constexpr char kUri[] = "ipp://192.168.1.5";
+constexpr char kUri[] = "ipp://192.168.1.5:631";
 constexpr char kTitle[] = "title";
 constexpr char kId[] = "id";
 constexpr char kPrinterId[] = "printerId";
diff --git a/chrome/browser/chromeos/printing/printer_configurer.cc b/chrome/browser/chromeos/printing/printer_configurer.cc
index f4139cb4..1cf5537 100644
--- a/chrome/browser/chromeos/printing/printer_configurer.cc
+++ b/chrome/browser/chromeos/printing/printer_configurer.cc
@@ -226,7 +226,7 @@
   base::MD5Context ctx;
   base::MD5Init(&ctx);
   base::MD5Update(&ctx, printer.id());
-  base::MD5Update(&ctx, printer.uri().GetNormalized());
+  base::MD5Update(&ctx, printer.uri().GetNormalized(false));
   base::MD5Update(&ctx, printer.ppd_reference().user_supplied_ppd_url);
   base::MD5Update(&ctx, printer.ppd_reference().effective_make_and_model);
   char autoconf = printer.ppd_reference().autoconf ? 1 : 0;
diff --git a/chrome/browser/chromeos/printing/server_printers_provider_unittest.cc b/chrome/browser/chromeos/printing/server_printers_provider_unittest.cc
index c7b45e3..8399c54 100644
--- a/chrome/browser/chromeos/printing/server_printers_provider_unittest.cc
+++ b/chrome/browser/chromeos/printing/server_printers_provider_unittest.cc
@@ -57,7 +57,7 @@
 Printer Printer2() {
   Printer printer("server-5da95e01216b1fe0ee1de25dc8d0a6e8");
   printer.set_display_name("Color Laser - Name");
-  std::string server("ipps://print-server.intranet.example.com");
+  std::string server("ipps://print-server.intranet.example.com:443");
   printer.set_print_server_uri(server);
   Uri url(
       "ipps://print-server.intranet.example.com/printers/Color Laser - Name");
@@ -151,12 +151,6 @@
   OnServersChanged(true, print_servers);
   task_environment_.RunUntilIdle();
 
-  auto first = server_printers_provider_->GetPrinters().front();
-
-  LOG(INFO) << first.printer.uri().GetNormalized()
-            << " server:" << first.printer.print_server_uri()
-            << " name:" << first.printer.display_name();
-
   EXPECT_THAT(server_printers_provider_->GetPrinters(),
               UnorderedElementsAre(PrinterMatcher(Printer1()),
                                    PrinterMatcher(Printer2())));
diff --git a/chrome/browser/chromeos/printing/specifics_translation_unittest.cc b/chrome/browser/chromeos/printing/specifics_translation_unittest.cc
index d433668..3df67bc1 100644
--- a/chrome/browser/chromeos/printing/specifics_translation_unittest.cc
+++ b/chrome/browser/chromeos/printing/specifics_translation_unittest.cc
@@ -19,7 +19,7 @@
 constexpr char kManufacturer[] = "Manufacturer";
 constexpr char kModel[] = "MODEL";
 constexpr char kMakeAndModel[] = "Manufacturer MODEL";
-constexpr char kUri[] = "ipps://notaprinter.chromium.org/ipp/print";
+constexpr char kUri[] = "ipps://notaprinter.chromium.org:123/ipp/print";
 constexpr char kUuid[] = "UUIDUUIDUUID";
 const base::Time kUpdateTime = base::Time::FromInternalValue(22114455660000);
 
@@ -49,7 +49,7 @@
   EXPECT_EQ(kDisplayName, result->display_name());
   EXPECT_EQ(kDescription, result->description());
   EXPECT_EQ(kMakeAndModel, result->make_and_model());
-  EXPECT_EQ(kUri, result->uri().GetNormalized());
+  EXPECT_EQ(kUri, result->uri().GetNormalized(false));
   EXPECT_EQ(kUuid, result->uuid());
 
   EXPECT_EQ(kEffectiveMakeAndModel,
@@ -125,7 +125,7 @@
   EXPECT_EQ(kManufacturer, result->manufacturer());
   EXPECT_EQ(kModel, result->model());
   EXPECT_EQ(kMakeAndModel, result->make_and_model());
-  EXPECT_EQ(kUri, result->uri().GetNormalized());
+  EXPECT_EQ(kUri, result->uri().GetNormalized(false));
   EXPECT_EQ(kUuid, result->uuid());
 
   EXPECT_TRUE(result->ppd_reference().effective_make_and_model.empty());
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
index 10868ad..138b62e 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
@@ -124,7 +124,7 @@
 
   auto printers = manager_->GetSavedPrinters();
   ASSERT_EQ(1U, printers.size());
-  EXPECT_EQ(kTestUri, printers[0].uri().GetNormalized());
+  EXPECT_EQ(kTestUri, printers[0].uri().GetNormalized(false));
 
   ExpectObservedPrinterIdsAre(observer.saved_printers(), {kTestPrinterId});
 }
diff --git a/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.cc b/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.cc
deleted file mode 100644
index 57059b5..0000000
--- a/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.cc
+++ /dev/null
@@ -1,21 +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.
-
-#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
-
-#include "base/system/sys_info.h"
-
-namespace chromeos {
-
-ScopedSetRunningOnChromeOSForTesting::ScopedSetRunningOnChromeOSForTesting(
-    const std::string& lsb_release,
-    const base::Time& lsb_release_time) {
-  base::SysInfo::SetChromeOSVersionInfoForTest(lsb_release, lsb_release_time);
-}
-
-ScopedSetRunningOnChromeOSForTesting::~ScopedSetRunningOnChromeOSForTesting() {
-  base::SysInfo::SetChromeOSVersionInfoForTest("", base::Time());
-}
-
-}  // namespace chromeos
diff --git a/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h b/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h
deleted file mode 100644
index 84203961..0000000
--- a/chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_SCOPED_SET_RUNNING_ON_CHROMEOS_FOR_TESTING_H_
-#define CHROME_BROWSER_CHROMEOS_SCOPED_SET_RUNNING_ON_CHROMEOS_FOR_TESTING_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-
-namespace chromeos {
-
-// unit_tests must always reset status of modified global singletons,
-// otherwise later tests will fail, because they are run in the same process.
-//
-// This object resets LSB-Release to empty string on destruction, forcing
-// is_running_on_chromeos() to return false.
-class ScopedSetRunningOnChromeOSForTesting {
- public:
-  ScopedSetRunningOnChromeOSForTesting(const std::string& lsb_release,
-                                       const base::Time& lsb_release_time);
-  ~ScopedSetRunningOnChromeOSForTesting();
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ScopedSetRunningOnChromeOSForTesting);
-};
-
-}  // namespace chromeos
-
-#endif  //  CHROME_BROWSER_CHROMEOS_SCOPED_SET_RUNNING_ON_CHROMEOS_FOR_TESTING_H_
diff --git a/chrome/browser/extensions/activity_log/activity_log_unittest.cc b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
index b843c68..9f1b1f9 100644
--- a/chrome/browser/extensions/activity_log/activity_log_unittest.cc
+++ b/chrome/browser/extensions/activity_log/activity_log_unittest.cc
@@ -80,6 +80,9 @@
   void ActivateExtension(const std::string& extension_id) override {}
   void SetActivityLoggingEnabled(bool enabled) override {}
   void UnloadExtension(const std::string& extension_id) override {}
+  void SetSessionInfo(version_info::Channel channel,
+                      mojom::FeatureSessionType session,
+                      bool is_lock_screen_context) override {}
 
   mojo::AssociatedReceiverSet<mojom::Renderer> receivers_;
 };
diff --git a/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc b/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc
index a4ddb8c9..f0b4942 100644
--- a/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/extensions/api/tabs/tabs_util.h"
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/assistant/assistant_state.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
@@ -11,6 +12,7 @@
 #include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
 #include "chrome/browser/chromeos/assistant/assistant_util.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
+#include "chrome/browser/ui/ash/chrome_capture_mode_delegate.h"
 #include "chrome/browser/ui/ash/chrome_screenshot_grabber.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_command_controller.h"
@@ -42,6 +44,11 @@
   // Disallow screenshots in locked fullscreen mode.
   ChromeScreenshotGrabber::Get()->set_screenshots_allowed(!locked);
 
+  // Disable both screenshots and video screen captures via the capture mode
+  // feature.
+  if (ash::features::IsCaptureModeEnabled())
+    ChromeCaptureModeDelegate::Get()->SetIsScreenCaptureLocked(locked);
+
   // Reset the clipboard and kill dev tools when entering or exiting locked
   // fullscreen (security concerns).
   ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
diff --git a/chrome/browser/font_access/chrome_font_access_delegate.cc b/chrome/browser/font_access/chrome_font_access_delegate.cc
new file mode 100644
index 0000000..adca350
--- /dev/null
+++ b/chrome/browser/font_access/chrome_font_access_delegate.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/font_access/chrome_font_access_delegate.h"
+
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/font_access/font_access_chooser.h"
+#include "chrome/browser/ui/font_access/font_access_chooser_controller.h"
+#include "content/public/browser/render_frame_host.h"
+
+ChromeFontAccessDelegate::ChromeFontAccessDelegate() = default;
+ChromeFontAccessDelegate::~ChromeFontAccessDelegate() = default;
+
+std::unique_ptr<content::FontAccessChooser>
+ChromeFontAccessDelegate::RunChooser(
+    content::RenderFrameHost* frame,
+    content::FontAccessChooser::Callback callback) {
+  // TODO(crbug.com/1151464): Decide whether or not to extend/refactor the
+  // bubble view launched by chrome::ShowDeviceChooserDialog() or build a new
+  // one.
+  return std::make_unique<FontAccessChooser>(chrome::ShowDeviceChooserDialog(
+      frame, std::make_unique<FontAccessChooserController>(
+                 frame, std::move(callback))));
+}
diff --git a/chrome/browser/font_access/chrome_font_access_delegate.h b/chrome/browser/font_access/chrome_font_access_delegate.h
new file mode 100644
index 0000000..e6ac3176
--- /dev/null
+++ b/chrome/browser/font_access/chrome_font_access_delegate.h
@@ -0,0 +1,24 @@
+// 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 CHROME_BROWSER_FONT_ACCESS_CHROME_FONT_ACCESS_DELEGATE_H_
+#define CHROME_BROWSER_FONT_ACCESS_CHROME_FONT_ACCESS_DELEGATE_H_
+
+#include "content/public/browser/font_access_chooser.h"
+#include "content/public/browser/font_access_delegate.h"
+
+class ChromeFontAccessDelegate : public content::FontAccessDelegate {
+ public:
+  ChromeFontAccessDelegate();
+  ~ChromeFontAccessDelegate() override;
+
+  ChromeFontAccessDelegate(ChromeFontAccessDelegate&) = delete;
+  ChromeFontAccessDelegate& operator=(ChromeFontAccessDelegate&) = delete;
+
+  std::unique_ptr<content::FontAccessChooser> RunChooser(
+      content::RenderFrameHost* frame,
+      content::FontAccessChooser::Callback callback) override;
+};
+
+#endif  // CHROME_BROWSER_FONT_ACCESS_CHROME_FONT_ACCESS_DELEGATE_H_
diff --git a/chrome/browser/lacros/lacros_chrome_service_delegate_impl.cc b/chrome/browser/lacros/lacros_chrome_service_delegate_impl.cc
index ecae44b..79b12792 100644
--- a/chrome/browser/lacros/lacros_chrome_service_delegate_impl.cc
+++ b/chrome/browser/lacros/lacros_chrome_service_delegate_impl.cc
@@ -4,8 +4,13 @@
 
 #include "chrome/browser/lacros/lacros_chrome_service_delegate_impl.h"
 
+#include "base/check.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/metrics/statistics_recorder.h"
+#include "base/path_service.h"
+#include "base/system/sys_info.h"
 #include "chrome/browser/feedback/feedback_dialog_utils.h"
 #include "chrome/browser/lacros/feedback_util.h"
 #include "chrome/browser/lacros/system_logs/lacros_system_log_fetcher.h"
@@ -15,6 +20,8 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_paths.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
 #include "components/feedback/feedback_report.h"
 #include "components/feedback/feedback_util.h"
 #include "components/feedback/system_logs/system_logs_fetcher.h"
@@ -24,12 +31,64 @@
 
 constexpr char kHistogramsFilename[] = "lacros_histograms.txt";
 
+// Default directories for the ash-side primary user.
+// TODO(https://crbug.com/1150702): Remove these after Lacros drops support for
+// Chrome OS M89.
+constexpr char kMyFilesPath[] = "/home/chronos/user/MyFiles";
+constexpr char kDefaultDownloadsPath[] = "/home/chronos/user/MyFiles/Downloads";
+
+// Overrides the directory specified by |key| but does not try to create the
+// directory if it does not exist. On device, the paths all exist already. For
+// tests and the linux emulator, we don't want to pollute the file system.
+void OverridePath(int key, const base::FilePath& path) {
+  // Ash should only send absolute paths.
+  CHECK(path.IsAbsolute()) << path;
+  base::PathService::OverrideAndCreateIfNeeded(key, path, /*is_absolute=*/true,
+                                               /*create=*/false);
+}
+
 }  // namespace
 
 LacrosChromeServiceDelegateImpl::LacrosChromeServiceDelegateImpl() = default;
 
 LacrosChromeServiceDelegateImpl::~LacrosChromeServiceDelegateImpl() = default;
 
+void LacrosChromeServiceDelegateImpl::OnInitialized(
+    const crosapi::mojom::LacrosInitParams& init_params) {
+  if (init_params.default_paths) {
+    // Set up default paths with values provided by ash.
+    OverridePath(chrome::DIR_USER_DOCUMENTS,
+                 init_params.default_paths->documents);
+    OverridePath(chrome::DIR_DEFAULT_DOWNLOADS,
+                 init_params.default_paths->downloads);
+    // "Safe" just means not configured by the user. On other platforms, the
+    // user can configure their default download directory to unsafe paths
+    // outside of chrome's control, but Chrome OS does not allow arbitrary
+    // path selection.
+    OverridePath(chrome::DIR_DEFAULT_DOWNLOADS_SAFE,
+                 init_params.default_paths->downloads);
+  } else {
+    // On older ash, provide some defaults.
+    // TODO(https://crbug.com/1150702): Remove this block after Lacros drops
+    // support for Chrome OS M89.
+    if (base::SysInfo::IsRunningOnChromeOS()) {
+      // On device, use /home/chronos/user paths.
+      OverridePath(chrome::DIR_USER_DOCUMENTS, base::FilePath(kMyFilesPath));
+      OverridePath(chrome::DIR_DEFAULT_DOWNLOADS,
+                   base::FilePath(kDefaultDownloadsPath));
+      OverridePath(chrome::DIR_DEFAULT_DOWNLOADS_SAFE,
+                   base::FilePath(kDefaultDownloadsPath));
+    } else {
+      // For developers on Linux desktop, just pick reasonable defaults.
+      base::FilePath home_dir = base::GetHomeDir();
+      OverridePath(chrome::DIR_USER_DOCUMENTS, home_dir.Append("Documents"));
+      OverridePath(chrome::DIR_DEFAULT_DOWNLOADS, home_dir.Append("Downloads"));
+      OverridePath(chrome::DIR_DEFAULT_DOWNLOADS_SAFE,
+                   home_dir.Append("Downloads"));
+    }
+  }
+}
+
 void LacrosChromeServiceDelegateImpl::NewWindow() {
   // TODO(crbug.com/1102815): Find what profile should be used.
   Profile* profile = ProfileManager::GetLastUsedProfileAllowedByPolicy();
diff --git a/chrome/browser/lacros/lacros_chrome_service_delegate_impl.h b/chrome/browser/lacros/lacros_chrome_service_delegate_impl.h
index 5cb8b33b..c36c2340 100644
--- a/chrome/browser/lacros/lacros_chrome_service_delegate_impl.h
+++ b/chrome/browser/lacros/lacros_chrome_service_delegate_impl.h
@@ -21,6 +21,8 @@
   ~LacrosChromeServiceDelegateImpl() override;
 
   // chromeos::LacrosChromeServiceDelegate:
+  void OnInitialized(
+      const crosapi::mojom::LacrosInitParams& init_params) override;
   void NewWindow() override;
   std::string GetChromeVersion() override;
   void GetFeedbackData(GetFeedbackDataCallback callback) override;
diff --git a/chrome/browser/lacros/lacros_chrome_service_delegate_impl_unittest.cc b/chrome/browser/lacros/lacros_chrome_service_delegate_impl_unittest.cc
new file mode 100644
index 0000000..7c324be
--- /dev/null
+++ b/chrome/browser/lacros/lacros_chrome_service_delegate_impl_unittest.cc
@@ -0,0 +1,96 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/lacros/lacros_chrome_service_delegate_impl.h"
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/system/sys_info.h"
+#include "base/test/scoped_path_override.h"
+#include "chrome/common/chrome_paths.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kLsbRelease[] =
+    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
+    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
+
+// Overrides base::SysInfo::IsRunningOnChromeOS() to return true.
+// TODO(jamescook): Switch to the shared helper once crrev.com/c/2538285 lands.
+class ScopedIsRunningOnChromeOS {
+ public:
+  ScopedIsRunningOnChromeOS() {
+    base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, base::Time());
+  }
+  ~ScopedIsRunningOnChromeOS() {
+    base::SysInfo::SetChromeOSVersionInfoForTest("", base::Time());
+  }
+};
+
+class LacrosChromeServiceDelegateImplTest : public testing::Test {
+ public:
+  LacrosChromeServiceDelegateImplTest() = default;
+  LacrosChromeServiceDelegateImplTest(
+      const LacrosChromeServiceDelegateImplTest&) = delete;
+  LacrosChromeServiceDelegateImplTest& operator=(
+      const LacrosChromeServiceDelegateImplTest&) = delete;
+  ~LacrosChromeServiceDelegateImplTest() override = default;
+
+ private:
+  ScopedIsRunningOnChromeOS running_on_chromeos_;
+  // Ensure we restore the previous paths for subsequent tests. We don't
+  // actually use these paths, so just point them at /tmp to avoid
+  // ScopedPathOverride from creating unnecessary temp directories.
+  base::ScopedPathOverride documents_override_{chrome::DIR_USER_DOCUMENTS,
+                                               base::FilePath("/tmp")};
+  base::ScopedPathOverride downloads_override_{chrome::DIR_DEFAULT_DOWNLOADS,
+                                               base::FilePath("/tmp")};
+  base::ScopedPathOverride downloads_safe_override_{
+      chrome::DIR_DEFAULT_DOWNLOADS_SAFE, base::FilePath("/tmp")};
+};
+
+TEST_F(LacrosChromeServiceDelegateImplTest, DefaultPaths) {
+  LacrosChromeServiceDelegateImpl delegate_impl;
+
+  // Simulate ash sending some paths at startup.
+  auto default_paths = crosapi::mojom::DefaultPaths::New();
+  default_paths->documents = base::FilePath("/test/documents");
+  default_paths->downloads = base::FilePath("/test/downloads");
+  auto init_params = crosapi::mojom::LacrosInitParams::New();
+  init_params->default_paths = std::move(default_paths);
+  delegate_impl.OnInitialized(*init_params);
+
+  // PathService has the new values.
+  base::FilePath path;
+  base::PathService::Get(chrome::DIR_USER_DOCUMENTS, &path);
+  EXPECT_EQ(path.AsUTF8Unsafe(), "/test/documents");
+  base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &path);
+  EXPECT_EQ(path.AsUTF8Unsafe(), "/test/downloads");
+  base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &path);
+  EXPECT_EQ(path.AsUTF8Unsafe(), "/test/downloads");
+}
+
+// TODO(https://crbug.com/1150702): Delete this test after Lacros drops
+// support for Chrome OS M89.
+TEST_F(LacrosChromeServiceDelegateImplTest, DefaultPathsWithLegacyAsh) {
+  LacrosChromeServiceDelegateImpl delegate_impl;
+
+  // Simulate ash not sending paths at startup.
+  auto init_params = crosapi::mojom::LacrosInitParams::New();
+  ASSERT_TRUE(init_params->default_paths.is_null());
+  delegate_impl.OnInitialized(*init_params);
+
+  // PathService has reasonable values.
+  base::FilePath path;
+  base::PathService::Get(chrome::DIR_USER_DOCUMENTS, &path);
+  EXPECT_EQ(path.AsUTF8Unsafe(), "/home/chronos/user/MyFiles");
+  base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &path);
+  EXPECT_EQ(path.AsUTF8Unsafe(), "/home/chronos/user/MyFiles/Downloads");
+  base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &path);
+  EXPECT_EQ(path.AsUTF8Unsafe(), "/home/chronos/user/MyFiles/Downloads");
+}
+
+}  // namespace
diff --git a/chrome/browser/nearby_sharing/nearby_notification_manager.cc b/chrome/browser/nearby_sharing/nearby_notification_manager.cc
index 12c10b3..5cafa3f4 100644
--- a/chrome/browser/nearby_sharing/nearby_notification_manager.cc
+++ b/chrome/browser/nearby_sharing/nearby_notification_manager.cc
@@ -170,6 +170,20 @@
                         : IDS_NEARBY_NOTIFICATION_SEND_FAILURE_TITLE);
 }
 
+base::Optional<base::string16> GetFailureNotificationMessage(
+    TransferMetadata::Status status) {
+  switch (status) {
+    case TransferMetadata::Status::kTimedOut:
+      return l10n_util::GetStringUTF16(IDS_NEARBY_ERROR_TIME_OUT);
+    case TransferMetadata::Status::kNotEnoughSpace:
+      return l10n_util::GetStringUTF16(IDS_NEARBY_ERROR_NOT_ENOUGH_SPACE);
+    case TransferMetadata::Status::kUnsupportedAttachmentType:
+      return l10n_util::GetStringUTF16(IDS_NEARBY_ERROR_UNSUPPORTED_FILE_TYPE);
+    default:
+      return base::nullopt;
+  }
+}
+
 base::string16 GetConnectionRequestNotificationMessage(
     const ShareTarget& share_target,
     const TransferMetadata& transfer_metadata) {
@@ -545,11 +559,11 @@
     case TransferMetadata::Status::kFailed:
     case TransferMetadata::Status::kNotEnoughSpace:
     case TransferMetadata::Status::kUnsupportedAttachmentType:
-      ShowFailure(share_target);
+      ShowFailure(share_target, transfer_metadata);
       break;
     default:
       if (transfer_metadata.is_final_status())
-        ShowFailure(share_target);
+        ShowFailure(share_target, transfer_metadata);
       break;
   }
 
@@ -742,13 +756,21 @@
   }
 }
 
-void NearbyNotificationManager::ShowFailure(const ShareTarget& share_target) {
+void NearbyNotificationManager::ShowFailure(
+    const ShareTarget& share_target,
+    const TransferMetadata& transfer_metadata) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   message_center::Notification notification =
       CreateNearbyNotification(kNearbyNotificationId);
   notification.set_title(GetFailureNotificationTitle(share_target));
 
+  base::Optional<base::string16> message =
+      GetFailureNotificationMessage(transfer_metadata.status());
+  if (message) {
+    notification.set_message(*message);
+  }
+
   delegate_map_.erase(kNearbyNotificationId);
 
   notification_display_service_->Display(
diff --git a/chrome/browser/nearby_sharing/nearby_notification_manager.h b/chrome/browser/nearby_sharing/nearby_notification_manager.h
index 6e446b5..71c7d37 100644
--- a/chrome/browser/nearby_sharing/nearby_notification_manager.h
+++ b/chrome/browser/nearby_sharing/nearby_notification_manager.h
@@ -79,7 +79,8 @@
   void ShowSuccess(const ShareTarget& share_target);
 
   // Shows a notification for send or receive failure.
-  void ShowFailure(const ShareTarget& share_target);
+  void ShowFailure(const ShareTarget& share_target,
+                   const TransferMetadata& transfer_metadata);
 
   // Closes any currently shown transfer notification (e.g. progress or
   // connection).
diff --git a/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc b/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc
index 469fbcd..b135e431 100644
--- a/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc
@@ -463,19 +463,39 @@
   for (FileAttachment::Type type : param.file_attachments)
     share_target.file_attachments.push_back(CreateFileAttachment(type));
 
-  manager()->ShowFailure(share_target);
+  for (std::pair<TransferMetadata::Status, int> error :
+       std::vector<std::pair<TransferMetadata::Status, int>>{
+           {TransferMetadata::Status::kNotEnoughSpace,
+            IDS_NEARBY_ERROR_NOT_ENOUGH_SPACE},
+           {TransferMetadata::Status::kTimedOut, IDS_NEARBY_ERROR_TIME_OUT},
+           {TransferMetadata::Status::kUnsupportedAttachmentType,
+            IDS_NEARBY_ERROR_UNSUPPORTED_FILE_TYPE},
+           {TransferMetadata::Status::kFailed, 0},
+       }) {
+    manager()->ShowFailure(
+        share_target,
+        TransferMetadataBuilder().set_status(error.first).build());
 
-  base::string16 expected = FormatNotificationTitle(
-      is_incoming ? IDS_NEARBY_NOTIFICATION_RECEIVE_FAILURE_TITLE
-                  : IDS_NEARBY_NOTIFICATION_SEND_FAILURE_TITLE,
-      param, device_name);
+    base::string16 expected_title = FormatNotificationTitle(
+        is_incoming ? IDS_NEARBY_NOTIFICATION_RECEIVE_FAILURE_TITLE
+                    : IDS_NEARBY_NOTIFICATION_SEND_FAILURE_TITLE,
+        param, device_name);
+    base::string16 expected_message =
+        error.second ? l10n_util::GetStringUTF16(error.second)
+                     : base::string16();
 
-  std::vector<message_center::Notification> notifications =
-      GetDisplayedNotifications();
-  ASSERT_EQ(1u, notifications.size());
+    std::vector<message_center::Notification> notifications =
+        GetDisplayedNotifications();
+    ASSERT_EQ(1u, notifications.size());
 
-  const message_center::Notification& notification = notifications[0];
-  EXPECT_EQ(expected, notification.title());
+    const message_center::Notification& notification = notifications[0];
+    EXPECT_EQ(expected_title, notification.title());
+    EXPECT_EQ(expected_message, notification.message());
+
+    notification_tester_->RemoveNotification(
+        NotificationHandler::Type::NEARBY_SHARE, notifications[0].id(),
+        /*by_user=*/true);
+  }
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -610,7 +630,7 @@
 }
 
 TEST_F(NearbyNotificationManagerTest, ShowFailure_ShowsNotification) {
-  manager()->ShowFailure(ShareTarget());
+  manager()->ShowFailure(ShareTarget(), TransferMetadataBuilder().build());
 
   std::vector<message_center::Notification> notifications =
       GetDisplayedNotifications();
diff --git a/chrome/browser/net/chrome_network_delegate_unittest.cc b/chrome/browser/net/chrome_network_delegate_unittest.cc
index d07d963..5c93fda 100644
--- a/chrome/browser/net/chrome_network_delegate_unittest.cc
+++ b/chrome/browser/net/chrome_network_delegate_unittest.cc
@@ -13,6 +13,7 @@
 
 #if defined(OS_CHROMEOS) || BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "base/system/sys_info.h"
+#include "base/test/scoped_running_on_chromeos.h"
 #include "base/time/time.h"
 #include "chrome/browser/download/download_prefs.h"
 #endif
@@ -31,24 +32,6 @@
       base::FilePath::FromUTF8Unsafe(profile_path));
 }
 
-#if defined(OS_CHROMEOS) || BUILDFLAG(IS_CHROMEOS_LACROS)
-const char kLsbRelease[] =
-    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
-
-// TODO(jamescook): Merge with chromeos::ScopedSetRunningOnChromeOSForTesting.
-// We can't use that here due to dependency issues.
-class ScopedIsRunningOnChromeOS {
- public:
-  ScopedIsRunningOnChromeOS() {
-    base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, base::Time());
-  }
-  ~ScopedIsRunningOnChromeOS() {
-    base::SysInfo::SetChromeOSVersionInfoForTest("", base::Time());
-  }
-};
-#endif  // defined(OS_CHROMEOS) || BUILDFLAG(IS_CHROMEOS_LACROS)
-
 }  // namespace
 
 TEST(ChromeNetworkDelegateStaticTest, IsAccessAllowed) {
@@ -103,14 +86,24 @@
   EXPECT_FALSE(IsAccessAllowed("/profile/GCache/v2", "/profile"));
   EXPECT_FALSE(IsAccessAllowed("/home/chronos/user/GCache/v2/id/Logs", ""));
 
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
+  // TODO(https://crbug.com/1150575): The block below seems wrong. We should
+  // probably explicitly allow GetHomeDir().Append("Downloads") in
+  // ChromeNetworkDelegate::IsAccessAllowed on linux-chromeos. The default
+  // download directory should always be accessible, whether on linux-chromeos
+  // or device. The EXPECT_FALSE currently fails because ChromeTestSuite
+  // overrides DIR_USER_DOWNLOADS with a temp dir, which is under /tmp, which is
+  // always allowed.
+
   // $HOME/Downloads is allowed for linux-chromeos, but not on devices.
   const std::string& home_downloads =
       DownloadPrefs::GetDefaultDownloadDirectory().value();
   EXPECT_TRUE(IsAccessAllowed(home_downloads, ""));
   {
-    ScopedIsRunningOnChromeOS is_running_on_chromeos;
+    base::test::ScopedRunningOnChromeOS running_on_chromeos;
     EXPECT_FALSE(IsAccessAllowed(home_downloads, ""));
   }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
 #elif defined(OS_ANDROID)
   // Android allows the following directories.
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index 1a0288c6..8055459 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -210,6 +210,20 @@
       optimization_target, std::move(callback), target_decision);
 }
 
+void OptimizationGuideKeyedService::AddObserverForOptimizationTargetModel(
+    optimization_guide::proto::OptimizationTarget optimization_target,
+    optimization_guide::OptimizationTargetModelObserver* observer) {
+  // TODO(crbug/1146151): Passthrough to prediction manager.
+  NOTREACHED();
+}
+
+void OptimizationGuideKeyedService::RemoveObserverForOptimizationTargetModel(
+    optimization_guide::proto::OptimizationTarget optimization_target,
+    optimization_guide::OptimizationTargetModelObserver* observer) {
+  // TODO(crbug/1146151): Passthrough to prediction manager.
+  NOTREACHED();
+}
+
 void OptimizationGuideKeyedService::RegisterOptimizationTypes(
     const std::vector<optimization_guide::proto::OptimizationType>&
         optimization_types) {
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
index ba6f0a33..80019ae 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -62,6 +62,12 @@
                            float>& client_model_feature_values,
       optimization_guide::OptimizationGuideTargetDecisionCallback callback)
       override;
+  void AddObserverForOptimizationTargetModel(
+      optimization_guide::proto::OptimizationTarget optimization_target,
+      optimization_guide::OptimizationTargetModelObserver* observer) override;
+  void RemoveObserverForOptimizationTargetModel(
+      optimization_guide::proto::OptimizationTarget optimization_target,
+      optimization_guide::OptimizationTargetModelObserver* observer) override;
   void RegisterOptimizationTypes(
       const std::vector<optimization_guide::proto::OptimizationType>&
           optimization_types) override;
diff --git a/chrome/browser/platform_util_lacros.cc b/chrome/browser/platform_util_lacros.cc
index 33a593e..81af39ff 100644
--- a/chrome/browser/platform_util_lacros.cc
+++ b/chrome/browser/platform_util_lacros.cc
@@ -10,6 +10,8 @@
 #include "chrome/browser/platform_util_internal.h"
 #include "chromeos/crosapi/mojom/file_manager.mojom.h"
 #include "chromeos/lacros/lacros_chrome_service_impl.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 
 namespace platform_util {
 namespace {
@@ -24,11 +26,9 @@
   LOG(ERROR) << "Unable to open " << path.AsUTF8Unsafe() << " " << result;
 }
 
-}  // namespace
-
-namespace internal {
-
-void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) {
+// File manager remote can only be accessed on UI thread.
+void OpenItemOnUiThread(const base::FilePath& path, OpenItemType type) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   auto* service = chromeos::LacrosChromeServiceImpl::Get();
   if (service->GetInterfaceVersion(crosapi::mojom::FileManager::Uuid_) < 1) {
     LOG(ERROR) << "Unsupported ash version.";
@@ -46,9 +46,20 @@
   }
 }
 
+}  // namespace
+
+namespace internal {
+
+void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) {
+  // This function is called on a blocking thread, so open on the UI thread.
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&OpenItemOnUiThread, path, type));
+}
+
 }  // namespace internal
 
 void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   auto* service = chromeos::LacrosChromeServiceImpl::Get();
   int interface_version =
       service->GetInterfaceVersion(crosapi::mojom::FileManager::Uuid_);
diff --git a/chrome/browser/printing/cloud_print/test/cloud_print_policy_browsertest.cc b/chrome/browser/printing/cloud_print/test/cloud_print_policy_browsertest.cc
index 2bceddb..f1eba272 100644
--- a/chrome/browser/printing/cloud_print/test/cloud_print_policy_browsertest.cc
+++ b/chrome/browser/printing/cloud_print/test/cloud_print_policy_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/process/launch.h"
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/common/chrome_result_codes.h"
 #include "chrome/common/chrome_switches.h"
@@ -16,10 +17,10 @@
 #include "content/public/common/result_codes.h"
 #include "content/public/test/browser_test.h"
 
-// These tests don't apply to the Mac version; see GetCommandLineForRelaunch
+// These tests don't apply to Mac or Lacros; see GetCommandLineForRelaunch
 // for details.
-#if defined(OS_MAC)
-#error This test file should not be part of the Mac build.
+#if defined(OS_MAC) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#error Not supported on this platform.
 #endif
 
 namespace {
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.html b/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.html
index 3e03dfd8..661ca90 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.html
+++ b/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.html
@@ -84,10 +84,14 @@
         display: inline-flex;
       }
 
+      .content {
+        height: 200px;
+        overflow: scroll;
+      }
+
       #zeroStateContainer {
         display: flex;
         justify-content: center;
-        margin: 8px;
       }
 
       #zeroStateImageContainer {
@@ -147,6 +151,10 @@
         margin-block-end: 8px;
       }
 
+      #explanationContainer {
+        border-bottom: var(--cr-separator-line);
+      }
+
       .explanation-section {
         display: flex;
       }
@@ -159,21 +167,17 @@
       }
 
       #contactList {
-        border-bottom: var(--cr-separator-line);
         border-top: var(--cr-separator-line);
+        padding-block-start: 8px;
       }
 
       #contactsHeading {
         font-size: 1.2rem;
       }
 
-      .contacts-section {
-        height: 72px;
-      }
-
       .contact-item {
         display: flex;
-        height: 50px;
+        height: 40px;
         padding-block-end: 8px;
         padding-block-start: 8px;
       }
@@ -194,8 +198,6 @@
 
       #noContactsContainer {
         align-items: center;
-        border-bottom: var(--cr-separator-line);
-        border-top: var(--cr-separator-line);
         display: flex;
         flex-direction: column;
         padding-block-end: 16px;
@@ -250,7 +252,7 @@
       <!-- Zero state is shown only when no selection has been made yet. -->
       <template is="dom-if"
           if="[[showZeroState_(selectedVisibility, contactsState)]]">
-        <div id="zeroStateContainer">
+        <div id="zeroStateContainer" class="content">
           <div id="zeroStateImageContainer">
             <iron-icon id="zeroStateImage"
                 icon="nearby-images:nearby-device-visibility">
@@ -273,7 +275,7 @@
       <!-- Shown when contacts are currently being downloaded. -->
       <template is="dom-if"
           if="[[inContactsState_(contactsState, ContactsState.PENDING)]]">
-        <div id="contactsPending" class="contacts-section">
+        <div id="contactsPending" class="content">
           <div>$i18n{nearbyShareContactVisibilityDownloading}</div>
         </div>
       </template>
@@ -282,7 +284,7 @@
       <template is="dom-if"
           if="[[inContactsState_(contactsState, ContactsState.FAILED)]]"
           on-dom-change="domChangeDownloadFailed_">
-        <div id="contactsFailed" class="contacts-section">
+        <div id="contactsFailed" class="content">
           <iron-icon id="contactsFailedImage"
               icon="nearby-images:contacts-download-failed">
           </iron-icon>
@@ -292,100 +294,107 @@
         </div>
       </template>
 
+      <!-- Show when the user has downloaded contacts but there are none.-->
       <template is="dom-if"
-          if="[[showExplanationState_(selectedVisibility,
-              contactsState)]]">
-        <div id="explanation">
-
-          <div class="explanation-section">
-            <iron-icon icon="nearby20:radar" class="padded-icon grey-icon">
-            </iron-icon>
-            <div class="viz-description-section">
-              <div class="cr-title-text">
-                $i18n{nearbyShareContactVisibilityOthersTitle}
-              </div>
-              <div class="cr-secondary-text">
-                $i18n{nearbyShareContactVisibilityOthers}
-              </div>
-            </div>
+          if="[[showEmptyState_(selectedVisibility, contactsState)]]">
+        <div id="noContactsContainer" class="content">
+          <iron-icon id="noContactsImage"
+              icon="nearby-images:contacts-empty">
+          </iron-icon>
+          <div class="cr-title-text">
+            $i18n{nearbyShareContactVisibilityNoContactsTitle}
           </div>
+          <div class="cr-secondary-text">
+            $i18n{nearbyShareContactVisibilityNoContactsSubtitle}
+          </div>
+        </div>
+      </template>
 
-          <div class="explanation-section">
-            <iron-icon icon="nearby20:visibility" class="padded-icon grey-icon">
-            </iron-icon>
-            <div class="viz-description-section">
-              <div class="cr-title-text">
-                $i18n{nearbyShareContactVisibilityOwnTitle}
+      <!-- Show when contacts have downloaded and a state is selected. -->
+      <template is="dom-if"
+          if="[[showContactsContainer_(selectedVisibility, contactsState)]]">
+        <div id="explanationContainer" class="content">
+
+          <template is="dom-if"
+              if="[[showExplanationState_(selectedVisibility,
+                  contactsState)]]">
+            <div id="explanation">
+
+              <div class="explanation-section">
+                <iron-icon icon="nearby20:radar" class="padded-icon grey-icon">
+                </iron-icon>
+                <div class="viz-description-section">
+                  <div class="cr-title-text">
+                    $i18n{nearbyShareContactVisibilityOthersTitle}
+                  </div>
+                  <div class="cr-secondary-text">
+                    $i18n{nearbyShareContactVisibilityOthers}
+                  </div>
+                </div>
               </div>
-              <template is="dom-if"
-                  if="[[isVisibility_(selectedVisibility,'all')]]">
-                <div class="cr-secondary-text">
-                  $i18n{nearbyShareContactVisibilityOwnAll}
+
+              <div class="explanation-section">
+                <iron-icon icon="nearby20:visibility"
+                    class="padded-icon grey-icon">
+                </iron-icon>
+                <div class="viz-description-section">
+                  <div class="cr-title-text">
+                    $i18n{nearbyShareContactVisibilityOwnTitle}
+                  </div>
+                  <template is="dom-if"
+                      if="[[isVisibility_(selectedVisibility,'all')]]">
+                    <div class="cr-secondary-text">
+                      $i18n{nearbyShareContactVisibilityOwnAll}
+                    </div>
+                  </template>
+                  <template is="dom-if"
+                      if="[[isVisibility_(selectedVisibility,'some')]]">
+                    <div class="cr-secondary-text">
+                      $i18n{nearbyShareContactVisibilityOwnSome}
+                    </div>
+                  </template>
+                  <template is="dom-if"
+                      if="[[isVisibility_(selectedVisibility,'none')]]">
+                    <div class="cr-secondary-text">
+                      $i18n{nearbyShareContactVisibilityOwnNone}
+                    </div>
+                  </template>
                 </div>
-              </template>
-              <template is="dom-if"
-                  if="[[isVisibility_(selectedVisibility,'some')]]">
-                <div class="cr-secondary-text">
-                  $i18n{nearbyShareContactVisibilityOwnSome}
-                </div>
-              </template>
-              <template is="dom-if"
-                  if="[[isVisibility_(selectedVisibility,'none')]]">
-                <div class="cr-secondary-text">
-                  $i18n{nearbyShareContactVisibilityOwnNone}
+              </div>
+
+            </div>
+          </template>
+
+          <!-- Show when the user has one or more contacts downloaded. -->
+          <template is="dom-if"
+              if="[[showContactList_(selectedVisibility,
+                  contactsState)]]">
+            <div id="contactList">
+              <template is="dom-repeat" items="[[contacts]]">
+                <div class="contact-item"
+                    disabled$="[[isVisibility_(selectedVisibility,'none')]]">
+                  <iron-icon icon="cr:person"
+                      class="padded-icon grey-icon contact-icon">
+                  </iron-icon>
+                  <div>
+                    <div class="cr-title-text">[[item.name]]</div>
+                    <div class="cr-secondary-text">[[item.description]]</div>
+                  </div>
+                  <template is="dom-if"
+                      if="[[showContactCheckBoxes_(selectedVisibility)]]">
+                    <cr-toggle class="contact-toggle" checked="{{item.checked}}"
+                        disabled="[[!isVisibility_(selectedVisibility,'some')]]"
+                        on-click="syncContactToggleState_">
+                    </cr-toggle>
+                  </template>
                 </div>
               </template>
             </div>
-          </div>
+          </template>
 
         </div>
-
-        <!-- Show when the user has downloaded contacts but there are none.-->
-        <template is="dom-if"
-            if="[[showEmptyState_(selectedVisibility, contactsState)]]">
-          <div id="noContactsContainer">
-            <iron-icon id="noContactsImage"
-                icon="nearby-images:contacts-empty">
-            </iron-icon>
-            <div class="cr-title-text">
-              $i18n{nearbyShareContactVisibilityNoContactsTitle}
-            </div>
-            <div class="cr-secondary-text">
-              $i18n{nearbyShareContactVisibilityNoContactsSubtitle}
-            </div>
-          </div>
-        </template>
-
-        <!-- Show when the user has one or more contacts downloaded. -->
-        <template is="dom-if"
-            if="[[showContactList_(selectedVisibility,
-                contactsState)]]">
-          <iron-list id="contactList" items="[[contacts]]"
-              class="contacts-section">
-            <template>
-              <div class="contact-item"
-                  disabled$="[[isVisibility_(selectedVisibility,'none')]]">
-                <iron-icon icon="cr:person"
-                    class="padded-icon grey-icon contact-icon">
-                </iron-icon>
-                <div>
-                  <div class="cr-title-text">[[item.name]]</div>
-                  <div class="cr-secondary-text">[[item.description]]</div>
-                </div>
-                <template is="dom-if"
-                    if="[[showContactCheckBoxes_(selectedVisibility)]]">
-                  <cr-toggle class="contact-toggle" checked="{{item.checked}}"
-                      disabled="[[!isVisibility_(selectedVisibility,'some')]]"
-                      on-click="syncContactToggleState_">
-                  </cr-toggle>
-                </template>
-              </div>
-            </template>
-          </iron-list>
-        </template>
-
-      </div>
-    </template>
+      </template>
+    </div>
   </template>
   <script src="nearby_contact_visibility.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.js b/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.js
index 4fe68c17..1cd2597 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.js
+++ b/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.js
@@ -377,13 +377,28 @@
   /**
    * @param {string} selectedVisibility
    * @param {string} contactsState
+   * @return {boolean} true when explanation container should be shown
+   * @private
+   */
+  showContactsContainer_(selectedVisibility, contactsState) {
+    return this.showExplanationState_(selectedVisibility, contactsState) ||
+        this.showContactList_(selectedVisibility, contactsState);
+  },
+
+  /**
+   * @param {string} selectedVisibility
+   * @param {string} contactsState
    * @return {boolean} true when explanation state should be shown
    * @private
    */
   showExplanationState_(selectedVisibility, contactsState) {
-    return !this.showZeroState_(selectedVisibility, contactsState) &&
-        !this.inContactsState_(contactsState, ContactsState.PENDING) &&
-        !this.inContactsState_(contactsState, ContactsState.FAILED);
+    if (!selectedVisibility || contactsState === ContactsState.PENDING ||
+        contactsState === ContactsState.FAILED) {
+      return false;
+    }
+
+    return selectedVisibility === 'none' ||
+        contactsState === ContactsState.HAS_CONTACTS;
   },
 
   /**
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html b/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html
index 3180ef6..936a299 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html
+++ b/chrome/browser/resources/nearby_share/shared/nearby_shared_icons.html
@@ -51,8 +51,9 @@
         <path d="M0 0h200v200H0z"></path><path d="M150.441 100.728c0 26.949-21.772 48.794-48.63 48.794-26.858 0-48.63-21.845-48.63-48.794 0-26.95 21.772-48.795 48.63-48.795 26.858 0 48.63 21.845 48.63 48.795" fill="#D2E3FC"></path><path d="M176.711 53.44l-12.39-3.317a1.021 1.021 0 0 1-.722-1.25l3.32-12.383a1.022 1.022 0 0 1 1.252-.722l12.39 3.317c.544.146.868.707.722 1.251l-3.32 12.383a1.022 1.022 0 0 1-1.252.722" fill="#34A853"></path><path d="M31.832 148.371l-9.514 5.538c-3.553 2.068-4.77 6.644-2.72 10.223 2.053 3.584 6.603 4.813 10.161 2.742l9.514-5.538c3.48-2.026 4.718-6.454 2.844-9.998l-.124-.225c-2.053-3.584-6.603-4.813-10.161-2.742zm8.425 3.736c1.505 2.627.612 5.986-1.99 7.5l-9.514 5.539c-2.597 1.511-5.918.614-7.42-2.008-1.504-2.627-.61-5.986 1.991-7.5l9.514-5.539c2.529-1.471 5.743-.66 7.297 1.804l.122.204z" fill="#EA4335" fill-rule="nonzero"></path><path d="M26.693 54.6l-5.899-5.896a2.743 2.743 0 0 1-.711-2.653l2.159-8.054a2.745 2.745 0 0 1 1.943-1.941l8.058-2.158a2.75 2.75 0 0 1 2.655.71l5.9 5.896c.693.694.965 1.705.71 2.653L39.35 51.21a2.746 2.746 0 0 1-1.943 1.942l-8.058 2.158a2.751 2.751 0 0 1-2.655-.71" fill="#FBBC05"></path><path d="M26.536 141.312l-5.553-4.145a.552.552 0 0 1-.113-.773l4.149-5.55a.552.552 0 0 1 .773-.112l5.554 4.145a.552.552 0 0 1 .112.773l-4.149 5.55a.552.552 0 0 1-.773.112" fill="#F882FF"></path><path d="M102.004 29.332c-39.453 0-71.437 31.965-71.437 71.396 0 39.43 31.984 71.395 71.437 71.395 39.453 0 71.437-31.965 71.437-71.395 0-39.431-31.983-71.396-71.437-71.396zm0 2c38.35 0 69.437 31.07 69.437 69.396s-31.088 69.395-69.437 69.395-69.437-31.07-69.437-69.395c0-38.326 31.088-69.396 69.437-69.396z" fill="#4285F4" fill-rule="nonzero"></path><path d="M104.18 73.085a8.864 8.864 0 0 0-4.738 0l-19.099 7.12c-1.232.342-2.075 1.387-2.075 2.572v14.13c0 11.668 5.686 22.805 15.688 29.978 3.332 2.39 6.297 3.737 7.855 3.737 1.558 0 4.523-1.348 7.855-3.737 10.003-7.173 15.688-18.31 15.688-29.977V82.777c0-1.185-.842-2.23-2.075-2.573l-19.099-7.12z" fill="#4285F4"></path><path d="M101.768 94.59c-3.19 0-5.789 2.668-5.789 5.945 0 3.276 2.599 5.945 5.79 5.945 3.19 0 5.789-2.669 5.789-5.945 0-3.277-2.6-5.945-5.79-5.945zm-.193 9.115c-1.81 0-3.28-1.51-3.28-3.368 0-1.86 1.47-3.37 3.28-3.37 1.81 0 3.281 1.51 3.281 3.37 0 1.859-1.47 3.368-3.28 3.368zm.193-12.682c-6.14 0-11.384 3.944-13.508 9.512 2.124 5.567 7.368 9.512 13.508 9.512 6.14 0 11.384-3.945 13.509-9.512-2.125-5.568-7.368-9.512-13.509-9.512zm-.193 16.646c-4.726 0-8.942-2.763-11-7.134 2.058-4.371 6.274-7.134 11-7.134 4.727 0 8.942 2.763 11 7.134-2.058 4.37-6.273 7.134-11 7.134z" fill="#FFF" fill-rule="nonzero"></path>
       </g>
 
-      <path id="a" d="M.008.144h30.71v26.652H.007z"/><path d="M3.798.549A5.982 5.982 0 0 0 .812 8.458l5.764 12.741a5.97 5.97 0 0 0 7.9 2.99 5.982 5.982 0 0 0 2.987-7.909L11.697 3.54A5.96 5.96 0 0 0 3.798.55" id="c"/><path d="M1.258 10.212c-1.955 7.205 1.886 14.751 8.58 16.855l.196.063c6.694 2.104 13.705-2.03 15.66-9.234C27.65 10.691 23.81 3.145 17.117 1.04L16.92.978a11.78 11.78 0 0 0-3.546-.549C7.907.43 2.868 4.28 1.258 10.212z" id="e"/><path id="g" d="M0 .13h36.463v36.73H0z"/><path d="M25.094.245L.89 9.568c-.687.265-1.037 1.056-.782 1.769l8.985 25.114c.255.712 1.018 1.075 1.704.811L35 27.939c.687-.265 1.037-1.056.782-1.769L26.798 1.056A1.329 1.329 0 0 0 25.555.16c-.153 0-.31.028-.461.086" id="i"/>
-      <g id="nearby-onboarding-splash" fill="none" fill-rule="evenodd"><path d="M0 0h200v200H0z"/><path d="M110.893 164.142l-6.065 12.062c1.606.235 2.96.506 4.06.814 4.544 1.27 10.262 2.852 15.561 5.64 7.226 3.802 10.566 4.087 10.02.856-3.964-3.923-4.913-5.674-2.848-5.254 2.065.42 5.403 1.107 10.015 2.062.624-.243.804-.791.539-1.644-.208-.67-3.266-2.054-4.064-3.237-.146-.216.396-.215 1.624.003 2.632.41 4.12.547 4.465.41 1.163-.466-.097-2.017-.003-2.827.054-.468.801-.549 1.216-.806.288-.179.267-.554.36-.741.42-.842-3.068-1.44-2.8-2.494.274-1.075 4.274-2.606 1.607-3.406-5.282-1.583-10.813.808-16.629.56-3.877-.166-9.563-.832-17.058-1.998z" fill="#D2E3FC"/><path d="M141.354 171.58l.57.095 1.311.246c1.522.274 1.905.19 2.129-.434.02-.056-.109-.25-.638-.581-.664-.415-1.721-.839-3.074-1.237a.805.805 0 1 1 .454-1.543l.57.174c3.14.992 4.78 2.126 4.204 3.732-.561 1.563-1.636 1.851-3.696 1.512l-1.341-.249c-.275-.05-.504-.09-.735-.126a12.992 12.992 0 0 0-2.067-.174 32.862 32.862 0 0 0-4.908.396.806.806 0 0 1-.92-.672.803.803 0 0 1 .673-.918 34.47 34.47 0 0 1 5.148-.414c.753-.002 1.497.064 2.32.193z" fill="#4682F4" fill-rule="nonzero"/><path d="M134.892 176.05a.803.803 0 1 1 .003-1.609l1.163.008c.546.006.966.015 1.232.028l.092.005a18.507 18.507 0 0 1 2.379.322l.654.138c.519.115 1.48.336 1.707.384l.395.079c1.162.217 1.48.119 1.591-.306.206-.791-.878-1.532-3.807-2.045a.804.804 0 1 1 .276-1.585c3.753.658 5.634 1.944 5.09 4.037-.428 1.64-1.63 1.868-3.876 1.394l-1.942-.436a18.28 18.28 0 0 0-2.16-.345l-.401-.031-.424-.016a88.28 88.28 0 0 0-1.972-.022z" fill="#4682F4" fill-rule="nonzero"/><path d="M141.644 179.567c.18-.31.013-.618-.823-1.159-1.14-.736-3.19-1.51-6.125-2.293a.806.806 0 0 1-.571-.986.803.803 0 0 1 .984-.57c3.084.825 5.27 1.649 6.583 2.498 1.565 1.01 2.102 2.273 1.225 3.504-.514.722-1.262.731-2.752.368l-.552-.143c-.57-.153-1.557-.428-1.585-.435a32.331 32.331 0 0 0-2.211-.532 19.356 19.356 0 0 0-1.642-.258c-1.193-.132-2.08-.185-2.641-.16l-.234.016-.035.06a.798.798 0 0 1-.223.222l-.095.054a.805.805 0 0 1-1.075-.374c-.503-1.04.296-1.495 1.408-1.576.672-.05 1.687.005 3.07.158.58.065 1.169.158 1.778.28.61.12 1.21.262 1.896.443l2.295.625c.494.126.85.198 1.108.22l.186.012c.018.002.028.006.031.013v.013zm-13.848-1.352a.806.806 0 0 1-1.006-.534c-.452-1.475-.264-2.833.571-3.995.9-1.253 2.656-2.153 5.813-3.27l3.54-1.22.187-.068c.695-.257 1.397-.438 2.698-.713l1.718-.36c1.35-.292 2.061-.494 2.677-.773.748-.34.941-.604.836-.965-.172-.587-3.21-.75-7.838-.247l-.935.106c-.32.038-.647.079-.985.122l-5.142.7c-.596.077-1.065.135-1.254.15-2.193.181-7.982-.704-17.529-2.658a.804.804 0 1 1 .323-1.576l2.02.409c8.168 1.631 13.228 2.373 15.054 2.223l.305-.033c1.269-.15 5.264-.712 5.497-.742l1.029-.132c.332-.04.656-.08.973-.116l1.221-.13c5.403-.544 8.286-.313 8.807 1.474.376 1.29-.324 2.248-1.715 2.88l-.285.122c-.624.255-1.323.45-2.45.7l-2.466.52c-.771.17-1.286.304-1.735.454l-1.52.534c-1.005.347-2.173.743-2.13.728-3.045 1.058-4.745 1.9-5.405 2.818-.533.743-.649 1.579-.34 2.588a.804.804 0 0 1-.534 1.004z" fill="#4682F4" fill-rule="nonzero"/><path d="M135.161 182.503c.791 1.153.728 2.38-.312 3.488-1.426 1.52-4.087.752-10.188-2.074l-3.96-1.853a66.2 66.2 0 0 0-2.227-.987c-3.772-1.591-8.276-2.854-13.51-3.787a.806.806 0 0 1-.652-.933.803.803 0 0 1 .932-.652c5.064.903 9.474 2.113 13.233 3.632l.62.257c.83.349 1.486.641 2.657 1.185l3.215 1.507 1.046.481c4.608 2.09 7.048 2.774 7.66 2.122.511-.545.531-.935.16-1.477-.356-.519-.985-1.053-2.102-1.825l-.786-.532c-1.524-1.024-2.835-2.028-3.934-3.013a.805.805 0 1 1 1.072-1.198 33.243 33.243 0 0 0 3.277 2.548l1.027.695c1.343.913 2.13 1.548 2.662 2.262l.11.154z" fill="#4682F4" fill-rule="nonzero"/><path d="M92.163 196.078c.982.182 2.643.273 3.633.406.83.112 1.619-.175 2.423-.405.803-.232.747-.847.919-1.664.695-.113 1.406-.232 2.02-.574.616-.342 1.125-.96 1.137-1.661.007-.395-.558-.19-.444-.568.073-.236.66-1.04.785-1.255.527-.906.161-2.213-.76-2.716.363-.226.476-.726.358-1.135-.118-.409-.42-.74-.742-1.018-2.532-2.183-6.36-1.812-9.443-3.116.583-.446 1.347-.564 2.022-.851.675-.287 1.338-.898 1.241-1.624-.073-.554-.585-.969-1.125-1.117-.542-.148-1.115-.088-1.674-.049-3.745.265-7.532-.417-11.249.108-.252.037-.515.083-.717.238-.27.21-3.883-1.106-3.963-.774-.482 1.974-7.031-4.966-7.839-4.154-.807.812-11.512 9.186-11.706 10.15-.164.811 14.052 7.893 15.264 8.424 2.096.917 12.76 2.606 14.612 2.948 1.548.287 3.7.12 5.248.407z" fill="#D2E3FC"/><path d="M87.628 192.91a.806.806 0 0 1 .445-1.548c4.335 1.25 7.827 1.874 10.84 1.8.923-.021 1.458-.12 1.76-.356.22-.172.26-.694.048-.846a.806.806 0 0 1-.184-1.124.803.803 0 0 1 1.122-.185c1.153.829 1.014 2.634.003 3.425-.67.523-1.447.667-2.71.697-3.209.078-6.845-.572-11.324-1.863zm-.619-10.486a.806.806 0 0 1 .433-1.551l.341.091c1.706.442 6.23 1.446 7.773 1.731.713.132 1.388.288 2.361.538l.836.219c1.116.292 1.938.623 2.638 1.14.524.388.923.874 1.114 1.442.183.543.153 1.141-.125 1.632-.527.932-1.542 1.258-2.914 1.245a10.974 10.974 0 0 1-1.039-.064l-1.324-.156c-.403-.053-.82-.12-1.435-.226l-1.496-.259-1.511-.258a35.12 35.12 0 0 1-3.64-.82.806.806 0 0 1 .433-1.552c1.282.359 2.228.567 3.841.848l3.05.523c.51.085.867.137 1.248.183l.719.084c.47.055.826.083 1.169.086.843.008 1.344-.153 1.5-.428.038-.067.044-.192 0-.324-.074-.22-.266-.454-.545-.66-.445-.328-1.011-.573-1.789-.795l-.8-.214-.913-.233c-.55-.136-1-.238-1.446-.324l-.639-.122c-2.01-.396-6.554-1.415-7.84-1.776z" fill="#4285F4" fill-rule="nonzero"/><path d="M88.364 189.969a.806.806 0 0 1 .423-1.554c2.262.617 6.487 1.543 8.684 1.896 1.54.248 2.447.295 3.257.092.322-.081.51-.183.722-.395.12-.119.2-.406.171-.674-.037-.348-.215-.568-.462-.665l-.096-.03a.806.806 0 0 1 .39-1.563c.924.232 1.649.99 1.767 2.084.078.723-.14 1.497-.636 1.99-.425.423-.87.666-1.466.815-1.087.273-2.155.217-3.902-.064-2.255-.362-6.536-1.3-8.852-1.932zm-20.049-15.742a.806.806 0 0 1-.207-1.12.803.803 0 0 1 1.118-.207l.719.491c5.425 3.677 8.916 5.295 10.089 4.886 1.98-.69 3.677-.83 6.624-.68l4.398.28.608.03.294-.001.671-.012c.994-.007 1.669.11 2.315.521.71.453 1.16 1.257 1.056 2.078-.074.587-.414 1.102-.902 1.477-.446.344-1.076.6-1.943.84l-.503.13c-.52.126-1.46.338-1.569.365a.805.805 0 0 1-.394-1.562l1.556-.361.211-.052c.82-.212 1.391-.428 1.662-.637.172-.132.272-.284.287-.401.02-.154-.107-.38-.325-.518-.287-.183-.648-.259-1.235-.268l-.204-.001-.804.013c-.117 0-.213 0-.312-.004l-1.364-.076-2.767-.185c-3.14-.198-4.788-.125-6.595.465l-.237.08c-1.954.681-5.89-1.188-12.247-5.571zm-11.4 11.051a.806.806 0 0 1 .89-1.342c5.989 3.979 11.392 6.766 16.202 8.364l.532.172c3.514 1.113 6.104 1.57 11.318 2.09l4.4.419c1.719.173 2.897.325 4.1.538l.242.044c1.209.22 1.785.266 2.39.132.619-.137 1.118-.537 1.264-.985l.028-.113a.804.804 0 1 1 1.581.295c-.221 1.193-1.273 2.098-2.527 2.376-.756.167-1.398.149-2.457-.022l-1.265-.22c-1.168-.191-2.397-.337-4.238-.513l-2.742-.256c-5.96-.571-8.723-1.028-12.579-2.25-5.1-1.614-10.81-4.525-17.139-8.73z" fill="#4285F4" fill-rule="nonzero"/><g fill-rule="nonzero"><path d="M78.868 136.46l-.531.008c-.323.008-.621.021-.894.039l-.49.038-.12.016-9.915.101-.382-.018c-3.068-.115-6.78.197-10.366 1.302-2.21.682-4.172 1.626-5.815 2.868-3.822 2.89-5.187 7.466-6.244 17.638-.966 9.309 1.07 16.723 5.068 22.384a24.107 24.107 0 0 0 3.667 4.132c.76.675 1.36 1.117 1.72 1.34.266.167.602.16.861-.005l.093-.07 14.23-12.368c.335-.292.37-.8.078-1.135a.806.806 0 0 0-1.049-.144l-.087.066-13.753 11.953-.117-.088c-.21-.16-.432-.341-.667-.542l-.24-.209a22.505 22.505 0 0 1-3.42-3.857c-3.777-5.348-5.707-12.376-4.781-21.291l.161-1.49c.988-8.676 2.303-12.65 5.453-15.032 1.481-1.12 3.276-1.983 5.318-2.613 3.194-.985 6.534-1.306 9.34-1.247l.704.024.18.009 10.07-.106.264-.026.102-.008c.311-.025.67-.044 1.071-.054a25.64 25.64 0 0 1 3.7.18c3.7.447 7.071 1.653 9.76 3.825 1.895 1.531 3.246 3.777 4.756 7.685l.373.991c.454 1.273 3.058 8.852 4.112 11.6a.805.805 0 0 0 .544.49l.109.02 8.336 1.035-7.493 14.353-.253-.054c-2.408-.554-5.444-2.118-9.083-4.702l-.422-.302c-4.065-2.937-5.343-7.106-3.845-12.686a.806.806 0 0 0-1.556-.416c-1.663 6.198-.174 11.06 4.457 14.405 4.444 3.21 8.108 5.03 11.03 5.445a.81.81 0 0 0 .77-.329l.058-.096 8.3-15.901a.804.804 0 0 0-.513-1.15l-.101-.02-9.004-1.117-.494-1.347a368.1 368.1 0 0 1-.94-2.636l-2.547-7.277c-1.739-4.697-3.268-7.372-5.58-9.24-2.953-2.386-6.604-3.692-10.58-4.173a27.422 27.422 0 0 0-3.403-.198z" fill="#4285F4"/><path d="M61.551 153.314l-.1.002a.805.805 0 0 0-.742.764l.002.1 1.185 15.019a.802.802 0 0 0 .285.552l.087.064 6.522 4.123a.803.803 0 1 0 .95-1.294l-.088-.065-6.18-3.908-1.156-14.617a.805.805 0 0 0-.657-.728l-.108-.012z" fill="#4682F4"/><path d="M63.276 165.48a.805.805 0 0 0-.101 1.601l.101.007h22.233a.805.805 0 0 0 .101-1.602l-.1-.006H63.275z" fill="#4682F4"/></g><path d="M69.765 131.356h5.865l1.533 6.908c-.569 1.266-2.293 2.186-4.332 2.186-2.094 0-3.855-.97-4.376-2.287l1.31-6.807z" fill="#CFE3FF"/><path d="M75.63 130.552h-5.865a.805.805 0 0 0-.791.652l-1.31 6.807a.803.803 0 0 0 .042.447c.666 1.684 2.762 2.796 5.125 2.796 2.296 0 4.343-1.05 5.067-2.661a.803.803 0 0 0 .051-.503l-1.533-6.908a.805.805 0 0 0-.786-.63zm-.647 1.608l1.329 5.987-.027.046c-.557.839-1.897 1.453-3.454 1.453l-.228-.004c-1.506-.058-2.771-.69-3.272-1.52l-.036-.065 1.135-5.897h4.553z" fill="#4285F4" fill-rule="nonzero"/><path d="M76.445 122.877a6.147 6.147 0 0 1 .694 8.688 6.202 6.202 0 0 1-8.72.692 6.147 6.147 0 0 1-.694-8.689 6.202 6.202 0 0 1 8.72-.69z" fill="#CFE3FF"/><path d="M67.112 123.046a6.95 6.95 0 0 0 .785 9.823 7.008 7.008 0 0 0 9.855-.781 6.95 6.95 0 0 0-.785-9.823 7.008 7.008 0 0 0-9.855.781zm8.811.444a5.344 5.344 0 0 1 .604 7.553 5.396 5.396 0 0 1-7.587.601 5.344 5.344 0 0 1-.603-7.553 5.396 5.396 0 0 1 7.586-.601z" fill="#4285F4" fill-rule="nonzero"/><path fill="#4285F4" d="M66.246 124.853h11.057l-.299-2.25-4.155-1.117-2.297.041-2.571 1.65z"/><path d="M81.62 121.472c-.335.523-.8 1.009-1.21 1.469l-1.697 1.912h-6.757l2.306-2.545c1.127-1.244 1.971-2.238 3.384-3.065 1.483-.869 3.377-1.116 4.163.034l.024.037c.355.55.274 1.401-.212 2.158" fill="#0F3EC1"/><path d="M69.625 127.974c.4-.082.79.147.921.52l.03.106c.083.405.424.766.824.886l.121.03a.804.804 0 1 1-.275 1.584 2.827 2.827 0 0 1-2.249-2.178.804.804 0 0 1 .628-.948z" fill="#4285F4" fill-rule="nonzero"/><g transform="translate(87.766 169.167)"><path d="M0 12.79L2.687 1.536A2 2 0 0 1 4.632 0h11.823a2 2 0 0 1 1.928 2.533l-3.88 14.02L0 12.79z" fill="#4682F4"/><ellipse fill="#FFF" cx="6.285" cy="2.818" rx="1.007" ry="1.001"/></g><path d="M89.666 149.902c.488.11.795.595.685 1.082l-.412 1.874a39.22 39.22 0 0 0-.172.87 23.414 23.414 0 0 0-.372 2.834c-.129 2.02-.374 3.896-.757 5.674-.484 2.245-1.087 3.591-2.423 4.63a.907.907 0 0 1-1.271-.159.904.904 0 0 1 .158-1.27c.909-.706 1.363-1.72 1.764-3.582.318-1.475.537-3.028.67-4.69l.05-.72c.053-.81.153-1.625.303-2.51l.154-.84c.123-.641.252-1.23.54-2.509a.906.906 0 0 1 .97-.702l.113.018zM159.996 35.664h-5.559a.805.805 0 0 0-.737.48l-1.71 3.862a.804.804 0 0 0 .734 1.13l9.034.036a.804.804 0 0 0 .737-1.135l-1.765-3.9a.805.805 0 0 0-.734-.473zm-.522 1.608l1.036 2.286-6.55-.027 1.001-2.26h4.513z" fill="#4285F4" fill-rule="nonzero"/><path d="M161.948 41.21l-.376-.071-1.578.03c-2.899.047-5.226.04-6.982-.021l-.323-.012c-12.927-.517-20.131 5.42-29.147 22.158a.804.804 0 0 1-.397.36l-.105.036-19.632 5.21 5.93 13.082.087.008c7.582.697 14.44-1.076 20.608-5.325l.439-.308c6.424-4.572 10.44-10.685 12.07-18.37a.805.805 0 0 1 1.576.333c-1.714 8.08-5.959 14.541-12.71 19.347-6.75 4.804-14.327 6.763-22.696 5.868a.806.806 0 0 1-.599-.374l-.05-.094-6.515-14.37a.803.803 0 0 1 .427-1.074l.1-.034 20.214-5.364.112-.206c8.97-16.498 16.507-22.78 29.546-22.514l1.744.054c1.767.045 4.052.043 6.855-.007l1.075-.021.147.01.31.056.39.08c.139.028.286.06.443.096.888.203 1.871.466 2.924.799 3.008.95 5.976 2.264 8.694 4.002 7.825 5.004 12.045 12.458 10.865 22.73-1.18 10.273-7.632 17.093-17.468 21.09-3.416 1.388-7.007 2.33-10.556 2.913a49.07 49.07 0 0 1-3.42.443l-.511.043-.742.049a.806.806 0 0 1-.826-.478l-.037-.104-5.588-19.616a.806.806 0 1 1 1.512-.542l.038.103 5.41 18.992.344-.026.251-.023a47.465 47.465 0 0 0 3.307-.428c3.438-.564 6.915-1.477 10.21-2.816 9.339-3.794 15.371-10.171 16.475-19.784 1.105-9.613-2.793-16.499-10.132-21.192-2.588-1.655-5.43-2.913-8.311-3.823a36.183 36.183 0 0 0-2.365-.662l-.431-.102a25.57 25.57 0 0 0-.606-.13z" fill="#4285F4" fill-rule="nonzero"/><g fill="#4285F4" fill-rule="nonzero"><path d="M152.34 90.536c.23.38.11.875-.27 1.106l-3.443 2.127-.68.406-.49.283c-1.488.848-3.158 1.685-5.746 2.87l-.569.254c-1.158.505-1.873.71-2.765.739-1.447.046-2.845-.649-3.411-1.88a.804.804 0 1 1 1.408-.77l.053.096c.268.583 1.048.97 1.899.944.696-.022 1.31-.216 2.46-.732l.256-.116c2.689-1.231 4.354-2.072 5.857-2.94l.451-.265.52-.312c.554-.34 2.592-1.611 3.366-2.081a.803.803 0 0 1 1.104.27z"/><path d="M145.072 91.074a.806.806 0 0 1-.333 1.09l-3.63 1.972c-.836.448-1.504.79-2.17 1.108-1.928.919-3.64 1.517-5.47 1.857-1.122.208-2.188.11-3.06-.383-1.26-.711-1.826-2.492-.811-3.658a.803.803 0 0 1 1.134-.078c.335.292.37.8.078 1.136-.266.306-.063.942.388 1.197.502.283 1.196.347 1.977.202 1.68-.312 3.263-.865 5.073-1.727l.484-.236a59.293 59.293 0 0 0 1.936-1.01l2.845-1.551.471-.253a.804.804 0 0 1 1.088.334z"/><path d="M143.99 88.052a.806.806 0 0 1-.31 1.095l-.713.41-1.405.829c-.92.536-1.538.866-2.245 1.187-1.85.84-3.59 1.482-5.303 1.933l-.75.186-.715.166c-.634.14-1.054.204-1.551.231-1.406.08-2.565-.275-3.268-1.278-.648-.92-.53-2.234.165-3.135 1.224-1.586 3.911-2.467 7.583-2.904a.805.805 0 0 1 .19 1.6c-3.254.387-5.635 1.168-6.501 2.289-.288.373-.336.919-.122 1.224.318.453.954.647 1.863.596.446-.025.84-.09 1.529-.249l.633-.15c1.79-.428 3.615-1.083 5.583-1.976l.35-.164c.58-.278 1.156-.598 2.01-1.1l.97-.575c.368-.217.627-.366.912-.526a.803.803 0 0 1 1.094.31zm4.263-12.866a.805.805 0 0 1-.813.795c-3.483-.04-7.396.654-12.402 2.014l-.606.167c-1.284.355-1.901.552-2.54.843l-.138.064c-.477.225-.785.44-.974.692-.176.233-.227.516-.15.683.127.274.612.423 1.446.41l.718-.03 6.73-.366a.805.805 0 0 1 .068 1.609l-2.754.145-3.532.2c-.362.018-.663.033-.963.046-1.563.066-2.662-.23-3.172-1.338-.347-.75-.185-1.651.324-2.328.377-.501.881-.854 1.572-1.18.695-.329 1.291-.536 2.39-.85l.546-.153c5.433-1.507 9.646-2.283 13.456-2.238.444.005.8.37.794.815z"/><path d="M142.915 84.726c.21.392.062.88-.33 1.09-4.438 2.373-7.59 3.063-12.93 2.921-.825-.022-1.339-.085-1.886-.289-.766-.285-1.391-.839-1.654-1.57-.431-1.202.238-2.558 1.377-3.236.584-.347 1.188-.525 2.052-.654l.856-.11c4.22-.512 6.293-1.111 10.039-2.917a.806.806 0 0 1 .698 1.451c-3.594 1.733-5.781 2.42-9.528 2.935l-1.56.197c-.827.107-1.321.237-1.736.483-.531.317-.826.913-.685 1.306.088.243.348.474.7.605.337.125.71.171 1.37.189 5.07.134 7.946-.495 12.13-2.732a.804.804 0 0 1 1.087.33z"/></g><path d="M142.765 48.928a.806.806 0 0 1 1.475.646c-.578 1.314-.63 2.915-.27 4.932l.073.382c.063.317.123.59.218 1.007l.38 1.656c.312 1.414.436 2.243.471 3.256l.007.314c.002.16 0 .333-.002.54l-.016 1.037c-.001.648.026 1.024.106 1.448.173.915.67 1.642 1.244 1.851l.059.017 7.962.085c7.285.062 11.603.047 12.686-.037l.063-.006.01-.03c.035-.143.06-.348.067-.61l.003-.166c.003-.9-.166-2.256-.508-4.05a.804.804 0 0 1 1.582-.301l.12.645c.697 3.868.621 5.743-.748 6.057l-.131.023c-1.028.134-5.758.155-14.565.071l-6.666-.073-.17-.02c-1.35-.31-2.299-1.612-2.59-3.158l-.054-.314c-.067-.462-.086-.912-.08-1.618l.017-1.052c.002-.173.001-.319-.002-.46l-.004-.138c-.035-1.026-.18-1.875-.58-3.608l-.273-1.183c-.063-.279-.11-.498-.157-.728l-.028-.14c-.498-2.492-.473-4.516.301-6.275z" fill="#4285F4" fill-rule="nonzero"/><g transform="translate(161.459 2.303)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M.031 21.66c.255-2.568 2.554-4.442 5.135-4.182a4.682 4.682 0 0 1 3.421 2.037 4.695 4.695 0 0 1-.024-1.107c.255-2.568 2.554-4.442 5.13-4.181l.028.003c.964.097 1.783-.638 1.842-1.6.008-.125.02-.251.038-.378.31-2.172 2.166-3.863 4.365-4a4.6 4.6 0 0 1 1.472.143c.975.258 1.933-.483 1.994-1.484l.21-3.452c.163-2.651 3.1-4.178 5.379-2.795 2.277 1.383 2.258 4.683-.036 6.04l-3.811 2.254a1.61 1.61 0 0 0-.644 2.053c.325.718.47 1.527.384 2.366-.256 2.563-2.555 4.436-5.13 4.181l-.03-.003c-.942-.095-1.791.605-1.839 1.546a4.692 4.692 0 0 1-5.151 4.419 4.692 4.692 0 0 1-3.423-2.037c.05.36.064.73.026 1.107-.256 2.563-2.556 4.436-5.135 4.183-2.575-.26-4.457-2.55-4.201-5.113" fill="#4285F4" mask="url(#b)"/></g><path d="M161.475 26.911a6.484 6.484 0 0 1 .733 9.17c-2.341 2.733-6.464 3.06-9.21.73a6.486 6.486 0 0 1-.733-9.17 6.554 6.554 0 0 1 9.21-.73" fill="#FFF"/><path d="M151.653 27.118a7.289 7.289 0 0 0 .823 10.305c3.084 2.618 7.715 2.25 10.344-.82a7.288 7.288 0 0 0-.823-10.305c-3.084-2.617-7.714-2.25-10.344.82zm9.3.406a5.681 5.681 0 0 1 .643 8.034 5.747 5.747 0 0 1-8.076.64 5.683 5.683 0 0 1-.643-8.035 5.748 5.748 0 0 1 7.884-.796l.193.157z" fill="#4285F4" fill-rule="nonzero"/><path d="M163.92 32.616c.85-1.668-2.037-5.655-4.051-6.672-2.014-1.018-6.642-.056-7.493 1.612 1.71 1.768 8.243 4.389 10.677 4.884l.866.176z" fill="#4285F4"/><path d="M150.526 32.93a.806.806 0 0 1 1.043.18l.063.09c.277.454.758.755 1.199.755.442 0 .862-.225 1.05-.542l.05-.098a.806.806 0 0 1 1.485.623c-.418.994-1.478 1.625-2.585 1.625-1.034 0-2.02-.616-2.575-1.529a.803.803 0 0 1 .27-1.104z" fill="#4285F4" fill-rule="nonzero"/><path d="M167.507 66.135a.805.805 0 0 1 .433 1.546l-.106.029-20.582 4.257a.805.805 0 0 1-.432-1.546l.105-.029 20.582-4.257z" fill="#4682F4" fill-rule="nonzero"/><g fill="#4285F4" fill-rule="nonzero"><path d="M95.347 77.103a.806.806 0 0 1 .962-.61.803.803 0 0 1 .609.96c-.48 2.136-1.868 4.347-3.66 5.503l-.812.482c-.59.355-.932.637-1.146.966-.101.156-.136.288-.122.348.017.073.188.191.419.21.282.025.594-.046 1.156-.249l.903-.328c5.416-1.995 8.043-3.418 9.986-5.892.551-.702.944-1.426 1.083-2.05a.806.806 0 0 1 .961-.61.803.803 0 0 1 .61.959c-.199.893-.7 1.818-1.388 2.693-2.248 2.863-5.135 4.386-11.143 6.572l-.469.17c-.768.277-1.258.389-1.84.339-.864-.074-1.65-.615-1.845-1.443-.129-.545.019-1.102.34-1.595.335-.515.773-.902 1.414-1.312l.474-.287.375-.218c1.483-.863 2.725-2.788 3.133-4.608z"/><path d="M83.987 77.326c1.354-2.038 3.087-3.135 6.81-4.776l.392-.172c4.907-2.135 8.03-3.214 11.302-3.63a.803.803 0 1 1 .201 1.596c-3.076.39-6.09 1.432-10.862 3.509l-.714.315c-3.208 1.434-4.7 2.408-5.788 4.047-.354.533-.485 1.327-.337 2.025l.664 3.258c.478 2.406.985 5.058 1.1 5.804l.012.086c.109.792.274 1.145.438 1.154.259.015.718-.6 1.018-1.502.282-.847.3-1.733.109-3.212l-.21-1.433-.089-.5c-.05-.3-.087-.59-.11-.867-.137-1.667.196-2.735.589-3.205.616-.928 1.614-1.338 3.586-1.714l1.785-.313c.724-.13 1.088-.217 1.48-.347a.804.804 0 1 1 .505 1.527l-.233.074c-.464.14-.93.237-1.847.396l-.827.142-.537.098c-1.44.277-2.2.563-2.56 1.01l-.06.081c-.175.213-.379.866-.276 2.117.013.154.03.314.054.48l.143.835.16 1.09c.265 1.893.265 3.047-.135 4.247-.517 1.553-1.354 2.675-2.64 2.602-1.18-.067-1.645-.843-1.877-2.15l-.11-.699c-.251-1.478-1.239-6.519-1.707-8.723-.234-1.107-.032-2.343.57-3.25z"/><path d="M83.462 78.295a.803.803 0 1 1 1.388.814l-1.39 2.38c-1.383 2.392-2.463 4.338-3.33 6.014l-.114.229c-.108.233-.136.355-.118.421.018.067.257.263.373.279.19.026.441-.035.728-.184.611-.317 1.142-.857 1.777-1.744.438-.611.923-1.447 1.451-2.462a.803.803 0 1 1 1.428.74c-.56 1.078-1.077 1.97-1.57 2.658-.77 1.075-1.46 1.776-2.346 2.236-.558.29-1.125.427-1.689.35-.734-.1-1.514-.737-1.707-1.45-.165-.611-.026-1.073.357-1.811l.34-.65c1.108-2.1 2.53-4.606 4.422-7.82z"/></g><path d="M57.963 76.556a1.512 1.512 0 0 1 2.027-.102l.108.098 17.27 17.198 24.354-7.42a1.51 1.51 0 0 1 1.835.866l.049.136a1.51 1.51 0 0 1-.869 1.834l-.137.049-25.22 7.683a1.51 1.51 0 0 1-1.394-.273l-.112-.101-17.91-17.836a1.506 1.506 0 0 1-.001-2.132z" fill="#4285F4" fill-rule="nonzero"/><g><path d="M73.735 27.798a.957.957 0 0 1-.899-1.087 9.21 9.21 0 0 1 9.562-7.95c4.828.22 8.699 4.061 9.082 8.793a.922.922 0 0 1-.96 1.004l-16.785-.76z" fill="#F882FF"/><path d="M46.537 44.15l-7.78 3.522c-1.1.499-1.249 1.981-.268 2.67l6.939 4.87c.98.69 2.354.068 2.473-1.118l.842-8.396c.12-1.187-1.107-2.047-2.206-1.549" fill="#D2E3FC"/><path d="M45.335 46.642c-2.101-2.484-1.676-6.184.948-8.262l11.107-8.82c2.625-2.08 6.456-1.752 8.557.732s1.677 6.184-.948 8.263l-11.106 8.82c-2.625 2.08-6.456 1.75-8.558-.733" fill="#FABD04"/><g transform="rotate(-102 42.988 6.811)"><mask id="d" fill="#fff"><use xlink:href="#c"/></mask><path d="M3.01-6.275c-.34 0-.687.118-.977.375L-4.024-.53c-.856.758-.57 2.16.514 2.523L4.162 4.56c.16.053.32.078.476.078.896 0 1.644-.83 1.45-1.785L4.472-5.084a1.484 1.484 0 0 0-1.461-1.19M2.817-4.19l1.398 6.865-6.638-2.22 5.24-4.645" fill="#FFF" mask="url(#d)"/></g><path d="M43.26 66.655l-3.847-6.7a2.573 2.573 0 0 0-2.228-1.28l-7.868-.026a2.63 2.63 0 0 0-2.261 1.264l-4.022 6.673a2.504 2.504 0 0 0-.032 2.543l3.847 6.7a2.572 2.572 0 0 0 2.228 1.28l7.868.026a2.632 2.632 0 0 0 2.262-1.264l4.02-6.672a2.503 2.503 0 0 0 .033-2.544" fill="#38A953"/><path d="M42.107 102.465h0v-.002a8.461 8.461 0 0 0-1.253-1.538l-1.343-1.65a6.942 6.942 0 0 1-1.515-5.197l.28-2.386.122-1.045-.055-.007a5.94 5.94 0 0 0-.06-.752c-.492-3.26-3.588-5.485-6.915-4.972-3.328.513-5.628 3.571-5.135 6.83.037.252.097.496.165.735l-.05.024.424.96.97 2.194a6.96 6.96 0 0 1 .088 5.417l-.795 1.98a8.491 8.491 0 0 0-.741 1.845l-.001.002h0a8.374 8.374 0 0 0-.038 4.236c1.006 4.032 4.942 6.764 9.169 6.373 5.048-.468 8.615-4.991 7.886-9.824a8.323 8.323 0 0 0-1.203-3.223z" stroke="#D2E3FC" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M40.532 135.356c-7.25-.375-12.761-6.448-12.311-13.564l.013-.21c.449-7.116 6.69-12.581 13.939-12.206 7.25.375 12.761 6.448 12.311 13.564l-.013.21c-.45 7.117-6.69 12.581-13.939 12.206" fill="#EA4335"/><g transform="rotate(-103 70.313 56.882)"><mask id="f" fill="#fff"><use xlink:href="#e"/></mask><path d="M28.924.89c-4.53.083-8.37 3.754-8.984 8.531-.727 5.66 3.137 10.797 8.463 11.23a8.667 8.667 0 0 0 3.378-.392l.274-.097.373-.145c.485-.2.925-.425 1.339-.683l.258-.17 1.826-.942a5.58 5.58 0 0 1 4.559-.274l3.188 1.201.11.034c.11.026.223.031.331.016l.02-.004.258.04c.092.013.183.022.275.03 3.737.304 6.968-2.686 7.245-6.64.278-3.947-2.488-7.413-6.216-7.717l-.31-.016h-.227l-.028-.01a.899.899 0 0 0-.324-.035l-.156.024-3.276.663-.275.048c-1.467.221-2.962-.153-4.2-1.061l-1.68-1.235-.225-.203a9.26 9.26 0 0 0-1.183-.869l-.317-.186A8.772 8.772 0 0 0 29.265.889h-.341zm.35 1.81a6.957 6.957 0 0 1 2.96.745l.296.157c.515.29.993.642 1.427 1.049l.083.068 1.714 1.26c1.723 1.265 3.848 1.744 5.905 1.328l3.01-.61.078.011.055.002.056-.001a4.46 4.46 0 0 1 .615.002c2.71.22 4.77 2.8 4.56 5.787-.21 2.979-2.592 5.184-5.294 4.964a4.622 4.622 0 0 1-.611-.097l-.111-.017-.079-.003-2.89-1.09a7.39 7.39 0 0 0-6.029.36l-1.87.968-.095.056a7.217 7.217 0 0 1-1.55.802l-.298.103a6.838 6.838 0 0 1-2.657.304c-4.266-.347-7.413-4.532-6.814-9.196.502-3.911 3.616-6.887 7.222-6.954l.316.001z" fill="#FFF" fill-rule="nonzero" mask="url(#f)"/></g></g><g><g transform="rotate(9 44.073 633.419)"><mask id="h" fill="#fff"><use xlink:href="#g"/></mask><path d="M35.644 27.522l-24.76 9.258a1.253 1.253 0 0 1-1.614-.745L.079 11.095a1.268 1.268 0 0 1 .74-1.627L25.58.21c.65-.243 1.372.09 1.614.745l9.191 24.94a1.268 1.268 0 0 1-.74 1.627" fill="#4285F4" mask="url(#h)"/></g><path d="M121.048 11.073a4.033 4.033 0 0 0-1.246.577l-.217.161c-.247.196-.47.42-.664.668l-.548.544a2.164 2.164 0 0 1-1.531.64h-1.374l-.094.007a.686.686 0 0 0-.177.052l-.072.014-.12.03a3.055 3.055 0 0 0-2.149 3.73 3.024 3.024 0 0 0 3.71 2.157l.2-.064.06-.007a.65.65 0 0 0 .13-.042l.178-.099 1.076-.625a2.152 2.152 0 0 1 1.64-.217l.815.22c.218.091.522.173.835.22.667.097 1.35.027 2-.215 1.854-.691 2.95-2.67 2.562-4.618-.461-2.313-2.759-3.742-5.014-3.133zm3.732 3.388c.263 1.322-.485 2.672-1.737 3.139a2.723 2.723 0 0 1-1.965-.019l-.888-.244a3.459 3.459 0 0 0-2.636.35l-1.052.609-.072.024a1.717 1.717 0 0 1-2.31-1.157 1.748 1.748 0 0 1 1.224-2.136l.065-.016c.045-.01.094-.018.15-.025l.069-.016 1.215-.001a3.47 3.47 0 0 0 2.456-1.024l.598-.601.106-.127a3.04 3.04 0 0 1 .4-.388l.16-.117a2.734 2.734 0 0 1 4.217 1.749zm-21.012-1.449a3.139 3.139 0 0 0-2.443 3.69 3.11 3.11 0 0 0 3.669 2.451 3.139 3.139 0 0 0 2.442-3.689 3.111 3.111 0 0 0-3.668-2.452zm2.387 2.706a1.832 1.832 0 0 1-1.422 2.155 1.803 1.803 0 0 1-2.126-1.426 1.833 1.833 0 0 1 1.422-2.155 1.803 1.803 0 0 1 2.09 1.281l.036.145z" fill="#FFF" fill-rule="nonzero"/><g transform="rotate(9 43.883 633.603)"><mask id="j" fill="#fff"><use xlink:href="#i"/></mask><path d="M13.402 18.342a.654.654 0 0 1 .782-.355l.09.035 25.268 11.894a.653.653 0 0 1-.467 1.217l-.09-.035-24.67-11.613-6.724 14.77a.654.654 0 0 1-.775.357l-.09-.033a.653.653 0 0 1-.358-.775l.034-.09 7-15.372z" fill="#FFF" fill-rule="nonzero" mask="url(#j)"/></g></g></g>
+      <g id="nearby-onboarding-splash" fill="none" fill-rule="evenodd">
+        <path d="M0 0h200v200H0z"/><path d="M110.893 164.142l-6.065 12.062c1.606.235 2.96.506 4.06.814 4.544 1.27 10.262 2.852 15.561 5.64 7.226 3.802 10.566 4.087 10.02.856-3.964-3.923-4.913-5.674-2.848-5.254 2.065.42 5.403 1.107 10.015 2.062.624-.243.804-.791.539-1.644-.208-.67-3.266-2.054-4.064-3.237-.146-.216.396-.215 1.624.003 2.632.41 4.12.547 4.465.41 1.163-.466-.097-2.017-.003-2.827.054-.468.801-.549 1.216-.806.288-.179.267-.554.36-.741.42-.842-3.068-1.44-2.8-2.494.274-1.075 4.274-2.606 1.607-3.406-5.282-1.583-10.813.808-16.629.56-3.877-.166-9.563-.832-17.058-1.998z" fill="#D2E3FC"/><path d="M141.354 171.58l.57.095 1.311.246c1.522.274 1.905.19 2.129-.434.02-.056-.109-.25-.638-.581-.664-.415-1.721-.839-3.074-1.237a.805.805 0 1 1 .454-1.543l.57.174c3.14.992 4.78 2.126 4.204 3.732-.561 1.563-1.636 1.851-3.696 1.512l-1.341-.249c-.275-.05-.504-.09-.735-.126a12.992 12.992 0 0 0-2.067-.174 32.862 32.862 0 0 0-4.908.396.806.806 0 0 1-.92-.672.803.803 0 0 1 .673-.918 34.47 34.47 0 0 1 5.148-.414c.753-.002 1.497.064 2.32.193z" fill="#4682F4" fill-rule="nonzero"/><path d="M134.892 176.05a.803.803 0 1 1 .003-1.609l1.163.008c.546.006.966.015 1.232.028l.092.005a18.507 18.507 0 0 1 2.379.322l.654.138c.519.115 1.48.336 1.707.384l.395.079c1.162.217 1.48.119 1.591-.306.206-.791-.878-1.532-3.807-2.045a.804.804 0 1 1 .276-1.585c3.753.658 5.634 1.944 5.09 4.037-.428 1.64-1.63 1.868-3.876 1.394l-1.942-.436a18.28 18.28 0 0 0-2.16-.345l-.401-.031-.424-.016a88.28 88.28 0 0 0-1.972-.022z" fill="#4682F4" fill-rule="nonzero"/><path d="M141.644 179.567c.18-.31.013-.618-.823-1.159-1.14-.736-3.19-1.51-6.125-2.293a.806.806 0 0 1-.571-.986.803.803 0 0 1 .984-.57c3.084.825 5.27 1.649 6.583 2.498 1.565 1.01 2.102 2.273 1.225 3.504-.514.722-1.262.731-2.752.368l-.552-.143c-.57-.153-1.557-.428-1.585-.435a32.331 32.331 0 0 0-2.211-.532 19.356 19.356 0 0 0-1.642-.258c-1.193-.132-2.08-.185-2.641-.16l-.234.016-.035.06a.798.798 0 0 1-.223.222l-.095.054a.805.805 0 0 1-1.075-.374c-.503-1.04.296-1.495 1.408-1.576.672-.05 1.687.005 3.07.158.58.065 1.169.158 1.778.28.61.12 1.21.262 1.896.443l2.295.625c.494.126.85.198 1.108.22l.186.012c.018.002.028.006.031.013v.013zm-13.848-1.352a.806.806 0 0 1-1.006-.534c-.452-1.475-.264-2.833.571-3.995.9-1.253 2.656-2.153 5.813-3.27l3.54-1.22.187-.068c.695-.257 1.397-.438 2.698-.713l1.718-.36c1.35-.292 2.061-.494 2.677-.773.748-.34.941-.604.836-.965-.172-.587-3.21-.75-7.838-.247l-.935.106c-.32.038-.647.079-.985.122l-5.142.7c-.596.077-1.065.135-1.254.15-2.193.181-7.982-.704-17.529-2.658a.804.804 0 1 1 .323-1.576l2.02.409c8.168 1.631 13.228 2.373 15.054 2.223l.305-.033c1.269-.15 5.264-.712 5.497-.742l1.029-.132c.332-.04.656-.08.973-.116l1.221-.13c5.403-.544 8.286-.313 8.807 1.474.376 1.29-.324 2.248-1.715 2.88l-.285.122c-.624.255-1.323.45-2.45.7l-2.466.52c-.771.17-1.286.304-1.735.454l-1.52.534c-1.005.347-2.173.743-2.13.728-3.045 1.058-4.745 1.9-5.405 2.818-.533.743-.649 1.579-.34 2.588a.804.804 0 0 1-.534 1.004z" fill="#4682F4" fill-rule="nonzero"/><path d="M135.161 182.503c.791 1.153.728 2.38-.312 3.488-1.426 1.52-4.087.752-10.188-2.074l-3.96-1.853a66.2 66.2 0 0 0-2.227-.987c-3.772-1.591-8.276-2.854-13.51-3.787a.806.806 0 0 1-.652-.933.803.803 0 0 1 .932-.652c5.064.903 9.474 2.113 13.233 3.632l.62.257c.83.349 1.486.641 2.657 1.185l3.215 1.507 1.046.481c4.608 2.09 7.048 2.774 7.66 2.122.511-.545.531-.935.16-1.477-.356-.519-.985-1.053-2.102-1.825l-.786-.532c-1.524-1.024-2.835-2.028-3.934-3.013a.805.805 0 1 1 1.072-1.198 33.243 33.243 0 0 0 3.277 2.548l1.027.695c1.343.913 2.13 1.548 2.662 2.262l.11.154z" fill="#4682F4" fill-rule="nonzero"/><path d="M92.163 196.078c.982.182 2.643.273 3.633.406.83.112 1.619-.175 2.423-.405.803-.232.747-.847.919-1.664.695-.113 1.406-.232 2.02-.574.616-.342 1.125-.96 1.137-1.661.007-.395-.558-.19-.444-.568.073-.236.66-1.04.785-1.255.527-.906.161-2.213-.76-2.716.363-.226.476-.726.358-1.135-.118-.409-.42-.74-.742-1.018-2.532-2.183-6.36-1.812-9.443-3.116.583-.446 1.347-.564 2.022-.851.675-.287 1.338-.898 1.241-1.624-.073-.554-.585-.969-1.125-1.117-.542-.148-1.115-.088-1.674-.049-3.745.265-7.532-.417-11.249.108-.252.037-.515.083-.717.238-.27.21-3.883-1.106-3.963-.774-.482 1.974-7.031-4.966-7.839-4.154-.807.812-11.512 9.186-11.706 10.15-.164.811 14.052 7.893 15.264 8.424 2.096.917 12.76 2.606 14.612 2.948 1.548.287 3.7.12 5.248.407z" fill="#D2E3FC"/><path d="M87.628 192.91a.806.806 0 0 1 .445-1.548c4.335 1.25 7.827 1.874 10.84 1.8.923-.021 1.458-.12 1.76-.356.22-.172.26-.694.048-.846a.806.806 0 0 1-.184-1.124.803.803 0 0 1 1.122-.185c1.153.829 1.014 2.634.003 3.425-.67.523-1.447.667-2.71.697-3.209.078-6.845-.572-11.324-1.863zm-.619-10.486a.806.806 0 0 1 .433-1.551l.341.091c1.706.442 6.23 1.446 7.773 1.731.713.132 1.388.288 2.361.538l.836.219c1.116.292 1.938.623 2.638 1.14.524.388.923.874 1.114 1.442.183.543.153 1.141-.125 1.632-.527.932-1.542 1.258-2.914 1.245a10.974 10.974 0 0 1-1.039-.064l-1.324-.156c-.403-.053-.82-.12-1.435-.226l-1.496-.259-1.511-.258a35.12 35.12 0 0 1-3.64-.82.806.806 0 0 1 .433-1.552c1.282.359 2.228.567 3.841.848l3.05.523c.51.085.867.137 1.248.183l.719.084c.47.055.826.083 1.169.086.843.008 1.344-.153 1.5-.428.038-.067.044-.192 0-.324-.074-.22-.266-.454-.545-.66-.445-.328-1.011-.573-1.789-.795l-.8-.214-.913-.233c-.55-.136-1-.238-1.446-.324l-.639-.122c-2.01-.396-6.554-1.415-7.84-1.776z" fill="#4285F4" fill-rule="nonzero"/><path d="M88.364 189.969a.806.806 0 0 1 .423-1.554c2.262.617 6.487 1.543 8.684 1.896 1.54.248 2.447.295 3.257.092.322-.081.51-.183.722-.395.12-.119.2-.406.171-.674-.037-.348-.215-.568-.462-.665l-.096-.03a.806.806 0 0 1 .39-1.563c.924.232 1.649.99 1.767 2.084.078.723-.14 1.497-.636 1.99-.425.423-.87.666-1.466.815-1.087.273-2.155.217-3.902-.064-2.255-.362-6.536-1.3-8.852-1.932zm-20.049-15.742a.806.806 0 0 1-.207-1.12.803.803 0 0 1 1.118-.207l.719.491c5.425 3.677 8.916 5.295 10.089 4.886 1.98-.69 3.677-.83 6.624-.68l4.398.28.608.03.294-.001.671-.012c.994-.007 1.669.11 2.315.521.71.453 1.16 1.257 1.056 2.078-.074.587-.414 1.102-.902 1.477-.446.344-1.076.6-1.943.84l-.503.13c-.52.126-1.46.338-1.569.365a.805.805 0 0 1-.394-1.562l1.556-.361.211-.052c.82-.212 1.391-.428 1.662-.637.172-.132.272-.284.287-.401.02-.154-.107-.38-.325-.518-.287-.183-.648-.259-1.235-.268l-.204-.001-.804.013c-.117 0-.213 0-.312-.004l-1.364-.076-2.767-.185c-3.14-.198-4.788-.125-6.595.465l-.237.08c-1.954.681-5.89-1.188-12.247-5.571zm-11.4 11.051a.806.806 0 0 1 .89-1.342c5.989 3.979 11.392 6.766 16.202 8.364l.532.172c3.514 1.113 6.104 1.57 11.318 2.09l4.4.419c1.719.173 2.897.325 4.1.538l.242.044c1.209.22 1.785.266 2.39.132.619-.137 1.118-.537 1.264-.985l.028-.113a.804.804 0 1 1 1.581.295c-.221 1.193-1.273 2.098-2.527 2.376-.756.167-1.398.149-2.457-.022l-1.265-.22c-1.168-.191-2.397-.337-4.238-.513l-2.742-.256c-5.96-.571-8.723-1.028-12.579-2.25-5.1-1.614-10.81-4.525-17.139-8.73z" fill="#4285F4" fill-rule="nonzero"/><g fill-rule="nonzero"><path d="M78.868 136.46l-.531.008c-.323.008-.621.021-.894.039l-.49.038-.12.016-9.915.101-.382-.018c-3.068-.115-6.78.197-10.366 1.302-2.21.682-4.172 1.626-5.815 2.868-3.822 2.89-5.187 7.466-6.244 17.638-.966 9.309 1.07 16.723 5.068 22.384a24.107 24.107 0 0 0 3.667 4.132c.76.675 1.36 1.117 1.72 1.34.266.167.602.16.861-.005l.093-.07 14.23-12.368c.335-.292.37-.8.078-1.135a.806.806 0 0 0-1.049-.144l-.087.066-13.753 11.953-.117-.088c-.21-.16-.432-.341-.667-.542l-.24-.209a22.505 22.505 0 0 1-3.42-3.857c-3.777-5.348-5.707-12.376-4.781-21.291l.161-1.49c.988-8.676 2.303-12.65 5.453-15.032 1.481-1.12 3.276-1.983 5.318-2.613 3.194-.985 6.534-1.306 9.34-1.247l.704.024.18.009 10.07-.106.264-.026.102-.008c.311-.025.67-.044 1.071-.054a25.64 25.64 0 0 1 3.7.18c3.7.447 7.071 1.653 9.76 3.825 1.895 1.531 3.246 3.777 4.756 7.685l.373.991c.454 1.273 3.058 8.852 4.112 11.6a.805.805 0 0 0 .544.49l.109.02 8.336 1.035-7.493 14.353-.253-.054c-2.408-.554-5.444-2.118-9.083-4.702l-.422-.302c-4.065-2.937-5.343-7.106-3.845-12.686a.806.806 0 0 0-1.556-.416c-1.663 6.198-.174 11.06 4.457 14.405 4.444 3.21 8.108 5.03 11.03 5.445a.81.81 0 0 0 .77-.329l.058-.096 8.3-15.901a.804.804 0 0 0-.513-1.15l-.101-.02-9.004-1.117-.494-1.347a368.1 368.1 0 0 1-.94-2.636l-2.547-7.277c-1.739-4.697-3.268-7.372-5.58-9.24-2.953-2.386-6.604-3.692-10.58-4.173a27.422 27.422 0 0 0-3.403-.198z" fill="#4285F4"/><path d="M61.551 153.314l-.1.002a.805.805 0 0 0-.742.764l.002.1 1.185 15.019a.802.802 0 0 0 .285.552l.087.064 6.522 4.123a.803.803 0 1 0 .95-1.294l-.088-.065-6.18-3.908-1.156-14.617a.805.805 0 0 0-.657-.728l-.108-.012z" fill="#4682F4"/><path d="M63.276 165.48a.805.805 0 0 0-.101 1.601l.101.007h22.233a.805.805 0 0 0 .101-1.602l-.1-.006H63.275z" fill="#4682F4"/></g><path d="M69.765 131.356h5.865l1.533 6.908c-.569 1.266-2.293 2.186-4.332 2.186-2.094 0-3.855-.97-4.376-2.287l1.31-6.807z" fill="#CFE3FF"/><path d="M75.63 130.552h-5.865a.805.805 0 0 0-.791.652l-1.31 6.807a.803.803 0 0 0 .042.447c.666 1.684 2.762 2.796 5.125 2.796 2.296 0 4.343-1.05 5.067-2.661a.803.803 0 0 0 .051-.503l-1.533-6.908a.805.805 0 0 0-.786-.63zm-.647 1.608l1.329 5.987-.027.046c-.557.839-1.897 1.453-3.454 1.453l-.228-.004c-1.506-.058-2.771-.69-3.272-1.52l-.036-.065 1.135-5.897h4.553z" fill="#4285F4" fill-rule="nonzero"/><path d="M76.445 122.877a6.147 6.147 0 0 1 .694 8.688 6.202 6.202 0 0 1-8.72.692 6.147 6.147 0 0 1-.694-8.689 6.202 6.202 0 0 1 8.72-.69z" fill="#CFE3FF"/><path d="M67.112 123.046a6.95 6.95 0 0 0 .785 9.823 7.008 7.008 0 0 0 9.855-.781 6.95 6.95 0 0 0-.785-9.823 7.008 7.008 0 0 0-9.855.781zm8.811.444a5.344 5.344 0 0 1 .604 7.553 5.396 5.396 0 0 1-7.587.601 5.344 5.344 0 0 1-.603-7.553 5.396 5.396 0 0 1 7.586-.601z" fill="#4285F4" fill-rule="nonzero"/><path fill="#4285F4" d="M66.246 124.853h11.057l-.299-2.25-4.155-1.117-2.297.041-2.571 1.65z"/><path d="M81.62 121.472c-.335.523-.8 1.009-1.21 1.469l-1.697 1.912h-6.757l2.306-2.545c1.127-1.244 1.971-2.238 3.384-3.065 1.483-.869 3.377-1.116 4.163.034l.024.037c.355.55.274 1.401-.212 2.158" fill="#0F3EC1"/><path d="M69.625 127.974c.4-.082.79.147.921.52l.03.106c.083.405.424.766.824.886l.121.03a.804.804 0 1 1-.275 1.584 2.827 2.827 0 0 1-2.249-2.178.804.804 0 0 1 .628-.948z" fill="#4285F4" fill-rule="nonzero"/><g transform="translate(87.766 169.167)"><path d="M0 12.79L2.687 1.536A2 2 0 0 1 4.632 0h11.823a2 2 0 0 1 1.928 2.533l-3.88 14.02L0 12.79z" fill="#4682F4"/><ellipse fill="#FFF" cx="6.285" cy="2.818" rx="1.007" ry="1.001"/></g><path d="M89.666 149.902c.488.11.795.595.685 1.082l-.412 1.874a39.22 39.22 0 0 0-.172.87 23.414 23.414 0 0 0-.372 2.834c-.129 2.02-.374 3.896-.757 5.674-.484 2.245-1.087 3.591-2.423 4.63a.907.907 0 0 1-1.271-.159.904.904 0 0 1 .158-1.27c.909-.706 1.363-1.72 1.764-3.582.318-1.475.537-3.028.67-4.69l.05-.72c.053-.81.153-1.625.303-2.51l.154-.84c.123-.641.252-1.23.54-2.509a.906.906 0 0 1 .97-.702l.113.018zM159.996 35.664h-5.559a.805.805 0 0 0-.737.48l-1.71 3.862a.804.804 0 0 0 .734 1.13l9.034.036a.804.804 0 0 0 .737-1.135l-1.765-3.9a.805.805 0 0 0-.734-.473zm-.522 1.608l1.036 2.286-6.55-.027 1.001-2.26h4.513z" fill="#4285F4" fill-rule="nonzero"/><path d="M161.948 41.21l-.376-.071-1.578.03c-2.899.047-5.226.04-6.982-.021l-.323-.012c-12.927-.517-20.131 5.42-29.147 22.158a.804.804 0 0 1-.397.36l-.105.036-19.632 5.21 5.93 13.082.087.008c7.582.697 14.44-1.076 20.608-5.325l.439-.308c6.424-4.572 10.44-10.685 12.07-18.37a.805.805 0 0 1 1.576.333c-1.714 8.08-5.959 14.541-12.71 19.347-6.75 4.804-14.327 6.763-22.696 5.868a.806.806 0 0 1-.599-.374l-.05-.094-6.515-14.37a.803.803 0 0 1 .427-1.074l.1-.034 20.214-5.364.112-.206c8.97-16.498 16.507-22.78 29.546-22.514l1.744.054c1.767.045 4.052.043 6.855-.007l1.075-.021.147.01.31.056.39.08c.139.028.286.06.443.096.888.203 1.871.466 2.924.799 3.008.95 5.976 2.264 8.694 4.002 7.825 5.004 12.045 12.458 10.865 22.73-1.18 10.273-7.632 17.093-17.468 21.09-3.416 1.388-7.007 2.33-10.556 2.913a49.07 49.07 0 0 1-3.42.443l-.511.043-.742.049a.806.806 0 0 1-.826-.478l-.037-.104-5.588-19.616a.806.806 0 1 1 1.512-.542l.038.103 5.41 18.992.344-.026.251-.023a47.465 47.465 0 0 0 3.307-.428c3.438-.564 6.915-1.477 10.21-2.816 9.339-3.794 15.371-10.171 16.475-19.784 1.105-9.613-2.793-16.499-10.132-21.192-2.588-1.655-5.43-2.913-8.311-3.823a36.183 36.183 0 0 0-2.365-.662l-.431-.102a25.57 25.57 0 0 0-.606-.13z" fill="#4285F4" fill-rule="nonzero"/><g fill="#4285F4" fill-rule="nonzero"><path d="M152.34 90.536c.23.38.11.875-.27 1.106l-3.443 2.127-.68.406-.49.283c-1.488.848-3.158 1.685-5.746 2.87l-.569.254c-1.158.505-1.873.71-2.765.739-1.447.046-2.845-.649-3.411-1.88a.804.804 0 1 1 1.408-.77l.053.096c.268.583 1.048.97 1.899.944.696-.022 1.31-.216 2.46-.732l.256-.116c2.689-1.231 4.354-2.072 5.857-2.94l.451-.265.52-.312c.554-.34 2.592-1.611 3.366-2.081a.803.803 0 0 1 1.104.27z"/><path d="M145.072 91.074a.806.806 0 0 1-.333 1.09l-3.63 1.972c-.836.448-1.504.79-2.17 1.108-1.928.919-3.64 1.517-5.47 1.857-1.122.208-2.188.11-3.06-.383-1.26-.711-1.826-2.492-.811-3.658a.803.803 0 0 1 1.134-.078c.335.292.37.8.078 1.136-.266.306-.063.942.388 1.197.502.283 1.196.347 1.977.202 1.68-.312 3.263-.865 5.073-1.727l.484-.236a59.293 59.293 0 0 0 1.936-1.01l2.845-1.551.471-.253a.804.804 0 0 1 1.088.334z"/><path d="M143.99 88.052a.806.806 0 0 1-.31 1.095l-.713.41-1.405.829c-.92.536-1.538.866-2.245 1.187-1.85.84-3.59 1.482-5.303 1.933l-.75.186-.715.166c-.634.14-1.054.204-1.551.231-1.406.08-2.565-.275-3.268-1.278-.648-.92-.53-2.234.165-3.135 1.224-1.586 3.911-2.467 7.583-2.904a.805.805 0 0 1 .19 1.6c-3.254.387-5.635 1.168-6.501 2.289-.288.373-.336.919-.122 1.224.318.453.954.647 1.863.596.446-.025.84-.09 1.529-.249l.633-.15c1.79-.428 3.615-1.083 5.583-1.976l.35-.164c.58-.278 1.156-.598 2.01-1.1l.97-.575c.368-.217.627-.366.912-.526a.803.803 0 0 1 1.094.31zm4.263-12.866a.805.805 0 0 1-.813.795c-3.483-.04-7.396.654-12.402 2.014l-.606.167c-1.284.355-1.901.552-2.54.843l-.138.064c-.477.225-.785.44-.974.692-.176.233-.227.516-.15.683.127.274.612.423 1.446.41l.718-.03 6.73-.366a.805.805 0 0 1 .068 1.609l-2.754.145-3.532.2c-.362.018-.663.033-.963.046-1.563.066-2.662-.23-3.172-1.338-.347-.75-.185-1.651.324-2.328.377-.501.881-.854 1.572-1.18.695-.329 1.291-.536 2.39-.85l.546-.153c5.433-1.507 9.646-2.283 13.456-2.238.444.005.8.37.794.815z"/><path d="M142.915 84.726c.21.392.062.88-.33 1.09-4.438 2.373-7.59 3.063-12.93 2.921-.825-.022-1.339-.085-1.886-.289-.766-.285-1.391-.839-1.654-1.57-.431-1.202.238-2.558 1.377-3.236.584-.347 1.188-.525 2.052-.654l.856-.11c4.22-.512 6.293-1.111 10.039-2.917a.806.806 0 0 1 .698 1.451c-3.594 1.733-5.781 2.42-9.528 2.935l-1.56.197c-.827.107-1.321.237-1.736.483-.531.317-.826.913-.685 1.306.088.243.348.474.7.605.337.125.71.171 1.37.189 5.07.134 7.946-.495 12.13-2.732a.804.804 0 0 1 1.087.33z"/></g><path d="M142.765 48.928a.806.806 0 0 1 1.475.646c-.578 1.314-.63 2.915-.27 4.932l.073.382c.063.317.123.59.218 1.007l.38 1.656c.312 1.414.436 2.243.471 3.256l.007.314c.002.16 0 .333-.002.54l-.016 1.037c-.001.648.026 1.024.106 1.448.173.915.67 1.642 1.244 1.851l.059.017 7.962.085c7.285.062 11.603.047 12.686-.037l.063-.006.01-.03c.035-.143.06-.348.067-.61l.003-.166c.003-.9-.166-2.256-.508-4.05a.804.804 0 0 1 1.582-.301l.12.645c.697 3.868.621 5.743-.748 6.057l-.131.023c-1.028.134-5.758.155-14.565.071l-6.666-.073-.17-.02c-1.35-.31-2.299-1.612-2.59-3.158l-.054-.314c-.067-.462-.086-.912-.08-1.618l.017-1.052c.002-.173.001-.319-.002-.46l-.004-.138c-.035-1.026-.18-1.875-.58-3.608l-.273-1.183c-.063-.279-.11-.498-.157-.728l-.028-.14c-.498-2.492-.473-4.516.301-6.275z" fill="#4285F4" fill-rule="nonzero"/><g transform="translate(161.459 2.303)"><mask id="b" fill="#fff"><path id="a" d="M.008.144h30.71v26.652H.007z"/></mask><path d="M.031 21.66c.255-2.568 2.554-4.442 5.135-4.182a4.682 4.682 0 0 1 3.421 2.037 4.695 4.695 0 0 1-.024-1.107c.255-2.568 2.554-4.442 5.13-4.181l.028.003c.964.097 1.783-.638 1.842-1.6.008-.125.02-.251.038-.378.31-2.172 2.166-3.863 4.365-4a4.6 4.6 0 0 1 1.472.143c.975.258 1.933-.483 1.994-1.484l.21-3.452c.163-2.651 3.1-4.178 5.379-2.795 2.277 1.383 2.258 4.683-.036 6.04l-3.811 2.254a1.61 1.61 0 0 0-.644 2.053c.325.718.47 1.527.384 2.366-.256 2.563-2.555 4.436-5.13 4.181l-.03-.003c-.942-.095-1.791.605-1.839 1.546a4.692 4.692 0 0 1-5.151 4.419 4.692 4.692 0 0 1-3.423-2.037c.05.36.064.73.026 1.107-.256 2.563-2.556 4.436-5.135 4.183-2.575-.26-4.457-2.55-4.201-5.113" fill="#4285F4" mask="url(#b)"/></g><path d="M161.475 26.911a6.484 6.484 0 0 1 .733 9.17c-2.341 2.733-6.464 3.06-9.21.73a6.486 6.486 0 0 1-.733-9.17 6.554 6.554 0 0 1 9.21-.73" fill="#FFF"/><path d="M151.653 27.118a7.289 7.289 0 0 0 .823 10.305c3.084 2.618 7.715 2.25 10.344-.82a7.288 7.288 0 0 0-.823-10.305c-3.084-2.617-7.714-2.25-10.344.82zm9.3.406a5.681 5.681 0 0 1 .643 8.034 5.747 5.747 0 0 1-8.076.64 5.683 5.683 0 0 1-.643-8.035 5.748 5.748 0 0 1 7.884-.796l.193.157z" fill="#4285F4" fill-rule="nonzero"/><path d="M163.92 32.616c.85-1.668-2.037-5.655-4.051-6.672-2.014-1.018-6.642-.056-7.493 1.612 1.71 1.768 8.243 4.389 10.677 4.884l.866.176z" fill="#4285F4"/><path d="M150.526 32.93a.806.806 0 0 1 1.043.18l.063.09c.277.454.758.755 1.199.755.442 0 .862-.225 1.05-.542l.05-.098a.806.806 0 0 1 1.485.623c-.418.994-1.478 1.625-2.585 1.625-1.034 0-2.02-.616-2.575-1.529a.803.803 0 0 1 .27-1.104z" fill="#4285F4" fill-rule="nonzero"/><path d="M167.507 66.135a.805.805 0 0 1 .433 1.546l-.106.029-20.582 4.257a.805.805 0 0 1-.432-1.546l.105-.029 20.582-4.257z" fill="#4682F4" fill-rule="nonzero"/><g fill="#4285F4" fill-rule="nonzero"><path d="M95.347 77.103a.806.806 0 0 1 .962-.61.803.803 0 0 1 .609.96c-.48 2.136-1.868 4.347-3.66 5.503l-.812.482c-.59.355-.932.637-1.146.966-.101.156-.136.288-.122.348.017.073.188.191.419.21.282.025.594-.046 1.156-.249l.903-.328c5.416-1.995 8.043-3.418 9.986-5.892.551-.702.944-1.426 1.083-2.05a.806.806 0 0 1 .961-.61.803.803 0 0 1 .61.959c-.199.893-.7 1.818-1.388 2.693-2.248 2.863-5.135 4.386-11.143 6.572l-.469.17c-.768.277-1.258.389-1.84.339-.864-.074-1.65-.615-1.845-1.443-.129-.545.019-1.102.34-1.595.335-.515.773-.902 1.414-1.312l.474-.287.375-.218c1.483-.863 2.725-2.788 3.133-4.608z"/><path d="M83.987 77.326c1.354-2.038 3.087-3.135 6.81-4.776l.392-.172c4.907-2.135 8.03-3.214 11.302-3.63a.803.803 0 1 1 .201 1.596c-3.076.39-6.09 1.432-10.862 3.509l-.714.315c-3.208 1.434-4.7 2.408-5.788 4.047-.354.533-.485 1.327-.337 2.025l.664 3.258c.478 2.406.985 5.058 1.1 5.804l.012.086c.109.792.274 1.145.438 1.154.259.015.718-.6 1.018-1.502.282-.847.3-1.733.109-3.212l-.21-1.433-.089-.5c-.05-.3-.087-.59-.11-.867-.137-1.667.196-2.735.589-3.205.616-.928 1.614-1.338 3.586-1.714l1.785-.313c.724-.13 1.088-.217 1.48-.347a.804.804 0 1 1 .505 1.527l-.233.074c-.464.14-.93.237-1.847.396l-.827.142-.537.098c-1.44.277-2.2.563-2.56 1.01l-.06.081c-.175.213-.379.866-.276 2.117.013.154.03.314.054.48l.143.835.16 1.09c.265 1.893.265 3.047-.135 4.247-.517 1.553-1.354 2.675-2.64 2.602-1.18-.067-1.645-.843-1.877-2.15l-.11-.699c-.251-1.478-1.239-6.519-1.707-8.723-.234-1.107-.032-2.343.57-3.25z"/><path d="M83.462 78.295a.803.803 0 1 1 1.388.814l-1.39 2.38c-1.383 2.392-2.463 4.338-3.33 6.014l-.114.229c-.108.233-.136.355-.118.421.018.067.257.263.373.279.19.026.441-.035.728-.184.611-.317 1.142-.857 1.777-1.744.438-.611.923-1.447 1.451-2.462a.803.803 0 1 1 1.428.74c-.56 1.078-1.077 1.97-1.57 2.658-.77 1.075-1.46 1.776-2.346 2.236-.558.29-1.125.427-1.689.35-.734-.1-1.514-.737-1.707-1.45-.165-.611-.026-1.073.357-1.811l.34-.65c1.108-2.1 2.53-4.606 4.422-7.82z"/></g><path d="M57.963 76.556a1.512 1.512 0 0 1 2.027-.102l.108.098 17.27 17.198 24.354-7.42a1.51 1.51 0 0 1 1.835.866l.049.136a1.51 1.51 0 0 1-.869 1.834l-.137.049-25.22 7.683a1.51 1.51 0 0 1-1.394-.273l-.112-.101-17.91-17.836a1.506 1.506 0 0 1-.001-2.132z" fill="#4285F4" fill-rule="nonzero"/><g><path d="M73.735 27.798a.957.957 0 0 1-.899-1.087 9.21 9.21 0 0 1 9.562-7.95c4.828.22 8.699 4.061 9.082 8.793a.922.922 0 0 1-.96 1.004l-16.785-.76z" fill="#F882FF"/><path d="M46.537 44.15l-7.78 3.522c-1.1.499-1.249 1.981-.268 2.67l6.939 4.87c.98.69 2.354.068 2.473-1.118l.842-8.396c.12-1.187-1.107-2.047-2.206-1.549" fill="#D2E3FC"/><path d="M45.335 46.642c-2.101-2.484-1.676-6.184.948-8.262l11.107-8.82c2.625-2.08 6.456-1.752 8.557.732s1.677 6.184-.948 8.263l-11.106 8.82c-2.625 2.08-6.456 1.75-8.558-.733" fill="#FABD04"/><g transform="rotate(-102 42.988 6.811)"><mask id="d" fill="#fff"><path id="c" d="M3.798.549A5.982 5.982 0 0 0 .812 8.458l5.764 12.741a5.97 5.97 0 0 0 7.9 2.99 5.982 5.982 0 0 0 2.987-7.909L11.697 3.54A5.96 5.96 0 0 0 3.798.55"/></mask><path d="M3.01-6.275c-.34 0-.687.118-.977.375L-4.024-.53c-.856.758-.57 2.16.514 2.523L4.162 4.56c.16.053.32.078.476.078.896 0 1.644-.83 1.45-1.785L4.472-5.084a1.484 1.484 0 0 0-1.461-1.19M2.817-4.19l1.398 6.865-6.638-2.22 5.24-4.645" fill="#FFF" mask="url(#d)"/></g><path d="M43.26 66.655l-3.847-6.7a2.573 2.573 0 0 0-2.228-1.28l-7.868-.026a2.63 2.63 0 0 0-2.261 1.264l-4.022 6.673a2.504 2.504 0 0 0-.032 2.543l3.847 6.7a2.572 2.572 0 0 0 2.228 1.28l7.868.026a2.632 2.632 0 0 0 2.262-1.264l4.02-6.672a2.503 2.503 0 0 0 .033-2.544" fill="#38A953"/><path d="M42.107 102.465h0v-.002a8.461 8.461 0 0 0-1.253-1.538l-1.343-1.65a6.942 6.942 0 0 1-1.515-5.197l.28-2.386.122-1.045-.055-.007a5.94 5.94 0 0 0-.06-.752c-.492-3.26-3.588-5.485-6.915-4.972-3.328.513-5.628 3.571-5.135 6.83.037.252.097.496.165.735l-.05.024.424.96.97 2.194a6.96 6.96 0 0 1 .088 5.417l-.795 1.98a8.491 8.491 0 0 0-.741 1.845l-.001.002h0a8.374 8.374 0 0 0-.038 4.236c1.006 4.032 4.942 6.764 9.169 6.373 5.048-.468 8.615-4.991 7.886-9.824a8.323 8.323 0 0 0-1.203-3.223z" stroke="#D2E3FC" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M40.532 135.356c-7.25-.375-12.761-6.448-12.311-13.564l.013-.21c.449-7.116 6.69-12.581 13.939-12.206 7.25.375 12.761 6.448 12.311 13.564l-.013.21c-.45 7.117-6.69 12.581-13.939 12.206" fill="#EA4335"/><g transform="rotate(-103 70.313 56.882)"><mask id="f" fill="#fff"><path id="e" d="M1.258 10.212c-1.955 7.205 1.886 14.751 8.58 16.855l.196.063c6.694 2.104 13.705-2.03 15.66-9.234C27.65 10.691 23.81 3.145 17.117 1.04L16.92.978a11.78 11.78 0 0 0-3.546-.549C7.907.43 2.868 4.28 1.258 10.212z"/></mask><path d="M28.924.89c-4.53.083-8.37 3.754-8.984 8.531-.727 5.66 3.137 10.797 8.463 11.23a8.667 8.667 0 0 0 3.378-.392l.274-.097.373-.145c.485-.2.925-.425 1.339-.683l.258-.17 1.826-.942a5.58 5.58 0 0 1 4.559-.274l3.188 1.201.11.034c.11.026.223.031.331.016l.02-.004.258.04c.092.013.183.022.275.03 3.737.304 6.968-2.686 7.245-6.64.278-3.947-2.488-7.413-6.216-7.717l-.31-.016h-.227l-.028-.01a.899.899 0 0 0-.324-.035l-.156.024-3.276.663-.275.048c-1.467.221-2.962-.153-4.2-1.061l-1.68-1.235-.225-.203a9.26 9.26 0 0 0-1.183-.869l-.317-.186A8.772 8.772 0 0 0 29.265.889h-.341zm.35 1.81a6.957 6.957 0 0 1 2.96.745l.296.157c.515.29.993.642 1.427 1.049l.083.068 1.714 1.26c1.723 1.265 3.848 1.744 5.905 1.328l3.01-.61.078.011.055.002.056-.001a4.46 4.46 0 0 1 .615.002c2.71.22 4.77 2.8 4.56 5.787-.21 2.979-2.592 5.184-5.294 4.964a4.622 4.622 0 0 1-.611-.097l-.111-.017-.079-.003-2.89-1.09a7.39 7.39 0 0 0-6.029.36l-1.87.968-.095.056a7.217 7.217 0 0 1-1.55.802l-.298.103a6.838 6.838 0 0 1-2.657.304c-4.266-.347-7.413-4.532-6.814-9.196.502-3.911 3.616-6.887 7.222-6.954l.316.001z" fill="#FFF" fill-rule="nonzero" mask="url(#f)"/></g></g><g><g transform="rotate(9 44.073 633.419)"><mask id="h" fill="#fff"><path id="g" d="M0 .13h36.463v36.73H0z"/></mask><path d="M35.644 27.522l-24.76 9.258a1.253 1.253 0 0 1-1.614-.745L.079 11.095a1.268 1.268 0 0 1 .74-1.627L25.58.21c.65-.243 1.372.09 1.614.745l9.191 24.94a1.268 1.268 0 0 1-.74 1.627" fill="#4285F4" mask="url(#h)"/></g><path d="M121.048 11.073a4.033 4.033 0 0 0-1.246.577l-.217.161c-.247.196-.47.42-.664.668l-.548.544a2.164 2.164 0 0 1-1.531.64h-1.374l-.094.007a.686.686 0 0 0-.177.052l-.072.014-.12.03a3.055 3.055 0 0 0-2.149 3.73 3.024 3.024 0 0 0 3.71 2.157l.2-.064.06-.007a.65.65 0 0 0 .13-.042l.178-.099 1.076-.625a2.152 2.152 0 0 1 1.64-.217l.815.22c.218.091.522.173.835.22.667.097 1.35.027 2-.215 1.854-.691 2.95-2.67 2.562-4.618-.461-2.313-2.759-3.742-5.014-3.133zm3.732 3.388c.263 1.322-.485 2.672-1.737 3.139a2.723 2.723 0 0 1-1.965-.019l-.888-.244a3.459 3.459 0 0 0-2.636.35l-1.052.609-.072.024a1.717 1.717 0 0 1-2.31-1.157 1.748 1.748 0 0 1 1.224-2.136l.065-.016c.045-.01.094-.018.15-.025l.069-.016 1.215-.001a3.47 3.47 0 0 0 2.456-1.024l.598-.601.106-.127a3.04 3.04 0 0 1 .4-.388l.16-.117a2.734 2.734 0 0 1 4.217 1.749zm-21.012-1.449a3.139 3.139 0 0 0-2.443 3.69 3.11 3.11 0 0 0 3.669 2.451 3.139 3.139 0 0 0 2.442-3.689 3.111 3.111 0 0 0-3.668-2.452zm2.387 2.706a1.832 1.832 0 0 1-1.422 2.155 1.803 1.803 0 0 1-2.126-1.426 1.833 1.833 0 0 1 1.422-2.155 1.803 1.803 0 0 1 2.09 1.281l.036.145z" fill="#FFF" fill-rule="nonzero"/><g transform="rotate(9 43.883 633.603)"><mask id="j" fill="#fff"><path id="i" d="M25.094.245L.89 9.568c-.687.265-1.037 1.056-.782 1.769l8.985 25.114c.255.712 1.018 1.075 1.704.811L35 27.939c.687-.265 1.037-1.056.782-1.769L26.798 1.056A1.329 1.329 0 0 0 25.555.16c-.153 0-.31.028-.461.086"/></mask><path d="M13.402 18.342a.654.654 0 0 1 .782-.355l.09.035 25.268 11.894a.653.653 0 0 1-.467 1.217l-.09-.035-24.67-11.613-6.724 14.77a.654.654 0 0 1-.775.357l-.09-.033a.653.653 0 0 1-.358-.775l.034-.09 7-15.372z" fill="#FFF" fill-rule="nonzero" mask="url(#j)"/></g></g>
+      </g>
     </defs>
   </svg>
 </iron-iconset-svg>
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_visibility_page.html b/chrome/browser/resources/nearby_share/shared/nearby_visibility_page.html
index 244af76..a33e9cb 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_visibility_page.html
+++ b/chrome/browser/resources/nearby_share/shared/nearby_visibility_page.html
@@ -18,6 +18,10 @@
         margin-inline-start: 24px;
         overflow: hidden;
       }
+
+      nearby-contact-visibility {
+        width: 100%;
+      }
     </style>
     <nearby-page-template title="$i18n{nearbyShareVisibilityPageTitle}"
         sub-title="$i18n{nearbyShareVisibilityPageSubtitle}"
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 29b9d6d3..a781ab8 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -349,6 +349,8 @@
     "chromeos/nearby_share_page/nearby_share_subpage.m.js",
     "chromeos/nearby_share_page/types.m.js",
     "chromeos/on_startup_page/on_startup_page.m.js",
+    "chromeos/os_a11y_page/tts_subpage.m.js",
+    "chromeos/os_a11y_page/tts_subpage_browser_proxy.m.js",
     "chromeos/os_about_page/channel_switcher_dialog.m.js",
     "chromeos/os_about_page/detailed_build_info.m.js",
     "chromeos/os_about_page/device_name_browser_proxy.m.js",
@@ -436,6 +438,7 @@
     "controls/settings_boolean_control_behavior.m.js",
     "controls/settings_dropdown_menu.m.js",
     "controls/settings_radio_group.m.js",
+    "controls/settings_slider.m.js",
     "controls/settings_textarea.m.js",
     "controls/settings_toggle_button.m.js",
     "extension_control_browser_proxy.m.js",
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_a11y_page/BUILD.gn
index 61a612c..c404ade 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//chrome/browser/resources/settings/chromeos/os_settings.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 
 js_type_check("closure_compile") {
@@ -108,7 +109,7 @@
 js_type_check("closure_compile_module") {
   is_polymer3 = true
   deps = [
-    #    ":externs.m",
+    ":externs.m",
     #    ":manage_a11y_page.m",
     ":manage_a11y_page_browser_proxy.m",
 
@@ -118,7 +119,7 @@
     #    ":switch_access_subpage.m",
     ":switch_access_subpage_browser_proxy.m",
 
-    #    ":tts_subpage.m"
+    ":tts_subpage.m",
     ":tts_subpage_browser_proxy.m",
   ]
 }
@@ -179,7 +180,19 @@
 js_library("tts_subpage.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.m.js" ]
   deps = [
-    # TODO: Fill those in.
+    ":tts_subpage_browser_proxy.m",
+    "//chrome/browser/resources/settings:router.m",
+    "//chrome/browser/resources/settings/chromeos:deep_linking_behavior.m",
+    "//chrome/browser/resources/settings/chromeos:os_route.m",
+    "//chrome/browser/resources/settings/controls:settings_slider.m",
+    "//chrome/browser/resources/settings/languages_page:languages_browser_proxy.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
+    "//ui/webui/resources/cr_elements/cr_expand_button:cr_expand_button.m",
+    "//ui/webui/resources/cr_elements/cr_input:cr_input.m",
+    "//ui/webui/resources/cr_elements/cr_slider:cr_slider.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
   extra_deps = [ ":tts_subpage_module" ]
 }
@@ -225,6 +238,8 @@
   js_file = "tts_subpage.js"
   html_file = "tts_subpage.html"
   html_type = "dom-module"
+  auto_imports = os_settings_auto_imports
+  namespace_rewrites = os_settings_namespace_rewrites
 }
 
 import("//ui/webui/resources/tools/js_modulizer.gni")
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.html
index 44657c4..87532bb5 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage.html
@@ -3,17 +3,17 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_slider/cr_slider.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="../../controls/settings_slider.html">
+<link rel="import" href="../../languages_page/languages_browser_proxy.html">
+<link rel="import" href="../../router.html">
+<link rel="import" href="../../settings_shared_css.html">
 <link rel="import" href="../deep_linking_behavior.html">
 <link rel="import" href="../os_route.html">
-<link rel="import" href="../../router.html">
-<link rel="import" href="../../i18n_setup.html">
-<link rel="import" href="../../languages_page/languages_browser_proxy.html">
-<link rel="import" href="../../settings_shared_css.html">
 <link rel="import" href="tts_subpage_browser_proxy.html">
 
 <dom-module id="settings-tts-subpage">
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index 199bba6d..b7869bf 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -112,6 +112,7 @@
                              "chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_account_manager_browser_proxy.html|NearbyAccountManagerBrowserProxy,NearbyAccountManagerBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_manager.html|setReceiveManagerForTesting,getReceiveManager,observeReceiveManager",
                              "chrome/browser/resources/settings/chromeos/nearby_share_page/types.html|NearbyShareDataUsage,dataUsageStringToEnum",
+                             "chrome/browser/resources/settings/chromeos/os_a11y_page/tts_subpage_browser_proxy.html|TtsSubpageBrowserProxy,TtsSubpageBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/os_languages_page/input_method_util.html|generateOptions,getFirstPartyInputMethodEngineId,getOptionLabelName,getOptionMenuItems,getOptionUiType,getOptionUrl,hasOptionsPageInSettings,InputToolCode,isNumberValue,OPTION_DEFAULT,OptionType,UiType",
                              "chrome/browser/resources/settings/chromeos/os_languages_page/languages_metrics_proxy.html|LanguagesMetricsProxy, LanguagesMetricsProxyImpl, LanguagesPageInteraction",
                              "chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_browser_proxy.html|FingerprintInfo,FingerprintBrowserProxy,FingerprintResultType,FingerprintBrowserProxyImpl,FingerprintAttempt,FingerprintScan",
@@ -151,6 +152,7 @@
                              "chrome/browser/resources/settings/about_page/about_page_browser_proxy.html|AboutPageBrowserProxyImpl,AboutPageUpdateInfo,AboutPageBrowserProxy,browserChannelToI18nId,VersionInfo,ChannelInfo,BrowserChannel,isTargetChannelMoreStable,UpdateStatus,UpdateStatusChangedEvent,RegulatoryInfo,TPMFirmwareUpdateStatusChangedEvent",
                              "chrome/browser/resources/settings/chromeos/os_about_page/device_name_browser_proxy.html|DeviceNameBrowserProxy,DeviceNameBrowserProxyImpl",
                              "chrome/browser/resources/settings/chromeos/os_settings_page/main_page_behavior.html|MainPageBehavior",
+                             "ui/webui/resources/cr_elements/cr_slider/cr_slider.html|SliderTick",
                              "ui/webui/resources/html/parse_html_subset.html|parseHtmlSubset",
                            ]
 
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index 90ba34eb..94c54df 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -24,6 +24,7 @@
 import '../prefs/prefs.m.js';
 import './personalization_page/personalization_page.m.js';
 import './personalization_page/change_picture.m.js';
+import './os_a11y_page/tts_subpage.m.js';
 import './os_people_page/account_manager.m.js';
 import './os_people_page/kerberos_accounts.m.js';
 import './parental_controls_page/parental_controls_page.m.js';
@@ -58,6 +59,7 @@
 export {Account, NearbyAccountManagerBrowserProxy, NearbyAccountManagerBrowserProxyImpl} from './nearby_share_page/nearby_account_manager_browser_proxy.m.js';
 export {getReceiveManager, observeReceiveManager, setReceiveManagerForTesting} from './nearby_share_page/nearby_share_receive_manager.m.js';
 export {dataUsageStringToEnum, NearbyShareDataUsage} from './nearby_share_page/types.m.js';
+export {TtsSubpageBrowserProxy, TtsSubpageBrowserProxyImpl} from './os_a11y_page/tts_subpage_browser_proxy.m.js';
 export {DeviceNameBrowserProxy, DeviceNameBrowserProxyImpl} from './os_about_page/device_name_browser_proxy.m.js';
 export {KerberosAccountsBrowserProxyImpl, KerberosConfigErrorCode, KerberosErrorType} from './os_people_page/kerberos_accounts_browser_proxy.m.js';
 export {OsSyncBrowserProxyImpl} from './os_people_page/os_sync_browser_proxy.m.js';
diff --git a/chrome/browser/sessions/session_restore_browsertest.cc b/chrome/browser/sessions/session_restore_browsertest.cc
index e41d9d8e..0c3fc53 100644
--- a/chrome/browser/sessions/session_restore_browsertest.cc
+++ b/chrome/browser/sessions/session_restore_browsertest.cc
@@ -21,6 +21,7 @@
 #include "base/time/time.h"
 #include "base/util/memory_pressure/fake_memory_pressure_monitor.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/first_run/first_run.h"
@@ -1479,10 +1480,11 @@
   ASSERT_EQ(new_browser->tab_strip_model()->active_index(), 1);
 }
 
-#if !defined(OS_CHROMEOS) && !defined(OS_MAC)
-// This test doesn't apply to the Mac version; see GetCommandLineForRelaunch
+#if !defined(OS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS) && \
+    !BUILDFLAG(IS_CHROMEOS_ASH)
+// This test doesn't apply to Mac or Lacros; see GetCommandLineForRelaunch
 // for details. It was disabled for a long time so might never have worked on
-// ChromeOS.
+// Chrome OS Ash.
 
 // Launches an app window, closes tabbed browser, launches and makes sure
 // we restore the tabbed browser url.
@@ -1511,7 +1513,8 @@
             new_browser->tab_strip_model()->GetActiveWebContents()->GetURL());
 }
 
-#endif  // !defined(OS_CHROMEOS) && !defined(OS_MAC)
+#endif  // !!defined(OS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS) &&
+        // !BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Creates two windows, closes one, restores, make sure only one window open.
 IN_PROC_BROWSER_TEST_F(SessionRestoreTest, TwoWindowsCloseOneRestoreOnlyOne) {
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
index ebd066b..0013511 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.cc
@@ -34,6 +34,10 @@
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if defined(OS_CHROMEOS)
+#include "chrome/common/extensions/extension_constants.h"
+#endif
+
 using extensions::EventRouter;
 using extensions::Extension;
 using extensions::ExtensionSystem;
@@ -345,6 +349,29 @@
 #endif
 }
 
+bool TtsExtensionEngine::IsBuiltInTtsEngineInitialized(
+    content::BrowserContext* browser_context) {
+  if (!browser_context || disable_built_in_tts_engine_for_testing_)
+    return true;
+
+#if defined(OS_CHROMEOS)
+  std::vector<content::VoiceData> voices;
+  GetVoices(browser_context, &voices);
+  bool saw_google_tts = false;
+  bool saw_espeak = false;
+  for (const auto& voice : voices) {
+    saw_google_tts |=
+        voice.engine_id == extension_misc::kGoogleSpeechSynthesisExtensionId;
+    saw_espeak |=
+        voice.engine_id == extension_misc::kEspeakSpeechSynthesisExtensionId;
+  }
+  return saw_google_tts && saw_espeak;
+#else
+  // Vacuously; no built in engines on other platforms yet. TODO: network tts?
+  return true;
+#endif
+}
+
 ExtensionFunction::ResponseAction
 ExtensionTtsEngineUpdateVoicesFunction::Run() {
   base::ListValue* voices_data = nullptr;
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api.h b/chrome/browser/speech/extension_api/tts_engine_extension_api.h
index 23f8f83..a124f2f 100644
--- a/chrome/browser/speech/extension_api/tts_engine_extension_api.h
+++ b/chrome/browser/speech/extension_api/tts_engine_extension_api.h
@@ -36,6 +36,8 @@
   void Pause(content::TtsUtterance* utterance) override;
   void Resume(content::TtsUtterance* utterance) override;
   bool LoadBuiltInTtsEngine(content::BrowserContext* browser_context) override;
+  bool IsBuiltInTtsEngineInitialized(
+      content::BrowserContext* browser_context) override;
 
   void DisableBuiltInTTSEngineForTesting() {
     disable_built_in_tts_engine_for_testing_ = true;
diff --git a/chrome/browser/speech/extension_api/tts_extension_apitest.cc b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
index d7087f9..7f5c1d0 100644
--- a/chrome/browser/speech/extension_api/tts_extension_apitest.cc
+++ b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
@@ -259,6 +259,7 @@
     content::TtsController* tts_controller =
         content::TtsController::GetInstance();
     tts_controller->SetTtsPlatform(&mock_platform_impl_);
+    TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
     tts_controller->SetTtsEngineDelegate(TtsExtensionEngine::GetInstance());
   }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c8bad73..f3c2a97 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -961,6 +961,10 @@
       "find_bar/find_bar_platform_helper.h",
       "focus_tab_after_navigation_helper.cc",
       "focus_tab_after_navigation_helper.h",
+      "font_access/font_access_chooser.cc",
+      "font_access/font_access_chooser.h",
+      "font_access/font_access_chooser_controller.cc",
+      "font_access/font_access_chooser_controller.h",
       "global_error/global_error.cc",
       "global_error/global_error.h",
       "global_error/global_error_bubble_view_base.h",
diff --git a/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc b/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
index ba0845b2..73d8d6a 100644
--- a/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_capture_mode_delegate.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/platform_util.h"
@@ -29,6 +30,8 @@
 
 namespace {
 
+ChromeCaptureModeDelegate* g_instance = nullptr;
+
 ScreenshotArea ConvertToScreenshotArea(const aura::Window* window,
                                        const gfx::Rect& bounds) {
   return window->IsRootWindow()
@@ -36,11 +39,39 @@
              : ScreenshotArea::CreateForWindow(window);
 }
 
+bool IsScreenCaptureDisabledByPolicy() {
+  return g_browser_process->local_state()->GetBoolean(
+      prefs::kDisableScreenshots);
+}
+
 }  // namespace
 
-ChromeCaptureModeDelegate::ChromeCaptureModeDelegate() = default;
+ChromeCaptureModeDelegate::ChromeCaptureModeDelegate() {
+  DCHECK_EQ(g_instance, nullptr);
+  g_instance = this;
+}
 
-ChromeCaptureModeDelegate::~ChromeCaptureModeDelegate() = default;
+ChromeCaptureModeDelegate::~ChromeCaptureModeDelegate() {
+  DCHECK_EQ(g_instance, this);
+  g_instance = nullptr;
+}
+
+// static
+ChromeCaptureModeDelegate* ChromeCaptureModeDelegate::Get() {
+  DCHECK(g_instance);
+  return g_instance;
+}
+
+void ChromeCaptureModeDelegate::SetIsScreenCaptureLocked(bool locked) {
+  is_screen_capture_locked_ = locked;
+  if (is_screen_capture_locked_)
+    InterruptVideoRecordingIfAny();
+}
+
+void ChromeCaptureModeDelegate::InterruptVideoRecordingIfAny() {
+  if (interrupt_video_recording_callback_)
+    std::move(interrupt_video_recording_callback_).Run();
+}
 
 base::FilePath ChromeCaptureModeDelegate::GetActiveUserDownloadsDir() const {
   DCHECK(chromeos::LoginState::Get()->IsUserLoggedIn());
@@ -86,12 +117,19 @@
 }
 
 bool ChromeCaptureModeDelegate::IsCaptureModeInitRestricted() const {
-  return policy::DlpContentManager::Get()->IsCaptureModeInitRestricted();
+  return is_screen_capture_locked_ || IsScreenCaptureDisabledByPolicy() ||
+         policy::DlpContentManager::Get()->IsCaptureModeInitRestricted();
 }
 
 bool ChromeCaptureModeDelegate::IsCaptureAllowed(const aura::Window* window,
                                                  const gfx::Rect& bounds,
                                                  bool for_video) const {
+  if (is_screen_capture_locked_)
+    return false;
+
+  if (IsScreenCaptureDisabledByPolicy())
+    return false;
+
   policy::DlpContentManager* dlp_content_manager =
       policy::DlpContentManager::Get();
   const ScreenshotArea area = ConvertToScreenshotArea(window, bounds);
@@ -103,11 +141,16 @@
     const aura::Window* window,
     const gfx::Rect& bounds,
     base::OnceClosure stop_callback) {
+  // The order here matters, since DlpContentManager::OnVideoCaptureStarted()
+  // may call InterruptVideoRecordingIfAny() right away, so the callback must be
+  // set first.
+  interrupt_video_recording_callback_ = std::move(stop_callback);
   policy::DlpContentManager::Get()->OnVideoCaptureStarted(
-      ConvertToScreenshotArea(window, bounds), std::move(stop_callback));
+      ConvertToScreenshotArea(window, bounds));
 }
 
 void ChromeCaptureModeDelegate::StopObservingRestrictedContent() {
+  interrupt_video_recording_callback_.Reset();
   policy::DlpContentManager::Get()->OnVideoCaptureStopped();
 }
 
diff --git a/chrome/browser/ui/ash/chrome_capture_mode_delegate.h b/chrome/browser/ui/ash/chrome_capture_mode_delegate.h
index c7253487..c550d6d 100644
--- a/chrome/browser/ui/ash/chrome_capture_mode_delegate.h
+++ b/chrome/browser/ui/ash/chrome_capture_mode_delegate.h
@@ -18,6 +18,16 @@
       delete;
   ~ChromeCaptureModeDelegate() override;
 
+  static ChromeCaptureModeDelegate* Get();
+
+  // Sets |is_screen_capture_locked_| to the given |locked|, and interrupts any
+  // on going video capture.
+  void SetIsScreenCaptureLocked(bool locked);
+
+  // Interrupts an on going video recording if any, due to some restricted
+  // content showing up on the screen, or if screen capture becomes locked.
+  void InterruptVideoRecordingIfAny();
+
   // ash::CaptureModeDelegate:
   base::FilePath GetActiveUserDownloadsDir() const override;
   void ShowScreenCaptureItemInFolder(const base::FilePath& file_path) override;
@@ -37,6 +47,19 @@
       override;
   void BindAudioStreamFactory(
       mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) override;
+
+ private:
+  // Used to temporarily disable capture mode in certain cases for which neither
+  // a device policy, nor DLP will be triggered. For example, Some extension
+  // APIs can request that a tab operate in a locked fullscreen mode, and in
+  // that, capturing the screen is disabled.
+  bool is_screen_capture_locked_ = false;
+
+  // A callback to terminate an on going video recording on ash side due to a
+  // restricted content showing up on the screen, or screen capture becoming
+  // locked.
+  // This is only non-null during recording.
+  base::OnceClosure interrupt_video_recording_callback_;
 };
 
 #endif  // CHROME_BROWSER_UI_ASH_CHROME_CAPTURE_MODE_DELEGATE_H_
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader.cc b/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader.cc
index 2c8f9020..56d6efb 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader.h"
 
+#include "ash/public/cpp/holding_space/holding_space_color_provider.h"
 #include "ash/public/cpp/image_downloader.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -20,6 +21,8 @@
 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
 #include "chrome/browser/extensions/api/messaging/native_message_port.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ash/holding_space/holding_space_util.h"
+#include "chromeos/ui/vector_icons/vector_icons.h"
 #include "extensions/browser/api/messaging/channel_endpoint.h"
 #include "extensions/browser/api/messaging/message_service.h"
 #include "extensions/browser/api/messaging/native_message_host.h"
@@ -32,6 +35,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/skia_util.h"
 #include "url/gurl.h"
 
@@ -44,6 +48,9 @@
 constexpr char kNativeMessageHostName[] =
     "com.google.holding_space_thumbnail_loader";
 
+// The DIP size for the folder icon.
+const size_t kFolderIconDipSize = 20;
+
 using ThumbnailDataCallback = base::OnceCallback<void(const std::string& data)>;
 
 // Handles a parsed message sent from image loader extension in response to a
@@ -189,8 +196,9 @@
 
 HoldingSpaceThumbnailLoader::ThumbnailRequest::ThumbnailRequest(
     const base::FilePath& item_path,
-    const gfx::Size& size)
-    : item_path(item_path), size(size) {}
+    const gfx::Size& size,
+    float scale_factor)
+    : item_path(item_path), size(size), scale_factor(scale_factor) {}
 
 HoldingSpaceThumbnailLoader::ThumbnailRequest::~ThumbnailRequest() = default;
 
@@ -223,8 +231,16 @@
     return;
   }
 
+  // Short-circuit icons for folders.
   if (file_info.is_directory) {
-    std::move(callback).Run(nullptr);
+    std::move(callback).Run(
+        holding_space_util::CreatePlaceholderImage(
+            gfx::CreateVectorIcon(
+                chromeos::kFiletypeFolderIcon,
+                request.scale_factor * kFolderIconDipSize,
+                HoldingSpaceColorProvider::Get()->GetFileIconColor()),
+            request.size)
+            .bitmap());
     return;
   }
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader.h b/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader.h
index 3b3c8fd..401d5e7 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader.h
@@ -38,7 +38,9 @@
 
   // Thumbnail request data that will be forwarded to the image loader.
   struct ThumbnailRequest {
-    ThumbnailRequest(const base::FilePath& item_path, const gfx::Size& size);
+    ThumbnailRequest(const base::FilePath& item_path,
+                     const gfx::Size& size,
+                     float scale_factor);
     ~ThumbnailRequest();
 
     // The absolute item file path.
@@ -46,6 +48,9 @@
 
     // The desired bitmap size.
     const gfx::Size size;
+
+    // The scale factor for which the bitmap is being generated.
+    float scale_factor;
   };
 
   // Returns a weak pointer to this instance.
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader_browsertest.cc
index a30eff2..f357afe3 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_thumbnail_loader_browsertest.cc
@@ -166,7 +166,8 @@
 
   base::RunLoop run_loop;
   ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request(
-      GetTestPath(TestPath::kNonExistent), gfx::Size(48, 48));
+      GetTestPath(TestPath::kNonExistent), gfx::Size(48, 48),
+      /*scale_factor=*/1.0f);
   loader->Load(request,
                base::BindLambdaForTesting([&run_loop](const SkBitmap* bitmap) {
                  EXPECT_FALSE(bitmap);
@@ -180,14 +181,33 @@
   ASSERT_TRUE(loader);
 
   base::RunLoop run_loop;
+  SkBitmap bitmap;
   ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request(
-      GetTestPath(TestPath::kEmptyDir), gfx::Size(48, 48));
-  loader->Load(request,
-               base::BindLambdaForTesting([&run_loop](const SkBitmap* bitmap) {
-                 EXPECT_FALSE(bitmap);
-                 run_loop.Quit();
-               }));
+      GetTestPath(TestPath::kEmptyDir), gfx::Size(48, 48),
+      /*scale_factor=*/1.0f);
+  loader->Load(request, base::BindOnce(&CopyBitmapAndRunClosure,
+                                       run_loop.QuitClosure(), &bitmap));
   run_loop.Run();
+  EXPECT_FALSE(bitmap.isNull());
+  EXPECT_EQ(48, bitmap.width());
+  EXPECT_EQ(48, bitmap.height());
+}
+
+IN_PROC_BROWSER_TEST_F(HoldingSpaceThumbnailLoaderTest, LoadFolderScale2) {
+  ash::HoldingSpaceThumbnailLoader* loader = GetThumbnailLoader();
+  ASSERT_TRUE(loader);
+
+  base::RunLoop run_loop;
+  SkBitmap bitmap;
+  ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request(
+      GetTestPath(TestPath::kEmptyDir), gfx::Size(48, 48),
+      /*scale_factor=*/2.0f);
+  loader->Load(request, base::BindOnce(&CopyBitmapAndRunClosure,
+                                       run_loop.QuitClosure(), &bitmap));
+  run_loop.Run();
+  EXPECT_FALSE(bitmap.isNull());
+  EXPECT_EQ(48, bitmap.width());
+  EXPECT_EQ(48, bitmap.height());
 }
 
 IN_PROC_BROWSER_TEST_F(HoldingSpaceThumbnailLoaderTest, LoadJpg) {
@@ -197,7 +217,7 @@
   SkBitmap bitmap;
   base::RunLoop run_loop;
   ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request(
-      GetTestPath(TestPath::kJpg), gfx::Size(48, 48));
+      GetTestPath(TestPath::kJpg), gfx::Size(48, 48), /*scale_factor=*/1.0f);
   loader->Load(request, base::BindOnce(&CopyBitmapAndRunClosure,
                                        run_loop.QuitClosure(), &bitmap));
   run_loop.Run();
@@ -213,7 +233,8 @@
 
   base::RunLoop run_loop;
   ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request(
-      GetTestPath(TestPath::kBrokenJpg), gfx::Size(48, 48));
+      GetTestPath(TestPath::kBrokenJpg), gfx::Size(48, 48),
+      /*scale_factor=*/1.0f);
   loader->Load(request,
                base::BindLambdaForTesting([&run_loop](const SkBitmap* bitmap) {
                  EXPECT_FALSE(bitmap);
@@ -229,7 +250,7 @@
   SkBitmap bitmap;
   base::RunLoop run_loop;
   ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request(
-      GetTestPath(TestPath::kPng), gfx::Size(48, 48));
+      GetTestPath(TestPath::kPng), gfx::Size(48, 48), /*scale_factor=*/1.0f);
   loader->Load(request, base::BindOnce(&CopyBitmapAndRunClosure,
                                        run_loop.QuitClosure(), &bitmap));
   run_loop.Run();
@@ -243,7 +264,7 @@
   ASSERT_TRUE(loader);
 
   ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request(
-      GetTestPath(TestPath::kPng), gfx::Size(48, 48));
+      GetTestPath(TestPath::kPng), gfx::Size(48, 48), /*scale_factor=*/1.0f);
 
   SkBitmap bitmap1;
   base::RunLoop run_loop1;
@@ -284,14 +305,14 @@
 
   SkBitmap bitmap1;
   ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request1(
-      GetTestPath(TestPath::kPng), gfx::Size(48, 48));
+      GetTestPath(TestPath::kPng), gfx::Size(48, 48), /*scale_factor=*/1.0f);
   base::RunLoop run_loop1;
   loader->Load(request1, base::BindOnce(&CopyBitmapAndRunClosure,
                                         run_loop1.QuitClosure(), &bitmap1));
 
   SkBitmap bitmap2;
   ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request2(
-      GetTestPath(TestPath::kPng), gfx::Size(96, 96));
+      GetTestPath(TestPath::kPng), gfx::Size(96, 96), /*scale_factor=*/2.0f);
   base::RunLoop run_loop2;
   loader->Load(request2, base::BindOnce(&CopyBitmapAndRunClosure,
                                         run_loop2.QuitClosure(), &bitmap2));
@@ -299,7 +320,7 @@
   SkBitmap bitmap3;
   base::RunLoop run_loop3;
   ash::HoldingSpaceThumbnailLoader::ThumbnailRequest request3(
-      GetTestPath(TestPath::kJpg), gfx::Size(48, 48));
+      GetTestPath(TestPath::kJpg), gfx::Size(48, 48), /*scale_factor=*/1.0f);
   loader->Load(request3, base::BindOnce(&CopyBitmapAndRunClosure,
                                         run_loop3.QuitClosure(), &bitmap3));
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_util.cc b/chrome/browser/ui/ash/holding_space/holding_space_util.cc
index 61f94b4..3ab9fb5 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_util.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_util.cc
@@ -43,14 +43,7 @@
   }
 
   const SkColor color = HoldingSpaceColorProvider::Get()->GetFileIconColor();
-
-  // NOTE: We superimpose the file type icon for `file_path` over a transparent
-  // bitmap in order to center it within the placeholder image at a fixed size.
-  SkBitmap bitmap;
-  bitmap.allocN32Pixels(size.width(), size.height());
-  return gfx::ImageSkiaOperations::CreateSuperimposedImage(
-      gfx::ImageSkia::CreateFrom1xBitmap(bitmap),
-      GetIconForPath(file_path, color));
+  return CreatePlaceholderImage(GetIconForPath(file_path, color), size);
 }
 
 }  // namespace
@@ -190,13 +183,24 @@
       base::BindRepeating(
           [](const base::WeakPtr<HoldingSpaceThumbnailLoader>& thumbnail_loader,
              const base::FilePath& file_path, const gfx::Size& size,
-             HoldingSpaceImage::BitmapCallback callback) {
+             float scale_factor, HoldingSpaceImage::BitmapCallback callback) {
             if (thumbnail_loader)
-              thumbnail_loader->Load({file_path, size}, std::move(callback));
+              thumbnail_loader->Load({file_path, size, scale_factor},
+                                     std::move(callback));
           },
           thumbnail_loader->GetWeakPtr(), file_path));
 }
 
+gfx::ImageSkia CreatePlaceholderImage(const gfx::ImageSkia& file_type_image,
+                                      const gfx::Size& size) {
+  // NOTE: We superimpose the file type icon for `file_path` over a transparent
+  // bitmap in order to center it within the placeholder image at a fixed size.
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(size.width(), size.height());
+  return gfx::ImageSkiaOperations::CreateSuperimposedImage(
+      gfx::ImageSkia::CreateFrom1xBitmap(bitmap), file_type_image);
+}
+
 void SetNowForTesting(base::Optional<base::Time> now) {
   now_for_testing = now;
 }
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_util.h b/chrome/browser/ui/ash/holding_space/holding_space_util.h
index 7ed4c750..d64e11f9 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_util.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_util.h
@@ -19,6 +19,11 @@
 class FilePath;
 }  // namespace base
 
+namespace gfx {
+class ImageSkia;
+class Size;
+}  // namespace gfx
+
 namespace ash {
 
 class HoldingSpaceImage;
@@ -74,6 +79,11 @@
     HoldingSpaceItem::Type type,
     const base::FilePath& file_path);
 
+// Given a base image for a file type, returns a placeholder image to be used in
+// the holding space UI.
+gfx::ImageSkia CreatePlaceholderImage(const gfx::ImageSkia& file_type_image,
+                                      const gfx::Size& size);
+
 void SetNowForTesting(base::Optional<base::Time> now);
 
 }  // namespace holding_space_util
diff --git a/chrome/browser/ui/ash/recording_service_browsertest.cc b/chrome/browser/ui/ash/recording_service_browsertest.cc
index 72547e8..f2e4b3d 100644
--- a/chrome/browser/ui/ash/recording_service_browsertest.cc
+++ b/chrome/browser/ui/ash/recording_service_browsertest.cc
@@ -129,6 +129,12 @@
     // To improve the test efficiency, we set the display to a small size.
     display::test::DisplayManagerTestApi(ash::ShellTestApi().display_manager())
         .UpdateDisplay("300x200");
+    // To avoid flaky tests, we disable audio recording, since the bots won't
+    // capture any audio and won't produce any audio frames. This will cause the
+    // muxer to discard video frames if it expects audio frames but got none,
+    // which may cause the produced webm file to be empty. See issues
+    // https://crbug.com/1151167 and https://crbug.com/1151418.
+    ash::CaptureModeTestApi().SetAudioRecordingEnabled(false);
   }
 
   aura::Window* GetBrowserWindow() const {
@@ -207,3 +213,15 @@
   test_api.SetUserSelectedRegion(gfx::Rect(50, 200));
   FinishVideoRecordingTest(&test_api);
 }
+
+// Tests that recording will be interrupted once screen capture becomes locked.
+IN_PROC_BROWSER_TEST_F(RecordingServiceBrowserTest,
+                       RecordingInterruptedOnCaptureLocked) {
+  ash::CaptureModeTestApi test_api;
+  test_api.StartForFullscreen(/*for_video=*/true);
+  test_api.PerformCapture();
+  WaitForMilliseconds(1000);
+  ChromeCaptureModeDelegate::Get()->SetIsScreenCaptureLocked(true);
+  const base::FilePath video_path = WaitForVideoFileToBeSaved();
+  VerifyVideoFileAndDelete(video_path);
+}
diff --git a/chrome/browser/ui/font_access/OWNERS b/chrome/browser/ui/font_access/OWNERS
new file mode 100644
index 0000000..4c791d4
--- /dev/null
+++ b/chrome/browser/ui/font_access/OWNERS
@@ -0,0 +1,4 @@
+file://content/browser/font_access/OWNERS
+
+# TEAM: storage-dev@chromium.org
+# COMPONENT: Blink>Storage>FontAccess
diff --git a/chrome/browser/ui/font_access/font_access_chooser.cc b/chrome/browser/ui/font_access/font_access_chooser.cc
new file mode 100644
index 0000000..9cf97e3c
--- /dev/null
+++ b/chrome/browser/ui/font_access/font_access_chooser.cc
@@ -0,0 +1,9 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/font_access/font_access_chooser.h"
+#include "content/public/browser/font_access_chooser.h"
+
+FontAccessChooser::FontAccessChooser(base::OnceClosure close_callback)
+    : closure_runner_(std::move(close_callback)) {}
diff --git a/chrome/browser/ui/font_access/font_access_chooser.h b/chrome/browser/ui/font_access/font_access_chooser.h
new file mode 100644
index 0000000..ecdb6c7
--- /dev/null
+++ b/chrome/browser/ui/font_access/font_access_chooser.h
@@ -0,0 +1,20 @@
+// 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 CHROME_BROWSER_UI_FONT_ACCESS_FONT_ACCESS_CHOOSER_H_
+#define CHROME_BROWSER_UI_FONT_ACCESS_FONT_ACCESS_CHOOSER_H_
+
+#include "base/callback_helpers.h"
+#include "content/public/browser/font_access_chooser.h"
+
+class FontAccessChooser : public content::FontAccessChooser {
+ public:
+  explicit FontAccessChooser(base::OnceClosure close_callback);
+  ~FontAccessChooser() override = default;
+
+ private:
+  base::ScopedClosureRunner closure_runner_;
+};
+
+#endif  // CHROME_BROWSER_UI_FONT_ACCESS_FONT_ACCESS_CHOOSER_H_
diff --git a/chrome/browser/ui/font_access/font_access_chooser_controller.cc b/chrome/browser/ui/font_access/font_access_chooser_controller.cc
new file mode 100644
index 0000000..0a4bedd
--- /dev/null
+++ b/chrome/browser/ui/font_access/font_access_chooser_controller.cc
@@ -0,0 +1,142 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/font_access/font_access_chooser_controller.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/grit/generated_resources.h"
+#include "content/public/browser/font_access_chooser.h"
+#include "content/public/browser/font_access_context.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "third_party/blink/public/mojom/font_access/font_access.mojom-shared.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+content::FontAccessContext* GetChooserContext(content::RenderFrameHost* frame) {
+  return frame->GetStoragePartition()->GetFontAccessContext();
+}
+
+}  // namespace
+
+FontAccessChooserController::FontAccessChooserController(
+    content::RenderFrameHost* frame,
+    content::FontAccessChooser::Callback callback)
+    : ChooserController(frame,
+                        IDS_FONT_ACCESS_CHOOSER_PROMPT_ORIGIN,
+                        // Extensions are not supported. This is stub text.
+                        IDS_FONT_ACCESS_CHOOSER_PROMPT_ORIGIN),
+      callback_(std::move(callback)) {
+  DCHECK(frame);
+
+  content::FontAccessContext* chooser_context = GetChooserContext(frame);
+  if (chooser_context == nullptr) {
+    std::move(callback_).Run(
+        blink::mojom::FontEnumerationStatus::kUnexpectedError, {});
+    return;
+  }
+
+  chooser_context->FindAllFonts(
+      base::BindOnce(&FontAccessChooserController::DidFindAllFonts,
+                     weak_factory_.GetWeakPtr()));
+}
+
+FontAccessChooserController::~FontAccessChooserController() {
+  if (callback_) {
+    std::move(callback_).Run(
+        blink::mojom::FontEnumerationStatus::kUnexpectedError, {});
+  }
+}
+
+base::string16 FontAccessChooserController::GetNoOptionsText() const {
+  return l10n_util::GetStringUTF16(
+      IDS_FONT_ACCESS_CHOOSER_NO_FONTS_FOUND_PROMPT);
+}
+
+base::string16 FontAccessChooserController::GetOkButtonLabel() const {
+  return l10n_util::GetStringUTF16(IDS_FONT_ACCESS_CHOOSER_IMPORT_BUTTON_TEXT);
+}
+
+size_t FontAccessChooserController::NumOptions() const {
+  return items_.size();
+}
+
+base::string16 FontAccessChooserController::GetOption(size_t index) const {
+  DCHECK_LT(index, items_.size());
+  DCHECK(base::Contains(font_metadata_map_, items_[index]));
+
+  return base::UTF8ToUTF16(items_[index]);
+}
+
+bool FontAccessChooserController::ShouldShowHelpButton() const {
+  return false;
+}
+
+bool FontAccessChooserController::ShouldShowReScanButton() const {
+  return false;
+}
+
+bool FontAccessChooserController::BothButtonsAlwaysEnabled() const {
+  // Import button is disabled if there isn't at least one selection.
+  return false;
+}
+
+bool FontAccessChooserController::AllowMultipleSelection() const {
+  return true;
+}
+
+bool FontAccessChooserController::TableViewAlwaysDisabled() const {
+  return false;
+}
+
+void FontAccessChooserController::Select(const std::vector<size_t>& indices) {
+  DCHECK_GT(indices.size(), 0u);
+
+  std::vector<blink::mojom::FontMetadataPtr> fonts;
+
+  for (auto& index : indices) {
+    DCHECK_LT(index, items_.size());
+    auto found = font_metadata_map_.find(items_[index]);
+    if (found == font_metadata_map_.end()) {
+      continue;
+    }
+    fonts.push_back(found->second.Clone());
+  }
+
+  std::move(callback_).Run(blink::mojom::FontEnumerationStatus::kOk,
+                           std::move(fonts));
+}
+
+void FontAccessChooserController::Cancel() {
+  std::move(callback_).Run(blink::mojom::FontEnumerationStatus::kCanceled, {});
+}
+
+void FontAccessChooserController::Close() {
+  std::move(callback_).Run(blink::mojom::FontEnumerationStatus::kCanceled, {});
+}
+
+void FontAccessChooserController::OpenHelpCenterUrl() const {
+  NOTIMPLEMENTED();
+}
+
+void FontAccessChooserController::DidFindAllFonts(
+    blink::mojom::FontEnumerationStatus status,
+    std::vector<blink::mojom::FontMetadata> fonts) {
+  for (auto& font : fonts) {
+    auto found = font_metadata_map_.find(font.postscript_name);
+    // If the font is already in the map, skip it.
+    if (found != font_metadata_map_.end()) {
+      continue;
+    }
+    items_.push_back(font.postscript_name);
+    font_metadata_map_[font.postscript_name] = std::move(font);
+  }
+  if (view())
+    view()->OnOptionsInitialized();
+
+  if (ready_callback_for_testing_)
+    std::move(ready_callback_for_testing_).Run();
+}
diff --git a/chrome/browser/ui/font_access/font_access_chooser_controller.h b/chrome/browser/ui/font_access/font_access_chooser_controller.h
new file mode 100644
index 0000000..e6e737a
--- /dev/null
+++ b/chrome/browser/ui/font_access/font_access_chooser_controller.h
@@ -0,0 +1,65 @@
+// 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 CHROME_BROWSER_UI_FONT_ACCESS_FONT_ACCESS_CHOOSER_CONTROLLER_H_
+#define CHROME_BROWSER_UI_FONT_ACCESS_FONT_ACCESS_CHOOSER_CONTROLLER_H_
+
+#include "chrome/browser/chooser_controller/chooser_controller.h"
+#include "content/public/browser/font_access_chooser.h"
+#include "content/public/browser/render_frame_host.h"
+#include "third_party/blink/public/mojom/font_access/font_access.mojom-shared.h"
+#include "third_party/blink/public/mojom/font_access/font_access.mojom.h"
+
+namespace content {
+
+class RenderFrameHost;
+
+}  // namespace content
+
+class FontAccessChooserController : public ChooserController {
+ public:
+  FontAccessChooserController(content::RenderFrameHost* render_frame_host,
+                              content::FontAccessChooser::Callback callback);
+  ~FontAccessChooserController() override;
+
+  // Disallow copy and assign.
+  FontAccessChooserController(FontAccessChooserController&) = delete;
+  FontAccessChooserController& operator=(FontAccessChooserController&) = delete;
+
+  // ChooserController:
+  base::string16 GetNoOptionsText() const override;
+  base::string16 GetOkButtonLabel() const override;
+  size_t NumOptions() const override;
+  base::string16 GetOption(size_t index) const override;
+
+  bool ShouldShowHelpButton() const override;
+  bool ShouldShowReScanButton() const override;
+  bool BothButtonsAlwaysEnabled() const override;
+  bool TableViewAlwaysDisabled() const override;
+  bool AllowMultipleSelection() const override;
+
+  void Select(const std::vector<size_t>& indices) override;
+  void Cancel() override;
+  void Close() override;
+  void OpenHelpCenterUrl() const override;
+
+  void SetReadyCallbackForTesting(base::Callback<void()> callback) {
+    ready_callback_for_testing_ = callback;
+  }
+
+ private:
+  void DidFindAllFonts(blink::mojom::FontEnumerationStatus status,
+                       std::vector<blink::mojom::FontMetadata> fonts);
+  content::FontAccessChooser::Callback callback_;
+  base::Callback<void()> ready_callback_for_testing_;
+
+  std::map<std::string, blink::mojom::FontMetadata> font_metadata_map_;
+  // An ordered list of font names that determines the order of items in the
+  // chooser.
+  std::vector<std::string> items_;
+
+  base::WeakPtrFactory<FontAccessChooserController> weak_factory_{this};
+};
+
+#endif  // CHROME_BROWSER_UI_FONT_ACCESS_FONT_ACCESS_CHOOSER_CONTROLLER_H_
diff --git a/chrome/browser/ui/font_access/font_access_chooser_controller_unittest.cc b/chrome/browser/ui/font_access/font_access_chooser_controller_unittest.cc
new file mode 100644
index 0000000..cd63892
--- /dev/null
+++ b/chrome/browser/ui/font_access/font_access_chooser_controller_unittest.cc
@@ -0,0 +1,121 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/font_access/font_access_chooser_controller.h"
+
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/chooser_controller/mock_chooser_controller_view.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+
+class FontAccessChooserControllerTest : public ChromeRenderViewHostTestHarness {
+ public:
+  FontAccessChooserControllerTest() {
+    scoped_feature_list_.InitAndEnableFeature(blink::features::kFontAccess);
+  }
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    mock_font_chooser_view_ = std::make_unique<MockChooserControllerView>();
+  }
+
+ protected:
+  std::unique_ptr<MockChooserControllerView> mock_font_chooser_view_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(FontAccessChooserControllerTest, MultiSelectTest) {
+  base::RunLoop run_loop;
+  FontAccessChooserController controller(
+      main_rfh(), base::BindLambdaForTesting(
+                      [&](blink::mojom::FontEnumerationStatus status,
+                          std::vector<blink::mojom::FontMetadataPtr> items) {
+                        EXPECT_EQ(status,
+                                  blink::mojom::FontEnumerationStatus::kOk);
+                        EXPECT_EQ(items.size(), 2u);
+                        run_loop.Quit();
+                      }));
+
+  base::RunLoop readiness_loop;
+  controller.SetReadyCallbackForTesting(
+      base::BindLambdaForTesting([&]() { readiness_loop.Quit(); }));
+  readiness_loop.Run();
+
+  controller.set_view(mock_font_chooser_view_.get());
+  EXPECT_GT(controller.NumOptions(), 1u)
+      << "FontAccessChooserController has more than 2 options";
+  controller.Select(std::vector<size_t>({0, 1}));
+  run_loop.Run();
+}
+
+TEST_F(FontAccessChooserControllerTest, CancelTest) {
+  base::RunLoop run_loop;
+  FontAccessChooserController controller(
+      main_rfh(),
+      base::BindLambdaForTesting(
+          [&](blink::mojom::FontEnumerationStatus status,
+              std::vector<blink::mojom::FontMetadataPtr> items) {
+            EXPECT_EQ(status, blink::mojom::FontEnumerationStatus::kCanceled);
+            EXPECT_EQ(items.size(), 0u);
+            run_loop.Quit();
+          }));
+
+  base::RunLoop readiness_loop;
+  controller.SetReadyCallbackForTesting(
+      base::BindLambdaForTesting([&]() { readiness_loop.Quit(); }));
+  readiness_loop.Run();
+
+  controller.set_view(mock_font_chooser_view_.get());
+  controller.Cancel();
+  run_loop.Run();
+}
+
+TEST_F(FontAccessChooserControllerTest, CloseTest) {
+  base::RunLoop run_loop;
+  FontAccessChooserController controller(
+      main_rfh(),
+      base::BindLambdaForTesting(
+          [&](blink::mojom::FontEnumerationStatus status,
+              std::vector<blink::mojom::FontMetadataPtr> items) {
+            EXPECT_EQ(status, blink::mojom::FontEnumerationStatus::kCanceled);
+            EXPECT_EQ(items.size(), 0u);
+            run_loop.Quit();
+          }));
+
+  base::RunLoop readiness_loop;
+  controller.SetReadyCallbackForTesting(
+      base::BindLambdaForTesting([&]() { readiness_loop.Quit(); }));
+  readiness_loop.Run();
+
+  controller.set_view(mock_font_chooser_view_.get());
+  controller.Close();
+  run_loop.Run();
+}
+
+TEST_F(FontAccessChooserControllerTest, DestructorTest) {
+  base::RunLoop run_loop;
+  std::unique_ptr<FontAccessChooserController> controller =
+      std::make_unique<FontAccessChooserController>(
+          main_rfh(),
+          base::BindLambdaForTesting(
+              [&](blink::mojom::FontEnumerationStatus status,
+                  std::vector<blink::mojom::FontMetadataPtr> items) {
+                EXPECT_EQ(
+                    status,
+                    blink::mojom::FontEnumerationStatus::kUnexpectedError);
+                EXPECT_EQ(items.size(), 0u);
+                run_loop.Quit();
+              }));
+
+  base::RunLoop readiness_loop;
+  controller->SetReadyCallbackForTesting(
+      base::BindLambdaForTesting([&]() { readiness_loop.Quit(); }));
+  readiness_loop.Run();
+
+  controller.reset();
+  run_loop.Run();
+}
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc
index b05bf1b..474a14a 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc
@@ -12,11 +12,11 @@
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/system/sys_info.h"
+#include "base/test/scoped_running_on_chromeos.h"
 #include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/test/test_arc_session_manager.h"
 #include "chrome/browser/chromeos/file_manager/fake_disk_mount_manager.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
-#include "chrome/browser/chromeos/scoped_set_running_on_chromeos_for_testing.h"
 #include "chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator_test_api.h"
 #include "chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h"
 #include "chrome/common/chrome_paths.h"
@@ -38,10 +38,6 @@
 
 namespace {
 
-const char kLsbRelease[] =
-    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
-
 class TestStorageHandler : public StorageHandler {
  public:
   explicit TestStorageHandler(Profile* profile,
@@ -104,8 +100,7 @@
     // Create and register My files directory.
     // By emulating chromeos running, GetMyFilesFolderForProfile will return the
     // profile's temporary location instead of $HOME/Downloads.
-    chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
-                                                                base::Time());
+    base::test::ScopedRunningOnChromeOS running_on_chromeos;
     const base::FilePath my_files_path =
         file_manager::util::GetMyFilesFolderForProfile(profile_);
     CHECK(base::CreateDirectory(my_files_path));
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 482213b1..677aabf 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -385,7 +385,10 @@
     qr_generator_key.emplace();
     crypto::RandBytes(*qr_generator_key);
     qr_string = device::cablev2::qr::Encode(*qr_generator_key);
-    paired_phones = GetCablePairings();
+
+    if (!cable_extension_provided) {
+      paired_phones = GetCablePairings();
+    }
     have_paired_phones = !paired_phones.empty();
 
     mojo::Remote<device::mojom::UsbDeviceManager> usb_device_manager;
@@ -396,7 +399,7 @@
         SystemNetworkContextManager::GetInstance()->GetContext());
   }
 
-  if (pairings.empty() && !qr_generator_key) {
+  if (!cable_extension_provided && !qr_generator_key) {
     return;
   }
 
@@ -655,6 +658,10 @@
 
 bool ChromeAuthenticatorRequestDelegate::ShouldPermitCableExtension(
     const url::Origin& origin) {
+  if (base::FeatureList::IsEnabled(device::kWebAuthCableExtensionAnywhere)) {
+    return true;
+  }
+
   // Because the future of the caBLE extension might be that we transition
   // everything to QR-code or sync-based pairing, we don't want use of the
   // extension to spread without consideration. Therefore it's limited to
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index b92205a..c0f6e77 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1605895128-ea3f86e674d4361d5284b25de8910e19181b674b.profdata
+chrome-linux-master-1605916713-42186a780bbf4a6606356f169d60384bd3619cf2.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index c1e70c2..f614359 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1605895128-00d55e6bb356ebbc13be21f27095ad3409f51b0e.profdata
+chrome-mac-master-1605916713-5c2651f0c6431583f545a63fcbde858a51a7c7b1.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 8e1ce212..3fafcf52 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1605873573-36e181f5df5da0c9aa7b43458275355d6ec5a576.profdata
+chrome-win32-master-1605905851-758120749a95f1789e143f45329cd7e7ce463c52.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index a222678..146be17 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1605884021-38c8ae5c2e6706d9e606dfe2bcddc4d1100361a4.profdata
+chrome-win64-master-1605905851-000df8c5d9233d8cd72a75f96d63436b1fbb2537.profdata
diff --git a/chrome/common/chrome_paths_lacros.cc b/chrome/common/chrome_paths_lacros.cc
index bfffdd1..8c177dd 100644
--- a/chrome/common/chrome_paths_lacros.cc
+++ b/chrome/common/chrome_paths_lacros.cc
@@ -6,6 +6,7 @@
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/logging.h"
 #include "base/system/sys_info.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
 
@@ -29,23 +30,17 @@
 }
 
 bool GetUserDocumentsDirectory(base::FilePath* result) {
-  if (base::SysInfo::IsRunningOnChromeOS()) {
-    *result = base::FilePath(crosapi::kMyFilesPath);
-  } else {
-    // For developers on Linux desktop, just pick a reasonable default.
-    *result = base::GetHomeDir().Append("Documents");
-  }
-  return true;
+  // NOTE: Lacros overrides the path with a value from ash early in startup. See
+  // crosapi::mojom::LacrosInitParams.
+  LOG(FATAL) << "Path not initialized";
+  return false;
 }
 
 bool GetUserDownloadsDirectorySafe(base::FilePath* result) {
-  if (base::SysInfo::IsRunningOnChromeOS()) {
-    *result = base::FilePath(crosapi::kDefaultDownloadsPath);
-  } else {
-    // For developers on Linux desktop, just pick a reasonable default.
-    *result = base::GetHomeDir().Append("Downloads");
-  }
-  return true;
+  // NOTE: Lacros overrides the path with a value from ash early in startup. See
+  // crosapi::mojom::LacrosInitParams.
+  LOG(FATAL) << "Path not initialized";
+  return false;
 }
 
 bool GetUserDownloadsDirectory(base::FilePath* result) {
diff --git a/chrome/common/chrome_paths_lacros_unittest.cc b/chrome/common/chrome_paths_lacros_unittest.cc
index a30f4466..f8e2da4 100644
--- a/chrome/common/chrome_paths_lacros_unittest.cc
+++ b/chrome/common/chrome_paths_lacros_unittest.cc
@@ -5,32 +5,16 @@
 #include "chrome/common/chrome_paths_internal.h"
 
 #include "base/files/file_path.h"
-#include "base/system/sys_info.h"
-#include "base/time/time.h"
+#include "base/test/scoped_running_on_chromeos.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chrome {
 namespace {
 
-const char kLsbRelease[] =
-    "CHROMEOS_RELEASE_NAME=Chrome OS\n"
-    "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
-
-// Overrides base::SysInfo::IsRunningOnChromeOS() to return true.
-class ScopedIsRunningOnChromeOS {
- public:
-  ScopedIsRunningOnChromeOS() {
-    base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, base::Time());
-  }
-  ~ScopedIsRunningOnChromeOS() {
-    base::SysInfo::SetChromeOSVersionInfoForTest("", base::Time());
-  }
-};
-
 TEST(ChromePaths, UserDataDirectoryIsInsideEncryptedPartition) {
   // Force paths to behave like they do on device.
-  ScopedIsRunningOnChromeOS is_running_on_chromeos;
+  base::test::ScopedRunningOnChromeOS running_on_chromeos;
   base::FilePath user_data_dir;
   ASSERT_TRUE(GetDefaultUserDataDirectory(&user_data_dir));
   // The Lacros user data directory contains profile information, including
diff --git a/chrome/services/sharing/nearby/BUILD.gn b/chrome/services/sharing/nearby/BUILD.gn
index 2f01472b..b45f5fc 100644
--- a/chrome/services/sharing/nearby/BUILD.gn
+++ b/chrome/services/sharing/nearby/BUILD.gn
@@ -10,6 +10,8 @@
     "nearby_connections.h",
     "nearby_connections_conversions.cc",
     "nearby_connections_conversions.h",
+    "nearby_connections_stream_buffer_manager.cc",
+    "nearby_connections_stream_buffer_manager.h",
     "platform.cc",
   ]
 
@@ -29,7 +31,10 @@
 source_set("unit_tests") {
   testonly = true
 
-  sources = [ "nearby_connections_unittest.cc" ]
+  sources = [
+    "nearby_connections_stream_buffer_manager_unittest.cc",
+    "nearby_connections_unittest.cc",
+  ]
 
   deps = [
     ":nearby",
diff --git a/chrome/services/sharing/nearby/nearby_connections.cc b/chrome/services/sharing/nearby/nearby_connections.cc
index a7f71f1..181cc5ba 100644
--- a/chrome/services/sharing/nearby/nearby_connections.cc
+++ b/chrome/services/sharing/nearby/nearby_connections.cc
@@ -407,8 +407,8 @@
   // Capturing Core* is safe as Core owns PayloadListener.
   PayloadListener payload_listener = {
       .payload_cb =
-          [remote, core = GetCore(service_id)](const std::string& endpoint_id,
-                                               Payload payload) {
+          [&, remote, core = GetCore(service_id)](
+              const std::string& endpoint_id, Payload payload) {
             if (!remote)
               return;
 
@@ -444,15 +444,16 @@
                 break;
               }
               case Payload::Type::kStream:
-                // Stream payload is not supported.
+                buffer_manager_.StartTrackingPayload(std::move(payload));
+                break;
               case Payload::Type::kUnknown:
                 core->CancelPayload(payload.GetId(), /*callback=*/{});
                 return;
             }
           },
       .payload_progress_cb =
-          [remote](const std::string& endpoint_id,
-                   const PayloadProgressInfo& info) {
+          [&, remote](const std::string& endpoint_id,
+                      const PayloadProgressInfo& info) {
             if (!remote)
               return;
 
@@ -463,6 +464,41 @@
                 mojom::PayloadTransferUpdate::New(
                     info.payload_id, PayloadStatusToMojom(info.status),
                     info.total_bytes, info.bytes_transferred));
+
+            if (!buffer_manager_.IsTrackingPayload(info.payload_id))
+              return;
+
+            switch (info.status) {
+              case PayloadProgressInfo::Status::kFailure:
+                FALLTHROUGH;
+              case PayloadProgressInfo::Status::kCanceled:
+                buffer_manager_.StopTrackingFailedPayload(info.payload_id);
+                break;
+
+              case PayloadProgressInfo::Status::kInProgress:
+                // Note that |info.bytes_transferred| is a cumulative measure of
+                // bytes that have been sent so far in the payload.
+                buffer_manager_.HandleBytesTransferred(info.payload_id,
+                                                       info.bytes_transferred);
+                break;
+
+              case PayloadProgressInfo::Status::kSuccess:
+                // When kSuccess is passed, we are guaranteed to have received a
+                // previous kInProgress update with the same |bytes_transferred|
+                // value.
+                // Since we have completed fetching the full payload, return the
+                // completed payload as a "bytes" payload.
+                remote->OnPayloadReceived(
+                    endpoint_id,
+                    mojom::Payload::New(
+                        info.payload_id,
+                        mojom::PayloadContent::NewBytes(
+                            mojom::BytesPayload::New(ByteArrayToMojom(
+                                buffer_manager_
+                                    .GetCompletePayloadAndStopTracking(
+                                        info.payload_id))))));
+                break;
+            }
           }};
 
   GetCore(service_id)
diff --git a/chrome/services/sharing/nearby/nearby_connections.h b/chrome/services/sharing/nearby/nearby_connections.h
index 3f1b5135..68b3255 100644
--- a/chrome/services/sharing/nearby/nearby_connections.h
+++ b/chrome/services/sharing/nearby/nearby_connections.h
@@ -19,6 +19,7 @@
 #include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
 #include "base/thread_annotations.h"
+#include "chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager.h"
 #include "chromeos/services/nearby/public/mojom/nearby_connections.mojom.h"
 #include "chromeos/services/nearby/public/mojom/webrtc_signaling_messenger.mojom.h"
 #include "device/bluetooth/public/mojom/adapter.mojom.h"
@@ -176,6 +177,10 @@
   // ServiceController instance.
   base::flat_map<std::string, std::unique_ptr<Core>> service_id_to_core_map_;
 
+  // Handles incoming stream payloads. This object buffers partial streams as
+  // they arrive and provides a getter for the final buffer when it is complete.
+  NearbyConnectionsStreamBufferManager buffer_manager_;
+
   // input_file_map_ is accessed from background threads.
   base::Lock input_file_lock_;
   // A map of payload_id to file for InputFile.
diff --git a/chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager.cc b/chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager.cc
new file mode 100644
index 0000000..e4d1746
--- /dev/null
+++ b/chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager.cc
@@ -0,0 +1,105 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager.h"
+
+#include <utility>
+
+#include "chrome/browser/nearby_sharing/logging/logging.h"
+#include "third_party/nearby/src/cpp/platform/base/exception.h"
+#include "third_party/nearby/src/cpp/platform/base/input_stream.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+
+NearbyConnectionsStreamBufferManager::PayloadWithBuffer::PayloadWithBuffer(
+    Payload payload)
+    : payload(std::move(payload)) {}
+
+NearbyConnectionsStreamBufferManager::NearbyConnectionsStreamBufferManager() =
+    default;
+
+NearbyConnectionsStreamBufferManager::~NearbyConnectionsStreamBufferManager() =
+    default;
+
+void NearbyConnectionsStreamBufferManager::StartTrackingPayload(
+    Payload payload) {
+  int64_t payload_id = payload.GetId();
+  NS_LOG(VERBOSE) << "Starting to track stream payload with ID " << payload_id;
+
+  id_to_payload_with_buffer_map_[payload_id] =
+      std::make_unique<PayloadWithBuffer>(std::move(payload));
+}
+
+bool NearbyConnectionsStreamBufferManager::IsTrackingPayload(
+    int64_t payload_id) const {
+  return base::Contains(id_to_payload_with_buffer_map_, payload_id);
+}
+
+void NearbyConnectionsStreamBufferManager::StopTrackingFailedPayload(
+    int64_t payload_id) {
+  id_to_payload_with_buffer_map_.erase(payload_id);
+  NS_LOG(VERBOSE) << "Stopped tracking payload with ID " << payload_id << " "
+                  << "and cleared internal memory.";
+}
+
+void NearbyConnectionsStreamBufferManager::HandleBytesTransferred(
+    int64_t payload_id,
+    int64_t cumulative_bytes_transferred_so_far) {
+  auto it = id_to_payload_with_buffer_map_.find(payload_id);
+  if (it == id_to_payload_with_buffer_map_.end()) {
+    NS_LOG(ERROR) << "Attempted to handle stream bytes for payload with ID "
+                  << payload_id << ", but this payload was not being tracked.";
+    return;
+  }
+
+  PayloadWithBuffer* payload_with_buffer = it->second.get();
+
+  // We only need to read the new bytes which have not already been inserted
+  // into the buffer.
+  size_t bytes_to_read =
+      cumulative_bytes_transferred_so_far - payload_with_buffer->buffer.size();
+
+  InputStream* stream = payload_with_buffer->payload.AsStream();
+  if (!stream) {
+    NS_LOG(ERROR) << "Payload with ID " << payload_id << " is not a stream "
+                  << "payload; transfer has failed.";
+    StopTrackingFailedPayload(payload_id);
+    return;
+  }
+
+  ExceptionOr<ByteArray> bytes = stream->Read(bytes_to_read);
+  if (!bytes.ok()) {
+    NS_LOG(ERROR) << "Payload with ID " << payload_id << " encountered "
+                  << "exception while reading; transfer has failed.";
+    StopTrackingFailedPayload(payload_id);
+    return;
+  }
+
+  payload_with_buffer->buffer += static_cast<std::string>(bytes.result());
+}
+
+ByteArray
+NearbyConnectionsStreamBufferManager::GetCompletePayloadAndStopTracking(
+    int64_t payload_id) {
+  auto it = id_to_payload_with_buffer_map_.find(payload_id);
+  if (it == id_to_payload_with_buffer_map_.end()) {
+    NS_LOG(ERROR) << "Attempted to get complete payload with ID " << payload_id
+                  << ", but this payload was not being tracked.";
+    return ByteArray();
+  }
+
+  ByteArray complete_payload(it->second->buffer);
+
+  // Close stream and erase internal state before returning payload.
+  it->second->payload.AsStream()->Close();
+  id_to_payload_with_buffer_map_.erase(it);
+
+  return complete_payload;
+}
+
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
diff --git a/chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager.h b/chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager.h
new file mode 100644
index 0000000..28bc273f
--- /dev/null
+++ b/chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager.h
@@ -0,0 +1,73 @@
+// 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 CHROME_SERVICES_SHARING_NEARBY_NEARBY_CONNECTIONS_STREAM_BUFFER_MANAGER_H_
+#define CHROME_SERVICES_SHARING_NEARBY_NEARBY_CONNECTIONS_STREAM_BUFFER_MANAGER_H_
+
+#include <memory>
+#include <unordered_map>
+
+#include "third_party/nearby/src/cpp/core/payload.h"
+#include "third_party/nearby/src/cpp/platform/base/byte_array.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+
+// Manages payloads with type "stream" received over Nearby Connections. Streams
+// over a certain size are delivered in chunks and need to be reassembled upon
+// completion.
+//
+// Clients should start tracking a payload via StartTrackingPayload(). When
+// more bytes have been transferred, clients should invoke
+// HandleBytesTransferred(), passing the cumulative number of bytes that have
+// been transferred. When all bytes have finished being transferred, clients
+// should invoke GetCompletePayloadAndStopTracking() to get the complete,
+// reassembled payload.
+//
+// If a payload has failed or been canceled, clients should invoke
+// StopTrackingFailedPayload() so that this class can clean up its internal
+// buffer.
+class NearbyConnectionsStreamBufferManager {
+ public:
+  NearbyConnectionsStreamBufferManager();
+  ~NearbyConnectionsStreamBufferManager();
+
+  // Starts tracking the given payload.
+  void StartTrackingPayload(Payload payload);
+
+  // Returns whether a payload with the provided ID is being tracked.
+  bool IsTrackingPayload(int64_t payload_id) const;
+
+  // Stops tracking the payload with the provided ID and cleans up internal
+  // memory being used to buffer the partially-completed transfer.
+  void StopTrackingFailedPayload(int64_t payload_id);
+
+  // Processes incoming bytes by reading from the input stream.
+  void HandleBytesTransferred(int64_t payload_id,
+                              int64_t cumulative_bytes_transferred_so_far);
+
+  // Returns the completed buffer and deletes internal buffers.
+  ByteArray GetCompletePayloadAndStopTracking(int64_t payload_id);
+
+ private:
+  struct PayloadWithBuffer {
+    explicit PayloadWithBuffer(Payload payload);
+
+    Payload payload;
+
+    // Partially-complete buffer which contains the bytes which have been read
+    // up to this point.
+    std::string buffer;
+  };
+
+  std::unordered_map<int64_t, std::unique_ptr<PayloadWithBuffer>>
+      id_to_payload_with_buffer_map_;
+};
+
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
+
+#endif  // CHROME_SERVICES_SHARING_NEARBY_NEARBY_CONNECTIONS_STREAM_BUFFER_MANAGER_H_
diff --git a/chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager_unittest.cc b/chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager_unittest.cc
new file mode 100644
index 0000000..b9bcac75
--- /dev/null
+++ b/chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/sharing/nearby/nearby_connections_stream_buffer_manager.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/nearby/src/cpp/core/payload.h"
+#include "third_party/nearby/src/cpp/platform/base/byte_array.h"
+#include "third_party/nearby/src/cpp/platform/base/exception.h"
+#include "third_party/nearby/src/cpp/platform/base/input_stream.h"
+
+namespace location {
+namespace nearby {
+namespace connections {
+namespace {
+
+class FakeStream : public InputStream {
+ public:
+  FakeStream() = default;
+  ~FakeStream() override = default;
+
+  ExceptionOr<ByteArray> Read(std::int64_t size) override {
+    if (should_throw_exception)
+      return ExceptionOr<ByteArray>(Exception::kIo);
+    return ExceptionOr<ByteArray>(ByteArray(std::string(size, '\0')));
+  }
+
+  Exception Close() override {
+    if (should_throw_exception)
+      return {.value = Exception::kIo};
+    return {.value = Exception::kSuccess};
+  }
+
+  bool should_throw_exception = false;
+};
+
+}  // namespace
+
+class NearbyConnectionsStreamBufferManagerTest : public testing::Test {
+ protected:
+  NearbyConnectionsStreamBufferManagerTest() = default;
+  ~NearbyConnectionsStreamBufferManagerTest() override = default;
+
+  Payload CreatePayload(int64_t payload_id, FakeStream** fake_stream) {
+    auto stream = std::make_unique<FakeStream>();
+    FakeStream* stream_ptr = stream.get();
+
+    *fake_stream = stream_ptr;
+
+    Payload payload(payload_id,
+                    [stream_ptr]() -> InputStream& { return *stream_ptr; });
+
+    fake_streams_.emplace_back(std::move(stream));
+    return payload;
+  }
+
+  NearbyConnectionsStreamBufferManager buffer_manager_;
+
+ private:
+  std::vector<std::unique_ptr<FakeStream>> fake_streams_;
+};
+
+TEST_F(NearbyConnectionsStreamBufferManagerTest, Success) {
+  FakeStream* stream;
+  Payload payload = CreatePayload(/*payload_id=*/1, &stream);
+
+  buffer_manager_.StartTrackingPayload(std::move(payload));
+  EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
+
+  buffer_manager_.HandleBytesTransferred(
+      /*payload_id=*/1,
+      /*cumulative_bytes_transferred_so_far=*/1980);
+  buffer_manager_.HandleBytesTransferred(
+      /*payload_id=*/1,
+      /*cumulative_bytes_transferred_so_far=*/2500);
+
+  ByteArray array =
+      buffer_manager_.GetCompletePayloadAndStopTracking(/*payload_id=*/1);
+  EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
+  EXPECT_EQ(2500u, array.size());
+}
+
+TEST_F(NearbyConnectionsStreamBufferManagerTest, Success_MultipleStreams) {
+  FakeStream* stream1;
+  Payload payload1 = CreatePayload(/*payload_id=*/1, &stream1);
+
+  FakeStream* stream2;
+  Payload payload2 = CreatePayload(/*payload_id=*/2, &stream2);
+
+  buffer_manager_.StartTrackingPayload(std::move(payload1));
+  EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
+
+  buffer_manager_.StartTrackingPayload(std::move(payload2));
+  EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/2));
+
+  buffer_manager_.HandleBytesTransferred(
+      /*payload_id=*/1,
+      /*cumulative_bytes_transferred_so_far=*/1980);
+  buffer_manager_.HandleBytesTransferred(
+      /*payload_id=*/2,
+      /*cumulative_bytes_transferred_so_far=*/1980);
+  buffer_manager_.HandleBytesTransferred(
+      /*payload_id=*/1,
+      /*cumulative_bytes_transferred_so_far=*/2500);
+  buffer_manager_.HandleBytesTransferred(
+      /*payload_id=*/2,
+      /*cumulative_bytes_transferred_so_far=*/3000);
+
+  ByteArray array1 =
+      buffer_manager_.GetCompletePayloadAndStopTracking(/*payload_id=*/1);
+  EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
+  EXPECT_EQ(2500u, array1.size());
+
+  ByteArray array2 =
+      buffer_manager_.GetCompletePayloadAndStopTracking(/*payload_id=*/2);
+  EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/2));
+  EXPECT_EQ(3000u, array2.size());
+}
+
+TEST_F(NearbyConnectionsStreamBufferManagerTest, Failure) {
+  FakeStream* stream;
+  Payload payload = CreatePayload(/*payload_id=*/1, &stream);
+
+  buffer_manager_.StartTrackingPayload(std::move(payload));
+  EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
+
+  buffer_manager_.HandleBytesTransferred(
+      /*payload_id=*/1,
+      /*cumulative_bytes_transferred_so_far=*/1980);
+  buffer_manager_.StopTrackingFailedPayload(/*payload_id=*/1);
+  EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
+}
+
+TEST_F(NearbyConnectionsStreamBufferManagerTest, Exception) {
+  FakeStream* stream;
+  Payload payload = CreatePayload(/*payload_id=*/1, &stream);
+
+  buffer_manager_.StartTrackingPayload(std::move(payload));
+  EXPECT_TRUE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
+
+  stream->should_throw_exception = true;
+  buffer_manager_.HandleBytesTransferred(
+      /*payload_id=*/1,
+      /*cumulative_bytes_transferred_so_far=*/1980);
+
+  EXPECT_FALSE(buffer_manager_.IsTrackingPayload(/*payload_id=*/1));
+}
+
+}  // namespace connections
+}  // namespace nearby
+}  // namespace location
diff --git a/chrome/services/sharing/nearby/nearby_connections_unittest.cc b/chrome/services/sharing/nearby/nearby_connections_unittest.cc
index 6f74d00..3c22d77e 100644
--- a/chrome/services/sharing/nearby/nearby_connections_unittest.cc
+++ b/chrome/services/sharing/nearby/nearby_connections_unittest.cc
@@ -1304,20 +1304,42 @@
       AcceptConnection(fake_payload_listener, endpoint_data.remote_endpoint_id);
   accepted_run_loop.Run();
 
+  base::RunLoop payload_run_loop;
   fake_payload_listener.payload_cb = base::BindLambdaForTesting(
-      [](const std::string& endpoint_id, mojom::PayloadPtr payload) {
-        NOTREACHED();
+      [&](const std::string& endpoint_id, mojom::PayloadPtr payload) {
+        EXPECT_EQ(endpoint_data.remote_endpoint_id, endpoint_id);
+        EXPECT_EQ(kPayloadId, payload->id);
+        ASSERT_TRUE(payload->content->is_bytes());
+        EXPECT_EQ(expected_payload, payload->content->get_bytes()->bytes);
+        payload_run_loop.Quit();
       });
 
-  EXPECT_CALL(*service_controller_ptr_,
-              CancelPayload(testing::_, testing::Eq(kPayloadId)))
-      .WillOnce(testing::Return(Status{Status::kSuccess}));
-
+  std::string expected_payload_str(expected_payload.begin(),
+                                   expected_payload.end());
   testing::NiceMock<MockInputStream> input_stream;
+  EXPECT_CALL(input_stream, Read(_))
+      .WillOnce(
+          Return(ExceptionOr<ByteArray>(ByteArray(expected_payload_str))));
+  EXPECT_CALL(input_stream, Close());
+
   client_proxy->OnPayload(
       endpoint_data.remote_endpoint_id,
       Payload(kPayloadId,
               [&input_stream]() -> InputStream& { return input_stream; }));
+  client_proxy->OnPayloadProgress(
+      endpoint_data.remote_endpoint_id,
+      {.payload_id = kPayloadId,
+       .status = PayloadProgressInfo::Status::kInProgress,
+       .total_bytes = expected_payload.size(),
+       .bytes_transferred = expected_payload.size()});
+  client_proxy->OnPayloadProgress(
+      endpoint_data.remote_endpoint_id,
+      {.payload_id = kPayloadId,
+       .status = PayloadProgressInfo::Status::kSuccess,
+       .total_bytes = expected_payload.size(),
+       .bytes_transferred = expected_payload.size()});
+
+  payload_run_loop.Run();
 }
 
 }  // namespace connections
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 23671f6..5956bde 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -989,7 +989,6 @@
       "../browser/chrome_cross_origin_opener_policy_browsertest.cc",
       "../browser/chrome_do_not_track_browsertest.cc",
       "../browser/chrome_find_request_manager_browsertest.cc",
-      "../browser/chrome_main_browsertest.cc",
       "../browser/chrome_navigation_browsertest.cc",
       "../browser/chrome_origin_trials_browsertest.cc",
       "../browser/chrome_security_exploit_browsertest.cc",
@@ -1271,7 +1270,6 @@
       "../browser/previews/previews_test_util.cc",
       "../browser/previews/previews_test_util.h",
       "../browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc",
-      "../browser/process_singleton_browsertest.cc",
       "../browser/profile_resetter/profile_resetter_browsertest.cc",
       "../browser/profiles/host_zoom_map_browsertest.cc",
       "../browser/profiles/profile_activity_metrics_recorder_browsertest.cc",
@@ -1611,12 +1609,21 @@
       ]
     }
 
+    if (is_linux && !is_chromeos_lacros) {
+      sources += [
+        "../browser/chrome_main_browsertest.cc",
+        "../browser/process_singleton_browsertest.cc",
+      ]
+    }
+
     if (is_chromeos) {
       sources += [
+        "../browser/chrome_main_browsertest.cc",
         "../browser/chromeos/login/saml/test_client_cert_saml_idp_mixin.cc",
         "../browser/chromeos/login/saml/test_client_cert_saml_idp_mixin.h",
         "../browser/chromeos/scoped_test_system_nss_key_slot_mixin.cc",
         "../browser/chromeos/scoped_test_system_nss_key_slot_mixin.h",
+        "../browser/process_singleton_browsertest.cc",
         "../browser/sessions/session_restore_browsertest_chromeos.cc",
         "../browser/ui/browser_navigator_browsertest_chromeos.cc",
         "../browser/ui/settings_window_manager_browsertest_chromeos.cc",
@@ -1628,8 +1635,10 @@
 
     if (is_win) {
       sources += [
+        "../browser/chrome_main_browsertest.cc",
         "../browser/importer/edge_importer_browsertest_win.cc",
         "../browser/importer/ie_importer_browsertest_win.cc",
+        "../browser/process_singleton_browsertest.cc",
         "../browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc",
         "../browser/ui/startup/startup_browser_creator_corrupt_profiles_browsertest_win.cc",
         "../browser/ui/startup/startup_browser_creator_triggered_reset_browsertest_win.cc",
@@ -2952,9 +2961,6 @@
         # Mac, which does not use hunspell by default.
         "../browser/spellchecker/spellcheck_service_browsertest.cc",
 
-        # ProcessSingletonMac doesn"t do anything.
-        "../browser/process_singleton_browsertest.cc",
-
         # TaskManagerView is not used or built on Mac.
         "../browser/ui/views/task_manager_view_browsertest.cc",
       ]
@@ -3087,7 +3093,7 @@
       if (enable_extensions && !is_chromeos) {
         sources += [ "../browser/extensions/api/cloud_print_private/cloud_print_private_apitest.cc" ]
       }
-      if (!is_mac && !is_chromeos) {
+      if (!is_mac && !is_chromeos_ash && !is_chromeos_lacros) {
         sources += [
           # This test depends on GetCommandLineForRelaunch, which is not
           # available on Mac. It is also not intended to run on ChromeOS.
@@ -4551,6 +4557,7 @@
       "../browser/ui/extensions/extension_message_bubble_bridge_unittest.cc",
       "../browser/ui/extensions/extension_settings_overridden_dialog_unittest.cc",
       "../browser/ui/extensions/settings_overridden_params_providers_unittest.cc",
+      "../browser/ui/font_access/font_access_chooser_controller_unittest.cc",
       "../browser/ui/global_error/global_error_service_unittest.cc",
       "../browser/ui/global_media_controls/cast_media_notification_item_unittest.cc",
       "../browser/ui/global_media_controls/cast_media_notification_provider_unittest.cc",
@@ -4868,10 +4875,10 @@
     }
   }
 
-  # TODO(crbug.com/1052397): Rename chromeos_is_browser_only to is_lacros.
-  if (chromeos_is_browser_only) {
+  if (is_chromeos_lacros) {
     assert(enable_native_notifications)
     sources += [
+      "../browser/lacros/lacros_chrome_service_delegate_impl_unittest.cc",
       "../browser/notifications/notification_platform_bridge_lacros_unittest.cc",
       "../common/chrome_paths_lacros_unittest.cc",
     ]
diff --git a/chrome/test/base/chrome_test_suite.cc b/chrome/test/base/chrome_test_suite.cc
index f2b9df75..5008ae51 100644
--- a/chrome/test/base/chrome_test_suite.cc
+++ b/chrome/test/base/chrome_test_suite.cc
@@ -14,6 +14,7 @@
 #include "base/path_service.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "chrome/app/chrome_main_delegate.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
@@ -31,6 +32,11 @@
 #include "chromeos/constants/chromeos_paths.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "base/check.h"
+#include "base/files/file_util.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 #if defined(OS_MAC)
 #include "base/mac/bundle_locations.h"
 #include "base/mac/scoped_nsautorelease_pool.h"
@@ -99,6 +105,18 @@
   path = path.Append(chrome::kFrameworkName);
   base::mac::SetOverrideFrameworkBundlePath(path);
 #endif
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // The lacros binary receives certain paths from ash very early in startup and
+  // overrides the path service entries. Simulate that behavior here. See
+  // chrome_paths_lacros.cc for details. The specific path doesn't matter as
+  // long as it exists.
+  CHECK(scoped_temp_dir_.CreateUniqueTempDir());
+  base::FilePath temp_path = scoped_temp_dir_.GetPath();
+  base::PathService::Override(chrome::DIR_USER_DOCUMENTS, temp_path);
+  base::PathService::Override(chrome::DIR_DEFAULT_DOWNLOADS, temp_path);
+  base::PathService::Override(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, temp_path);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 }
 
 void ChromeTestSuite::Shutdown() {
diff --git a/chrome/test/base/chrome_test_suite.h b/chrome/test/base/chrome_test_suite.h
index b2a2e0a8..fcfd82d 100644
--- a/chrome/test/base/chrome_test_suite.h
+++ b/chrome/test/base/chrome_test_suite.h
@@ -9,8 +9,13 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "build/chromeos_buildflags.h"
 #include "content/public/test/content_test_suite_base.h"
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "base/files/scoped_temp_dir.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 // Test suite for unit and browser tests. Creates services needed by both.
 // See also ChromeUnitTestSuite for additional services created for unit tests.
 class ChromeTestSuite : public content::ContentTestSuiteBase {
@@ -31,6 +36,11 @@
 
   // Alternative path to browser binaries.
   base::FilePath browser_dir_;
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // Used for download and documents path overrides.
+  base::ScopedTempDir scoped_temp_dir_;
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 };
 
 #endif  // CHROME_TEST_BASE_CHROME_TEST_SUITE_H_
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index e2bf63d..281dbd47 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -540,7 +540,7 @@
   browser->window()->Show();
 }
 
-#if !defined(OS_MAC)
+#if !defined(OS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS)
 base::CommandLine InProcessBrowserTest::GetCommandLineForRelaunch() {
   base::CommandLine new_command_line(
       base::CommandLine::ForCurrentProcess()->GetProgram());
@@ -561,7 +561,7 @@
   }
   return new_command_line;
 }
-#endif
+#endif  // !defined(OS_MAC) && !BUILDFLAG(IS_CHROMEOS_LACROS)
 
 base::FilePath InProcessBrowserTest::GetChromeTestDataDir() const {
   return base::FilePath(FILE_PATH_LITERAL("chrome/test/data"));
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index 1025cad..58aa8ecbe 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -14,6 +14,7 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -247,13 +248,14 @@
   // the navigation to complete, and show the browser's window.
   void AddBlankTabAndShow(Browser* browser);
 
-#if !defined OS_MAC
+#if !defined OS_MAC && !BUILDFLAG(IS_CHROMEOS_LACROS)
   // Return a CommandLine object that is used to relaunch the browser_test
   // binary as a browser process. This function is deliberately not defined on
   // the Mac because re-using an existing browser process when launching from
   // the command line isn't a concept that we support on the Mac; AppleEvents
   // are the Mac solution for the same need. Any test based on these functions
-  // doesn't apply to the Mac.
+  // doesn't apply to the Mac. Likewise, Lacros is always launched by ash, and
+  // not by the the process restarting itself.
   base::CommandLine GetCommandLineForRelaunch();
 #endif
 
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index c28bd70..e02731d 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -286,6 +286,10 @@
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/bluetooth_page_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cellular_networks_list_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cellular_setup_dialog_test.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cups_printer_test_utils.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cups_printer_entry_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/date_time_page_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/fake_bluetooth_private.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/fake_bluetooth.m.js",
@@ -313,6 +317,7 @@
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/network_summary_item_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/network_summary_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/on_startup_page_tests.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_about_page_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_edit_dictionary_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_files_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_languages_page_tests.m.js",
@@ -322,13 +327,15 @@
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_privacy_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_printing_page_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_search_page_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/parental_controls_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/people_page_account_manager_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/people_page_change_picture_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/people_page_kerberos_accounts_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/personalization_page_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/smart_inputs_page_test.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/smb_shares_page_tests.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_cups_printers_browser_proxy.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_multidevice_browser_proxy.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_os_languages_browser_proxy.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_os_languages_metrics_proxy.m.js",
@@ -339,13 +346,7 @@
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/tether_connection_dialog_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/timezone_selector_test.m.js",
         "$root_gen_dir/chrome/test/data/webui/settings/chromeos/timezone_subpage_test.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_cups_printers_browser_proxy.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cups_printer_test_utils.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cups_printer_landing_page_tests.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/cups_printer_entry_tests.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/os_about_page_tests.m.js",
-        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/test_about_page_browser_proxy_chromeos.m.js",
+        "$root_gen_dir/chrome/test/data/webui/settings/chromeos/tts_subpage_test.m.js",
       ]
     }
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
index 86937663..e814cf9e 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
@@ -19,6 +19,12 @@
   let eSimPage;
   let eSimManagerRemote;
 
+  async function flushAsync() {
+    Polymer.dom.flush();
+    // Use setTimeout to wait for the next macrotask.
+    return new Promise(resolve => setTimeout(resolve));
+  }
+
   setup(function() {
     eSimManagerRemote = new cellular_setup.FakeESimManagerRemote();
     cellular_setup.setESimManagerRemoteForTesting(eSimManagerRemote);
@@ -30,41 +36,164 @@
     Polymer.dom.flush();
   });
 
-  test('Forward navigation goes to final page', function() {
-    const profileDiscoveryPage = eSimPage.$$('#profileDiscoveryPage');
+  test('No eSIM profile flow', async function() {
+    eSimManagerRemote.addEuiccForTest(0);
+
+    const profileLoadingPage = eSimPage.$$('#profileLoadingPage');
+    const activationCodePage = eSimPage.$$('#activationCodePage');
     const finalPage = eSimPage.$$('#finalPage');
 
-    assertTrue(!!profileDiscoveryPage);
+    assertTrue(!!profileLoadingPage);
+    assertTrue(!!activationCodePage);
     assertTrue(!!finalPage);
 
+    // Loading page should be showing.
     assertTrue(
         eSimPage.selectedESimPageName_ ===
-            cellular_setup.ESimPageName.PROFILE_DISCOVERY &&
-        eSimPage.selectedESimPageName_ === profileDiscoveryPage.id);
+            cellular_setup.ESimPageName.PROFILE_LOADING &&
+        eSimPage.selectedESimPageName_ === profileLoadingPage.id);
+
+    await flushAsync();
+
+    // Should now be at the activation code page.
+    assertTrue(
+        eSimPage.selectedESimPageName_ ===
+            cellular_setup.ESimPageName.ACTIVATION_CODE &&
+        eSimPage.selectedESimPageName_ === activationCodePage.id);
+    // Insert an activation code.
+    activationCodePage.$$('#activationCode').value = 'ACTIVATION_CODE';
+
+    // Next button should now be enabled.
+    assertTrue(
+        eSimPage.buttonState.next ===
+        cellularSetup.ButtonState.SHOWN_AND_ENABLED);
 
     eSimPage.navigateForward();
     Polymer.dom.flush();
 
-    // TODO(crbug.com/1093185) Update this test when the navigation between
-    // profile discovery and activation code pages is wired up.
+    // Should now be at the final page.
     assertTrue(
         eSimPage.selectedESimPageName_ === cellular_setup.ESimPageName.FINAL &&
         eSimPage.selectedESimPageName_ === finalPage.id);
   });
 
+  test('Single eSIM profile flow', async function() {
+    eSimManagerRemote.addEuiccForTest(1);
 
-  // TODO(crbug.com/1093185) Update this test when the navigation between
-  // profile discovery and activation code pages is wired up.
-  test('Enable done button', function() {
-    assertTrue(eSimPage.buttonState.done === cellularSetup.ButtonState.HIDDEN);
+    const profileLoadingPage = eSimPage.$$('#profileLoadingPage');
+    const finalPage = eSimPage.$$('#finalPage');
 
+    assertTrue(!!profileLoadingPage);
+    assertTrue(!!finalPage);
+
+    // Loading page should be showing.
+    assertTrue(
+        eSimPage.selectedESimPageName_ ===
+            cellular_setup.ESimPageName.PROFILE_LOADING &&
+        eSimPage.selectedESimPageName_ === profileLoadingPage.id);
+
+    await flushAsync();
+
+    // Should go directly to final page.
+    assertTrue(
+        eSimPage.selectedESimPageName_ === cellular_setup.ESimPageName.FINAL &&
+        eSimPage.selectedESimPageName_ === finalPage.id);
+  });
+
+  test('Multiple eSIM profiles skip discovery flow', async function() {
+    eSimManagerRemote.addEuiccForTest(2);
+
+    const profileLoadingPage = eSimPage.$$('#profileLoadingPage');
     const profileDiscoveryPage = eSimPage.$$('#profileDiscoveryPage');
+    const activationCodePage = eSimPage.$$('#activationCodePage');
+    const finalPage = eSimPage.$$('#finalPage');
+
+    assertTrue(!!profileLoadingPage);
+    assertTrue(!!profileDiscoveryPage);
+    assertTrue(!!activationCodePage);
+    assertTrue(!!finalPage);
+
+    // Loading page should be showing.
+    assertTrue(
+        eSimPage.selectedESimPageName_ ===
+            cellular_setup.ESimPageName.PROFILE_LOADING &&
+        eSimPage.selectedESimPageName_ === profileLoadingPage.id);
+
+    await flushAsync();
+
+    // Should go to profile discovery page.
+    assertTrue(
+        eSimPage.selectedESimPageName_ ===
+            cellular_setup.ESimPageName.PROFILE_DISCOVERY &&
+        eSimPage.selectedESimPageName_ === profileDiscoveryPage.id);
+
+    // Simulate pressing 'Skip'.
+    assertTrue(
+        eSimPage.buttonState.skipDiscovery ===
+        cellularSetup.ButtonState.SHOWN_AND_ENABLED);
+    eSimPage.navigateForward();
+    Polymer.dom.flush();
+
+    // Should now be at the activation code page.
+    assertTrue(
+        eSimPage.selectedESimPageName_ ===
+            cellular_setup.ESimPageName.ACTIVATION_CODE &&
+        eSimPage.selectedESimPageName_ === activationCodePage.id);
+
+    // Insert an activation code.
+    activationCodePage.$$('#activationCode').value = 'ACTIVATION_CODE';
+
+    // Simulate pressing 'Next'.
+    assertTrue(
+        eSimPage.buttonState.next ===
+        cellularSetup.ButtonState.SHOWN_AND_ENABLED);
+    eSimPage.navigateForward();
+    Polymer.dom.flush();
+
+    // Should now be at the final page.
+    assertTrue(
+        eSimPage.selectedESimPageName_ === cellular_setup.ESimPageName.FINAL &&
+        eSimPage.selectedESimPageName_ === finalPage.id);
+  });
+
+  test('Multiple eSIM profiles select flow', async function() {
+    eSimManagerRemote.addEuiccForTest(2);
+
+    const profileLoadingPage = eSimPage.$$('#profileLoadingPage');
+    const profileDiscoveryPage = eSimPage.$$('#profileDiscoveryPage');
+    const activationCodePage = eSimPage.$$('#activationCodePage');
+    const finalPage = eSimPage.$$('#finalPage');
+
+    assertTrue(!!profileLoadingPage);
+    assertTrue(!!profileDiscoveryPage);
+    assertTrue(!!activationCodePage);
+    assertTrue(!!finalPage);
+
+    // Loading page should be showing.
+    assertTrue(
+        eSimPage.selectedESimPageName_ ===
+            cellular_setup.ESimPageName.PROFILE_LOADING &&
+        eSimPage.selectedESimPageName_ === profileLoadingPage.id);
+
+    await flushAsync();
+
+    // Should go to profile discovery page.
+    assertTrue(
+        eSimPage.selectedESimPageName_ ===
+            cellular_setup.ESimPageName.PROFILE_DISCOVERY &&
+        eSimPage.selectedESimPageName_ === profileDiscoveryPage.id);
+
+    // Select the first profile on the list.
     const profileList = profileDiscoveryPage.$$('#profileList');
     profileList.selectItem(profileList.items[0]);
     Polymer.dom.flush();
 
+    // The 'Done' button should now be enabled.
     assertTrue(
         eSimPage.buttonState.done ===
         cellularSetup.ButtonState.SHOWN_AND_ENABLED);
+    assertTrue(
+        eSimPage.buttonState.skipDiscovery ===
+        cellularSetup.ButtonState.HIDDEN);
   });
 });
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js
index 51bb309..c10fe5d 100644
--- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js
+++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/fake_esim_manager_remote.js
@@ -4,43 +4,98 @@
 
 
 cr.define('cellular_setup', function() {
-  /** @implements {chromeos.cellularSetup.mojom.ESimManagerInterface} */
-  /* #export */ class FakeESimManagerRemote {
+  /** @implements {chromeos.cellularSetup.mojom.ESimProfile} */
+  class FakeProfile {
+    constructor(id) {
+      this.properties_ = {
+        activationCode: 'activation-code-' + id,
+        eid: '1',
+        iccid: id,
+        name: 'profile' + id,
+        nickname: 'profile' + id,
+        serviceProvider: 'provider' + id,
+        state: chromeos.cellularSetup.mojom.ProfileState.kPending,
+      };
+    }
+
     /**
      * @override
-     * @return {!Promise<{euiccs: !Array<!Euicc>,}>}
+     * @return {!Promise<{properties:
+     *     chromeos.cellularSetup.mojom.ESimProfileProperties},}>}
      */
-    getAvailableEuiccs() {
+    getProperties() {
       return new Promise((res) => {
         res({
-          euiccs: [{
-            eid: '1',
-            isActive: true,
-          }]
+          properties: this.properties_,
+        });
+      });
+    }
+  }
+
+  /** @implements {chromeos.cellularSetup.mojom.Euicc} */
+  class FakeEuicc {
+    constructor(numProfiles) {
+      this.profiles_ = [];
+      for (let i = 0; i < numProfiles; i++) {
+        this.addProfileForTest_();
+      }
+    }
+
+    /**
+     * @override
+     * @return {!Promise<{result:
+     *     chromeos.cellularSetup.mojom.ESimOperationResult},}>}
+     */
+    requestPendingProfiles() {
+      return new Promise((res) => {
+        res({
+          result: chromeos.cellularSetup.mojom.ESimOperationResult.kSuccess
         });
       });
     }
 
     /**
      * @override
-     * @param { !string } eid
      * @return {!Promise<{profiles: Array<!ESimProfile>,}>}
      */
-    getProfiles(eid) {
+    getProfileList() {
       return new Promise((res) => {
         res({
-          profiles: [{
-            activationCode: 'activation-code-1',
-            eid: '1',
-            iccid: '1',
-            name: 'profile1',
-            nickname: 'profile1',
-            serviceProvider: 'provider1',
-            state: chromeos.cellularSetup.mojom.ProfileState.kPending,
-          }]
+          profiles: this.profiles_,
         });
       });
     }
+
+    /** @private */
+    addProfileForTest_() {
+      const id = this.profiles_.length + 1;
+      this.profiles_.push(new FakeProfile(id));
+    }
+  }
+
+  /** @implements {chromeos.cellularSetup.mojom.ESimManagerInterface} */
+  /* #export */ class FakeESimManagerRemote {
+    constructor() {
+      this.euiccs_ = [];
+    }
+
+    /**
+     * @override
+     * @return {!Promise<{euiccs: !Array<!Euicc>,}>}
+     */
+    getAvailableEuiccs() {
+      return new Promise((res) => {
+        res({euiccs: this.euiccs_});
+      });
+    }
+
+    /**
+     * @param {number} numProfiles The number of profiles the EUICC has.
+     */
+    addEuiccForTest(numProfiles) {
+      const euicc = new FakeEuicc(numProfiles);
+      this.euiccs_.push(euicc);
+    }
   }
 
   // #cr_define_end
diff --git a/chrome/test/data/webui/nearby_share/shared/nearby_contact_visibility_test.js b/chrome/test/data/webui/nearby_share/shared/nearby_contact_visibility_test.js
index 7e63a643..b8da5b6c 100644
--- a/chrome/test/data/webui/nearby_share/shared/nearby_contact_visibility_test.js
+++ b/chrome/test/data/webui/nearby_share/shared/nearby_contact_visibility_test.js
@@ -132,8 +132,9 @@
     assertFalse(isDownloadContactsFailedVisible());
     assertFalse(isDownloadContactsPendingVisible());
     assertTrue(areContactCheckBoxesVisible());
-    const list = visibilityElement.$$('#contactList');
-    assertEquals(fakeContactManager.contactRecords.length, list.items.length);
+    const items =
+        visibilityElement.$$('#contactList').querySelectorAll('.contact-item');
+    assertEquals(fakeContactManager.contactRecords.length, items.length);
   });
 
   test('Radio group disabled until successful download', async function() {
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index 843a6874..92296db 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -35,6 +35,10 @@
     "bluetooth_page_tests.js",
     "cellular_networks_list_test.js",
     "cellular_setup_dialog_test.js",
+    "cups_printer_entry_tests.js",
+    "cups_printer_landing_page_tests.js",
+    "cups_printer_page_tests.js",
+    "cups_printer_test_utils.js",
     "date_time_page_tests.js",
     "fake_bluetooth.js",
     "fake_bluetooth_private.js",
@@ -64,7 +68,9 @@
     "network_summary_test.js",
     "network_summary_item_test.js",
     "on_startup_page_tests.js",
+    "os_about_page_tests.js",
     "os_edit_dictionary_page_test.js",
+    "os_files_page_test.js",
     "os_languages_page_tests.js",
     "os_languages_page_v2_tests.js",
     "os_reset_page_test.js",
@@ -72,33 +78,28 @@
     "os_people_page_test.js",
     "os_privacy_page_test.js",
     "os_search_page_test.js",
-    "smb_shares_page_tests.js",
-    "os_files_page_test.js",
     "parental_controls_page_test.js",
     "people_page_account_manager_test.js",
     "people_page_change_picture_test.js",
     "people_page_kerberos_accounts_test.js",
     "personalization_page_test.js",
     "smart_inputs_page_test.js",
+    "smb_shares_page_tests.js",
+    "test_about_page_browser_proxy_chromeos.js",
+    "test_cups_printers_browser_proxy.js",
     "test_device_name_browser_proxy.js",
+    "test_multidevice_browser_proxy.js",
     "test_os_languages_browser_proxy.js",
     "test_os_languages_metrics_proxy.js",
     "test_os_lifetime_browser_proxy.js",
-    "test_multidevice_browser_proxy.js",
     "test_os_reset_browser_proxy.js",
-    "test_wallpaper_browser_proxy.js",
     "test_os_sync_browser_proxy.js",
+    "test_wallpaper_browser_proxy.js",
     "tether_connection_dialog_test.js",
     "timezone_selector_test.js",
     "timezone_subpage_test.js",
-    "test_cups_printers_browser_proxy.js",
+    "tts_subpage_test.js",
     "user_page_tests.js",
-    "cups_printer_page_tests.js",
-    "cups_printer_test_utils.js",
-    "cups_printer_landing_page_tests.js",
-    "cups_printer_entry_tests.js",
-    "os_about_page_tests.js",
-    "test_about_page_browser_proxy_chromeos.js",
   ]
   namespace_rewrites = os_settings_namespace_rewrites +
                        os_test_namespace_rewrites + [
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 2bd91b1..b1b7ae2 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -2208,4 +2208,27 @@
   mocha.run();
 });
 
+// Test fixture for the TTS Subpage.
+// eslint-disable-next-line no-var
+var OSSettingsTtsSubpageTest = class extends OSSettingsBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload +
+        'chromeos/os_a11y_page/tts_subpage.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
+      BROWSER_SETTINGS_PATH + '../test_util.js',
+      'tts_subpage_test.js',
+    ]);
+  }
+};
+
+TEST_F('OSSettingsTtsSubpageTest', 'AllJsTests', () => {
+  mocha.run();
+});
+
 GEN('#endif  // defined(NDEBUG)');
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 09d5deb..34e35fb 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -96,11 +96,16 @@
 
 TEST_F('OSSettingsNearbyShareSubPageV3Test', 'All', () => mocha.run());
 
-[['AmbientModePage', 'ambient_mode_page_test.m.js'],
+[['AboutPage', 'os_about_page_tests.m.js'],
+ ['AmbientModePage', 'ambient_mode_page_test.m.js'],
  ['BluetoothPage', 'bluetooth_page_tests.m.js'],
  ['CellularNetworksList', 'cellular_networks_list_test.m.js'],
  ['CellularSetupDialog', 'cellular_setup_dialog_test.m.js'],
+ ['CupsPrinterEntry', 'cups_printer_entry_tests.m.js'],
+ ['CupsPrinterLandingPage', 'cups_printer_landing_page_tests.m.js'],
+ ['CupsPrinterPage', 'cups_printer_page_tests.m.js'],
  ['DateTimePage', 'date_time_page_tests.m.js'],
+ ['FilesPage', 'os_files_page_test.m.js'],
  ['GoogleAssistantPage', 'google_assistant_page_test.m.js'],
  ['InputMethodOptionPage', 'input_method_options_page_test.m.js'],
  ['InputPage', 'input_page_test.m.js'],
@@ -135,17 +140,13 @@
  ['PersonalizationPage', 'personalization_page_test.m.js'],
  ['PrintingPage', 'os_printing_page_tests.m.js'],
  ['PrivacyPage', 'os_privacy_page_test.m.js'],
- ['SmbPage', 'smb_shares_page_tests.m.js'],
- ['FilesPage', 'os_files_page_test.m.js'],
  ['ResetPage', 'os_reset_page_test.m.js'],
  ['SmartInputsPage', 'smart_inputs_page_test.m.js'],
+ ['SmbPage', 'smb_shares_page_tests.m.js'],
  ['TetherConnectionDialog', 'tether_connection_dialog_test.m.js'],
  ['TimezoneSelector', 'timezone_selector_test.m.js'],
  ['TimezoneSubpage', 'timezone_subpage_test.m.js'],
- ['CupsPrinterPage', 'cups_printer_page_tests.m.js'],
- ['CupsPrinterLandingPage', 'cups_printer_landing_page_tests.m.js'],
- ['CupsPrinterEntry', 'cups_printer_entry_tests.m.js'],
- ['AboutPage', 'os_about_page_tests.m.js'],
+ ['TtsSubpage', 'tts_subpage_test.m.js'],
 ].forEach(test => registerTest(...test));
 
 function registerTest(testName, module, caseName) {
diff --git a/chrome/test/data/webui/settings/chromeos/tts_subpage_test.js b/chrome/test/data/webui/settings/chromeos/tts_subpage_test.js
new file mode 100644
index 0000000..0ff4b34
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/tts_subpage_test.js
@@ -0,0 +1,54 @@
+// 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.
+
+// clang-format off
+// #import 'chrome://os-settings/chromeos/os_settings.js';
+
+// #import {assertTrue, assertEquals} from '../../chai_assert.js';
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// clang-format on
+
+suite('TtsSubpage', function() {
+  /** @type {!TtsSubpageElement|undefined} */
+  let ttsSubpage;
+
+  setup(function() {
+    ttsSubpage = document.createElement('settings-tts-subpage');
+    document.body.appendChild(ttsSubpage);
+    Polymer.dom.flush();
+  });
+
+  test('Preview Voice Select Options', function() {
+    ttsSubpage.prefs = {
+      settings: {
+        'language': {
+          'preferred_languages': {
+            value: '',
+          },
+        },
+        'tts': {
+          'lang_to_voice_name': {
+            value: '',
+          },
+        },
+      },
+    };
+
+    ttsSubpage.set('allVoices', [
+      {id: "A", displayLanguage: "Klingon", name: "Star Trek"},
+      {id: "B", displayLanguage: "Goa'uld", name: "Star Gate"},
+      {id: "C", displayLanguage: "Dothraki", name: "Game of Thrones"},
+    ]);
+    Polymer.dom.flush();
+
+    const previewVoice = ttsSubpage.$.previewVoice;
+    assertTrue(!!previewVoice);
+    assertEquals(3, previewVoice.length);
+
+    // Check one of the language option details.
+    const secondVoice = ttsSubpage.$$('option[value=B]');
+    assertTrue(!!secondVoice);
+    assertEquals("Goa'uld - Star Gate", String(secondVoice.textContent).trim());
+  });
+});
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index cebda78..a9bbeef3 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -39,7 +39,6 @@
       "action_handler.h",
       "constants.cc",
       "constants.h",
-      "control_service.h",
       "crash_client.cc",
       "crash_client.h",
       "crash_reporter.cc",
@@ -59,6 +58,7 @@
       "unzipper.h",
       "update_service.cc",
       "update_service.h",
+      "update_service_internal.h",
       "util.cc",
       "util.h",
     ]
@@ -95,10 +95,6 @@
       "app/app_wake.h",
       "configurator.cc",
       "configurator.h",
-      "control_service_impl.cc",
-      "control_service_impl.h",
-      "control_service_impl_inactive.cc",
-      "control_service_impl_inactive.h",
       "crx_downloader_factory.h",
       "external_constants.cc",
       "external_constants.h",
@@ -118,6 +114,10 @@
       "update_service_impl.h",
       "update_service_impl_inactive.cc",
       "update_service_impl_inactive.h",
+      "update_service_internal_impl.cc",
+      "update_service_internal_impl.h",
+      "update_service_internal_impl_inactive.cc",
+      "update_service_internal_impl_inactive.h",
       "updater.cc",
       "updater.h",
     ]
@@ -138,8 +138,8 @@
         "launchd_util.cc",
         "launchd_util.h",
         "lib_util_mac.mm",
-        "mac/control_service_proxy.h",
-        "mac/control_service_proxy.mm",
+        "mac/update_service_internal_proxy.h",
+        "mac/update_service_internal_proxy.mm",
         "mac/update_service_proxy.h",
         "mac/update_service_proxy.mm",
         "prefs_mac.mm",
diff --git a/chrome/updater/app/app_install.cc b/chrome/updater/app/app_install.cc
index 8167ab64..4938e63 100644
--- a/chrome/updater/app/app_install.cc
+++ b/chrome/updater/app/app_install.cc
@@ -19,13 +19,13 @@
 #include "base/version.h"
 #include "build/build_config.h"
 #include "chrome/updater/constants.h"
-#include "chrome/updater/control_service.h"
 #include "chrome/updater/persisted_data.h"
 #include "chrome/updater/prefs.h"
 #include "chrome/updater/registration_data.h"
 #include "chrome/updater/setup.h"
 #include "chrome/updater/tag.h"
 #include "chrome/updater/update_service.h"
+#include "chrome/updater/update_service_internal.h"
 #include "chrome/updater/updater_version.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/chrome/updater/app/app_server.cc b/chrome/updater/app/app_server.cc
index 9a966e1..6f0d56f 100644
--- a/chrome/updater/app/app_server.cc
+++ b/chrome/updater/app/app_server.cc
@@ -12,14 +12,14 @@
 #include "base/version.h"
 #include "chrome/updater/configurator.h"
 #include "chrome/updater/constants.h"
-#include "chrome/updater/control_service.h"
-#include "chrome/updater/control_service_impl.h"
-#include "chrome/updater/control_service_impl_inactive.h"
 #include "chrome/updater/persisted_data.h"
 #include "chrome/updater/prefs.h"
 #include "chrome/updater/update_service.h"
 #include "chrome/updater/update_service_impl.h"
 #include "chrome/updater/update_service_impl_inactive.h"
+#include "chrome/updater/update_service_internal.h"
+#include "chrome/updater/update_service_internal_impl.h"
+#include "chrome/updater/update_service_internal_impl_inactive.h"
 #include "chrome/updater/updater_version.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/chrome/updater/app/app_server_unittest.cc b/chrome/updater/app/app_server_unittest.cc
index 44cec2b..87b6ecb 100644
--- a/chrome/updater/app/app_server_unittest.cc
+++ b/chrome/updater/app/app_server_unittest.cc
@@ -13,9 +13,9 @@
 #include "base/task/single_thread_task_executor.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "chrome/updater/constants.h"
-#include "chrome/updater/control_service.h"
 #include "chrome/updater/prefs.h"
 #include "chrome/updater/update_service.h"
+#include "chrome/updater/update_service_internal.h"
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/updater/app/app_wake.cc b/chrome/updater/app/app_wake.cc
index de8441cc..5f9a0f8 100644
--- a/chrome/updater/app/app_wake.cc
+++ b/chrome/updater/app/app_wake.cc
@@ -7,7 +7,7 @@
 #include "base/bind.h"
 #include "build/build_config.h"
 #include "chrome/updater/app/app.h"
-#include "chrome/updater/control_service.h"
+#include "chrome/updater/update_service_internal.h"
 
 namespace updater {
 
diff --git a/chrome/updater/app/server/mac/server.mm b/chrome/updater/app/server/mac/server.mm
index b1bf26283..3e19a83 100644
--- a/chrome/updater/app/server/mac/server.mm
+++ b/chrome/updater/app/server/mac/server.mm
@@ -21,11 +21,11 @@
 #include "chrome/updater/app/server/mac/service_delegate.h"
 #include "chrome/updater/configurator.h"
 #include "chrome/updater/constants.h"
-#include "chrome/updater/control_service.h"
 #include "chrome/updater/mac/setup/setup.h"
 #import "chrome/updater/mac/xpc_service_names.h"
 #include "chrome/updater/prefs.h"
 #include "chrome/updater/update_service.h"
+#include "chrome/updater/update_service_internal.h"
 
 namespace updater {
 
diff --git a/chrome/updater/app/server/mac/service_delegate.mm b/chrome/updater/app/server/mac/service_delegate.mm
index a9107878..50d395c9 100644
--- a/chrome/updater/app/server/mac/service_delegate.mm
+++ b/chrome/updater/app/server/mac/service_delegate.mm
@@ -21,10 +21,10 @@
 #import "chrome/updater/app/server/mac/server.h"
 #import "chrome/updater/app/server/mac/service_protocol.h"
 #import "chrome/updater/app/server/mac/update_service_wrappers.h"
-#include "chrome/updater/control_service.h"
 #include "chrome/updater/mac/setup/setup.h"
 #import "chrome/updater/mac/xpc_service_names.h"
 #include "chrome/updater/update_service.h"
+#include "chrome/updater/update_service_internal.h"
 #include "chrome/updater/updater_version.h"
 
 @interface CRUUpdateCheckServiceXPCImpl : NSObject <CRUUpdateChecking>
diff --git a/chrome/updater/app/server/win/server.cc b/chrome/updater/app/server/win/server.cc
index 48b0392..e9f2307f 100644
--- a/chrome/updater/app/server/win/server.cc
+++ b/chrome/updater/app/server/win/server.cc
@@ -20,9 +20,9 @@
 #include "chrome/updater/app/server/win/com_classes.h"
 #include "chrome/updater/app/server/win/com_classes_legacy.h"
 #include "chrome/updater/configurator.h"
-#include "chrome/updater/control_service.h"
 #include "chrome/updater/prefs.h"
 #include "chrome/updater/update_service.h"
+#include "chrome/updater/update_service_internal.h"
 #include "chrome/updater/win/constants.h"
 #include "chrome/updater/win/setup/uninstall.h"
 #include "chrome/updater/win/wrl_module.h"
diff --git a/chrome/updater/app/server/win/server.h b/chrome/updater/app/server/win/server.h
index ebc4223..2ca157f 100644
--- a/chrome/updater/app/server/win/server.h
+++ b/chrome/updater/app/server/win/server.h
@@ -14,8 +14,8 @@
 #include "base/win/scoped_com_initializer.h"
 #include "chrome/updater/app/app.h"
 #include "chrome/updater/app/app_server.h"
-#include "chrome/updater/control_service.h"
 #include "chrome/updater/update_service.h"
+#include "chrome/updater/update_service_internal.h"
 
 namespace updater {
 
diff --git a/chrome/updater/mac/control_service_proxy.h b/chrome/updater/mac/update_service_internal_proxy.h
similarity index 83%
rename from chrome/updater/mac/control_service_proxy.h
rename to chrome/updater/mac/update_service_internal_proxy.h
index e4af0be..64f83de 100644
--- a/chrome/updater/mac/control_service_proxy.h
+++ b/chrome/updater/mac/update_service_internal_proxy.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UPDATER_MAC_CONTROL_SERVICE_PROXY_H_
-#define CHROME_UPDATER_MAC_CONTROL_SERVICE_PROXY_H_
+#ifndef CHROME_UPDATER_MAC_UPDATE_SERVICE_INTERNAL_PROXY_H_
+#define CHROME_UPDATER_MAC_UPDATE_SERVICE_INTERNAL_PROXY_H_
 
 #import <Foundation/Foundation.h>
 
@@ -11,8 +11,8 @@
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
-#include "chrome/updater/control_service.h"
 #include "chrome/updater/service_scope.h"
+#include "chrome/updater/update_service_internal.h"
 
 @class CRUUpdateServiceInternalProxyImpl;
 
@@ -43,4 +43,4 @@
 
 }  // namespace updater
 
-#endif  // CHROME_UPDATER_MAC_CONTROL_SERVICE_PROXY_H_
+#endif  // CHROME_UPDATER_MAC_UPDATE_SERVICE_INTERNAL_PROXY_H_
diff --git a/chrome/updater/mac/control_service_proxy.mm b/chrome/updater/mac/update_service_internal_proxy.mm
similarity index 98%
rename from chrome/updater/mac/control_service_proxy.mm
rename to chrome/updater/mac/update_service_internal_proxy.mm
index 7d88008..0e6d56d 100644
--- a/chrome/updater/mac/control_service_proxy.mm
+++ b/chrome/updater/mac/update_service_internal_proxy.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/updater/mac/control_service_proxy.h"
+#include "chrome/updater/mac/update_service_internal_proxy.h"
 
 #import <Foundation/Foundation.h>
 
diff --git a/chrome/updater/service_factory_mac.mm b/chrome/updater/service_factory_mac.mm
index d63c482..9d93207 100644
--- a/chrome/updater/service_factory_mac.mm
+++ b/chrome/updater/service_factory_mac.mm
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/memory/ref_counted.h"
-#include "chrome/updater/mac/control_service_proxy.h"
+#include "chrome/updater/mac/update_service_internal_proxy.h"
 #include "chrome/updater/mac/update_service_proxy.h"
 #include "chrome/updater/service_scope.h"
 
diff --git a/chrome/updater/service_factory_win.cc b/chrome/updater/service_factory_win.cc
index 96730f5c..5e214f1 100644
--- a/chrome/updater/service_factory_win.cc
+++ b/chrome/updater/service_factory_win.cc
@@ -6,7 +6,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/no_destructor.h"
 #include "chrome/updater/service_scope.h"
-#include "chrome/updater/win/control_service_proxy.h"
+#include "chrome/updater/win/update_service_internal_proxy.h"
 #include "chrome/updater/win/update_service_proxy.h"
 #include "chrome/updater/win/wrl_module.h"
 
diff --git a/chrome/updater/control_service.h b/chrome/updater/update_service_internal.h
similarity index 91%
rename from chrome/updater/control_service.h
rename to chrome/updater/update_service_internal.h
index 0882d02c..dbdb6b9 100644
--- a/chrome/updater/control_service.h
+++ b/chrome/updater/update_service_internal.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UPDATER_CONTROL_SERVICE_H_
-#define CHROME_UPDATER_CONTROL_SERVICE_H_
+#ifndef CHROME_UPDATER_UPDATE_SERVICE_INTERNAL_H_
+#define CHROME_UPDATER_UPDATE_SERVICE_INTERNAL_H_
 
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
@@ -43,4 +43,4 @@
 
 }  // namespace updater
 
-#endif  // CHROME_UPDATER_CONTROL_SERVICE_H_
+#endif  // CHROME_UPDATER_UPDATE_SERVICE_INTERNAL_H_
diff --git a/chrome/updater/control_service_impl.cc b/chrome/updater/update_service_internal_impl.cc
similarity index 98%
rename from chrome/updater/control_service_impl.cc
rename to chrome/updater/update_service_internal_impl.cc
index 0ddfaf3..6742d226 100644
--- a/chrome/updater/control_service_impl.cc
+++ b/chrome/updater/update_service_internal_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/updater/control_service_impl.h"
+#include "chrome/updater/update_service_internal_impl.h"
 
 #include <string>
 #include <vector>
diff --git a/chrome/updater/control_service_impl.h b/chrome/updater/update_service_internal_impl.h
similarity index 93%
rename from chrome/updater/control_service_impl.h
rename to chrome/updater/update_service_internal_impl.h
index d9bd605..aaa3f377 100644
--- a/chrome/updater/control_service_impl.h
+++ b/chrome/updater/update_service_internal_impl.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UPDATER_CONTROL_SERVICE_IMPL_H_
-#define CHROME_UPDATER_CONTROL_SERVICE_IMPL_H_
+#ifndef CHROME_UPDATER_UPDATE_SERVICE_INTERNAL_IMPL_H_
+#define CHROME_UPDATER_UPDATE_SERVICE_INTERNAL_IMPL_H_
 
 #include <string>
 #include <vector>
@@ -14,7 +14,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "base/version.h"
-#include "chrome/updater/control_service.h"
+#include "chrome/updater/update_service_internal.h"
 
 namespace update_client {
 enum class Error;
@@ -104,4 +104,4 @@
 
 }  // namespace updater
 
-#endif  // CHROME_UPDATER_CONTROL_SERVICE_IMPL_H_
+#endif  // CHROME_UPDATER_UPDATE_SERVICE_INTERNAL_IMPL_H_
diff --git a/chrome/updater/control_service_impl_inactive.cc b/chrome/updater/update_service_internal_impl_inactive.cc
similarity index 90%
rename from chrome/updater/control_service_impl_inactive.cc
rename to chrome/updater/update_service_internal_impl_inactive.cc
index 3c1c637..f13508c 100644
--- a/chrome/updater/control_service_impl_inactive.cc
+++ b/chrome/updater/update_service_internal_impl_inactive.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/updater/control_service_impl_inactive.h"
+#include "chrome/updater/update_service_internal_impl_inactive.h"
 
 #include "base/callback.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chrome/updater/control_service.h"
+#include "chrome/updater/update_service_internal.h"
 
 namespace updater {
 
diff --git a/chrome/updater/control_service_impl_inactive.h b/chrome/updater/update_service_internal_impl_inactive.h
similarity index 64%
rename from chrome/updater/control_service_impl_inactive.h
rename to chrome/updater/update_service_internal_impl_inactive.h
index 24ab0102..caf0204 100644
--- a/chrome/updater/control_service_impl_inactive.h
+++ b/chrome/updater/update_service_internal_impl_inactive.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UPDATER_CONTROL_SERVICE_IMPL_INACTIVE_H_
-#define CHROME_UPDATER_CONTROL_SERVICE_IMPL_INACTIVE_H_
+#ifndef CHROME_UPDATER_UPDATE_SERVICE_INTERNAL_IMPL_INACTIVE_H_
+#define CHROME_UPDATER_UPDATE_SERVICE_INTERNAL_IMPL_INACTIVE_H_
 
 #include "base/memory/scoped_refptr.h"
 
@@ -15,4 +15,4 @@
 
 }  // namespace updater
 
-#endif  // CHROME_UPDATER_CONTROL_SERVICE_IMPL_INACTIVE_H_
+#endif  // CHROME_UPDATER_UPDATE_SERVICE_INTERNAL_IMPL_INACTIVE_H_
diff --git a/chrome/updater/win/BUILD.gn b/chrome/updater/win/BUILD.gn
index cee6b56..a48f180 100644
--- a/chrome/updater/win/BUILD.gn
+++ b/chrome/updater/win/BUILD.gn
@@ -76,8 +76,6 @@
 source_set("lib") {
   sources = [
     "action_handler.cc",
-    "control_service_proxy.cc",
-    "control_service_proxy.h",
     "group_policy_manager.cc",
     "group_policy_manager.h",
     "net/net_util.cc",
@@ -104,6 +102,8 @@
     "setup/uninstall.h",
     "task_scheduler.cc",
     "task_scheduler.h",
+    "update_service_internal_proxy.cc",
+    "update_service_internal_proxy.h",
     "update_service_proxy.cc",
     "update_service_proxy.h",
     "user_info.cc",
diff --git a/chrome/updater/win/app_install_controller.cc b/chrome/updater/win/app_install_controller.cc
index b5e34e8..c809e6c 100644
--- a/chrome/updater/win/app_install_controller.cc
+++ b/chrome/updater/win/app_install_controller.cc
@@ -26,8 +26,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/win/atl.h"
-#include "chrome/updater/control_service.h"
 #include "chrome/updater/update_service.h"
+#include "chrome/updater/update_service_internal.h"
 #include "chrome/updater/win/install_progress_observer.h"
 #include "chrome/updater/win/ui/progress_wnd.h"
 #include "chrome/updater/win/ui/resources/resources.grh"
diff --git a/chrome/updater/win/control_service_proxy.cc b/chrome/updater/win/update_service_internal_proxy.cc
similarity index 98%
rename from chrome/updater/win/control_service_proxy.cc
rename to chrome/updater/win/update_service_internal_proxy.cc
index 98a2983..78ef9723 100644
--- a/chrome/updater/win/control_service_proxy.cc
+++ b/chrome/updater/win/update_service_internal_proxy.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/updater/win/control_service_proxy.h"
+#include "chrome/updater/win/update_service_internal_proxy.h"
 
 #include <windows.h>
 #include <wrl/client.h>
diff --git a/chrome/updater/win/control_service_proxy.h b/chrome/updater/win/update_service_internal_proxy.h
similarity index 84%
rename from chrome/updater/win/control_service_proxy.h
rename to chrome/updater/win/update_service_internal_proxy.h
index 7504c81d4..e98bfb5 100644
--- a/chrome/updater/win/control_service_proxy.h
+++ b/chrome/updater/win/update_service_internal_proxy.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_UPDATER_WIN_CONTROL_SERVICE_PROXY_H_
-#define CHROME_UPDATER_WIN_CONTROL_SERVICE_PROXY_H_
+#ifndef CHROME_UPDATER_WIN_UPDATE_SERVICE_INTERNAL_PROXY_H_
+#define CHROME_UPDATER_WIN_UPDATE_SERVICE_INTERNAL_PROXY_H_
 
 #include "base/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
-#include "chrome/updater/control_service.h"
 #include "chrome/updater/service_scope.h"
+#include "chrome/updater/update_service_internal.h"
 
 namespace base {
 class SingleThreadTaskRunner;
@@ -44,4 +44,4 @@
 
 }  // namespace updater
 
-#endif  // CHROME_UPDATER_WIN_CONTROL_SERVICE_PROXY_H_
+#endif  // CHROME_UPDATER_WIN_UPDATE_SERVICE_INTERNAL_PROXY_H_
diff --git a/chromecast/media/audio/cast_audio_output_stream.cc b/chromecast/media/audio/cast_audio_output_stream.cc
index 5893100..b630ab5f 100644
--- a/chromecast/media/audio/cast_audio_output_stream.cc
+++ b/chromecast/media/audio/cast_audio_output_stream.cc
@@ -212,7 +212,6 @@
 }
 
 int64_t CastAudioOutputStream::MixerServiceWrapper::GetMaxBufferedFrames() {
-  DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
   int fill_size_frames = audio_params_.frames_per_buffer();
   base::TimeDelta target_max_buffered_ms = kMediaMaxBufferedFrames;
   if (GetContentType(device_id_) == AudioContentType::kCommunication) {
diff --git a/chromeos/components/scanning/resources/index.html b/chromeos/components/scanning/resources/index.html
index 6010e290..da14e63 100644
--- a/chromeos/components/scanning/resources/index.html
+++ b/chromeos/components/scanning/resources/index.html
@@ -7,6 +7,13 @@
     <meta charset="utf-8">
     <title>$i18n{appTitle}</title>
   </head>
+  <style>
+    body {
+      height: 100vh;
+      margin: 0;
+      overflow: hidden;
+    }
+  </style>
   <body>
     <scanning-app></scanning-app>
 
diff --git a/chromeos/components/scanning/resources/scan_preview.html b/chromeos/components/scanning/resources/scan_preview.html
index fc8ef43..4abfc84 100644
--- a/chromeos/components/scanning/resources/scan_preview.html
+++ b/chromeos/components/scanning/resources/scan_preview.html
@@ -12,6 +12,12 @@
     width: 60%;
   }
 
+  #helpOrProgress {
+    display: flex;
+    /* Calculate a height that results in a Letter aspect ratio (1:1.2941). */
+    height: calc(1.2941 * var(--left-panel-width));
+  }
+
   #progressText {
     @apply --scanning-progress-text-font;
     color: var(--scanning-progress-text-color);
@@ -24,19 +30,25 @@
   }
 
   .preview {
-    border: 1px solid var(--google-grey-200);
-    border-radius: 4px;
-    display: flex;
-    /* TODO(michaelcheco): Replace with correct height for
-     * preview container. */
-    height: 420px;
+    max-height: calc(100vh - var(--panel-container-margin-top));
     overflow-y: scroll;
   }
 
-  /* TODO(jschettler): Check with UX about adding a border around each image. */
-  .scanned-image {
+  .preview::-webkit-scrollbar {
+    -webkit-appearance: none;
+    width: 4px;
+  }
+
+  .preview::-webkit-scrollbar-thumb {
+    background-color: rgba(0, 0, 0, 0.5);
     border-radius: 4px;
-    width: 100%;
+  }
+
+  .preview-item {
+    border: 1px solid var(--google-grey-200);
+    border-radius: 4px;
+    /* Subtract 2px for the border. */
+    width: calc(100% - 2px);
   }
 
   /* Add top margin to all but the first scanned image. */
@@ -54,16 +66,19 @@
   }
 </style>
 <div class="preview">
-  <div id="helperText" hidden$="[[shouldHideHelperText_(appState)]]">
-    <span inner-h-t-m-l="[[getHelperTextHtml_()]]"></span>
-  </div>
-  <div id="scanProgress" hidden$="[[shouldHideProgress_(appState)]]">
-    <span id="progressText">[[getProgressTextString_(pageNumber)]]</span>
-    <paper-progress value="[[progressPercent]]"></paper-progress>
+  <div id="helpOrProgress" class="preview-item"
+      hidden$="[[shouldShowScannedImages_(appState)]]">
+    <div id="helperText" hidden$="[[shouldHideHelperText_(appState)]]">
+      <span inner-h-t-m-l="[[getHelperTextHtml_()]]"></span>
+    </div>
+    <div id="scanProgress" hidden$="[[shouldHideProgress_(appState)]]">
+      <span id="progressText">[[getProgressTextString_(pageNumber)]]</span>
+      <paper-progress value="[[progressPercent]]"></paper-progress>
+    </div>
   </div>
   <div id="scannedImages" hidden$="[[!shouldShowScannedImages_(appState)]]">
     <template is="dom-repeat" items="[[objectUrls]]" as="url">
-      <img class="scanned-image" src="[[url]]">
+      <img class="preview-item scanned-image" src="[[url]]">
     </template>
   </div>
 </div>
diff --git a/chromeos/components/scanning/resources/scanning_app.html b/chromeos/components/scanning/resources/scanning_app.html
index a154740..66783654 100644
--- a/chromeos/components/scanning/resources/scanning_app.html
+++ b/chromeos/components/scanning/resources/scanning_app.html
@@ -60,11 +60,11 @@
 
   .panel-container {
     display: flex;
-    margin-bottom: var(--panel-container-margin-bottom);
     margin-top: var(--panel-container-margin-top);
   }
 
   .right-panel {
+    margin-bottom: var(--right-panel-margin-bottom);
     padding-inline-end: var(--right-panel-padding-end);
     padding-inline-start: var(--right-panel-padding-start);
     width: var(--right-panel-width);
diff --git a/chromeos/components/scanning/resources/scanning_shared_css.html b/chromeos/components/scanning/resources/scanning_shared_css.html
index 0eb10d0..cf1588c5 100644
--- a/chromeos/components/scanning/resources/scanning_shared_css.html
+++ b/chromeos/components/scanning/resources/scanning_shared_css.html
@@ -17,8 +17,8 @@
         --left-panel-padding-end: 32px;
         --left-panel-padding-start: 32px;
         --left-panel-width: 288px;
-        --panel-container-margin-bottom: 20px;
         --panel-container-margin-top: 20px;
+        --right-panel-margin-bottom: 20px;
         --right-panel-padding-end: 32px;
         --right-panel-padding-start: 0px;
         --right-panel-width: 384px;
@@ -31,8 +31,8 @@
         --left-panel-padding-end: 48px;
         --left-panel-padding-start: 48px;
         --left-panel-width: 384px;
-        --panel-container-margin-bottom: 32px;
         --panel-container-margin-top: 20px;
+        --right-panel-margin-bottom: 32px;
         --right-panel-padding-end: 48px;
         --right-panel-padding-start: 48px;
         --right-panel-width: 384px;
@@ -45,8 +45,8 @@
         --left-panel-padding-end: 60px;
         --left-panel-padding-start: 164px;
         --left-panel-width: 416px;
-        --panel-container-margin-bottom: 64px;
         --panel-container-margin-top: 64px;
+        --right-panel-margin-bottom: 64px;
         --right-panel-padding-end: 164px;
         --right-panel-padding-start: 60px;
         --right-panel-width: 416px;
diff --git a/chromeos/crosapi/cpp/crosapi_constants.cc b/chromeos/crosapi/cpp/crosapi_constants.cc
index e7504b0..01973a0 100644
--- a/chromeos/crosapi/cpp/crosapi_constants.cc
+++ b/chromeos/crosapi/cpp/crosapi_constants.cc
@@ -15,13 +15,6 @@
 // a directory in the encrypted user data partition.
 const char kHomeChronosUserPath[] = "/home/chronos/user";
 
-// The "MyFiles" directory for the ash-side primary user.
-const char kMyFilesPath[] = "/home/chronos/user/MyFiles";
-
-// The "Downloads" directory for the ash-side primary user. Note that the user
-// can choose to download files to a different directory, see DownloadPrefs.
-const char kDefaultDownloadsPath[] = "/home/chronos/user/MyFiles/Downloads";
-
 // The default user-data-directory for Lacros.
 // NOTE: This is security sensitive. The directory must be inside the encrypted
 // user data partition.
diff --git a/chromeos/crosapi/cpp/crosapi_constants.h b/chromeos/crosapi/cpp/crosapi_constants.h
index 19009786..7b6f120 100644
--- a/chromeos/crosapi/cpp/crosapi_constants.h
+++ b/chromeos/crosapi/cpp/crosapi_constants.h
@@ -13,10 +13,6 @@
 
 COMPONENT_EXPORT(CROSAPI) extern const char kHomeChronosUserPath[];
 
-COMPONENT_EXPORT(CROSAPI) extern const char kMyFilesPath[];
-
-COMPONENT_EXPORT(CROSAPI) extern const char kDefaultDownloadsPath[];
-
 COMPONENT_EXPORT(CROSAPI) extern const char kLacrosUserDataPath[];
 
 COMPONENT_EXPORT(CROSAPI) extern const char kChromeOSReleaseTrack[];
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 71ea528..8b5c0667 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -12,6 +12,7 @@
 import "chromeos/crosapi/mojom/screen_manager.mojom";
 import "chromeos/crosapi/mojom/select_file.mojom";
 import "mojo/public/mojom/base/big_string.mojom";
+import "mojo/public/mojom/base/file_path.mojom";
 import "mojo/public/mojom/base/token.mojom";
 import "mojo/public/mojom/base/values.mojom";
 import "services/device/public/mojom/hid.mojom";
@@ -129,6 +130,22 @@
   kDemo,
 };
 
+// Default directories on the system.
+[Stable]
+struct DefaultPaths {
+  // The default (non-configurable) directory for documents. For example,
+  // /home/chronos/u-<hash>/MyFiles. We send the full path for future
+  // compatibility, to avoid assumptions about where on disk the directory is
+  // located.
+  mojo_base.mojom.FilePath documents@0;
+
+  // The default (non-configurable) directory for downloads. For example,
+  // /home/chronos/u-<hash>/MyFiles/Downloads.  We send the full path for future
+  // compatibility, to avoid assumptions about where on disk the directory is
+  // located.
+  mojo_base.mojom.FilePath downloads@1;
+};
+
 // LacrosInitParams is a set of parameters for initialization of lacros-chrome,
 // which is passed from ash-chrome to lacros-chrome. Since ash-chrome and
 // lacros-chrome may have different versions, lacros-chrome must handle this
@@ -181,6 +198,11 @@
   // Added in M88.
   [MinVersion=5]
   map<mojo_base.mojom.Token, uint32>? interface_versions@5;
+
+  // Default directories on the system.
+  // Added in M89.
+  [MinVersion=6]
+  DefaultPaths? default_paths@6;
 };
 
 // LacrosChromeService defines the APIs that live in lacros-chrome and
diff --git a/chromeos/dbus/hermes/fake_hermes_profile_client.cc b/chromeos/dbus/hermes/fake_hermes_profile_client.cc
index 8a19cb0d..ed6dd33 100644
--- a/chromeos/dbus/hermes/fake_hermes_profile_client.cc
+++ b/chromeos/dbus/hermes/fake_hermes_profile_client.cc
@@ -154,12 +154,6 @@
 
   // Update the cellular device properties so that they match the carrier
   // profile that was just enabled.
-  device_test->SetDeviceProperty(
-      kCellularDevicePath, shill::kCarrierProperty,
-      base::Value(properties->service_provider().value()), true);
-  device_test->SetDeviceProperty(
-      kCellularDevicePath, shill::kCarrierProperty,
-      base::Value(properties->service_provider().value()), true);
   base::DictionaryValue home_provider;
   home_provider.SetKey(shill::kNameProperty,
                        base::Value(properties->service_provider().value()));
diff --git a/chromeos/lacros/lacros_chrome_service_delegate.h b/chromeos/lacros/lacros_chrome_service_delegate.h
index 7dec91f6..1d4754970 100644
--- a/chromeos/lacros/lacros_chrome_service_delegate.h
+++ b/chromeos/lacros/lacros_chrome_service_delegate.h
@@ -12,6 +12,12 @@
 
 class GURL;
 
+namespace crosapi {
+namespace mojom {
+class LacrosInitParams;
+}  // namespace mojom
+}  // namespace crosapi
+
 namespace chromeos {
 
 // Interface to inject Chrome dependent behavior into LacrosChromeServiceImpl
@@ -20,6 +26,10 @@
  public:
   virtual ~LacrosChromeServiceDelegate() = default;
 
+  // Called during startup when |init_params| become available.
+  virtual void OnInitialized(
+      const crosapi::mojom::LacrosInitParams& init_params) = 0;
+
   // Opens a new browser window.
   virtual void NewWindow() = 0;
 
diff --git a/chromeos/lacros/lacros_chrome_service_impl.cc b/chromeos/lacros/lacros_chrome_service_impl.cc
index 0ef7157..a9f3797 100644
--- a/chromeos/lacros/lacros_chrome_service_impl.cc
+++ b/chromeos/lacros/lacros_chrome_service_impl.cc
@@ -295,6 +295,8 @@
                                     BindLacrosChromeServiceReceiver,
                                 weak_sequenced_state_, std::move(receiver)));
   sequenced_state_->WaitForInit();
+  DCHECK(init_params_);
+  delegate_->OnInitialized(*init_params_);
   did_bind_receiver_ = true;
 
   // Bind the remote for MessageCenter on the current thread, and then pass the
diff --git a/chromeos/network/onc/onc_signature.cc b/chromeos/network/onc/onc_signature.cc
index 6a233197..eea2723 100644
--- a/chromeos/network/onc/onc_signature.cc
+++ b/chromeos/network/onc/onc_signature.cc
@@ -287,7 +287,6 @@
     {::onc::kRecommended, &kRecommendedSignature},
     {::onc::cellular::kAPN, &kCellularApnSignature},
     {::onc::cellular::kAPNList, &kCellularApnListSignature},
-    {::onc::cellular::kCarrier, &kStringSignature},
     {::onc::cellular::kAutoConnect, &kBoolSignature},
     {NULL}};
 
diff --git a/chromeos/printing/uri.h b/chromeos/printing/uri.h
index 325708a5..5e0abaf 100644
--- a/chromeos/printing/uri.h
+++ b/chromeos/printing/uri.h
@@ -284,7 +284,7 @@
   // If the Port is specified (GetPort() != -1) and |always_print_port| is set
   // to true, a Port number is always included in the returned URI (even when
   // it equals to a Scheme's default port number).
-  std::string GetNormalized(bool always_print_port = false) const;
+  std::string GetNormalized(bool always_print_port = true) const;
 
   // Returns true <=> whole URL has no UTF-8 characters.
   bool IsASCII() const;
diff --git a/chromeos/printing/uri_unittest.cc b/chromeos/printing/uri_unittest.cc
index 01faf52..d29057fe 100644
--- a/chromeos/printing/uri_unittest.cc
+++ b/chromeos/printing/uri_unittest.cc
@@ -55,7 +55,7 @@
   uri.SetUserinfo(components.userinfo);
   ASSERT_EQ(uri.GetLastParsingError().status, Uri::ParserStatus::kNoErrors);
   // Check URI.
-  EXPECT_EQ(uri.GetNormalized(), normalized_uri);
+  EXPECT_EQ(uri.GetNormalized(false), normalized_uri);
 }
 
 // Verifies that |input_uri| set as parameter in Uri constructor is parsed
@@ -79,7 +79,7 @@
                        const std::string& normalized_uri) {
   Uri uri(input_uri);
   ASSERT_EQ(uri.GetLastParsingError().status, Uri::ParserStatus::kNoErrors);
-  EXPECT_EQ(uri.GetNormalized(), normalized_uri);
+  EXPECT_EQ(uri.GetNormalized(false), normalized_uri);
 }
 
 TEST(UriTest, DefaultConstructor) {
diff --git a/chromeos/printing/uri_unittest_consistency.cc b/chromeos/printing/uri_unittest_consistency.cc
index e25b75b..1705833 100644
--- a/chromeos/printing/uri_unittest_consistency.cc
+++ b/chromeos/printing/uri_unittest_consistency.cc
@@ -121,32 +121,36 @@
 // Build normalized URI from encoded components and make sure that it is
 // equal to the value returned by GetNormalized().
 TEST_P(UriConsistencyTest, UriBuilding) {
-  std::string expected_uri = uri_.GetScheme() + ":";
+  std::string scheme = uri_.GetScheme() + ":";
 
   // Build a part of URI called Authority (Userinfo@Host:Port).
   std::string authority_encoded;
   if (!uri_.GetUserinfoEncoded().empty())
     authority_encoded = uri_.GetUserinfoEncoded() + "@";
   authority_encoded += uri_.GetHostEncoded();
-  if (uri_.GetPort() != -1 &&
-      uri_.GetPort() != Uri::GetDefaultPort(uri_.GetScheme())) {
-    authority_encoded += ":" + base::NumberToString(uri_.GetPort());
+  std::string authority_with_port_encoded = authority_encoded;
+  if (uri_.GetPort() != -1) {
+    if (uri_.GetPort() != Uri::GetDefaultPort(uri_.GetScheme()))
+      authority_encoded += ":" + base::NumberToString(uri_.GetPort());
+    authority_with_port_encoded += ":" + base::NumberToString(uri_.GetPort());
   }
-  // If Authority is not empty, add it to |expected_uri|.
+  // If Authority is not empty, prefix it with "//".
   if (!authority_encoded.empty())
-    expected_uri += "//" + authority_encoded;
+    authority_encoded = "//" + authority_encoded;
+  if (!authority_with_port_encoded.empty())
+    authority_with_port_encoded = "//" + authority_with_port_encoded;
 
-  // Add Path and Query.
-  expected_uri += uri_.GetPathEncodedAsString();
-  const std::string expected_query = uri_.GetQueryEncodedAsString();
-  if (!expected_query.empty())
-    expected_uri += "?" + expected_query;
-
-  // Add Fragment to |expected_uri|.
+  // Build Path, Query and Fragment.
+  std::string path_query_fragment_encoded = uri_.GetPathEncodedAsString();
+  if (!uri_.GetQueryEncodedAsString().empty())
+    path_query_fragment_encoded += "?" + uri_.GetQueryEncodedAsString();
   if (!uri_.GetFragmentEncoded().empty())
-    expected_uri += "#" + uri_.GetFragmentEncoded();
+    path_query_fragment_encoded += "#" + uri_.GetFragmentEncoded();
 
-  EXPECT_EQ(uri_.GetNormalized(), expected_uri);
+  EXPECT_EQ(uri_.GetNormalized(false),
+            scheme + authority_encoded + path_query_fragment_encoded);
+  EXPECT_EQ(uri_.GetNormalized(true),
+            scheme + authority_with_port_encoded + path_query_fragment_encoded);
 }
 
 // Checks if the normalization algorithm is consistent.
@@ -154,7 +158,8 @@
   // Normalization of normalized uri must not change it.
   Uri uri2(uri_.GetNormalized());
   EXPECT_EQ(uri2.GetLastParsingError().status, Uri::ParserStatus::kNoErrors);
-  EXPECT_EQ(uri_.GetNormalized(), uri2.GetNormalized());
+  EXPECT_EQ(uri_.GetNormalized(true), uri2.GetNormalized(true));
+  EXPECT_EQ(uri_.GetNormalized(false), uri2.GetNormalized(false));
 
   // Normalization of normalized Path must not change it.
   uri2.SetPathEncoded(uri_.GetPathEncodedAsString());
diff --git a/chromeos/services/secure_channel/wire_message.cc b/chromeos/services/secure_channel/wire_message.cc
index b9e232ca..a0a31af92 100644
--- a/chromeos/services/secure_channel/wire_message.cc
+++ b/chromeos/services/secure_channel/wire_message.cc
@@ -10,36 +10,36 @@
 #include <limits>
 
 #include "base/base64url.h"
+#include "base/big_endian.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/notreached.h"
 #include "base/values.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 
-// The wire messages have a simple format:
-// [ message version ] [ body length ] [ JSON body ]
-//       1 byte            2 bytes      body length
-// When sending encrypted messages, the JSON body contains two fields: an
-// optional |permit_id| field and a required |payload| field.
-//
-// For non-encrypted messages, the message itself is the JSON body, and it
-// doesn't have a |payload| field.
-
 namespace chromeos {
 
 namespace secure_channel {
 
 namespace {
 
-// The length of the message header, in bytes.
-const size_t kHeaderLength = 3;
+// The number of bytes used to represent the protocol version in the message
+// header.
+const size_t kNumBytesInHeaderProtocolVersion = 1u;
 
-// The protocol version of the message format.
-const int kMessageFormatVersionThree = 3;
+// The number of bytes used to represent the message body size.
+const size_t kV3NumBytesInHeaderSize = 2u;  // 16-bit int
+const size_t kV4NumBytesInHeaderSize = 4u;  // 32-bit int
 
-const char kPayloadKey[] = "payload";
+// Supported Protocol versions.
+const uint8_t kV3HeaderVersion = 0x03;
+const uint8_t kV4HeaderVersion = 0x04;
+
+// JSON keys for feature and payload.
 const char kFeatureKey[] = "feature";
+const char kPayloadKey[] = "payload";
 
 // The default feature value. This is the default for backward compatibility
 // reasons; previously, the protocol did not transmit the feature in the
@@ -48,77 +48,41 @@
 // EasyUnlock by default.
 const char kDefaultFeature[] = "easy_unlock";
 
-// Parses the |serialized_message|'s header. Returns |true| iff the message has
-// a valid header, is complete, and is well-formed according to the header. Sets
-// |is_incomplete_message| to true iff the message does not have enough data to
-// parse the header, or if the message length encoded in the message header
-// exceeds the size of the |serialized_message|.
-bool ParseHeader(const std::string& serialized_message,
-                 bool* is_incomplete_message) {
-  *is_incomplete_message = false;
-  if (serialized_message.size() < kHeaderLength) {
-    *is_incomplete_message = true;
-    return false;
-  }
+// Features which were launched before protocol v4 was introduced. If we try to
+// serialize a WireMessage whose feature is one of these, we should use the v3
+// protocol.
+const char* const kV3Features[] = {
+    // Authentication protocol (used for all features).
+    "auth"
 
-  static_assert(kHeaderLength > 2, "kHeaderLength too small");
-  size_t version = serialized_message[0];
-  if (version != kMessageFormatVersionThree) {
-    PA_LOG(WARNING) << "Error: Invalid message version. Got " << version
-                    << ", expected " << kMessageFormatVersionThree;
-    return false;
-  }
+    // Smart Lock.
+    "easy_unlock",
 
-  uint16_t expected_body_length =
-      (static_cast<uint8_t>(serialized_message[1]) << 8) |
-      (static_cast<uint8_t>(serialized_message[2]) << 0);
-  size_t expected_message_length = kHeaderLength + expected_body_length;
-  if (serialized_message.size() < expected_message_length) {
-    *is_incomplete_message = true;
-    return false;
-  }
-  if (serialized_message.size() != expected_message_length) {
-    PA_LOG(WARNING) << "Error: Invalid message length. Got "
-                    << serialized_message.size() << ", expected "
-                    << expected_message_length;
-    return false;
-  }
+    // Instant Tethering.
+    "magic_tether",
+};
 
-  return true;
-}
-
-}  // namespace
-
-WireMessage::~WireMessage() {}
-
-// static
-std::unique_ptr<WireMessage> WireMessage::Deserialize(
-    const std::string& serialized_message,
-    bool* is_incomplete_message) {
-  if (!ParseHeader(serialized_message, is_incomplete_message))
-    return nullptr;
-
-  std::unique_ptr<base::Value> body_value = base::JSONReader::ReadDeprecated(
-      serialized_message.substr(kHeaderLength));
+std::unique_ptr<WireMessage> DeserializeJsonMessageBody(
+    const std::string& serialized_message_body) {
+  std::unique_ptr<base::Value> body_value =
+      base::JSONReader::ReadDeprecated(serialized_message_body);
   if (!body_value || !body_value->is_dict()) {
-    PA_LOG(WARNING) << "Error: Unable to parse message as JSON.";
+    PA_LOG(WARNING) << "Unable to parse message as JSON.";
     return nullptr;
   }
 
   base::DictionaryValue* body;
-  bool success = body_value->GetAsDictionary(&body);
-  DCHECK(success);
+  if (!body_value->GetAsDictionary(&body))
+    NOTREACHED();
 
   std::string payload_base64;
   if (!body->GetString(kPayloadKey, &payload_base64)) {
-    // The body is a valid JSON, but it doesn't contain a |payload| field. It
-    // must be a non-encrypted message.
-    return base::WrapUnique(
-        new WireMessage(serialized_message.substr(kHeaderLength)));
+    // Legacy case: Message without a payload.
+    return base::WrapUnique(new WireMessage(serialized_message_body));
   }
 
   if (payload_base64.empty()) {
-    PA_LOG(WARNING) << "Error: Missing payload.";
+    PA_LOG(WARNING) << "Message contains empty payload.";
     return nullptr;
   }
 
@@ -126,18 +90,103 @@
   if (!base::Base64UrlDecode(payload_base64,
                              base::Base64UrlDecodePolicy::REQUIRE_PADDING,
                              &payload)) {
-    PA_LOG(WARNING) << "Error: Invalid base64 encoding for payload.";
+    PA_LOG(WARNING) << "Payload contains invalid base64 encoding.";
     return nullptr;
   }
 
   std::string feature;
   if (!body->GetString(kFeatureKey, &feature) || feature.empty()) {
-    feature = std::string(kDefaultFeature);
+    feature = kDefaultFeature;
   }
 
   return base::WrapUnique(new WireMessage(payload, feature));
 }
 
+std::unique_ptr<WireMessage> DeserializeV3OrV4Message(
+    const std::string& serialized_message,
+    bool* is_incomplete_message,
+    bool is_v3) {
+  const size_t kHeaderSize =
+      kNumBytesInHeaderProtocolVersion +
+      (is_v3 ? kV3NumBytesInHeaderSize : kV4NumBytesInHeaderSize);
+
+  if (serialized_message.size() < kHeaderSize) {
+    PA_LOG(ERROR) << "Message was shorter than expected. "
+                  << "Size: " << serialized_message.size() << ", "
+                  << "Version: " << (is_v3 ? 3 : 4);
+    *is_incomplete_message = true;
+    return nullptr;
+  }
+
+  // Reads the expected body size, starting after the protocol message portion
+  // of the header. Because this value is received over the network, we must
+  // convert from big endian to host byte order.
+  base::BigEndianReader reader(
+      serialized_message.data() + kNumBytesInHeaderProtocolVersion,
+      serialized_message.size() - kNumBytesInHeaderProtocolVersion);
+
+  size_t expected_message_length;
+  if (is_v3) {
+    uint16_t body_length;
+    if (!reader.ReadU16(&body_length)) {
+      PA_LOG(ERROR) << "Failed to read v3 message length.";
+      *is_incomplete_message = true;
+      return nullptr;
+    }
+    expected_message_length = kHeaderSize + body_length;
+  } else {
+    uint32_t body_length;
+    if (!reader.ReadU32(&body_length)) {
+      PA_LOG(ERROR) << "Failed to read v4 message length.";
+      *is_incomplete_message = true;
+      return nullptr;
+    }
+    expected_message_length = kHeaderSize + body_length;
+  }
+
+  size_t message_length = serialized_message.size();
+  if (message_length != expected_message_length) {
+    PA_LOG(ERROR) << "Message length does not match expectation. "
+                  << "Size: " << serialized_message.size() << ", "
+                  << "Expected size: " << expected_message_length << ", "
+                  << "Version: " << (is_v3 ? 3 : 4);
+    *is_incomplete_message = message_length < expected_message_length;
+    return nullptr;
+  }
+
+  *is_incomplete_message = false;
+  return DeserializeJsonMessageBody(serialized_message.substr(kHeaderSize));
+}
+
+}  // namespace
+
+WireMessage::~WireMessage() = default;
+
+// static
+std::unique_ptr<WireMessage> WireMessage::Deserialize(
+    const std::string& serialized_message,
+    bool* is_incomplete_message) {
+  if (serialized_message.empty()) {
+    PA_LOG(ERROR) << "Attempted to deserialize empty message.";
+    *is_incomplete_message = true;
+    return nullptr;
+  }
+
+  // The first byte of the message is the protocol version as an unsigned 8-bit
+  // integer.
+  uint8_t protocol_version = serialized_message[0];
+
+  if (protocol_version == kV3HeaderVersion ||
+      protocol_version == kV4HeaderVersion) {
+    return DeserializeV3OrV4Message(serialized_message, is_incomplete_message,
+                                    protocol_version == kV3HeaderVersion);
+  }
+
+  PA_LOG(ERROR) << "Received message with unknown version " << protocol_version;
+  *is_incomplete_message = false;
+  return nullptr;
+}
+
 std::string WireMessage::Serialize() const {
   std::string json_body;
   if (body_.empty()) {
@@ -146,7 +195,7 @@
       return std::string();
     }
 
-    // Create JSON body containing permit id and payload.
+    // Create JSON body containing feature and payload.
     base::DictionaryValue body;
 
     std::string base64_payload;
@@ -164,23 +213,30 @@
     json_body = body_;
   }
 
-  // Create header containing version and payload size.
+  bool use_v3_encoding = body_.empty() || feature_.empty() ||
+                         base::Contains(kV3Features, feature_);
+
   size_t body_size = json_body.size();
-  if (body_size > std::numeric_limits<uint16_t>::max()) {
+  if (use_v3_encoding && body_size > std::numeric_limits<uint16_t>::max()) {
     PA_LOG(ERROR) << "Can not create WireMessage because body size exceeds "
                   << "16-bit unsigned integer: " << body_size;
     return std::string();
   }
 
-  uint8_t header[] = {
-      static_cast<uint8_t>(kMessageFormatVersionThree),
-      static_cast<uint8_t>((body_size >> 8) & 0xFF),
-      static_cast<uint8_t>(body_size & 0xFF),
-  };
-  static_assert(sizeof(header) == kHeaderLength, "Malformed header.");
+  const size_t kHeaderSize =
+      kNumBytesInHeaderProtocolVersion +
+      (use_v3_encoding ? kV3NumBytesInHeaderSize : kV4NumBytesInHeaderSize);
 
-  std::string header_string(kHeaderLength, 0);
-  std::memcpy(&header_string[0], header, kHeaderLength);
+  std::string header_string(kHeaderSize, 0);
+  base::BigEndianWriter writer(&header_string[0], kHeaderSize);
+  if (use_v3_encoding) {
+    writer.WriteU8(kV3HeaderVersion);
+    writer.WriteU16(static_cast<uint16_t>(body_size));
+  } else {
+    writer.WriteU8(kV4HeaderVersion);
+    writer.WriteU32(static_cast<uint32_t>(body_size));
+  }
+
   return header_string + json_body;
 }
 
diff --git a/chromeos/services/secure_channel/wire_message.h b/chromeos/services/secure_channel/wire_message.h
index a6be885a..d0b3bc31 100644
--- a/chromeos/services/secure_channel/wire_message.h
+++ b/chromeos/services/secure_channel/wire_message.h
@@ -14,6 +14,33 @@
 
 namespace secure_channel {
 
+// Message sent over the wire via SecureChannel. Each message sent using the
+// SecureChannel protocol has a feature name (a unique identifier for the client
+// sending/receiving a message) and a payload.
+//
+// A wire message in SecureChannel is serialized to a byte array (in the format
+// of a std::string). The first byte is always the protocol version expressed as
+// an unsigned 8-bit int. This class supports protocol versions 3 and 4.
+//
+// v3: One byte version (0x03), followed by 2 bytes of the message size as an
+//     unsigned 16-bit int, followed by a stringified JSON object containing a
+//     "feature" key whose value is the feature and a "payload" key whose value
+//     is the payload.
+//
+//     [ message version ] [ body length ] [ JSON body ]
+//           1 byte            2 bytes      body length
+//
+// v4: One byte version (0x04), followed by 4 bytes of the message size as an
+//     unsigned 32-bit int, followed by a stringified JSON object containing a
+//     "feature" key whose value is the feature and a "payload" key whose value
+//     is the payload.
+//
+//     [ message version ] [ body length ] [ JSON body ]
+//           1 byte            4 bytes      body length
+//
+// v3 is deprecated and all new features use v4, but we special-case features
+// which were released before v4 and send them via v3 for backward
+// compatibility.
 class WireMessage {
  public:
   // Creates a WireMessage containing |payload| for feature |feature| and
diff --git a/chromeos/services/secure_channel/wire_message_unittest.cc b/chromeos/services/secure_channel/wire_message_unittest.cc
index b83de7b9..34ede73 100644
--- a/chromeos/services/secure_channel/wire_message_unittest.cc
+++ b/chromeos/services/secure_channel/wire_message_unittest.cc
@@ -30,7 +30,7 @@
   EXPECT_FALSE(message);
 }
 
-TEST(SecureChannelWireMessageTest, Deserialize_IncompleteHeader) {
+TEST(SecureChannelWireMessageTest, Deserialize_IncompleteHeader_V3) {
   bool is_incomplete;
   std::unique_ptr<WireMessage> message =
       WireMessage::Deserialize("\3", &is_incomplete);
@@ -38,6 +38,14 @@
   EXPECT_FALSE(message);
 }
 
+TEST(SecureChannelWireMessageTest, Deserialize_IncompleteHeader_V4) {
+  bool is_incomplete;
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize("\4", &is_incomplete);
+  EXPECT_TRUE(is_incomplete);
+  EXPECT_FALSE(message);
+}
+
 TEST(SecureChannelWireMessageTest, Deserialize_UnexpectedMessageFormatVersion) {
   bool is_incomplete;
   // Version 2 is below the minimum supported version.
@@ -47,7 +55,7 @@
   EXPECT_FALSE(message);
 }
 
-TEST(SecureChannelWireMessageTest, Deserialize_BodyOfSizeZero) {
+TEST(SecureChannelWireMessageTest, Deserialize_BodyOfSizeZero_V3) {
   bool is_incomplete;
   std::unique_ptr<WireMessage> message =
       WireMessage::Deserialize(std::string("\3\0\0", 3), &is_incomplete);
@@ -55,7 +63,15 @@
   EXPECT_FALSE(message);
 }
 
-TEST(SecureChannelWireMessageTest, Deserialize_IncompleteBody) {
+TEST(SecureChannelWireMessageTest, Deserialize_BodyOfSizeZero_V4) {
+  bool is_incomplete;
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize(std::string("\4\0\0\0\0", 5), &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  EXPECT_FALSE(message);
+}
+
+TEST(SecureChannelWireMessageTest, Deserialize_IncompleteBody_V3) {
   bool is_incomplete;
   std::unique_ptr<WireMessage> message =
       WireMessage::Deserialize(std::string("\3\0\5", 3), &is_incomplete);
@@ -63,8 +79,16 @@
   EXPECT_FALSE(message);
 }
 
+TEST(SecureChannelWireMessageTest, Deserialize_IncompleteBody_V4) {
+  bool is_incomplete;
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize(std::string("\4\0\0\0\5", 5), &is_incomplete);
+  EXPECT_TRUE(is_incomplete);
+  EXPECT_FALSE(message);
+}
+
 TEST(SecureChannelWireMessageTest,
-     Deserialize_BodyLongerThanSpecifiedInHeader) {
+     Deserialize_BodyLongerThanSpecifiedInHeader_V3) {
   bool is_incomplete;
   std::unique_ptr<WireMessage> message = WireMessage::Deserialize(
       std::string("\3\0\5", 3) + "123456", &is_incomplete);
@@ -72,7 +96,16 @@
   EXPECT_FALSE(message);
 }
 
-TEST(SecureChannelWireMessageTest, Deserialize_BodyIsNotValidJSON) {
+TEST(SecureChannelWireMessageTest,
+     Deserialize_BodyLongerThanSpecifiedInHeader_V4) {
+  bool is_incomplete;
+  std::unique_ptr<WireMessage> message = WireMessage::Deserialize(
+      std::string("\4\0\0\0\5", 5) + "123456", &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  EXPECT_FALSE(message);
+}
+
+TEST(SecureChannelWireMessageTest, Deserialize_BodyIsNotValidJSON_V3) {
   bool is_incomplete;
   std::unique_ptr<WireMessage> message = WireMessage::Deserialize(
       std::string("\3\0\5", 3) + "12345", &is_incomplete);
@@ -80,7 +113,15 @@
   EXPECT_FALSE(message);
 }
 
-TEST(SecureChannelWireMessageTest, Deserialize_BodyIsNotADictionary) {
+TEST(SecureChannelWireMessageTest, Deserialize_BodyIsNotValidJSON_V4) {
+  bool is_incomplete;
+  std::unique_ptr<WireMessage> message = WireMessage::Deserialize(
+      std::string("\4\0\0\0\5", 5) + "12345", &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  EXPECT_FALSE(message);
+}
+
+TEST(SecureChannelWireMessageTest, Deserialize_BodyIsNotADictionary_V3) {
   bool is_incomplete;
   std::string header("\3\0\x15", 3);
   std::string bytes = header + "[{\"payload\": \"YQ==\"}]";
@@ -90,7 +131,17 @@
   EXPECT_FALSE(message);
 }
 
-TEST(SecureChannelWireMessageTest, Deserialize_NonEncryptedMessage) {
+TEST(SecureChannelWireMessageTest, Deserialize_BodyIsNotADictionary_V4) {
+  bool is_incomplete;
+  std::string header("\4\0\0\0\x15", 5);
+  std::string bytes = header + "[{\"payload\": \"YQ==\"}]";
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize(bytes, &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  EXPECT_FALSE(message);
+}
+
+TEST(SecureChannelWireMessageTest, Deserialize_NonEncryptedMessage_V3) {
   bool is_incomplete;
   std::string header("\3\0\x02", 3);
   std::string bytes = header + "{}";
@@ -101,7 +152,18 @@
   EXPECT_EQ("{}", message->body());
 }
 
-TEST(SecureChannelWireMessageTest, Deserialize_BodyHasEmptyPayload) {
+TEST(SecureChannelWireMessageTest, Deserialize_NonEncryptedMessage_V4) {
+  bool is_incomplete;
+  std::string header("\4\0\0\0\x02", 5);
+  std::string bytes = header + "{}";
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize(bytes, &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  ASSERT_TRUE(message);
+  EXPECT_EQ("{}", message->body());
+}
+
+TEST(SecureChannelWireMessageTest, Deserialize_BodyHasEmptyPayload_V3) {
   bool is_incomplete;
   std::string header("\3\0\x29", 3);
   std::string bytes =
@@ -112,7 +174,18 @@
   EXPECT_FALSE(message);
 }
 
-TEST(SecureChannelWireMessageTest, Deserialize_PayloadIsNotBase64Encoded) {
+TEST(SecureChannelWireMessageTest, Deserialize_BodyHasEmptyPayload_V4) {
+  bool is_incomplete;
+  std::string header("\4\0\0\0\x29", 5);
+  std::string bytes =
+      header + "{\"payload\": \"\", \"feature\": \"testFeature\"}";
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize(bytes, &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  EXPECT_FALSE(message);
+}
+
+TEST(SecureChannelWireMessageTest, Deserialize_PayloadIsNotBase64Encoded_V3) {
   bool is_incomplete;
   std::string header("\3\0\x30", 3);
   std::string bytes =
@@ -123,7 +196,18 @@
   EXPECT_FALSE(message);
 }
 
-TEST(SecureChannelWireMessageTest, Deserialize_ValidMessage) {
+TEST(SecureChannelWireMessageTest, Deserialize_PayloadIsNotBase64Encoded_V4) {
+  bool is_incomplete;
+  std::string header("\4\0\0\0\x30", 5);
+  std::string bytes =
+      header + "{\"payload\": \"garbage\", \"feature\": \"testFeature\"}";
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize(bytes, &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  EXPECT_FALSE(message);
+}
+
+TEST(SecureChannelWireMessageTest, Deserialize_ValidMessage_V3) {
   bool is_incomplete;
   std::string header("\3\0\x2d", 3);
   std::string bytes =
@@ -136,8 +220,21 @@
   EXPECT_EQ("testFeature", message->feature());
 }
 
+TEST(SecureChannelWireMessageTest, Deserialize_ValidMessage_V4) {
+  bool is_incomplete;
+  std::string header("\4\0\0\0\x2d", 5);
+  std::string bytes =
+      header + "{\"payload\": \"YQ==\", \"feature\": \"testFeature\"}";
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize(bytes, &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  ASSERT_TRUE(message);
+  EXPECT_EQ("a", message->payload());
+  EXPECT_EQ("testFeature", message->feature());
+}
+
 TEST(SecureChannelWireMessageTest,
-     Deserialize_ValidMessageWithBase64UrlEncoding) {
+     Deserialize_ValidMessageWithBase64UrlEncoding_V3) {
   bool is_incomplete;
   std::string header("\3\0\x2d", 3);
   std::string bytes =
@@ -151,7 +248,21 @@
 }
 
 TEST(SecureChannelWireMessageTest,
-     Deserialize_ValidMessageWithExtraUnknownFields) {
+     Deserialize_ValidMessageWithBase64UrlEncoding_V4) {
+  bool is_incomplete;
+  std::string header("\4\0\0\0\x2d", 5);
+  std::string bytes =
+      header + "{\"payload\": \"_-Y=\", \"feature\": \"testFeature\"}";
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize(bytes, &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  ASSERT_TRUE(message);
+  EXPECT_EQ("\xFF\xE6", message->payload());
+  EXPECT_EQ("testFeature", message->feature());
+}
+
+TEST(SecureChannelWireMessageTest,
+     Deserialize_ValidMessageWithExtraUnknownFields_V3) {
   bool is_incomplete;
   std::string header("\3\0\x4c", 3);
   std::string bytes = header +
@@ -168,6 +279,24 @@
   EXPECT_EQ("testFeature", message->feature());
 }
 
+TEST(SecureChannelWireMessageTest,
+     Deserialize_ValidMessageWithExtraUnknownFields_V4) {
+  bool is_incomplete;
+  std::string header("\4\0\0\0\x4c", 5);
+  std::string bytes = header +
+                      "{"
+                      "  \"payload\": \"YQ==\","
+                      "  \"feature\": \"testFeature\","
+                      "  \"unexpected\": \"surprise!\""
+                      "}";
+  std::unique_ptr<WireMessage> message =
+      WireMessage::Deserialize(bytes, &is_incomplete);
+  EXPECT_FALSE(is_incomplete);
+  ASSERT_TRUE(message);
+  EXPECT_EQ("a", message->payload());
+  EXPECT_EQ("testFeature", message->feature());
+}
+
 TEST(SecureChannelWireMessageTest, Deserialize_SizeEquals0x01FF) {
   // Create a message with a body of 0x01FF bytes to test the size contained in
   // the header is parsed correctly.
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
index c8cc0c5..2ba6d68 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
@@ -181,8 +181,12 @@
     public static final String TAB_SWITCHER_BUTTON_CLICKED = "tab_switcher_button_clicked";
 
     /** Read later related events. */
+    public static final String APP_MENU_BOOKMARK_STAR_ICON_PRESSED =
+            "app_menu_bookmark_star_icon_pressed";
     public static final String READ_LATER_CONTEXT_MENU_TAPPED = "read_later_context_menu_tapped";
     public static final String READ_LATER_ARTICLE_SAVED = "read_later_article_saved";
+    public static final String READ_LATER_BOTTOM_SHEET_FOLDER_SEEN =
+            "read_later_bottom_sheet_folder_seen";
     public static final String READ_LATER_BOOKMARK_FOLDER_OPENED =
             "read_later_bookmark_folder_opened";
 
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java
index 278f528..72b5741a 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java
@@ -51,7 +51,6 @@
             Resources resources, Runnable messageDismissed) {
         mModel = model;
         mMaxTranslationSupplier = maxTranslationSupplier;
-        mModel.set(TRANSLATION_Y, -mMaxTranslationSupplier.get());
         mHideThresholdPx = resources.getDimensionPixelSize(R.dimen.message_hide_threshold);
         mMessageDismissed = messageDismissed;
     }
@@ -61,6 +60,9 @@
      * @param messageShown The {@link Runnable} that will run once the message banner is shown.
      */
     void show(Runnable messageShown) {
+        if (mAnimatorSet == null) {
+            mModel.set(TRANSLATION_Y, -mMaxTranslationSupplier.get());
+        }
         cancelAnyAnimations();
         mAnimatorSet = createAnimatorSet(true);
         mAnimatorSet.addListener(new CancelAwareAnimatorListener() {
@@ -119,11 +121,12 @@
         // No need to animate if the message banner is in resting position.
         if (mModel.get(TRANSLATION_Y) == 0.f) return;
 
-        mAnimatorSet = createAnimatorSet(mModel.get(TRANSLATION_Y) > -mHideThresholdPx);
+        final boolean isShow = mModel.get(TRANSLATION_Y) > -mHideThresholdPx;
+        mAnimatorSet = createAnimatorSet(isShow);
         mAnimatorSet.addListener(new CancelAwareAnimatorListener() {
             @Override
             public void onEnd(Animator animator) {
-                mMessageDismissed.run();
+                if (!isShow) mMessageDismissed.run();
                 mAnimatorSet = null;
             }
         });
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java b/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java
index a8a083a..4058d4d 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java
@@ -48,4 +48,27 @@
         ViewUtils.setAncestorsShouldClipChildren(this, true);
         removeAllViews();
     }
+
+    /**
+     * Runs a {@link Runnable} after the initial layout. If the view is already laid out, the
+     * {@link Runnable} will be called immediately.
+     * @param runnable The {@link Runnable}.
+     */
+    void runAfterInitialLayout(Runnable runnable) {
+        if (getHeight() > 0) {
+            runnable.run();
+            return;
+        }
+
+        addOnLayoutChangeListener(new OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                if (v.getHeight() == 0) return;
+
+                runnable.run();
+                v.removeOnLayoutChangeListener(this);
+            }
+        });
+    }
 }
diff --git a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java
index cb49f66..417f9d19 100644
--- a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java
+++ b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java
@@ -57,11 +57,16 @@
                     mContainer.getResources(), mDismissHandler.bind(mModel));
         }
         mContainer.addMessage(mView);
-        final Runnable onShown = () -> {
+
+        final Runnable showRunnable = () -> mMessageBanner.show(() -> {
             mMessageBanner.setOnTouchRunnable(mAutoDismissTimer::resetTimer);
             mAutoDismissTimer.startTimer(() -> { mDismissHandler.onResult(mModel); });
-        };
-        mMessageBanner.show(onShown);
+        });
+
+        // Wait until the message and the container are measured before showing the message. This
+        // is required in case the animation set-up requires the height of the container, e.g.
+        // showing messages without the top controls visible.
+        mContainer.runAfterInitialLayout(showRunnable);
     }
 
     /**
diff --git a/components/onc/docs/onc_spec.md b/components/onc/docs/onc_spec.md
index 8317abee..3dd4e84 100644
--- a/components/onc/docs/onc_spec.md
+++ b/components/onc/docs/onc_spec.md
@@ -1372,10 +1372,6 @@
     * (optional) - **boolean**
     * Whether cellular data connections are allowed when the device is roaming.
 
-* **Carrier**
-    * (optional, read-only) - **string**
-    * The name of the carrier for which the device is configured.
-
 * **ESN**
     * (optional, read-only) - **string**
     * The Electronic Serial Number of the cellular modem.
diff --git a/components/onc/onc_constants.cc b/components/onc/onc_constants.cc
index bc96ee9..3cc746a 100644
--- a/components/onc/onc_constants.cc
+++ b/components/onc/onc_constants.cc
@@ -106,7 +106,6 @@
 const char kAllowRoaming[] = "AllowRoaming";
 const char kAPN[] = "APN";
 const char kAPNList[] = "APNList";
-const char kCarrier[] = "Carrier";
 const char kESN[] = "ESN";
 const char kFamily[] = "Family";
 const char kFirmwareRevision[] = "FirmwareRevision";
diff --git a/components/optimization_guide/BUILD.gn b/components/optimization_guide/BUILD.gn
index ad7a2d9..9cbe500 100644
--- a/components/optimization_guide/BUILD.gn
+++ b/components/optimization_guide/BUILD.gn
@@ -52,6 +52,7 @@
     "optimization_guide_util.h",
     "optimization_metadata.cc",
     "optimization_metadata.h",
+    "optimization_target_model_observer.h",
     "prediction_model.cc",
     "prediction_model.h",
     "store_update_data.cc",
diff --git a/components/optimization_guide/optimization_guide_decider.h b/components/optimization_guide/optimization_guide_decider.h
index 4da73d36..1072e61 100644
--- a/components/optimization_guide/optimization_guide_decider.h
+++ b/components/optimization_guide/optimization_guide_decider.h
@@ -11,6 +11,7 @@
 #include "base/containers/flat_map.h"
 #include "base/optional.h"
 #include "components/optimization_guide/optimization_metadata.h"
+#include "components/optimization_guide/optimization_target_model_observer.h"
 #include "components/optimization_guide/proto/hints.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
 
@@ -68,6 +69,27 @@
           client_model_feature_values,
       OptimizationGuideTargetDecisionCallback callback) = 0;
 
+  // Adds an observer for updates to the model for |optimization_target|.
+  //
+  // It is assumed that any model retrieved this way will be passed to the
+  // Machine Learning Service for inference.
+  //
+  // Still being implemented - DO NOT USE YET.
+  virtual void AddObserverForOptimizationTargetModel(
+      proto::OptimizationTarget optimization_target,
+      OptimizationTargetModelObserver* observer) = 0;
+
+  // Removes an observer for updates to the model for |optimization_target|.
+  //
+  // If |observer| is registered for multiple targets, |observer| must be
+  // removed for all targets that it is added for in order for it to be fully
+  // removed from receiving any calls.
+  //
+  // Still being implemented - DO NOT USE YET.
+  virtual void RemoveObserverForOptimizationTargetModel(
+      proto::OptimizationTarget optimization_target,
+      OptimizationTargetModelObserver* observer) = 0;
+
   // Registers the optimization types that intend to be queried during the
   // session. It is expected for this to be called after the browser has been
   // initialized.
diff --git a/components/optimization_guide/optimization_target_model_observer.h b/components/optimization_guide/optimization_target_model_observer.h
new file mode 100644
index 0000000..255407e4
--- /dev/null
+++ b/components/optimization_guide/optimization_target_model_observer.h
@@ -0,0 +1,30 @@
+// 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_OPTIMIZATION_GUIDE_OPTIMIZATION_TARGET_MODEL_OBSERVER_H_
+#define COMPONENTS_OPTIMIZATION_GUIDE_OPTIMIZATION_TARGET_MODEL_OBSERVER_H_
+
+#include "base/files/file_path.h"
+#include "base/observer_list_types.h"
+#include "components/optimization_guide/proto/models.pb.h"
+
+namespace optimization_guide {
+
+// Observes |optimization_guide::OptimizationGuideDecider| for updates to models
+// for a particular optimization target.
+class OptimizationTargetModelObserver : public base::CheckedObserver {
+ public:
+  // Invoked when a model for |optimization_target| has been updated. It is
+  // guaranteed that this method will only be invoked for targets that |this|
+  // is added as an observer for.
+  //
+  // When this observer is first added, it will call this function with the
+  // file path it already has on device, if applicable.
+  virtual void OnModelFileUpdated(proto::OptimizationTarget optimization_target,
+                                  const base::FilePath& file_path) = 0;
+};
+
+}  // namespace optimization_guide
+
+#endif  // COMPONENTS_OPTIMIZATION_GUIDE_OPTIMIZATION_TARGET_MODEL_OBSERVER_H_
diff --git a/components/optimization_guide/test_optimization_guide_decider.cc b/components/optimization_guide/test_optimization_guide_decider.cc
index f2c0617c..0520bdc 100644
--- a/components/optimization_guide/test_optimization_guide_decider.cc
+++ b/components/optimization_guide/test_optimization_guide_decider.cc
@@ -34,6 +34,14 @@
                           /*optimization_metadata=*/{});
 }
 
+void TestOptimizationGuideDecider::AddObserverForOptimizationTargetModel(
+    optimization_guide::proto::OptimizationTarget optimization_target,
+    optimization_guide::OptimizationTargetModelObserver* observer) {}
+
+void TestOptimizationGuideDecider::RemoveObserverForOptimizationTargetModel(
+    optimization_guide::proto::OptimizationTarget optimization_target,
+    optimization_guide::OptimizationTargetModelObserver* observer) {}
+
 OptimizationGuideDecision TestOptimizationGuideDecider::CanApplyOptimization(
     const GURL& url,
     proto::OptimizationType optimization_type,
diff --git a/components/optimization_guide/test_optimization_guide_decider.h b/components/optimization_guide/test_optimization_guide_decider.h
index de8755c7..7e56d380 100644
--- a/components/optimization_guide/test_optimization_guide_decider.h
+++ b/components/optimization_guide/test_optimization_guide_decider.h
@@ -29,6 +29,12 @@
       const base::flat_map<proto::ClientModelFeature, float>&
           client_model_feature_values,
       OptimizationGuideTargetDecisionCallback callback) override;
+  void AddObserverForOptimizationTargetModel(
+      proto::OptimizationTarget optimization_target,
+      OptimizationTargetModelObserver* observer) override;
+  void RemoveObserverForOptimizationTargetModel(
+      proto::OptimizationTarget optimization_target,
+      OptimizationTargetModelObserver* observer) override;
   void RegisterOptimizationTypes(
       const std::vector<proto::OptimizationType>& optimization_types) override;
   void CanApplyOptimizationAsync(
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.h b/content/browser/accessibility/browser_accessibility_cocoa.h
index a5324df..33a689c 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.h
+++ b/content/browser/accessibility/browser_accessibility_cocoa.h
@@ -122,10 +122,6 @@
 // on the characteristics of this accessibility node.
 - (content::BrowserAccessibility*)actionTarget;
 
-// Return the active descendant for this accessibility object or null if there
-// is no active descendant defined or in the case of an error.
-- (content::BrowserAccessibility*)activeDescendant;
-
 // Internally-used property.
 @property(nonatomic, readonly) NSPoint origin;
 
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index d9d756a..311715ec 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -1747,21 +1747,16 @@
   if (![self instanceActive])
     return nil;
 
-  //
-  // If the active descendant points to an element in a container with
-  // selectable children, add the "owns" relationship to point to that
-  // container. That's the only way activeDescendant is actually
-  // supported with VoiceOver.
-  //
-
-  BrowserAccessibility* activeDescendant = [self activeDescendant];
+  BrowserAccessibility* activeDescendant =
+      _owner->manager()->GetActiveDescendant(_owner);
   if (!activeDescendant)
     return nil;
 
   BrowserAccessibility* container = activeDescendant->PlatformGetParent();
   while (container &&
-         !ui::IsContainerWithSelectableChildren(container->GetRole()))
+         !ui::IsContainerWithSelectableChildren(container->GetRole())) {
     container = container->PlatformGetParent();
+  }
   if (!container)
     return nil;
 
@@ -2245,43 +2240,39 @@
 - (NSArray*)selectedChildren {
   if (![self instanceActive])
     return nil;
+
   NSMutableArray* ret = [[[NSMutableArray alloc] init] autorelease];
-  BrowserAccessibilityManager* manager = _owner->manager();
-  BrowserAccessibility* focusedChild = manager->GetFocus();
-  if (focusedChild == _owner)
-    focusedChild = manager->GetActiveDescendant(focusedChild);
-
-  if (focusedChild &&
-      (focusedChild == _owner || !focusedChild->IsDescendantOf(_owner)))
-    focusedChild = nullptr;
-
-  // If it's not multiselectable, try to skip iterating over the
-  // children.
-  if (!GetState(_owner, ax::mojom::State::kMultiselectable)) {
-    // First try the focused child.
-    if (focusedChild) {
+  BrowserAccessibility* focusedChild = _owner->manager()->GetFocus();
+  // "IsDescendantOf" also returns true when the two objects are equivalent.
+  if (focusedChild && focusedChild != _owner &&
+      focusedChild->IsDescendantOf(_owner)) {
+    // If this container is not multi-selectable, try to skip iterating over the
+    // children because there could only be at most one selected child. The
+    // selected child should also be equivalent to the focused child, because
+    // selection is tethered to the focus.
+    if (!GetState(_owner, ax::mojom::State::kMultiselectable)) {
       [ret addObject:ToBrowserAccessibilityCocoa(focusedChild)];
       return ret;
     }
+
+    // If this container is multi-selectable and the focused child is selected,
+    // add the focused child in the list of selected children first, because
+    // this is how VoiceOver determines where to draw the focus ring around the
+    // active item.
+    if (focusedChild->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
+      [ret addObject:ToBrowserAccessibilityCocoa(focusedChild)];
   }
 
-  // Put the focused one first, if it's focused, as this helps VO draw the
-  // focus box around the active item.
-  if (focusedChild &&
-      focusedChild->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected))
-    [ret addObject:ToBrowserAccessibilityCocoa(focusedChild)];
-
-  // If it's multiselectable or if the previous attempts failed,
-  // return any children with the "selected" state, which may
-  // come from aria-selected.
+  // If this container is multi-selectable, we need to return any additional
+  // children (other than the focused child) with the "selected" state. If this
+  // container is not multi-selectable, but none of its children have the focus,
+  // we need to return all its children with the "selected" state.
   for (auto it = _owner->PlatformChildrenBegin();
        it != _owner->PlatformChildrenEnd(); ++it) {
     BrowserAccessibility* child = it.get();
-    if (child->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) {
-      if (child == focusedChild)
-        continue;  // Already added as first item.
-      else
-        [ret addObject:ToBrowserAccessibilityCocoa(child)];
+    if (child->GetBoolAttribute(ax::mojom::BoolAttribute::kSelected) &&
+        child != focusedChild) {
+      [ret addObject:ToBrowserAccessibilityCocoa(child)];
     }
   }
 
@@ -3903,23 +3894,18 @@
   if (!ui::IsContainerWithSelectableChildren(_owner->node()->data().role))
     return _owner;
 
-  if (BrowserAccessibility* activeDescendant = [self activeDescendant])
+  // Active descendant takes priority over focus, because the webpage author has
+  // explicitly designated a different behavior for users of assistive software.
+  BrowserAccessibility* activeDescendant =
+      _owner->manager()->GetActiveDescendant(_owner);
+  if (activeDescendant != _owner)
     return activeDescendant;
 
-  BrowserAccessibility* focused = _owner->manager()->GetFocus();
-  if (focused && focused->IsDescendantOf(_owner))
-    return focused;
+  BrowserAccessibility* focus = _owner->manager()->GetFocus();
+  if (focus && focus->IsDescendantOf(_owner))
+    return focus;
 
   return _owner;
 }
 
-// Return the active descendant for this accessibility object or null if there
-// is no active descendant defined or in the case of an error.
-- (BrowserAccessibility*)activeDescendant {
-  int activeDescendantId;
-  if (!_owner->GetIntAttribute(ax::mojom::IntAttribute::kActivedescendantId,
-                               &activeDescendantId))
-    return nullptr;
-  return _owner->manager()->GetFromID(activeDescendantId);
-}
 @end
diff --git a/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm b/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
index 5cd9170..03b84fa 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa_browsertest.mm
@@ -593,7 +593,7 @@
                <div tabindex="2" role="treeitem">2</div>
              </div>)HTML");
 
-  EXPECT_TRUE(NavigateToURL(shell(), url));
+  ASSERT_TRUE(NavigateToURL(shell(), url));
   waiter.WaitForNotification();
 
   BrowserAccessibility* tree = FindNode(ax::mojom::Role::kTree);
@@ -601,8 +601,8 @@
       [ToBrowserAccessibilityCocoa(tree) retain]);
 
   NSArray* tree_children = [cocoa_tree children];
-  EXPECT_NSEQ(@"AXRow", [tree_children[0] role]);
-  EXPECT_NSEQ(@"AXRow", [tree_children[1] role]);
+  ASSERT_NSEQ(@"AXRow", [tree_children[0] role]);
+  ASSERT_NSEQ(@"AXRow", [tree_children[1] role]);
 
   content::RenderProcessHost* render_process_host =
       shell()->web_contents()->GetMainFrame()->GetProcess();
@@ -614,12 +614,12 @@
       TriggerContextMenuAndGetMenuLocation(cocoa_tree, menu_filter.get());
 
   menu_filter->Reset();
-  gfx::Point item_1_point =
+  gfx::Point item_2_point =
       TriggerContextMenuAndGetMenuLocation(tree_children[1], menu_filter.get());
-  ASSERT_NE(tree_point, item_1_point);
+  EXPECT_NE(tree_point, item_2_point);
 
   // Now focus the second child and trigger a context menu on the tree.
-  EXPECT_TRUE(
+  ASSERT_TRUE(
       content::ExecuteScript(shell()->web_contents(),
                              "document.body.children[0].children[1].focus();"));
   WaitForAccessibilityFocusChange();
@@ -629,7 +629,7 @@
   menu_filter->Reset();
   gfx::Point new_point =
       TriggerContextMenuAndGetMenuLocation(cocoa_tree, menu_filter.get());
-  ASSERT_EQ(new_point, item_1_point);
+  EXPECT_EQ(new_point, item_2_point);
 }
 
 IN_PROC_BROWSER_TEST_F(BrowserAccessibilityCocoaBrowserTest,
@@ -734,8 +734,8 @@
     EXPECT_EQ([second_child owner], [second_child actionTarget]);
     EXPECT_EQ(test.second, [second_child owner] == [parent actionTarget]);
 
-    // aria-activedescendant should take priority of focus for determining
-    // if an object is the action target.
+    // aria-activedescendant should take priority over focus for determining if
+    // an object is the action target.
     FocusAccessibilityElementAndWaitForFocusChange(first_child);
     EXPECT_EQ(test.second, [second_child owner] == [parent actionTarget]);
   }
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 3e8f736..05cec91 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -638,25 +638,25 @@
 }
 
 BrowserAccessibility* BrowserAccessibilityManager::GetActiveDescendant(
-    BrowserAccessibility* focus) const {
-  if (!focus)
+    BrowserAccessibility* node) const {
+  if (!node)
     return nullptr;
 
-  int32_t active_descendant_id;
+  ui::AXNode::AXID active_descendant_id;
   BrowserAccessibility* active_descendant = nullptr;
-  if (focus->GetIntAttribute(ax::mojom::IntAttribute::kActivedescendantId,
-                             &active_descendant_id)) {
-    active_descendant = focus->manager()->GetFromID(active_descendant_id);
+  if (node->GetIntAttribute(ax::mojom::IntAttribute::kActivedescendantId,
+                            &active_descendant_id)) {
+    active_descendant = node->manager()->GetFromID(active_descendant_id);
   }
 
-  // When getting the active descendant, we avoid calling IsInvisibleOrIgnored
-  // on the node because IsInvisibleOrIgnored takes the focused object into
-  // account by retrieving it. We already have the focused object, thus there is
-  // no need to re-retrieve it. Furthermore, doing so can lead to an infinite
-  // loop. Therefore just check the AXNodeData.
-
-  if (focus->GetRole() == ax::mojom::Role::kPopUpButton) {
-    BrowserAccessibility* child = focus->InternalGetFirstChild();
+  // When getting the active descendant, we avoid calling
+  // BrowserAccessibility::IsInvisibleOrIgnored on the node because
+  // IsInvisibleOrIgnored takes the focused object into account by retrieving
+  // it. We already have the focused object, thus there is no need to
+  // re-retrieve it. Furthermore, doing so can lead to an infinite loop.
+  // Therefore just check the AXNodeData.
+  if (node->GetRole() == ax::mojom::Role::kPopUpButton) {
+    BrowserAccessibility* child = node->InternalGetFirstChild();
     if (child && child->GetRole() == ax::mojom::Role::kMenuListPopup &&
         !child->GetData().IsInvisibleOrIgnored()) {
       // The active descendant is found on the menu list popup, i.e. on the
@@ -677,7 +677,7 @@
   if (active_descendant && !active_descendant->GetData().IsInvisibleOrIgnored())
     return active_descendant;
 
-  return focus;
+  return node;
 }
 
 bool BrowserAccessibilityManager::NativeViewHasFocus() {
@@ -714,7 +714,7 @@
 
 BrowserAccessibility*
 BrowserAccessibilityManager::GetFocusFromThisOrDescendantFrame() const {
-  int32_t focus_id = GetTreeData().focus_id;
+  ui::AXNode::AXID focus_id = GetTreeData().focus_id;
   BrowserAccessibility* obj = GetFromID(focus_id);
   // If nothing is focused, then the top document has the focus.
   if (!obj)
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index 2c1e194..d497a95 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -362,9 +362,10 @@
   // descendants.
   BrowserAccessibility* GetFocusFromThisOrDescendantFrame() const;
 
-  // Given a focused node |focus|, returns a descendant of that node if it
-  // has an active descendant, otherwise returns |focus|.
-  BrowserAccessibility* GetActiveDescendant(BrowserAccessibility* focus) const;
+  // Given a node, returns a descendant of that node if the node has an active
+  // descendant, otherwise returns the node itself. The node does not need to be
+  // focused.
+  BrowserAccessibility* GetActiveDescendant(BrowserAccessibility* node) const;
 
   // Returns true if native focus is anywhere in this WebContents or not.
   bool NativeViewHasFocus();
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index e0a751e3..c5e2399f 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -15,6 +15,12 @@
 
 namespace content {
 
+namespace {
+// The maximum number of TYPE_WINDOW_CONTENT_CHANGED events to fire in one
+// atomic update before we give up and fire it on the root node instead.
+constexpr int kMaxContentChangedEventsToFire = 5;
+}  // namespace
+
 // static
 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
     const ui::AXTreeUpdate& initial_tree,
@@ -195,9 +201,27 @@
 
   // Always send AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED to notify
   // the Android system that the accessibility hierarchy rooted at this
-  // node has changed.
-  if (event_type != ui::AXEventGenerator::Event::SUBTREE_CREATED)
-    wcax->HandleContentChanged(android_node->unique_id());
+  // node has changed. However, if there are a large number of changes
+  // it's too expensive to fire all of them, so we just fire one
+  // on the root instead.
+  if (event_type != ui::AXEventGenerator::Event::SUBTREE_CREATED) {
+    content_changed_events_++;
+    if (content_changed_events_ < kMaxContentChangedEventsToFire) {
+      // If it's less than the max event count, fire the event on the specific
+      // node that changed.
+      wcax->HandleContentChanged(android_node->unique_id());
+    } else if (content_changed_events_ == kMaxContentChangedEventsToFire) {
+      // If it's equal to the max event count, fire the event on the
+      // root instead.
+      BrowserAccessibilityManager* root_manager = GetRootManager();
+      if (root_manager) {
+        auto* root_node =
+            static_cast<BrowserAccessibilityAndroid*>(root_manager->GetRoot());
+        if (root_node)
+          wcax->HandleContentChanged(root_node->unique_id());
+      }
+    }
+  }
 
   switch (event_type) {
     case ui::AXEventGenerator::Event::ALERT: {
@@ -499,6 +523,9 @@
     ui::AXTree* tree,
     bool root_changed,
     const std::vector<ui::AXTreeObserver::Change>& changes) {
+  // Reset this every time we get an atomic update.
+  content_changed_events_ = 0;
+
   BrowserAccessibilityManager::OnAtomicUpdateFinished(tree, root_changed,
                                                       changes);
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.h b/content/browser/accessibility/browser_accessibility_manager_android.h
index 2a17563..f6e18fa 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.h
+++ b/content/browser/accessibility/browser_accessibility_manager_android.h
@@ -134,6 +134,10 @@
   // See docs for set_prune_tree_for_screen_reader, above.
   bool prune_tree_for_screen_reader_;
 
+  // A count of the number of TYPE_WINDOW_CONTENT_CHANGED events we've
+  // fired during a single atomic update.
+  int content_changed_events_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerAndroid);
 };
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index 79369535..4f5c9dd0 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -72,10 +72,6 @@
 
 BrowserAccessibility* BrowserAccessibilityManagerMac::GetFocus() const {
   BrowserAccessibility* focus = BrowserAccessibilityManager::GetFocus();
-  if (!focus)
-    return nullptr;
-
-  // Otherwise, follow the active descendant.
   return GetActiveDescendant(focus);
 }
 
diff --git a/content/browser/font_access/font_access_manager_impl.cc b/content/browser/font_access/font_access_manager_impl.cc
index b914894..107002e 100644
--- a/content/browser/font_access/font_access_manager_impl.cc
+++ b/content/browser/font_access/font_access_manager_impl.cc
@@ -13,7 +13,9 @@
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/font_access_delegate.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_client.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/font_access/font_enumeration_table.pb.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h"
@@ -106,6 +108,49 @@
 #endif
 }
 
+void FontAccessManagerImpl::ChooseLocalFonts(
+    ChooseLocalFontsCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+#if !defined(PLATFORM_HAS_LOCAL_FONT_ENUMERATION_IMPL)
+  std::move(callback).Run(blink::mojom::FontEnumerationStatus::kUnimplemented,
+                          {});
+#else
+  const BindingContext& context = receivers_.current_context();
+
+  RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(context.frame_id);
+  if (rfh == nullptr) {
+    std::move(callback).Run(
+        blink::mojom::FontEnumerationStatus::kUnexpectedError, {});
+    return;
+  }
+
+  // Page Visibility is required for the API to function at all.
+  if (rfh->visibility() == blink::mojom::FrameVisibility::kNotRendered) {
+    std::move(callback).Run(blink::mojom::FontEnumerationStatus::kNotVisible,
+                            {});
+    return;
+  }
+
+  // Transient User Activation required before showing the chooser.
+  // This action will consume it.
+  if (!rfh->HasTransientUserActivation()) {
+    std::move(callback).Run(
+        blink::mojom::FontEnumerationStatus::kNeedsUserActivation, {});
+    return;
+  }
+  rfh->frame_tree_node()->UpdateUserActivationState(
+      blink::mojom::UserActivationUpdateType::kConsumeTransientActivation,
+      blink::mojom::UserActivationNotificationType::kNone);
+
+  FontAccessDelegate* delegate =
+      GetContentClient()->browser()->GetFontAccessDelegate();
+  choosers_[context.frame_id] = delegate->RunChooser(
+      rfh, base::BindOnce(&FontAccessManagerImpl::DidChooseLocalFonts,
+                          base::Unretained(this), std::move(callback)));
+#endif
+}
+
 void FontAccessManagerImpl::FindAllFonts(FindAllFontsCallback callback) {
 #if !defined(PLATFORM_HAS_LOCAL_FONT_ENUMERATION_IMPL)
   std::move(callback).Run(blink::mojom::FontEnumerationStatus::kUnimplemented,
@@ -188,4 +233,18 @@
   std::move(callback).Run(status, std::move(data));
 }
 
+void FontAccessManagerImpl::DidChooseLocalFonts(
+    ChooseLocalFontsCallback callback,
+    blink::mojom::FontEnumerationStatus status,
+    std::vector<blink::mojom::FontMetadataPtr> fonts) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // The chooser has fulfilled its purpose. It's safe to dispose of it.
+  const BindingContext& context = receivers_.current_context();
+  int erased = choosers_.erase(context.frame_id);
+  DCHECK(erased == 1);
+
+  std::move(callback).Run(std::move(status), std::move(fonts));
+}
+
 }  // namespace content
diff --git a/content/browser/font_access/font_access_manager_impl.h b/content/browser/font_access/font_access_manager_impl.h
index 6d39e3d..104f9478 100644
--- a/content/browser/font_access/font_access_manager_impl.h
+++ b/content/browser/font_access/font_access_manager_impl.h
@@ -7,6 +7,7 @@
 
 #include "base/sequence_checker.h"
 #include "content/common/content_export.h"
+#include "content/public/browser/font_access_chooser.h"
 #include "content/public/browser/font_access_context.h"
 #include "content/public/browser/global_routing_id.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -15,6 +16,28 @@
 
 namespace content {
 
+// The ownership hierarchy for this class is:
+//
+// StoragePartitionImpl (1) <- (1) FontAccessManagerImpl
+//
+// FontAccessManagerImpl (1) <- (*) BindingContext
+// FontAccessManagerImpl (1) <- (*) FontAccessChooser
+//
+// BindingContext (1) <- (1) GlobalFrameRoutingId
+// GlobalFrameRoutingId (1) <-- (1) FontAccessChooser
+//
+// Legend:
+//
+// <- : owns
+// <-- : corresponds to
+// (N) : N is a number or *, which denotes zero or more
+//
+// In English:
+// * There's one FontAccessManagerImpl per StoragePartitionImpl
+// * Frames are bound to FontAccessManangerImpl via a BindingContext
+// * The FontAccessManagerImpl owns the lifetimes of FontAccessChoosers
+// * There is one FontAccessChooser for each Frame via its GlobalFrameRoutingId,
+//   obtained from a corresponding BindingContext
 class CONTENT_EXPORT FontAccessManagerImpl
     : public blink::mojom::FontAccessManager,
       public FontAccessContext {
@@ -40,6 +63,7 @@
 
   // blink.mojom.FontAccessManager:
   void EnumerateLocalFonts(EnumerateLocalFontsCallback callback) override;
+  void ChooseLocalFonts(ChooseLocalFontsCallback callback) override;
 
   // content::FontAccessContext:
   void FindAllFonts(FindAllFontsCallback callback) override;
@@ -54,6 +78,9 @@
   void DidFindAllFonts(FindAllFontsCallback callback,
                        blink::mojom::FontEnumerationStatus,
                        base::ReadOnlySharedMemoryRegion);
+  void DidChooseLocalFonts(ChooseLocalFontsCallback callback,
+                           blink::mojom::FontEnumerationStatus status,
+                           std::vector<blink::mojom::FontMetadataPtr> fonts);
 
   // Registered clients.
   mojo::ReceiverSet<blink::mojom::FontAccessManager, BindingContext> receivers_;
@@ -63,6 +90,9 @@
 
   bool skip_privacy_checks_for_testing_ = false;
 
+  // Here to keep the choosers alive for the user to interact with.
+  std::map<GlobalFrameRoutingId, std::unique_ptr<FontAccessChooser>> choosers_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/content/browser/speech/tts_controller_impl.cc b/content/browser/speech/tts_controller_impl.cc
index 9c190bd..8715e8e 100644
--- a/content/browser/speech/tts_controller_impl.cc
+++ b/content/browser/speech/tts_controller_impl.cc
@@ -129,11 +129,15 @@
     return;
   }
 
-  // If the TTS platform is still loading, queue or flush the utterance. The
-  // utterances can be sent to platform specific implementation or to the
-  // engine implementation. Every utterances are postponed until the platform
-  // specific implementation is loaded to avoid racy behaviors.
-  if (TtsPlatformLoading()) {
+  // If the TTS platform or tts engine delegate is still loading or
+  // initializing, queue or flush the utterance. The utterances can be sent to
+  // platform specific implementation or to the engine implementation. Every
+  // utterances are postponed until the platform specific implementation and
+  // built in tts engine are loaded to avoid races where the utterance gets
+  // dropped unexpectedly.
+  if (TtsPlatformLoading() ||
+      (engine_delegate_ && !engine_delegate_->IsBuiltInTtsEngineInitialized(
+                               utterance->GetBrowserContext()))) {
     if (utterance->GetShouldClearQueue())
       ClearUtteranceQueue(true);
 
diff --git a/content/browser/speech/tts_controller_unittest.cc b/content/browser/speech/tts_controller_unittest.cc
index 725d2c0d..31dd5bc 100644
--- a/content/browser/speech/tts_controller_unittest.cc
+++ b/content/browser/speech/tts_controller_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "content/browser/speech/tts_utterance_impl.h"
 #include "content/public/browser/tts_platform.h"
@@ -107,6 +108,45 @@
   base::OnceCallback<void(bool)> did_start_speaking_callback_;
 };
 
+class MockTtsEngineDelegate : public TtsEngineDelegate {
+ public:
+  int utterance_id() { return utterance_id_; }
+
+  void set_is_built_in_tts_engine_initialized(bool value) {
+    is_built_in_tts_engine_initialized_ = value;
+  }
+
+  void set_voices(const std::vector<VoiceData>& voices) { voices_ = voices; }
+
+  // TtsEngineDelegate:
+  void Speak(TtsUtterance* utterance, const VoiceData& voice) override {
+    utterance_id_ = utterance->GetId();
+  }
+
+  bool LoadBuiltInTtsEngine(BrowserContext* browser_context) override {
+    return true;
+  }
+
+  bool IsBuiltInTtsEngineInitialized(BrowserContext* browser_context) override {
+    return is_built_in_tts_engine_initialized_;
+  }
+
+  void GetVoices(BrowserContext* browser_context,
+                 std::vector<VoiceData>* out_voices) override {
+    *out_voices = voices_;
+  }
+
+  // Unused (TtsEngineDelegate:)
+  void Stop(TtsUtterance* utterance) override {}
+  void Pause(TtsUtterance* utterance) override {}
+  void Resume(TtsUtterance* utterance) override {}
+
+ private:
+  bool is_built_in_tts_engine_initialized_ = true;
+  int utterance_id_ = -1;
+  std::vector<VoiceData> voices_;
+};
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 class MockTtsControllerDelegate : public TtsControllerDelegate {
  public:
@@ -173,6 +213,11 @@
     platform_impl_ = std::make_unique<MockTtsPlatformImpl>(controller_.get());
     browser_context_ = std::make_unique<TestBrowserContext>();
     controller()->SetTtsPlatform(platform_impl_.get());
+#if !defined(OS_ANDROID)
+    // TtsEngineDelegate isn't set for Android in ChromeContentBrowserClient
+    // since it has no extensions.
+    controller()->SetTtsEngineDelegate(&engine_delegate_);
+#endif  // !defined(OS_ANDROID)
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     controller()->SetTtsControllerDelegateForTesting(&delegate_);
 #endif
@@ -187,6 +232,7 @@
   MockTtsPlatformImpl* platform_impl() { return platform_impl_.get(); }
   TestTtsControllerImpl* controller() { return controller_.get(); }
   TestBrowserContext* browser_context() { return browser_context_.get(); }
+  MockTtsEngineDelegate* engine_delegate() { return &engine_delegate_; }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   MockTtsControllerDelegate* delegate() { return &delegate_; }
@@ -225,6 +271,7 @@
   std::unique_ptr<TestTtsControllerImpl> controller_;
   std::unique_ptr<MockTtsPlatformImpl> platform_impl_;
   std::unique_ptr<TestBrowserContext> browser_context_;
+  MockTtsEngineDelegate engine_delegate_;
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   MockTtsControllerDelegate delegate_;
 #endif
@@ -791,7 +838,7 @@
   EXPECT_EQ(0, platform_impl()->stop_speaking_called());
 }
 
-TEST_F(TtsControllerTest, SpeakWhenLoading) {
+TEST_F(TtsControllerTest, SpeakWhenLoadingPlatformImpl) {
   platform_impl()->SetPlatformImplInitialized(false);
 
   std::unique_ptr<WebContents> web_contents = CreateWebContents();
@@ -821,4 +868,47 @@
   EXPECT_FALSE(controller()->IsSpeaking());
 }
 
+#if !defined(OS_ANDROID)
+TEST_F(TtsControllerTest, SpeakWhenLoadingBuiltInEngine) {
+  engine_delegate()->set_is_built_in_tts_engine_initialized(false);
+
+  std::vector<VoiceData> voices;
+  VoiceData voice_data;
+  voice_data.engine_id = "x";
+  voice_data.events.insert(TTS_EVENT_START);
+  voice_data.events.insert(TTS_EVENT_END);
+  voices.push_back(voice_data);
+  engine_delegate()->set_voices(voices);
+
+  std::unique_ptr<WebContents> web_contents = CreateWebContents();
+  std::unique_ptr<TtsUtteranceImpl> utterance =
+      CreateUtteranceImpl(web_contents.get());
+  utterance->SetShouldClearQueue(false);
+
+  // Speak an utterance while the built in engine is initializing, the utterance
+  // should be queued.
+  controller()->SpeakOrEnqueue(std::move(utterance));
+  EXPECT_FALSE(IsUtteranceListEmpty());
+  EXPECT_FALSE(TtsControllerCurrentUtterance());
+
+  // Simulate the completion of the initialisation.
+  engine_delegate()->set_is_built_in_tts_engine_initialized(true);
+  controller()->VoicesChanged();
+
+  int utterance_id = engine_delegate()->utterance_id();
+  controller()->OnTtsEvent(utterance_id, content::TTS_EVENT_START, 0, 0,
+                           std::string());
+  EXPECT_TRUE(IsUtteranceListEmpty());
+  EXPECT_TRUE(TtsControllerCurrentUtterance());
+  EXPECT_TRUE(controller()->IsSpeaking());
+
+  // Complete the playing utterance.
+  controller()->OnTtsEvent(utterance_id, content::TTS_EVENT_END, 0, 0,
+                           std::string());
+  EXPECT_TRUE(IsUtteranceListEmpty());
+  EXPECT_FALSE(TtsControllerCurrentUtterance());
+  EXPECT_FALSE(controller()->IsSpeaking());
+}
+#endif  // !defined(OS_ANDROID)
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 3587e6a6..718f4c2 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -177,6 +177,7 @@
 #include "ui/aura/window.h"
 #endif
 #include "ui/base/layout.h"
+#include "ui/base/pointer/pointer_device.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/display/screen.h"
@@ -2574,8 +2575,8 @@
         ui::GetAvailablePointerAndHoverTypes();
     prefs->primary_pointer_type = static_cast<blink::mojom::PointerType>(
         ui::GetPrimaryPointerType(prefs->available_pointer_types));
-    prefs->primary_hover_type =
-        ui::GetPrimaryHoverType(prefs->available_hover_types);
+    prefs->primary_hover_type = static_cast<blink::mojom::HoverType>(
+        ui::GetPrimaryHoverType(prefs->available_hover_types));
 
     prefs->pointer_events_max_touch_points = ui::MaxTouchPoints();
 
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 4c747ea..c6868a6 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -6330,6 +6330,7 @@
   auto discovery = std::make_unique<device::cablev2::Discovery>(
       network_context_.get(), qr_generator_key_,
       /*pairings=*/std::vector<std::unique_ptr<device::cablev2::Pairing>>(),
+      /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
       GetPairingCallback());
   auto* const discovery_ptr = discovery.get();
 
@@ -6354,6 +6355,7 @@
   auto discovery = std::make_unique<device::cablev2::Discovery>(
       network_context_.get(), qr_generator_key_,
       /*pairings=*/std::vector<std::unique_ptr<device::cablev2::Pairing>>(),
+      /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
       GetPairingCallback());
   auto* discovery_ptr = discovery.get();
 
@@ -6375,6 +6377,7 @@
   // Now do a pairing-based exchange.
   discovery = std::make_unique<device::cablev2::Discovery>(
       network_context_.get(), qr_generator_key_, std::move(pairings_),
+      /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
       GetPairingCallback());
   discovery_ptr = discovery.get();
 
@@ -6429,6 +6432,7 @@
   auto network_context = device::cablev2::NewMockTunnelServer(base::nullopt);
   auto discovery = std::make_unique<device::cablev2::Discovery>(
       network_context.get(), qr_generator_key_, std::move(pairings),
+      /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
       GetPairingCallback());
 
   AuthenticatorEnvironmentImpl::GetInstance()
diff --git a/content/browser/webauth/authenticator_mojom_traits.cc b/content/browser/webauth/authenticator_mojom_traits.cc
index e4d78b8..01c78d69 100644
--- a/content/browser/webauth/authenticator_mojom_traits.cc
+++ b/content/browser/webauth/authenticator_mojom_traits.cc
@@ -310,16 +310,41 @@
                   device::CableDiscoveryData>::
     Read(blink::mojom::CableAuthenticationDataView data,
          device::CableDiscoveryData* out) {
-  if (data.version() != 1) {
-    return false;
+  switch (data.version()) {
+    case 1: {
+      base::Optional<std::array<uint8_t, 16>> client_eid, authenticator_eid;
+      base::Optional<std::array<uint8_t, 32>> session_pre_key;
+      if (!data.ReadClientEid(&client_eid) || !client_eid ||
+          !data.ReadAuthenticatorEid(&authenticator_eid) ||
+          !authenticator_eid || !data.ReadSessionPreKey(&session_pre_key) ||
+          !session_pre_key) {
+        return false;
+      }
+
+      out->version = device::CableDiscoveryData::Version::V1;
+      out->v1.emplace();
+      out->v1->client_eid = *client_eid;
+      out->v1->authenticator_eid = *authenticator_eid;
+      out->v1->session_pre_key = *session_pre_key;
+      break;
+    }
+
+    case 2: {
+      base::Optional<std::vector<uint8_t>> server_link_data;
+      if (!data.ReadServerLinkData(&server_link_data) || !server_link_data) {
+        return false;
+      }
+
+      out->version = device::CableDiscoveryData::Version::V2;
+      out->v2.emplace(std::move(*server_link_data));
+
+      break;
+    }
+
+    default:
+      return false;
   }
-  out->version = device::CableDiscoveryData::Version::V1;
-  out->v1.emplace();
-  if (!data.ReadClientEid(&out->v1->client_eid) ||
-      !data.ReadAuthenticatorEid(&out->v1->authenticator_eid) ||
-      !data.ReadSessionPreKey(&out->v1->session_pre_key)) {
-    return false;
-  }
+
   return true;
 }
 
diff --git a/content/browser/webauth/authenticator_mojom_traits.h b/content/browser/webauth/authenticator_mojom_traits.h
index f22a501..bbd07beb 100644
--- a/content/browser/webauth/authenticator_mojom_traits.h
+++ b/content/browser/webauth/authenticator_mojom_traits.h
@@ -203,26 +203,47 @@
     StructTraits<blink::mojom::CableAuthenticationDataView,
                  device::CableDiscoveryData> {
   static uint8_t version(const device::CableDiscoveryData& in) {
-    CHECK_EQ(device::CableDiscoveryData::Version::V1, in.version);
-    return 1;
+    switch (in.version) {
+      case device::CableDiscoveryData::Version::V1:
+        return 1;
+      case device::CableDiscoveryData::Version::V2:
+        return 2;
+      case device::CableDiscoveryData::Version::INVALID:
+        CHECK(false);
+        return 0;
+    }
   }
 
-  static const device::CableEidArray& client_eid(
+  static base::Optional<device::CableEidArray> client_eid(
       const device::CableDiscoveryData& in) {
-    CHECK_EQ(device::CableDiscoveryData::Version::V1, in.version);
-    return in.v1->client_eid;
+    if (in.version == device::CableDiscoveryData::Version::V1) {
+      return in.v1->client_eid;
+    }
+    return base::nullopt;
   }
 
-  static const device::CableEidArray& authenticator_eid(
+  static const base::Optional<device::CableEidArray> authenticator_eid(
       const device::CableDiscoveryData& in) {
-    CHECK_EQ(device::CableDiscoveryData::Version::V1, in.version);
-    return in.v1->authenticator_eid;
+    if (in.version == device::CableDiscoveryData::Version::V1) {
+      return in.v1->authenticator_eid;
+    }
+    return base::nullopt;
   }
 
-  static const device::CableSessionPreKeyArray& session_pre_key(
+  static const base::Optional<device::CableSessionPreKeyArray> session_pre_key(
       const device::CableDiscoveryData& in) {
-    CHECK_EQ(device::CableDiscoveryData::Version::V1, in.version);
-    return in.v1->session_pre_key;
+    if (in.version == device::CableDiscoveryData::Version::V1) {
+      return in.v1->session_pre_key;
+    }
+    return base::nullopt;
+  }
+
+  static const base::Optional<std::vector<uint8_t>> server_link_data(
+      const device::CableDiscoveryData& in) {
+    if (in.version == device::CableDiscoveryData::Version::V2) {
+      return *in.v2;
+    }
+    return base::nullopt;
   }
 
   static bool Read(blink::mojom::CableAuthenticationDataView data,
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 8132c83..87bd663 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -153,7 +153,9 @@
     "file_select_listener.h",
     "file_url_loader.h",
     "focused_node_details.h",
+    "font_access_chooser.h",
     "font_access_context.h",
+    "font_access_delegate.h",
     "font_list_async.h",
     "frame_accept_header.cc",
     "frame_accept_header.h",
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 418501e..281a1aa0 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -891,6 +891,10 @@
   return nullptr;
 }
 
+FontAccessDelegate* ContentBrowserClient::GetFontAccessDelegate() {
+  return nullptr;
+}
+
 bool ContentBrowserClient::ShowPaymentHandlerWindow(
     content::BrowserContext* browser_context,
     const GURL& url,
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index a4dee7b..d4b02c6 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -196,6 +196,7 @@
 class ControllerPresentationServiceDelegate;
 class DevToolsManagerDelegate;
 class FeatureObserverClient;
+class FontAccessDelegate;
 class HidDelegate;
 class LoginDelegate;
 class MediaObserver;
@@ -1578,6 +1579,10 @@
   // Allows the embedder to provide an implementation of the Web Bluetooth API.
   virtual BluetoothDelegate* GetBluetoothDelegate();
 
+  // Allows the embedder to provide an implementation of the Local Font Access
+  // API.
+  virtual FontAccessDelegate* GetFontAccessDelegate();
+
   // Attempt to open the Payment Handler window inside its corresponding
   // PaymentRequest UI surface. Returns true if the ContentBrowserClient
   // implementation supports this operation (desktop Chrome) or false otherwise.
diff --git a/content/public/browser/font_access_chooser.h b/content/public/browser/font_access_chooser.h
new file mode 100644
index 0000000..8e77f3e
--- /dev/null
+++ b/content/public/browser/font_access_chooser.h
@@ -0,0 +1,29 @@
+// 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 CONTENT_PUBLIC_BROWSER_FONT_ACCESS_CHOOSER_H_
+#define CONTENT_PUBLIC_BROWSER_FONT_ACCESS_CHOOSER_H_
+
+#include "base/callback_forward.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/render_frame_host.h"
+#include "third_party/blink/public/mojom/font_access/font_access.mojom.h"
+
+namespace content {
+
+class CONTENT_EXPORT FontAccessChooser {
+ public:
+  using Callback =
+      base::OnceCallback<void(blink::mojom::FontEnumerationStatus,
+                              std::vector<blink::mojom::FontMetadataPtr>)>;
+  FontAccessChooser() = default;
+  virtual ~FontAccessChooser() = default;
+
+  FontAccessChooser(const FontAccessChooser&) = delete;
+  FontAccessChooser operator=(const FontAccessChooser&) = delete;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_FONT_ACCESS_CHOOSER_H_
diff --git a/content/public/browser/font_access_delegate.h b/content/public/browser/font_access_delegate.h
new file mode 100644
index 0000000..37d9e66
--- /dev/null
+++ b/content/public/browser/font_access_delegate.h
@@ -0,0 +1,26 @@
+// 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 CONTENT_PUBLIC_BROWSER_FONT_ACCESS_DELEGATE_H_
+#define CONTENT_PUBLIC_BROWSER_FONT_ACCESS_DELEGATE_H_
+
+#include "content/common/content_export.h"
+#include "content/public/browser/font_access_chooser.h"
+
+namespace content {
+
+class RenderFrameHost;
+
+class CONTENT_EXPORT FontAccessDelegate {
+ public:
+  virtual ~FontAccessDelegate() = default;
+
+  virtual std::unique_ptr<FontAccessChooser> RunChooser(
+      RenderFrameHost* frame,
+      FontAccessChooser::Callback callback) = 0;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_FONT_ACCESS_DELEGATE_H_
diff --git a/content/public/browser/tts_controller.h b/content/public/browser/tts_controller.h
index 04934c6..4acb517 100644
--- a/content/public/browser/tts_controller.h
+++ b/content/public/browser/tts_controller.h
@@ -67,6 +67,10 @@
 
   // Load the built-in TTS engine.
   virtual bool LoadBuiltInTtsEngine(BrowserContext* browser_context) = 0;
+
+  // Returns whether the built in engine is initialized.
+  virtual bool IsBuiltInTtsEngineInitialized(
+      BrowserContext* browser_context) = 0;
 };
 
 // Class that wants to be notified when the set of
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 112814b..6eb6475c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -432,7 +432,7 @@
 // Note that the origin policy-based variant of origin isolation is controlled
 // by kOriginPolicy, instead.
 const base::Feature kOriginIsolationHeader{"OriginIsolationHeader",
-                                           base::FEATURE_ENABLED_BY_DEFAULT};
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Origin Policy. See https://crbug.com/751996
 const base::Feature kOriginPolicy{"OriginPolicy",
diff --git a/content/test/fuzzer/OWNERS b/content/test/fuzzer/OWNERS
index 054ad6e..bd29a9e 100644
--- a/content/test/fuzzer/OWNERS
+++ b/content/test/fuzzer/OWNERS
@@ -1,3 +1,2 @@
-mmoroz@chromium.org
 ochang@chromium.org
 metzman@chromium.org
diff --git a/device/fido/cable/cable_discovery_data.cc b/device/fido/cable/cable_discovery_data.cc
index 71dffdc4..b5da2a06 100644
--- a/device/fido/cable/cable_discovery_data.cc
+++ b/device/fido/cable/cable_discovery_data.cc
@@ -54,6 +54,9 @@
              v1->authenticator_eid == other.v1->authenticator_eid &&
              v1->session_pre_key == other.v1->session_pre_key;
 
+    case CableDiscoveryData::Version::V2:
+      return v2.value() == other.v2.value();
+
     case CableDiscoveryData::Version::INVALID:
       CHECK(false);
       return false;
diff --git a/device/fido/cable/cable_discovery_data.h b/device/fido/cable/cable_discovery_data.h
index e494672..fcee5db4 100644
--- a/device/fido/cable/cable_discovery_data.h
+++ b/device/fido/cable/cable_discovery_data.h
@@ -51,6 +51,7 @@
   enum class Version {
     INVALID,
     V1,
+    V2,
   };
 
   CableDiscoveryData(Version version,
@@ -79,6 +80,10 @@
     CableSessionPreKeyArray session_pre_key;
   };
   base::Optional<V1Data> v1;
+
+  // For caBLEv2, the payload is the server-link data provided in the extension
+  // as the "sessionPreKey".
+  base::Optional<std::vector<uint8_t>> v2;
 };
 
 namespace cablev2 {
diff --git a/device/fido/cable/fido_cable_discovery.cc b/device/fido/cable/fido_cable_discovery.cc
index 219f89f..1f05454c 100644
--- a/device/fido/cable/fido_cable_discovery.cc
+++ b/device/fido/cable/fido_cable_discovery.cc
@@ -201,6 +201,7 @@
           device, nonce, discovery_data.v1->session_pre_key);
     }
 
+    case CableDiscoveryData::Version::V2:
     case CableDiscoveryData::Version::INVALID:
       CHECK(false);
       return nullptr;
diff --git a/device/fido/cable/v2_discovery.cc b/device/fido/cable/v2_discovery.cc
index 7fb8d6a..a7cf4b9 100644
--- a/device/fido/cable/v2_discovery.cc
+++ b/device/fido/cable/v2_discovery.cc
@@ -19,21 +19,14 @@
 Discovery::Discovery(network::mojom::NetworkContext* network_context,
                      base::span<const uint8_t, kQRKeySize> qr_generator_key,
                      std::vector<std::unique_ptr<Pairing>> pairings,
+                     const std::vector<CableDiscoveryData>& extension_contents,
                      base::Optional<base::RepeatingCallback<void(PairingEvent)>>
                          pairing_callback)
     : FidoDeviceDiscovery(
           FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy),
       network_context_(network_context),
-      local_identity_seed_(fido_parsing_utils::Materialize(
-          base::span<const uint8_t, kQRSeedSize>(qr_generator_key.data(),
-                                                 kQRSeedSize))),
-      qr_secret_(fido_parsing_utils::Materialize(
-          base::span<const uint8_t, kQRSecretSize>(
-              qr_generator_key.data() + kQRSeedSize,
-              kQRSecretSize))),
-      eid_key_(Derive<EXTENT(eid_key_)>(qr_secret_,
-                                        base::span<const uint8_t>(),
-                                        DerivedValueType::kEIDKey)),
+      qr_keys_(KeysFromQRGeneratorKey(qr_generator_key)),
+      extension_keys_(KeysFromExtension(extension_contents)),
       pairings_(std::move(pairings)),
       pairing_callback_(std::move(pairing_callback)) {
   static_assert(EXTENT(qr_generator_key) == kQRSecretSize + kQRSeedSize, "");
@@ -93,16 +86,30 @@
   }
 
   // Check whether the EID matches a QR code.
-  base::Optional<CableEidArray> plaintext = eid::Decrypt(advert, eid_key_);
+  base::Optional<CableEidArray> plaintext =
+      eid::Decrypt(advert, qr_keys_.eid_key);
   if (plaintext) {
     FIDO_LOG(DEBUG) << "  (" << base::HexEncode(advert) << " matches QR code)";
     AddDevice(std::make_unique<cablev2::FidoTunnelDevice>(
         network_context_,
         base::BindOnce(&Discovery::AddPairing, weak_factory_.GetWeakPtr()),
-        qr_secret_, local_identity_seed_, *plaintext));
+        qr_keys_.qr_secret, qr_keys_.local_identity_seed, *plaintext));
     return;
   }
 
+  // Check whether the EID matches the extension.
+  if (extension_keys_) {
+    plaintext = eid::Decrypt(advert, extension_keys_->eid_key);
+    if (plaintext) {
+      FIDO_LOG(DEBUG) << "  (" << base::HexEncode(advert)
+                      << " matches extension)";
+      AddDevice(std::make_unique<cablev2::FidoTunnelDevice>(
+          network_context_, base::DoNothing(), extension_keys_->qr_secret,
+          extension_keys_->local_identity_seed, *plaintext));
+      return;
+    }
+  }
+
   FIDO_LOG(DEBUG) << "  (" << base::HexEncode(advert) << ": no v2 match)";
 }
 
@@ -123,5 +130,39 @@
   pairing_callback_->Run(std::move(peer_public_key_x962));
 }
 
+// static
+Discovery::UnpairedKeys Discovery::KeysFromQRGeneratorKey(
+    base::span<const uint8_t, kQRKeySize> qr_generator_key) {
+  UnpairedKeys ret;
+  static_assert(EXTENT(qr_generator_key) == kQRSeedSize + kQRSecretSize, "");
+  ret.local_identity_seed = fido_parsing_utils::Materialize(
+      qr_generator_key.subspan<0, kQRSeedSize>());
+  ret.qr_secret = fido_parsing_utils::Materialize(
+      qr_generator_key.subspan<kQRSeedSize, kQRSecretSize>());
+  ret.eid_key = Derive<EXTENT(ret.eid_key)>(
+      ret.qr_secret, base::span<const uint8_t>(), DerivedValueType::kEIDKey);
+  return ret;
+}
+
+// static
+base::Optional<Discovery::UnpairedKeys> Discovery::KeysFromExtension(
+    const std::vector<CableDiscoveryData>& extension_contents) {
+  for (auto const& data : extension_contents) {
+    if (data.version != CableDiscoveryData::Version::V2) {
+      continue;
+    }
+
+    if (data.v2->size() != kQRKeySize) {
+      FIDO_LOG(ERROR) << "caBLEv2 extension has incorrect length ("
+                      << data.v2->size() << ")";
+      continue;
+    }
+
+    return KeysFromQRGeneratorKey(base::make_span<kQRKeySize>(*data.v2));
+  }
+
+  return base::nullopt;
+}
+
 }  // namespace cablev2
 }  // namespace device
diff --git a/device/fido/cable/v2_discovery.h b/device/fido/cable/v2_discovery.h
index e2c48510..c0042c9 100644
--- a/device/fido/cable/v2_discovery.h
+++ b/device/fido/cable/v2_discovery.h
@@ -36,6 +36,7 @@
   Discovery(network::mojom::NetworkContext* network_context,
             base::span<const uint8_t, kQRKeySize> qr_generator_key,
             std::vector<std::unique_ptr<Pairing>> pairings,
+            const std::vector<CableDiscoveryData>& extension_contents,
             // pairing_callback will be called when a QR-initiated connection
             // receives pairing information from the peer, or when an existing
             // pairing is found to be invalid.
@@ -52,14 +53,25 @@
   void OnBLEAdvertSeen(const std::array<uint8_t, kAdvertSize>& advert) override;
 
  private:
+  // UnpairedKeys are keys that are conveyed by QR code or that come from the
+  // server, i.e. keys that enable interactions with unpaired phones.
+  struct UnpairedKeys {
+    std::array<uint8_t, kQRSeedSize> local_identity_seed;
+    std::array<uint8_t, kQRSecretSize> qr_secret;
+    std::array<uint8_t, kEIDKeySize> eid_key;
+  };
+
   void AddPairing(std::unique_ptr<Pairing> pairing);
   void PairingIsInvalid(
       std::array<uint8_t, kP256X962Length> peer_public_key_x962);
+  static UnpairedKeys KeysFromQRGeneratorKey(
+      base::span<const uint8_t, kQRKeySize> qr_generator_key);
+  static base::Optional<UnpairedKeys> KeysFromExtension(
+      const std::vector<CableDiscoveryData>& extension_contents);
 
   network::mojom::NetworkContext* const network_context_;
-  const std::array<uint8_t, kQRSeedSize> local_identity_seed_;
-  const std::array<uint8_t, kQRSecretSize> qr_secret_;
-  const std::array<uint8_t, kEIDKeySize> eid_key_;
+  const UnpairedKeys qr_keys_;
+  const base::Optional<UnpairedKeys> extension_keys_;
   std::vector<std::unique_ptr<Pairing>> pairings_;
   const base::Optional<base::RepeatingCallback<void(PairingEvent)>>
       pairing_callback_;
diff --git a/device/fido/features.cc b/device/fido/features.cc
index 91a1e99..cb933dfc 100644
--- a/device/fido/features.cc
+++ b/device/fido/features.cc
@@ -25,6 +25,10 @@
 extern const base::Feature kWebAuthPhoneSupport{
     "WebAuthenticationPhoneSupport", base::FEATURE_DISABLED_BY_DEFAULT};
 
+extern const base::Feature kWebAuthCableExtensionAnywhere{
+    "WebAuthenticationCableExtensionAnywhere",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 extern const base::Feature kWebAuthGetAssertionFeaturePolicy{
     "WebAuthenticationGetAssertionFeaturePolicy",
     base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/device/fido/features.h b/device/fido/features.h
index a3c8844..9e7d1d6 100644
--- a/device/fido/features.h
+++ b/device/fido/features.h
@@ -32,6 +32,10 @@
 COMPONENT_EXPORT(DEVICE_FIDO)
 extern const base::Feature kWebAuthPhoneSupport;
 
+// Support the caBLE extension in assertion requests from any origin.
+COMPONENT_EXPORT(DEVICE_FIDO)
+extern const base::Feature kWebAuthCableExtensionAnywhere;
+
 // Enable WebAuthn GetAssertion calls in cross-origin iframes if allowed by
 // Feature Policy.
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/fido/fido_discovery_factory.cc b/device/fido/fido_discovery_factory.cc
index e839a103..24170ed 100644
--- a/device/fido/fido_discovery_factory.cc
+++ b/device/fido/fido_discovery_factory.cc
@@ -52,6 +52,7 @@
         if (qr_generator_key_.has_value()) {
           v2_discovery = std::make_unique<cablev2::Discovery>(
               network_context_, *qr_generator_key_, std::move(v2_pairings_),
+              cable_data_.value_or(std::vector<CableDiscoveryData>()),
               std::move(cable_pairing_callback_));
         }
         std::unique_ptr<FidoDiscoveryBase> v1_discovery =
diff --git a/device/vr/openxr/openxr_interaction_profiles.h b/device/vr/openxr/openxr_interaction_profiles.h
index 6222870..33aa2e4a 100644
--- a/device/vr/openxr/openxr_interaction_profiles.h
+++ b/device/vr/openxr/openxr_interaction_profiles.h
@@ -28,7 +28,7 @@
   kHTCVive = 4,
   kSamsungOdyssey = 5,
   kHPReverbG2 = 6,
-  kHandSelect = 7,
+  kHandSelectGrasp = 7,
   kCount = 8,
 };
 
@@ -119,8 +119,8 @@
 constexpr const char* kHPReverbG2InputProfiles[] = {
     "hp-mixed-reality", "oculus-touch", "generic-trigger-squeeze"};
 
-constexpr const char* kGenericHandSelectInputProfile[] = {
-    "generic-hand-select"};
+constexpr const char* kGenericHandSelectGraspInputProfile[] = {
+    "generic-hand-select-grasp", "generic-hand-select"};
 
 constexpr OpenXrButtonPathMap kMicrosoftMotionControllerButtonPathMaps[] = {
     {OpenXrButtonType::kTrigger,
@@ -279,10 +279,13 @@
      1},
 };
 
-constexpr OpenXrButtonPathMap kGenericHandSelectButtonPathMaps[] = {
+constexpr OpenXrButtonPathMap kGenericHandSelectGraspButtonPathMaps[] = {
     {OpenXrButtonType::kTrigger,
      {{OpenXrButtonActionType::kValue, "/input/select/value"}},
      1},
+    {OpenXrButtonType::kTrigger,
+     {{OpenXrButtonActionType::kValue, "/input/squeeze/value"}},
+     1},
 };
 
 constexpr OpenXrAxisPathMap kMicrosoftMotionControllerAxisPathMaps[] = {
@@ -408,16 +411,16 @@
 
 constexpr OpenXrControllerInteractionProfile
     kHandInteractionMSFTInteractionProfile = {
-        OpenXrInteractionProfileType::kHandSelect,
+        OpenXrInteractionProfileType::kHandSelectGrasp,
         "/interaction_profiles/microsoft/hand_interaction",
         kMSFTHandInteractionExtensionName,
         GamepadMapping::kNone,
-        kGenericHandSelectInputProfile,
-        base::size(kGenericHandSelectInputProfile),
-        kGenericHandSelectButtonPathMaps,
-        base::size(kGenericHandSelectButtonPathMaps),
-        kGenericHandSelectButtonPathMaps,
-        base::size(kGenericHandSelectButtonPathMaps),
+        kGenericHandSelectGraspInputProfile,
+        base::size(kGenericHandSelectGraspInputProfile),
+        kGenericHandSelectGraspButtonPathMaps,
+        base::size(kGenericHandSelectGraspButtonPathMaps),
+        kGenericHandSelectGraspButtonPathMaps,
+        base::size(kGenericHandSelectGraspButtonPathMaps),
         nullptr,
         0};
 
diff --git a/docs/how_to_add_your_feature_flag.md b/docs/how_to_add_your_feature_flag.md
index 4ca8af89..07c7ef8d 100644
--- a/docs/how_to_add_your_feature_flag.md
+++ b/docs/how_to_add_your_feature_flag.md
@@ -22,7 +22,7 @@
 [[2](https://chromium-review.googlesource.com/c/554510/8/content/public/common/content_features.h)]
 2. how to use it
 [[1](https://chromium-review.googlesource.com/c/554510/8/content/common/service_worker/service_worker_utils.cc#153)]
-3. how to wire your new base::Feature to a blink runtime feature[[1](https://chromium.googlesource.com/chromium/src/+/HEAD/content/child/InitializeBlinkFeatures.md)]
+3. how to wire your new base::Feature to a blink runtime feature[[1](https://chromium.googlesource.com/chromium/src/+/master/docs/initialize_blink_features.md)]
 4. how to use it in blink
 [[1](https://chromium-review.googlesource.com/c/554510/8/third_party/blnk/renderere/core/workers/worker_thread.cc)]
 
diff --git a/docs/security/OWNERS b/docs/security/OWNERS
index a862375..57ae6172 100644
--- a/docs/security/OWNERS
+++ b/docs/security/OWNERS
@@ -3,7 +3,6 @@
 dcheng@chromium.org
 estark@chromium.org
 felt@chromium.org
-mmoroz@chromium.org
 nasko@chromium.org
 meacer@chromium.org
 mkwst@chromium.org
@@ -11,4 +10,4 @@
 parisa@chromium.org
 rsesek@chromium.org
 tsepez@chromium.org
-vakh@chromium.org
\ No newline at end of file
+vakh@chromium.org
diff --git a/docs/speed/perf_lab_platforms.md b/docs/speed/perf_lab_platforms.md
index 72c441f..e251c696 100644
--- a/docs/speed/perf_lab_platforms.md
+++ b/docs/speed/perf_lab_platforms.md
@@ -24,7 +24,6 @@
  * [mac-10_12_laptop_low_end-perf](https://ci.chromium.org/p/chrome/builders/ci/mac-10_12_laptop_low_end-perf): MacBook Air, Core i5 1.8 GHz, 8GB RAM, 128GB SSD, HD Graphics.
  * [mac-10_13_laptop_high_end-perf](https://ci.chromium.org/p/chrome/builders/ci/mac-10_13_laptop_high_end-perf): MacBook Pro, Core i7 2.8 GHz, 16GB RAM, 256GB SSD, Radeon 55.
  * [mac-arm_dtk_arm-perf](https://ci.chromium.org/p/chrome/builders/ci/mac-arm_dtk_arm-perf): Mac ARM DTK (ARM Chrome).
- * [mac-arm_dtk_x86-perf](https://ci.chromium.org/p/chrome/builders/ci/mac-arm_dtk_x86-perf): Mac ARM DTK (X86 Chrome).
 
 ## Win
 
diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc
index d43f688..28b058f 100644
--- a/extensions/browser/renderer_startup_helper.cc
+++ b/extensions/browser/renderer_startup_helper.cc
@@ -122,9 +122,8 @@
   // renderers may have content scripts.
   bool is_lock_screen_context =
       client->IsLockScreenContext(process->GetBrowserContext());
-  process->Send(new ExtensionMsg_SetSessionInfo(GetCurrentChannel(),
-                                                GetCurrentFeatureSessionType(),
-                                                is_lock_screen_context));
+  renderer->SetSessionInfo(GetCurrentChannel(), GetCurrentFeatureSessionType(),
+                           is_lock_screen_context);
 
   // Platform apps need to know the system font.
   // TODO(dbeam): this is not the system font in all cases.
diff --git a/extensions/browser/renderer_startup_helper_unittest.cc b/extensions/browser/renderer_startup_helper_unittest.cc
index 6ee2f58..574e672 100644
--- a/extensions/browser/renderer_startup_helper_unittest.cc
+++ b/extensions/browser/renderer_startup_helper_unittest.cc
@@ -51,6 +51,10 @@
     unloaded_extensions_.push_back(extension_id);
   }
 
+  void SetSessionInfo(version_info::Channel channel,
+                      mojom::FeatureSessionType session,
+                      bool is_lock_screen_context) override {}
+
   std::vector<std::string> activated_extensions_;
   std::vector<std::string> unloaded_extensions_;
   mojo::AssociatedReceiverSet<mojom::Renderer> receivers_;
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 78fc44a..0c5e7e9b 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -40,6 +40,7 @@
 
     sources = [
       "mojom/app_window.mojom",
+      "mojom/channel.mojom",
       "mojom/feature_session_type.mojom",
       "mojom/guest_view.mojom",
       "mojom/keep_alive.mojom",
@@ -58,6 +59,18 @@
       "//url/mojom:url_mojom_gurl",
     ]
 
+    cpp_typemaps = [
+      {
+        types = [
+          {
+            mojom = "extensions.mojom.Channel"
+            cpp = "version_info::Channel"
+          },
+        ]
+        traits_headers = [ "//extensions/common/mojom/channel_mojom_traits.h" ]
+        traits_public_deps = [ "//components/version_info:channel" ]
+      },
+    ]
     overridden_deps = [ "//content/public/common:interfaces" ]
 
     component_deps = [ "//content/public/common" ]
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index 12050b6..d1c5f4c 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -703,13 +703,6 @@
                     extensions::PortId /* port_id */,
                     std::string /* error_message */)
 
-// Informs the renderer what channel (dev, beta, stable, etc) and user session
-// type is running.
-IPC_MESSAGE_CONTROL3(ExtensionMsg_SetSessionInfo,
-                     version_info::Channel /* channel */,
-                     extensions::mojom::FeatureSessionType /* session_type */,
-                     bool /* is_lock_screen_context */)
-
 // Notify the renderer that its window has closed.
 IPC_MESSAGE_ROUTED1(ExtensionMsg_AppWindowClosed, bool /* send_onclosed */)
 
diff --git a/extensions/common/mojom/OWNERS b/extensions/common/mojom/OWNERS
index 08850f4..1feb514 100644
--- a/extensions/common/mojom/OWNERS
+++ b/extensions/common/mojom/OWNERS
@@ -1,2 +1,4 @@
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/extensions/common/mojom/channel.mojom b/extensions/common/mojom/channel.mojom
new file mode 100644
index 0000000..e8868d5e
--- /dev/null
+++ b/extensions/common/mojom/channel.mojom
@@ -0,0 +1,14 @@
+// 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 extensions.mojom;
+
+// The possible channels for an installation. Refer to version_info::Channel.
+enum Channel {
+  kUnknown = 0,
+  kCanary = 1,
+  kDev = 2,
+  kBeta = 3,
+  kStable = 4,
+};
diff --git a/extensions/common/mojom/channel_mojom_traits.h b/extensions/common/mojom/channel_mojom_traits.h
new file mode 100644
index 0000000..97a286d
--- /dev/null
+++ b/extensions/common/mojom/channel_mojom_traits.h
@@ -0,0 +1,60 @@
+// 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 EXTENSIONS_COMMON_MOJOM_CHANNEL_MOJOM_TRAITS_H_
+#define EXTENSIONS_COMMON_MOJOM_CHANNEL_MOJOM_TRAITS_H_
+
+#include "components/version_info/channel.h"
+#include "extensions/common/mojom/channel.mojom-shared.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
+
+namespace mojo {
+
+template <>
+struct EnumTraits<extensions::mojom::Channel, version_info::Channel> {
+  static extensions::mojom::Channel ToMojom(version_info::Channel input) {
+    switch (input) {
+      case version_info::Channel::UNKNOWN:
+        return extensions::mojom::Channel::kUnknown;
+      case version_info::Channel::CANARY:
+        return extensions::mojom::Channel::kCanary;
+      case version_info::Channel::DEV:
+        return extensions::mojom::Channel::kDev;
+      case version_info::Channel::BETA:
+        return extensions::mojom::Channel::kBeta;
+      case version_info::Channel::STABLE:
+        return extensions::mojom::Channel::kStable;
+    }
+    NOTREACHED();
+    return extensions::mojom::Channel::kUnknown;
+  }
+
+  static bool FromMojom(extensions::mojom::Channel input,
+                        version_info::Channel* out) {
+    switch (input) {
+      case extensions::mojom::Channel::kUnknown:
+        *out = version_info::Channel::UNKNOWN;
+        return true;
+      case extensions::mojom::Channel::kCanary:
+        *out = version_info::Channel::CANARY;
+        return true;
+      case extensions::mojom::Channel::kDev:
+        *out = version_info::Channel::DEV;
+        return true;
+      case extensions::mojom::Channel::kBeta:
+        *out = version_info::Channel::BETA;
+        return true;
+      case extensions::mojom::Channel::kStable:
+        *out = version_info::Channel::STABLE;
+        return true;
+    }
+    NOTREACHED();
+    *out = version_info::Channel::UNKNOWN;
+    return false;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // EXTENSIONS_COMMON_MOJOM_CHANNEL_MOJOM_TRAITS_H_
diff --git a/extensions/common/mojom/renderer.mojom b/extensions/common/mojom/renderer.mojom
index fe22c10..f5696dbb 100644
--- a/extensions/common/mojom/renderer.mojom
+++ b/extensions/common/mojom/renderer.mojom
@@ -4,6 +4,9 @@
 
 module extensions.mojom;
 
+import "extensions/common/mojom/channel.mojom";
+import "extensions/common/mojom/feature_session_type.mojom";
+
 // This should be used for implementing browser-to-renderer control messages
 // which need to retain FIFO with respect to other mojo interfaces like
 // content.mojom.Renderer.
@@ -20,4 +23,11 @@
 
   // Notifies the renderer that an extension was unloaded in the browser.
   UnloadExtension(string extension_id);
+
+  // Informs the renderer what channel (dev, beta, stable, etc) and user session
+  // type is running. |is_lock_screen_context| represents whether the browser
+  // context is associated with Chrome OS lock screen.
+  SetSessionInfo(Channel channel,
+                 FeatureSessionType session,
+                 bool is_lock_screen_context);
 };
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index be05088..2e3a519 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -872,7 +872,6 @@
   IPC_MESSAGE_HANDLER(ExtensionMsg_Loaded, OnLoaded)
   IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnMessageInvoke)
   IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchEvent, OnDispatchEvent)
-  IPC_MESSAGE_HANDLER(ExtensionMsg_SetSessionInfo, OnSetSessionInfo)
   IPC_MESSAGE_HANDLER(ExtensionMsg_SetScriptingAllowlist,
                       OnSetScriptingAllowlist)
   IPC_MESSAGE_HANDLER(ExtensionMsg_SetSystemFont, OnSetSystemFont)
@@ -1158,9 +1157,9 @@
   }
 }
 
-void Dispatcher::OnSetSessionInfo(version_info::Channel channel,
-                                  mojom::FeatureSessionType session_type,
-                                  bool is_lock_screen_context) {
+void Dispatcher::SetSessionInfo(version_info::Channel channel,
+                                mojom::FeatureSessionType session_type,
+                                bool is_lock_screen_context) {
   SetCurrentChannel(channel);
   SetCurrentFeatureSessionType(session_type);
   script_context_set_->set_is_lock_screen_context(is_lock_screen_context);
diff --git a/extensions/renderer/dispatcher.h b/extensions/renderer/dispatcher.h
index 0d5093c..341ce13 100644
--- a/extensions/renderer/dispatcher.h
+++ b/extensions/renderer/dispatcher.h
@@ -217,6 +217,9 @@
   void ActivateExtension(const std::string& extension_id) override;
   void SetActivityLoggingEnabled(bool enabled) override;
   void UnloadExtension(const std::string& extension_id) override;
+  void SetSessionInfo(version_info::Channel channel,
+                      mojom::FeatureSessionType session_type,
+                      bool lock_screen_context) override;
 
   void OnRendererAssociatedRequest(
       mojo::PendingAssociatedReceiver<mojom::Renderer> receiver);
@@ -240,9 +243,6 @@
                        const base::ListValue& args);
   void OnDispatchEvent(const ExtensionMsg_DispatchEvent_Params& params,
                        const base::ListValue& event_args);
-  void OnSetSessionInfo(version_info::Channel channel,
-                        mojom::FeatureSessionType session_type,
-                        bool lock_screen_context);
   void OnSetScriptingAllowlist(
       const ExtensionsClient::ScriptingAllowlist& extension_ids);
   void OnSetSystemFont(const std::string& font_family,
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 510ab07..0b8ba065 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -1632,7 +1632,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -1679,7 +1679,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -2008,7 +2008,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -2055,7 +2055,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -9772,7 +9772,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -9819,7 +9819,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -9866,7 +9866,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -9913,7 +9913,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -9960,7 +9960,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10007,7 +10007,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10054,7 +10054,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10101,7 +10101,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10148,7 +10148,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10195,7 +10195,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10241,7 +10241,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10288,7 +10288,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium_clang_coverage_tot\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10335,7 +10335,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10382,7 +10382,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10429,7 +10429,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10476,7 +10476,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10523,7 +10523,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\",\"xcode_build_version\":\"12a7209\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10570,7 +10570,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\",\"xcode_build_version\":\"12a7209\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10617,7 +10617,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\",\"xcode_build_version\":\"12a7209\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10664,7 +10664,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium_clang_coverage_tot\",\"xcode_build_version\":\"12a7209\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10711,7 +10711,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10758,7 +10758,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10805,7 +10805,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10852,7 +10852,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10899,7 +10899,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10946,7 +10946,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -10993,7 +10993,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -11040,7 +11040,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium_clang_coverage_tot\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -11086,7 +11086,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\",\"xcode_build_version\":\"12a7209\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       caches {
         name: "xcode_ios_12a7209"
         path: "xcode_ios_12a7209.app"
@@ -11136,7 +11136,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\",\"xcode_build_version\":\"12a7209\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       caches {
         name: "xcode_ios_12a7209"
         path: "xcode_ios_12a7209.app"
@@ -11281,7 +11281,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
@@ -18669,7 +18669,7 @@
         cmd: "recipes"
       }
       properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.clang\",\"perf_dashboard_machine_group\":\"ChromiumClang\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 43200
+      execution_timeout_secs: 50400
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index bc75d2a..822c1a9 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -521,7 +521,7 @@
         # Because these run ToT Clang, goma is not used.
         # Naturally the runtime will be ~4-8h on average, depending on config.
         # CFI builds will take even longer - around 11h.
-        execution_timeout = 12 * time.hour,
+        execution_timeout = 14 * time.hour,
         properties = properties,
         **kwargs
     )
diff --git a/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm b/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
index a04bef5..d5b2c64 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
+++ b/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
@@ -278,10 +278,8 @@
     if (session_service_) {
       NSString* state_path = base::SysUTF8ToNSString(
           browser_state_->GetStatePath().AsUTF8Unsafe());
-      [session_service_
-          deleteLastSessionFileInDirectory:state_path
-                                completion:
-                                    CreatePendingTaskCompletionClosure()];
+      [session_service_ deleteAllSessionFilesInBrowserStateDirectory:state_path
+          completion:CreatePendingTaskCompletionClosure()];
     }
 
     // Remove the screenshots taken by the system when backgrounding the
diff --git a/ios/chrome/browser/sessions/session_service_ios.h b/ios/chrome/browser/sessions/session_service_ios.h
index 9260bd6..36365a0 100644
--- a/ios/chrome/browser/sessions/session_service_ios.h
+++ b/ios/chrome/browser/sessions/session_service_ios.h
@@ -47,9 +47,11 @@
 // of errors.
 - (SessionIOS*)loadSessionFromPath:(NSString*)sessionPath;
 
-// Schedules deletion of the file containing the last session in |directory|.
-- (void)deleteLastSessionFileInDirectory:(NSString*)directory
-                              completion:(base::OnceClosure)callback;
+// Schedules deletion of the all session files from a specific browser state
+// |directory|.
+- (void)deleteAllSessionFilesInBrowserStateDirectory:(NSString*)directory
+                                          completion:
+                                              (base::OnceClosure)callback;
 
 // Schedule deletion of session directories with |sessionIDs| which resides in
 // a specific browser state |directory|.
diff --git a/ios/chrome/browser/sessions/session_service_ios.mm b/ios/chrome/browser/sessions/session_service_ios.mm
index e93c3e1..e51c3f5a 100644
--- a/ios/chrome/browser/sessions/session_service_ios.mm
+++ b/ios/chrome/browser/sessions/session_service_ios.mm
@@ -171,11 +171,29 @@
   return base::mac::ObjCCastStrict<SessionIOS>(rootObject);
 }
 
-- (void)deleteLastSessionFileInDirectory:(NSString*)directory
-                              completion:(base::OnceClosure)callback {
-  NSString* sessionPath = [[self class] sessionPathForDirectory:directory];
-  [self deletePaths:[NSArray arrayWithObject:sessionPath]
-         completion:std::move(callback)];
+- (void)deleteAllSessionFilesInBrowserStateDirectory:(NSString*)directory
+                                          completion:
+                                              (base::OnceClosure)callback {
+  NSMutableArray<NSString*>* sessionFilesPaths = [[NSMutableArray alloc] init];
+  NSString* sessionsDirectoryPath =
+      [directory stringByAppendingPathComponent:kSessionDirectory];
+  NSArray<NSString*>* allSessionIDs = [[NSFileManager defaultManager]
+      contentsOfDirectoryAtPath:sessionsDirectoryPath
+                          error:nil];
+  for (NSString* sessionID in allSessionIDs) {
+    NSString* sessionPath =
+        [SessionServiceIOS sessionPathForSessionID:sessionID
+                                         directory:directory];
+    [sessionFilesPaths addObject:sessionPath];
+  }
+
+  // If there were no session ids, then scenes are not supported fall back to
+  // the original location
+  if (sessionFilesPaths.count == 0)
+    [sessionFilesPaths
+        addObject:[[self class] sessionPathForDirectory:directory]];
+
+  [self deletePaths:sessionFilesPaths completion:std::move(callback)];
 }
 
 - (void)deleteSessions:(NSArray<NSString*>*)sessionIDs
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 87e2d6e..2f0b144 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -312,8 +312,10 @@
 
 bool ChromeWebClient::IsLegacyTLSAllowedForHost(web::WebState* web_state,
                                                 const std::string& hostname) {
-  return LegacyTLSTabAllowList::FromWebState(web_state)->IsDomainAllowed(
-      hostname);
+  auto* allowlist = LegacyTLSTabAllowList::FromWebState(web_state);
+  if (!allowlist)
+    return false;
+  return allowlist->IsDomainAllowed(hostname);
 }
 
 void ChromeWebClient::PrepareErrorPage(
diff --git a/media/base/async_destroy_video_encoder.h b/media/base/async_destroy_video_encoder.h
index 8576205a..1a94cf5b 100644
--- a/media/base/async_destroy_video_encoder.h
+++ b/media/base/async_destroy_video_encoder.h
@@ -52,9 +52,12 @@
     wrapped_encoder_->Encode(std::move(frame), key_frame, std::move(done_cb));
   }
 
-  void ChangeOptions(const Options& options, StatusCB done_cb) override {
+  void ChangeOptions(const Options& options,
+                     OutputCB output_cb,
+                     StatusCB done_cb) override {
     DCHECK(wrapped_encoder_);
-    wrapped_encoder_->ChangeOptions(options, std::move(done_cb));
+    wrapped_encoder_->ChangeOptions(options, std::move(output_cb),
+                                    std::move(done_cb));
   }
 
   void Flush(StatusCB done_cb) override {
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 8b56c0e..832c7b4 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -286,6 +286,7 @@
   MOCK_METHOD(void,
               ChangeOptions,
               (const VideoEncoder::Options& options,
+               VideoEncoder::OutputCB output_cb,
                VideoEncoder::StatusCB done_cb),
               (override));
 
diff --git a/media/base/offloading_video_encoder.cc b/media/base/offloading_video_encoder.cc
index 2ac44c9..02acf713 100644
--- a/media/base/offloading_video_encoder.cc
+++ b/media/base/offloading_video_encoder.cc
@@ -57,12 +57,14 @@
 }
 
 void OffloadingVideoEncoder::ChangeOptions(const Options& options,
+                                           OutputCB output_cb,
                                            StatusCB done_cb) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   work_runner_->PostTask(
       FROM_HERE, base::BindOnce(&VideoEncoder::ChangeOptions,
                                 base::Unretained(wrapped_encoder_.get()),
-                                options, WrapCallback(std::move(done_cb))));
+                                options, WrapCallback(std::move(output_cb)),
+                                WrapCallback(std::move(done_cb))));
 }
 
 void OffloadingVideoEncoder::Flush(StatusCB done_cb) {
diff --git a/media/base/offloading_video_encoder.h b/media/base/offloading_video_encoder.h
index b4ceed5..6000c4cc 100644
--- a/media/base/offloading_video_encoder.h
+++ b/media/base/offloading_video_encoder.h
@@ -46,7 +46,9 @@
               bool key_frame,
               StatusCB done_cb) override;
 
-  void ChangeOptions(const Options& options, StatusCB done_cb) override;
+  void ChangeOptions(const Options& options,
+                     OutputCB output_cb,
+                     StatusCB done_cb) override;
 
   void Flush(StatusCB done_cb) override;
 
diff --git a/media/base/offloading_video_encoder_unittest.cc b/media/base/offloading_video_encoder_unittest.cc
index 9188b0a..3bccecf 100644
--- a/media/base/offloading_video_encoder_unittest.cc
+++ b/media/base/offloading_video_encoder_unittest.cc
@@ -109,14 +109,20 @@
     called_done = true;
   });
 
-  EXPECT_CALL(*mock_video_encoder_, ChangeOptions(_, _))
+  VideoEncoder::OutputCB output_cb = base::BindRepeating(
+      [](VideoEncoderOutput, base::Optional<VideoEncoder::CodecDescription>) {
+      });
+
+  EXPECT_CALL(*mock_video_encoder_, ChangeOptions(_, _, _))
       .WillOnce(Invoke([this](const VideoEncoder::Options& options,
+                              VideoEncoder::OutputCB output_cb,
                               VideoEncoder::StatusCB done_cb) {
         EXPECT_TRUE(work_runner_->RunsTasksInCurrentSequence());
         std::move(done_cb).Run(Status());
       }));
 
-  offloading_encoder_->ChangeOptions(options, std::move(done_cb));
+  offloading_encoder_->ChangeOptions(options, std::move(output_cb),
+                                     std::move(done_cb));
   RunLoop();
   EXPECT_TRUE(called_done);
 }
diff --git a/media/base/video_encoder.h b/media/base/video_encoder.h
index 7d25e645..50087ca6 100644
--- a/media/base/video_encoder.h
+++ b/media/base/video_encoder.h
@@ -92,14 +92,16 @@
                       bool key_frame,
                       StatusCB done_cb) = 0;
 
-  // Adjust encoder options for future frames, executing the
-  // |done_cb| upon completion.
+  // Adjust encoder options and the output callback for future frames, executing
+  // the |done_cb| upon completion.
   //
   // Note:
   // 1. Not all options can be changed on the fly.
   // 2. ChangeOptions() should be called after calling Flush() and waiting
   // for it to finish.
-  virtual void ChangeOptions(const Options& options, StatusCB done_cb) = 0;
+  virtual void ChangeOptions(const Options& options,
+                             OutputCB output_cb,
+                             StatusCB done_cb) = 0;
 
   // Requests all outputs for already encoded frames to be
   // produced via |output_cb| and calls |dene_cb| after that.
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
index 6571a6b6..9b01b096 100644
--- a/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
+++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl.cc
@@ -172,6 +172,29 @@
          proxy_fd_.is_valid();
 }
 
+void CameraHalDispatcherImpl::AddActiveClientObserver(
+    CameraActiveClientObserver* observer) {
+  base::WaitableEvent observer_added_event;
+  proxy_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &CameraHalDispatcherImpl::AddActiveClientObserverOnProxyThread,
+          base::Unretained(this), std::move(observer), &observer_added_event));
+  observer_added_event.Wait();
+}
+
+void CameraHalDispatcherImpl::RemoveActiveClientObserver(
+    CameraActiveClientObserver* observer) {
+  base::WaitableEvent observer_removed_event;
+  proxy_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &CameraHalDispatcherImpl::RemoveActiveClientObserverOnProxyThread,
+          base::Unretained(this), std::move(observer),
+          &observer_removed_event));
+  observer_removed_event.Wait();
+}
+
 CameraHalDispatcherImpl::CameraHalDispatcherImpl()
     : proxy_thread_("CameraProxyThread"),
       blocking_io_thread_("CameraBlockingIOThread"),
@@ -274,9 +297,40 @@
     int32_t camera_id,
     bool opened,
     cros::mojom::CameraClientType type) {
-  // TODO(b/170075468): Implement.
   VLOG(1) << type << (opened ? " opened " : " closed ") << "camera "
           << camera_id;
+  auto& camera_id_set = opened_camera_id_map_[type];
+  if (opened) {
+    auto result = camera_id_set.insert(camera_id);
+    if (!result.second) {  // No element inserted.
+      LOG(WARNING) << "Received duplicated open notification for camera "
+                   << camera_id;
+      return;
+    }
+    if (camera_id_set.size() == 1) {
+      VLOG(1) << type << " is active";
+      for (auto& observer : active_client_observers_) {
+        observer.OnActiveClientChange(type, /*is_active=*/true);
+      }
+    }
+  } else {
+    auto it = camera_id_set.find(camera_id);
+    if (it == camera_id_set.end()) {
+      // This can happen if something happened to the client process and it
+      // simultaneous lost connections to both CameraHalDispatcher and
+      // CameraHalServer.
+      LOG(WARNING) << "Received close notification for camera " << camera_id
+                   << " which is not opened";
+      return;
+    }
+    camera_id_set.erase(it);
+    if (camera_id_set.empty()) {
+      VLOG(1) << type << " is inactive";
+      for (auto& observer : active_client_observers_) {
+        observer.OnActiveClientChange(type, /*is_active=*/false);
+      }
+    }
+  }
 }
 
 base::UnguessableToken CameraHalDispatcherImpl::GetTokenForTrustedClient(
@@ -451,6 +505,29 @@
   VLOG(1) << "Camera HAL client registered";
 }
 
+void CameraHalDispatcherImpl::AddActiveClientObserverOnProxyThread(
+    CameraActiveClientObserver* observer,
+    base::WaitableEvent* observer_added_event) {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  for (auto& opened_camera_id_pair : opened_camera_id_map_) {
+    const auto& camera_client_type = opened_camera_id_pair.first;
+    const auto& camera_id_set = opened_camera_id_pair.second;
+    if (!camera_id_set.empty()) {
+      observer->OnActiveClientChange(camera_client_type, /*is_active=*/true);
+    }
+  }
+  active_client_observers_.AddObserver(observer);
+  observer_added_event->Signal();
+}
+
+void CameraHalDispatcherImpl::RemoveActiveClientObserverOnProxyThread(
+    CameraActiveClientObserver* observer,
+    base::WaitableEvent* observer_removed_event) {
+  DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  active_client_observers_.RemoveObserver(observer);
+  observer_removed_event->Signal();
+}
+
 void CameraHalDispatcherImpl::EstablishMojoChannel(
     CameraClientObserver* client_observer) {
   DCHECK(proxy_task_runner_->BelongsToCurrentThread());
@@ -476,11 +553,36 @@
   VLOG(1) << "Camera HAL server connection lost";
   camera_hal_server_.reset();
   camera_hal_server_callbacks_.reset();
+  for (auto& opened_camera_id_pair : opened_camera_id_map_) {
+    auto camera_client_type = opened_camera_id_pair.first;
+    const auto& camera_id_set = opened_camera_id_pair.second;
+    if (!camera_id_set.empty()) {
+      for (auto& observer : active_client_observers_) {
+        observer.OnActiveClientChange(camera_client_type, /*is_active=*/false);
+      }
+    }
+  }
+  opened_camera_id_map_.clear();
 }
 
 void CameraHalDispatcherImpl::OnCameraHalClientConnectionError(
     CameraClientObserver* client_observer) {
   DCHECK(proxy_task_runner_->BelongsToCurrentThread());
+  auto type = client_observer->GetType();
+  auto opened_it = opened_camera_id_map_.find(type);
+  if (opened_it == opened_camera_id_map_.end()) {
+    // This can happen if this camera client never opened a camera.
+    return;
+  }
+  const auto& camera_id_set = opened_it->second;
+  if (!camera_id_set.empty()) {
+    for (auto& observer : active_client_observers_) {
+      observer.OnActiveClientChange(type,
+                                    /*is_active=*/false);
+    }
+  }
+  opened_camera_id_map_.erase(opened_it);
+
   auto it = client_observers_.find(client_observer);
   if (it != client_observers_.end()) {
     client_observers_.erase(it);
diff --git a/media/capture/video/chromeos/camera_hal_dispatcher_impl.h b/media/capture/video/chromeos/camera_hal_dispatcher_impl.h
index 87e2359..f65227f0 100644
--- a/media/capture/video/chromeos/camera_hal_dispatcher_impl.h
+++ b/media/capture/video/chromeos/camera_hal_dispatcher_impl.h
@@ -8,9 +8,14 @@
 #include <memory>
 #include <set>
 
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/files/scoped_file.h"
 #include "base/memory/singleton.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
 #include "base/unguessable_token.h"
 #include "components/chromeos_camera/common/jpeg_encode_accelerator.mojom.h"
@@ -55,6 +60,12 @@
   base::UnguessableToken auth_token_;
 };
 
+class CAPTURE_EXPORT CameraActiveClientObserver : public base::CheckedObserver {
+ public:
+  virtual void OnActiveClientChange(cros::mojom::CameraClientType type,
+                                    bool is_active) = 0;
+};
+
 // The CameraHalDispatcherImpl hosts and waits on the unix domain socket
 // /var/run/camera3.sock.  CameraHalServer and CameraHalClients connect to the
 // unix domain socket to create the initial Mojo connections with the
@@ -80,6 +91,14 @@
 
   bool IsStarted();
 
+  // Adds an observer that watches for active camera client changes. Observer
+  // would be immediately notified of the current list of active clients.
+  void AddActiveClientObserver(CameraActiveClientObserver* observer);
+
+  // Removes the observer. A previously-added observer must be removed before
+  // being destroyed.
+  void RemoveActiveClientObserver(CameraActiveClientObserver* observer);
+
   // CameraHalDispatcher implementations.
   void RegisterServer(
       mojo::PendingRemote<cros::mojom::CameraHalServer> server) final;
@@ -136,10 +155,19 @@
       cros::mojom::CameraClientType type,
       base::UnguessableToken token,
       RegisterClientWithTokenCallback callback);
+
   void AddClientObserverOnProxyThread(
       std::unique_ptr<CameraClientObserver> observer,
       base::OnceCallback<void(int32_t)> result_callback);
 
+  void AddActiveClientObserverOnProxyThread(
+      CameraActiveClientObserver* observer,
+      base::WaitableEvent* observer_added_event);
+
+  void RemoveActiveClientObserverOnProxyThread(
+      CameraActiveClientObserver* observer,
+      base::WaitableEvent* observer_removed_event);
+
   void EstablishMojoChannel(CameraClientObserver* client_observer);
 
   // Handler for incoming Mojo connection on the unix domain socket.
@@ -178,6 +206,10 @@
 
   TokenManager token_manager_;
 
+  base::flat_map<cros::mojom::CameraClientType, base::flat_set<int32_t>>
+      opened_camera_id_map_;
+  base::ObserverList<CameraActiveClientObserver> active_client_observers_;
+
   DISALLOW_COPY_AND_ASSIGN(CameraHalDispatcherImpl);
 };
 
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 27a97143..8973c9d8 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -1483,15 +1483,13 @@
     uint32_t framerate) {
   if (current_bitrate_ == bitrate && current_framerate_ == framerate)
     return;
+  if (bitrate == 0 || framerate == 0)
+    return;
 
   VLOGF(2) << "bitrate=" << bitrate << ", framerate=" << framerate;
   DCHECK_CALLED_ON_VALID_SEQUENCE(encoder_sequence_checker_);
   TRACE_EVENT2("media,gpu", "V4L2VEA::RequestEncodingParametersChangeTask",
                "bitrate", bitrate, "framerate", framerate);
-
-  DCHECK_GT(bitrate, 0u);
-  DCHECK_GT(framerate, 0u);
-
   if (current_bitrate_ != bitrate &&
       !device_->SetExtCtrls(
           V4L2_CTRL_CLASS_MPEG,
diff --git a/media/video/openh264_video_encoder.cc b/media/video/openh264_video_encoder.cc
index b71972b..8c15b00 100644
--- a/media/video/openh264_video_encoder.cc
+++ b/media/video/openh264_video_encoder.cc
@@ -78,7 +78,7 @@
                                       const Options& options,
                                       OutputCB output_cb,
                                       StatusCB done_cb) {
-  done_cb = media::BindToCurrentLoop(std::move(done_cb));
+  done_cb = BindToCurrentLoop(std::move(done_cb));
   if (codec_) {
     std::move(done_cb).Run(StatusCode::kEncoderInitializeTwice);
     return;
@@ -132,7 +132,7 @@
   }
 
   options_ = options;
-  output_cb_ = media::BindToCurrentLoop(std::move(output_cb));
+  output_cb_ = BindToCurrentLoop(std::move(output_cb));
   codec_ = std::move(codec);
   std::move(done_cb).Run(Status());
 }
@@ -141,7 +141,7 @@
                                   bool key_frame,
                                   StatusCB done_cb) {
   Status status;
-  done_cb = media::BindToCurrentLoop(std::move(done_cb));
+  done_cb = BindToCurrentLoop(std::move(done_cb));
   if (!codec_) {
     std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
     return;
@@ -152,7 +152,7 @@
                                   "No frame provided for encoding."));
     return;
   }
-  if (!frame->IsMappable() || frame->format() != media::PIXEL_FORMAT_I420) {
+  if (!frame->IsMappable() || frame->format() != PIXEL_FORMAT_I420) {
     status =
         Status(StatusCode::kEncoderFailedEncode, "Unexpected frame format.")
             .WithData("IsMappable", frame->IsMappable())
@@ -246,20 +246,46 @@
 }
 
 void OpenH264VideoEncoder::ChangeOptions(const Options& options,
+                                         OutputCB output_cb,
                                          StatusCB done_cb) {
-  done_cb = media::BindToCurrentLoop(std::move(done_cb));
+  done_cb = BindToCurrentLoop(std::move(done_cb));
   if (!codec_) {
     std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
     return;
   }
 
-  // TODO(eugene): Not implemented yet.
+  Status status;
+  SEncParamExt params = {};
+  if (int err = codec_->GetDefaultParams(&params)) {
+    status = Status(StatusCode::kEncoderInitializationError,
+                    "Failed to get default params.")
+                 .WithData("error", err);
+    std::move(done_cb).Run(status);
+    return;
+  }
 
+  status = SetUpOpenH264Params(options, &params);
+  if (!status.is_ok()) {
+    std::move(done_cb).Run(status);
+    return;
+  }
+
+  if (int err =
+          codec_->SetOption(ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &params)) {
+    status = Status(StatusCode::kEncoderInitializationError,
+                    "OpenH264 encoder failed to set new SEncParamExt.")
+                 .WithData("error", err);
+    std::move(done_cb).Run(status);
+    return;
+  }
+
+  if (!output_cb.is_null())
+    output_cb_ = BindToCurrentLoop(std::move(output_cb));
   std::move(done_cb).Run(Status());
 }
 
 void OpenH264VideoEncoder::Flush(StatusCB done_cb) {
-  done_cb = media::BindToCurrentLoop(std::move(done_cb));
+  done_cb = BindToCurrentLoop(std::move(done_cb));
   if (!codec_) {
     std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
     return;
diff --git a/media/video/openh264_video_encoder.h b/media/video/openh264_video_encoder.h
index 34484a6e..f7bf3b0 100644
--- a/media/video/openh264_video_encoder.h
+++ b/media/video/openh264_video_encoder.h
@@ -30,7 +30,9 @@
   void Encode(scoped_refptr<VideoFrame> frame,
               bool key_frame,
               StatusCB done_cb) override;
-  void ChangeOptions(const Options& options, StatusCB done_cb) override;
+  void ChangeOptions(const Options& options,
+                     OutputCB output_cb,
+                     StatusCB done_cb) override;
   void Flush(StatusCB done_cb) override;
 
  private:
diff --git a/media/video/video_encode_accelerator_adapter.cc b/media/video/video_encode_accelerator_adapter.cc
index 528d6f7..8753a14 100644
--- a/media/video/video_encode_accelerator_adapter.cc
+++ b/media/video/video_encode_accelerator_adapter.cc
@@ -347,7 +347,47 @@
 }
 
 void VideoEncodeAcceleratorAdapter::ChangeOptions(const Options& options,
-                                                  StatusCB done_cb) {}
+                                                  OutputCB output_cb,
+                                                  StatusCB done_cb) {
+  DCHECK(!accelerator_task_runner_->RunsTasksInCurrentSequence());
+  accelerator_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &VideoEncodeAcceleratorAdapter::ChangeOptionsOnAcceleratorThread,
+          base::Unretained(this), options, WrapCallback(std::move(output_cb)),
+          WrapCallback(std::move(done_cb))));
+}
+
+void VideoEncodeAcceleratorAdapter::ChangeOptionsOnAcceleratorThread(
+    const Options options,
+    OutputCB output_cb,
+    StatusCB done_cb) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(accelerator_sequence_checker_);
+  DCHECK(active_encodes_.empty());
+  DCHECK(pending_encodes_.empty());
+  DCHECK_EQ(state_, State::kReadyToEncode);
+
+  if (options.width != options_.width || options.height != options_.height) {
+    auto status = Status(StatusCode::kEncoderInitializationError,
+                         "Resolution change is not supported.");
+    std::move(done_cb).Run(status);
+    return;
+  }
+
+  uint32_t bitrate =
+      std::min(options.bitrate.value_or(options.width * options.height *
+                                        kVEADefaultBitratePerPixel),
+               uint64_t{std::numeric_limits<uint32_t>::max()});
+
+  uint32_t framerate = uint32_t{std::round(
+      options.framerate.value_or(VideoEncodeAccelerator::kDefaultFramerate))};
+
+  accelerator_->RequestEncodingParametersChange(bitrate, framerate);
+  options_ = options;
+  if (!output_cb.is_null())
+    output_cb_ = BindToCurrentLoop(std::move(output_cb));
+  std::move(done_cb).Run(Status());
+}
 
 void VideoEncodeAcceleratorAdapter::Flush(StatusCB done_cb) {
   DCHECK(!accelerator_task_runner_->RunsTasksInCurrentSequence());
@@ -587,6 +627,8 @@
 template <class T>
 T VideoEncodeAcceleratorAdapter::WrapCallback(T cb) {
   DCHECK(callback_task_runner_);
+  if (cb.is_null())
+    return cb;
   return BindToLoop(callback_task_runner_.get(), std::move(cb));
 }
 
diff --git a/media/video/video_encode_accelerator_adapter.h b/media/video/video_encode_accelerator_adapter.h
index b93e09a..bd43cf5a 100644
--- a/media/video/video_encode_accelerator_adapter.h
+++ b/media/video/video_encode_accelerator_adapter.h
@@ -47,7 +47,9 @@
   void Encode(scoped_refptr<VideoFrame> frame,
               bool key_frame,
               StatusCB done_cb) override;
-  void ChangeOptions(const Options& options, StatusCB done_cb) override;
+  void ChangeOptions(const Options& options,
+                     OutputCB output_cb,
+                     StatusCB done_cb) override;
   void Flush(StatusCB done_cb) override;
 
   // VideoEncodeAccelerator::Client implementation
@@ -93,6 +95,9 @@
                                  bool key_frame,
                                  StatusCB done_cb);
   void FlushOnAcceleratorThread(StatusCB done_cb);
+  void ChangeOptionsOnAcceleratorThread(const Options options,
+                                        OutputCB output_cb,
+                                        StatusCB done_cb);
 
   template <class T>
   T WrapCallback(T cb);
diff --git a/media/video/vpx_video_encoder.cc b/media/video/vpx_video_encoder.cc
index c818d7a2..3fe5e35 100644
--- a/media/video/vpx_video_encoder.cc
+++ b/media/video/vpx_video_encoder.cc
@@ -95,7 +95,7 @@
                                  const Options& options,
                                  OutputCB output_cb,
                                  StatusCB done_cb) {
-  done_cb = media::BindToCurrentLoop(std::move(done_cb));
+  done_cb = BindToCurrentLoop(std::move(done_cb));
   if (codec_) {
     std::move(done_cb).Run(StatusCode::kEncoderInitializeTwice);
     return;
@@ -104,10 +104,9 @@
   profile_ = profile;
 
   vpx_codec_iface_t* iface = nullptr;
-  if (profile == media::VP8PROFILE_ANY) {
+  if (profile == VP8PROFILE_ANY) {
     iface = vpx_codec_vp8_cx();
-  } else if (profile == media::VP9PROFILE_PROFILE0 ||
-             profile == media::VP9PROFILE_PROFILE2) {
+  } else if (profile == VP9PROFILE_PROFILE0 || profile == VP9PROFILE_PROFILE2) {
     // TODO(https://crbug.com/1116617): Consider support for profiles 1 and 3.
     is_vp9_ = true;
     iface = vpx_codec_vp9_cx();
@@ -130,17 +129,17 @@
   vpx_img_fmt img_fmt = VPX_IMG_FMT_NONE;
   unsigned int bits_for_storage = 8;
   switch (profile) {
-    case media::VP9PROFILE_PROFILE1:
+    case VP9PROFILE_PROFILE1:
       codec_config_.g_profile = 1;
       break;
-    case media::VP9PROFILE_PROFILE2:
+    case VP9PROFILE_PROFILE2:
       codec_config_.g_profile = 2;
       img_fmt = VPX_IMG_FMT_I42016;
       bits_for_storage = 16;
       codec_config_.g_bit_depth = VPX_BITS_10;
       codec_config_.g_input_bit_depth = 10;
       break;
-    case media::VP9PROFILE_PROFILE3:
+    case VP9PROFILE_PROFILE3:
       codec_config_.g_profile = 3;
       break;
     default:
@@ -208,7 +207,7 @@
   }
 
   options_ = options;
-  output_cb_ = media::BindToCurrentLoop(std::move(output_cb));
+  output_cb_ = BindToCurrentLoop(std::move(output_cb));
   codec_ = std::move(codec);
   std::move(done_cb).Run(Status());
 }
@@ -217,7 +216,7 @@
                              bool key_frame,
                              StatusCB done_cb) {
   Status status;
-  done_cb = media::BindToCurrentLoop(std::move(done_cb));
+  done_cb = BindToCurrentLoop(std::move(done_cb));
   if (!codec_) {
     std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
     return;
@@ -228,7 +227,7 @@
                                   "No frame provided for encoding."));
     return;
   }
-  if (!frame->IsMappable() || frame->format() != media::PIXEL_FORMAT_I420) {
+  if (!frame->IsMappable() || frame->format() != PIXEL_FORMAT_I420) {
     status =
         Status(StatusCode::kEncoderFailedEncode, "Unexpected frame format.")
             .WithData("IsMappable", frame->IsMappable())
@@ -238,17 +237,17 @@
   }
 
   switch (profile_) {
-    case media::VP9PROFILE_PROFILE1:
+    case VP9PROFILE_PROFILE1:
       NOTREACHED();
       break;
-    case media::VP9PROFILE_PROFILE2:
+    case VP9PROFILE_PROFILE2:
       libyuv::I420ToI010(
-          frame->visible_data(media::VideoFrame::kYPlane),
-          frame->stride(media::VideoFrame::kYPlane),
-          frame->visible_data(media::VideoFrame::kUPlane),
-          frame->stride(media::VideoFrame::kUPlane),
-          frame->visible_data(media::VideoFrame::kVPlane),
-          frame->stride(media::VideoFrame::kVPlane),
+          frame->visible_data(VideoFrame::kYPlane),
+          frame->stride(VideoFrame::kYPlane),
+          frame->visible_data(VideoFrame::kUPlane),
+          frame->stride(VideoFrame::kUPlane),
+          frame->visible_data(VideoFrame::kVPlane),
+          frame->stride(VideoFrame::kVPlane),
           reinterpret_cast<uint16_t*>(vpx_image_.planes[VPX_PLANE_Y]),
           vpx_image_.stride[VPX_PLANE_Y] / 2,
           reinterpret_cast<uint16_t*>(vpx_image_.planes[VPX_PLANE_U]),
@@ -257,22 +256,19 @@
           vpx_image_.stride[VPX_PLANE_V] / 2, frame->coded_size().width(),
           frame->coded_size().height());
       break;
-    case media::VP9PROFILE_PROFILE3:
+    case VP9PROFILE_PROFILE3:
       NOTREACHED();
       break;
     default:
       vpx_image_.planes[VPX_PLANE_Y] =
-          const_cast<uint8_t*>(frame->visible_data(media::VideoFrame::kYPlane));
+          const_cast<uint8_t*>(frame->visible_data(VideoFrame::kYPlane));
       vpx_image_.planes[VPX_PLANE_U] =
-          const_cast<uint8_t*>(frame->visible_data(media::VideoFrame::kUPlane));
+          const_cast<uint8_t*>(frame->visible_data(VideoFrame::kUPlane));
       vpx_image_.planes[VPX_PLANE_V] =
-          const_cast<uint8_t*>(frame->visible_data(media::VideoFrame::kVPlane));
-      vpx_image_.stride[VPX_PLANE_Y] =
-          frame->stride(media::VideoFrame::kYPlane);
-      vpx_image_.stride[VPX_PLANE_U] =
-          frame->stride(media::VideoFrame::kUPlane);
-      vpx_image_.stride[VPX_PLANE_V] =
-          frame->stride(media::VideoFrame::kVPlane);
+          const_cast<uint8_t*>(frame->visible_data(VideoFrame::kVPlane));
+      vpx_image_.stride[VPX_PLANE_Y] = frame->stride(VideoFrame::kYPlane);
+      vpx_image_.stride[VPX_PLANE_U] = frame->stride(VideoFrame::kUPlane);
+      vpx_image_.stride[VPX_PLANE_V] = frame->stride(VideoFrame::kVPlane);
       break;
   }
 
@@ -298,8 +294,10 @@
   std::move(done_cb).Run(Status());
 }
 
-void VpxVideoEncoder::ChangeOptions(const Options& options, StatusCB done_cb) {
-  done_cb = media::BindToCurrentLoop(std::move(done_cb));
+void VpxVideoEncoder::ChangeOptions(const Options& options,
+                                    OutputCB output_cb,
+                                    StatusCB done_cb) {
+  done_cb = BindToCurrentLoop(std::move(done_cb));
   if (!codec_) {
     std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
     return;
@@ -307,20 +305,39 @@
 
   vpx_codec_enc_cfg_t new_config = codec_config_;
   auto status = SetUpVpxConfig(options, &new_config);
-  if (status.is_ok()) {
-    auto vpx_error = vpx_codec_enc_config_set(codec_.get(), &new_config);
-    if (vpx_error == VPX_CODEC_OK) {
-      codec_config_ = new_config;
-      options_ = options;
-    } else {
-      status = Status(StatusCode::kEncoderUnsupportedConfig,
-                      "Failed to set new VPX config")
-                   .WithData("vpx_error", vpx_error);
+  if (!status.is_ok()) {
+    std::move(done_cb).Run(status);
+    return;
+  }
+
+  if (options_.width != options.width || options_.height != options.height) {
+    // Need to re-allocate |vpx_image_| because the size has changed.
+    auto img_fmt = vpx_image_.fmt;
+    auto bit_depth = vpx_image_.bit_depth;
+    vpx_img_free(&vpx_image_);
+    if (&vpx_image_ != vpx_img_wrap(&vpx_image_, img_fmt, options.width,
+                                    options.height, 1, nullptr)) {
+      status = Status(StatusCode::kEncoderInitializationError,
+                      "Invalid format or frame size.");
+      std::move(done_cb).Run(status);
+      return;
     }
+    vpx_image_.bit_depth = bit_depth;
+  }
+
+  auto vpx_error = vpx_codec_enc_config_set(codec_.get(), &new_config);
+  if (vpx_error == VPX_CODEC_OK) {
+    codec_config_ = new_config;
+    options_ = options;
+    if (!output_cb.is_null())
+      output_cb_ = BindToCurrentLoop(std::move(output_cb));
+  } else {
+    status = Status(StatusCode::kEncoderUnsupportedConfig,
+                    "Failed to set new VPX config")
+                 .WithData("vpx_error", vpx_error);
   }
 
   std::move(done_cb).Run(std::move(status));
-  return;
 }
 
 base::TimeDelta VpxVideoEncoder::GetFrameDuration(const VideoFrame& frame) {
@@ -351,7 +368,7 @@
 }
 
 void VpxVideoEncoder::Flush(StatusCB done_cb) {
-  done_cb = media::BindToCurrentLoop(std::move(done_cb));
+  done_cb = BindToCurrentLoop(std::move(done_cb));
   if (!codec_) {
     std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
     return;
diff --git a/media/video/vpx_video_encoder.h b/media/video/vpx_video_encoder.h
index 4173e569f..eee7a1d 100644
--- a/media/video/vpx_video_encoder.h
+++ b/media/video/vpx_video_encoder.h
@@ -28,7 +28,9 @@
   void Encode(scoped_refptr<VideoFrame> frame,
               bool key_frame,
               StatusCB done_cb) override;
-  void ChangeOptions(const Options& options, StatusCB done_cb) override;
+  void ChangeOptions(const Options& options,
+                     OutputCB output_cb,
+                     StatusCB done_cb) override;
   void Flush(StatusCB done_cb) override;
 
  private:
diff --git a/net/base/isolation_info_unittest.cc b/net/base/isolation_info_unittest.cc
index e270d956..4e95c860 100644
--- a/net/base/isolation_info_unittest.cc
+++ b/net/base/isolation_info_unittest.cc
@@ -227,7 +227,8 @@
 }
 
 TEST_F(IsolationInfoTest, CreatePartialUpdateTopFrame) {
-  const NetworkIsolationKey kNIK(kOrigin1, kOrigin1);
+  const NetworkIsolationKey kNIK{SchemefulSite(kOrigin1),
+                                 SchemefulSite(kOrigin1)};
   IsolationInfo isolation_info = IsolationInfo::CreatePartial(
       IsolationInfo::RequestType::kMainFrame, kNIK);
   EXPECT_EQ(IsolationInfo::RequestType::kMainFrame,
@@ -242,7 +243,8 @@
 }
 
 TEST_F(IsolationInfoTest, CreatePartialUpdateFrameOnly) {
-  const NetworkIsolationKey kNIK(kOrigin1, kOrigin2);
+  const NetworkIsolationKey kNIK{SchemefulSite(kOrigin1),
+                                 SchemefulSite(kOrigin2)};
   IsolationInfo isolation_info =
       IsolationInfo::CreatePartial(IsolationInfo::RequestType::kSubFrame, kNIK);
   EXPECT_EQ(IsolationInfo::RequestType::kSubFrame,
@@ -257,7 +259,8 @@
 }
 
 TEST_F(IsolationInfoTest, CreatePartialUpdateNothing) {
-  const NetworkIsolationKey kNIK(kOrigin1, kOrigin2);
+  const NetworkIsolationKey kNIK{SchemefulSite(kOrigin1),
+                                 SchemefulSite(kOrigin2)};
   IsolationInfo isolation_info =
       IsolationInfo::CreatePartial(IsolationInfo::RequestType::kOther, kNIK);
   EXPECT_EQ(IsolationInfo::RequestType::kOther, isolation_info.request_type());
@@ -319,7 +322,8 @@
   feature_list.InitAndDisableFeature(
       features::kAppendFrameOriginToNetworkIsolationKey);
 
-  const NetworkIsolationKey kNIK(kOrigin1, kOrigin1);
+  const NetworkIsolationKey kNIK{SchemefulSite(kOrigin1),
+                                 SchemefulSite(kOrigin1)};
   EXPECT_FALSE(kNIK.GetFrameSite());
   IsolationInfo isolation_info = IsolationInfo::CreatePartial(
       IsolationInfo::RequestType::kMainFrame, kNIK);
@@ -339,7 +343,8 @@
   feature_list.InitAndDisableFeature(
       features::kAppendFrameOriginToNetworkIsolationKey);
 
-  const NetworkIsolationKey kNIK(kOrigin1, kOrigin2);
+  const NetworkIsolationKey kNIK{SchemefulSite(kOrigin1),
+                                 SchemefulSite(kOrigin2)};
   EXPECT_FALSE(kNIK.GetFrameSite());
   IsolationInfo isolation_info =
       IsolationInfo::CreatePartial(IsolationInfo::RequestType::kSubFrame, kNIK);
@@ -360,7 +365,8 @@
   feature_list.InitAndDisableFeature(
       features::kAppendFrameOriginToNetworkIsolationKey);
 
-  const NetworkIsolationKey kNIK(kOrigin1, kOrigin2);
+  const NetworkIsolationKey kNIK{SchemefulSite(kOrigin1),
+                                 SchemefulSite(kOrigin2)};
   EXPECT_FALSE(kNIK.GetFrameSite());
   IsolationInfo isolation_info =
       IsolationInfo::CreatePartial(IsolationInfo::RequestType::kOther, kNIK);
diff --git a/net/dns/context_host_resolver_unittest.cc b/net/dns/context_host_resolver_unittest.cc
index 3612768..3354b5b09 100644
--- a/net/dns/context_host_resolver_unittest.cc
+++ b/net/dns/context_host_resolver_unittest.cc
@@ -18,6 +18,7 @@
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/dns_test_util.h"
@@ -36,7 +37,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace net {
 
@@ -661,8 +661,8 @@
 // Do a lookup with a NetworkIsolationKey, and then make sure the entry added to
 // the cache is in fact using that NetworkIsolationKey.
 TEST_F(ContextHostResolverTest, ResultsAddedToCacheWithNetworkIsolationKey) {
-  const url::Origin kOrigin = url::Origin::Create(GURL("https://origin.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://origin.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
diff --git a/net/dns/host_cache_unittest.cc b/net/dns/host_cache_unittest.cc
index b478891..e67fc85f 100644
--- a/net/dns/host_cache_unittest.cc
+++ b/net/dns/host_cache_unittest.cc
@@ -17,10 +17,10 @@
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 using ::testing::ElementsAre;
 using ::testing::ElementsAreArray;
@@ -124,12 +124,10 @@
   const char kHostname[] = "hostname.test";
   const base::TimeDelta kTTL = base::TimeDelta::FromSeconds(10);
 
-  const url::Origin kOrigin1(
-      url::Origin::Create(GURL("https://origin1.test/")));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2(
-      url::Origin::Create(GURL("https://origin2.test/")));
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://site1.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://site2.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   HostCache::Key key1(kHostname, DnsQueryType::UNSPECIFIED, 0,
                       HostResolverSource::ANY, kNetworkIsolationKey1);
@@ -988,11 +986,11 @@
 TEST(HostCacheTest, SerializeAndDeserializeWithNetworkIsolationKey) {
   const char kHostname[] = "hostname.test";
   const base::TimeDelta kTTL = base::TimeDelta::FromSeconds(10);
-  const url::Origin kOrigin(url::Origin::Create(GURL("https://origin.test/")));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
-  const url::Origin kOpaqueOrigin;
-  const NetworkIsolationKey kOpaqueNetworkIsolationKey(kOpaqueOrigin,
-                                                       kOpaqueOrigin);
+  const SchemefulSite kSite(GURL("https://site.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
+  const SchemefulSite kOpaqueSite;
+  const NetworkIsolationKey kOpaqueNetworkIsolationKey(kOpaqueSite,
+                                                       kOpaqueSite);
 
   HostCache::Key key1(kHostname, DnsQueryType::UNSPECIFIED, 0,
                       HostResolverSource::ANY, kNetworkIsolationKey);
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 9ff16ec5..02f0091 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -50,6 +50,7 @@
 #include "net/base/mock_network_change_notifier.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/dns/dns_client.h"
 #include "net/dns/dns_config.h"
 #include "net/dns/dns_test_util.h"
@@ -74,7 +75,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 #if BUILDFLAG(ENABLE_MDNS)
 #include "net/dns/mdns_client_impl.h"
@@ -3521,12 +3521,10 @@
 
 // Check that entries are written to the cache with the right NIK.
 TEST_F(HostResolverManagerTest, NetworkIsolationKeyWriteToHostCache) {
-  const url::Origin kOrigin1 =
-      url::Origin::Create(GURL("https://origin1.test/"));
-  const url::Origin kOrigin2 =
-      url::Origin::Create(GURL("https://origin2.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://origin1.test/"));
+  const SchemefulSite kSite2(GURL("https://origin2.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   const char kFirstDnsResult[] = "192.168.1.42";
   const char kSecondDnsResult[] = "192.168.1.43";
@@ -3623,12 +3621,10 @@
 
 // Check that entries are read to the cache with the right NIK.
 TEST_F(HostResolverManagerTest, NetworkIsolationKeyReadFromHostCache) {
-  const url::Origin kOrigin1 =
-      url::Origin::Create(GURL("https://origin1.test/"));
-  const url::Origin kOrigin2 =
-      url::Origin::Create(GURL("https://origin2.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://origin1.test/"));
+  const SchemefulSite kSite2(GURL("https://origin2.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   struct CacheEntry {
     NetworkIsolationKey network_isolation_key;
@@ -3700,12 +3696,10 @@
 // Test that two requests made with different NetworkIsolationKeys are not
 // merged if |features::kSplitHostCacheByNetworkIsolationKey| is enabled.
 TEST_F(HostResolverManagerTest, NetworkIsolationKeyTwoRequestsAtOnce) {
-  const url::Origin kOrigin1 =
-      url::Origin::Create(GURL("https://origin1.test/"));
-  const url::Origin kOrigin2 =
-      url::Origin::Create(GURL("https://origin2.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://origin1.test/"));
+  const SchemefulSite kSite2(GURL("https://origin2.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   const char kDnsResult[] = "192.168.1.42";
 
diff --git a/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc b/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc
index 5f270e8..dbed66d 100644
--- a/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc
+++ b/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc
@@ -236,11 +236,11 @@
   // risk of tests passing due to comparing origins that are the same but come
   // from different sources.
   const NetworkIsolationKey kNik1_ = NetworkIsolationKey(
-      url::Origin::Create(GURL("https://top-frame-origin-nik1.test")),
-      url::Origin::Create(GURL("https://frame-origin-nik1.test")));
+      SchemefulSite(GURL("https://top-frame-origin-nik1.test")),
+      SchemefulSite(GURL("https://frame-origin-nik1.test")));
   const NetworkIsolationKey kNik2_ = NetworkIsolationKey(
-      url::Origin::Create(GURL("https://top-frame-origin-nik2.test")),
-      url::Origin::Create(GURL("https://frame-origin-nik2.test")));
+      SchemefulSite(GURL("https://top-frame-origin-nik2.test")),
+      SchemefulSite(GURL("https://frame-origin-nik2.test")));
 
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<SQLitePersistentReportingAndNelStore> store_;
diff --git a/net/http/broken_alternative_services_unittest.cc b/net/http/broken_alternative_services_unittest.cc
index 7ef8ba7f..851a3c5 100644
--- a/net/http/broken_alternative_services_unittest.cc
+++ b/net/http/broken_alternative_services_unittest.cc
@@ -10,9 +10,9 @@
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/time/tick_clock.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace net {
 
@@ -30,10 +30,10 @@
         test_task_runner_context_(test_task_runner_),
         broken_services_clock_(test_task_runner_->GetMockTickClock()),
         broken_services_(50, this, broken_services_clock_) {
-    auto origin1 = url::Origin::Create(GURL("http://foo.test"));
-    auto origin2 = url::Origin::Create(GURL("http://bar.test"));
-    network_isolation_key1_ = NetworkIsolationKey(origin1, origin1);
-    network_isolation_key2_ = NetworkIsolationKey(origin2, origin2);
+    SchemefulSite site1(GURL("http://foo.test"));
+    SchemefulSite site2(GURL("http://bar.test"));
+    network_isolation_key1_ = NetworkIsolationKey(site1, site1);
+    network_isolation_key2_ = NetworkIsolationKey(site2, site2);
   }
 
   // BrokenAlternativeServices::Delegate implementation
diff --git a/net/http/http_auth_cache_unittest.cc b/net/http/http_auth_cache_unittest.cc
index 6cd9654..18dc408 100644
--- a/net/http/http_auth_cache_unittest.cc
+++ b/net/http/http_auth_cache_unittest.cc
@@ -12,9 +12,9 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/http/http_auth_cache.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "url/origin.h"
 
 using base::ASCIIToUTF16;
 
@@ -328,10 +328,10 @@
 // Make sure server credentials with different NetworkIsolationKeys are treated
 // separately if |key_entries_by_network_isolation_key| is set to true.
 TEST(HttpAuthCacheTest, SeparateServersByNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   GURL kPseudoOrigin("http://www.google.com");
   const char kPath[] = "/";
@@ -427,10 +427,10 @@
 // Make sure added proxy credentials ignore NetworkIsolationKey, even if if
 // |key_entries_by_network_isolation_key| is set to true.
 TEST(HttpAuthCacheTest, NeverSeparateProxiesByNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   GURL kPseudoOrigin("http://www.google.com");
   const char kPath[] = "/";
diff --git a/net/http/http_cache_lookup_manager_unittest.cc b/net/http/http_cache_lookup_manager_unittest.cc
index 73e760b..55302912 100644
--- a/net/http/http_cache_lookup_manager_unittest.cc
+++ b/net/http/http_cache_lookup_manager_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/test/task_environment.h"
 #include "net/base/features.h"
 #include "net/base/net_errors.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/http/http_cache_lookup_manager.h"
 #include "net/http/http_transaction_test_util.h"
@@ -29,8 +30,7 @@
  public:
   explicit MockServerPushHelper(const GURL& url)
       : request_url_(url),
-        network_isolation_key_(url::Origin::Create(url),
-                               url::Origin::Create(url)) {}
+        network_isolation_key_(SchemefulSite(url), SchemefulSite(url)) {}
 
   const GURL& GetURL() const override { return request_url_; }
 
@@ -195,9 +195,9 @@
   std::unique_ptr<MockServerPushHelper> push_helper =
       std::make_unique<MockServerPushHelper>(request_url);
   if (!use_same_network_isolation_key) {
-    url::Origin origin = url::Origin::Create(GURL("http://www.abc.com"));
+    SchemefulSite site(GURL("http://www.abc.com"));
     push_helper->set_network_isolation_key(
-        net::NetworkIsolationKey(origin, origin));
+        net::NetworkIsolationKey(site, site));
   }
 
   MockServerPushHelper* push_helper_ptr = push_helper.get();
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 2d207f9..d20774c 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -41,6 +41,7 @@
 #include "net/base/load_timing_info.h"
 #include "net/base/load_timing_info_test_util.h"
 #include "net/base/net_errors.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/cert/cert_status_flags.h"
 #include "net/cert/x509_certificate.h"
@@ -70,6 +71,7 @@
 #include "net/websockets/websocket_handshake_stream_base.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/origin.h"
 
 using net::test::IsError;
 using net::test::IsOk;
@@ -786,12 +788,11 @@
 
   std::string ComputeCacheKey(const std::string& url_string) {
     GURL url(url_string);
-    const auto kOrigin = url::Origin::Create(url);
+    SchemefulSite site(url);
     net::HttpRequestInfo request_info;
     request_info.url = url;
     request_info.method = "GET";
-    request_info.network_isolation_key =
-        net::NetworkIsolationKey(kOrigin, kOrigin);
+    request_info.network_isolation_key = net::NetworkIsolationKey(site, site);
     MockHttpCache cache;
     return cache.http_cache()->GenerateCacheKeyForTest(&request_info);
   }
@@ -943,7 +944,7 @@
   base::HistogramTester histograms;
   const std::string histogram_name = "WebFont.HttpCacheStatus_roboto";
 
-  url::Origin origin_a = url::Origin::Create(GURL("http://www.a.com"));
+  SchemefulSite site_a(GURL("http://www.a.com"));
 
   MockHttpCache cache;
 
@@ -951,7 +952,7 @@
   transaction.url = "http://themes.googleusercontent.com/static/fonts/roboto";
   AddMockTransaction(&transaction);
   MockHttpRequest request(transaction);
-  request.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  request.network_isolation_key = NetworkIsolationKey(site_a, site_a);
 
   // Attempt to populate the cache.
   RunTransactionTestWithRequest(cache.http_cache(), transaction, request,
@@ -6488,22 +6489,22 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
       net::features::kSplitCacheByNetworkIsolationKey);
-  url::Origin origin_a = url::Origin::Create(GURL("http://a.com"));
-  url::Origin origin_b = url::Origin::Create(GURL("http://b.com"));
+  SchemefulSite site_a(GURL("http://a.com"));
+  SchemefulSite site_b(GURL("http://b.com"));
 
   MockHttpCache cache;
 
   MockTransaction transaction(kSimpleGET_Transaction);
   AddMockTransaction(&transaction);
   MockHttpRequest req1(transaction);
-  req1.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  req1.network_isolation_key = NetworkIsolationKey(site_a, site_a);
 
   // Attempt to populate the cache.
   RunTransactionTestWithRequest(cache.http_cache(), transaction, req1, nullptr);
 
   // Same for a different origin.
   MockHttpRequest req1b(transaction);
-  req1b.network_isolation_key = NetworkIsolationKey(origin_b, origin_b);
+  req1b.network_isolation_key = NetworkIsolationKey(site_b, site_b);
   RunTransactionTestWithRequest(cache.http_cache(), transaction, req1b,
                                 nullptr);
 
@@ -6520,7 +6521,7 @@
   transaction.status = "HTTP/1.1 205 No Content";
   MockHttpRequest req2(transaction);
   req2.upload_data_stream = &upload_data_stream;
-  req2.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  req2.network_isolation_key = NetworkIsolationKey(site_a, site_a);
 
   RunTransactionTestWithRequest(cache.http_cache(), transaction, req2, nullptr);
 
@@ -10463,15 +10464,14 @@
   MockHttpCache cache;
   HttpResponseInfo response;
 
-  url::Origin origin_a = url::Origin::Create(GURL("http://a.com"));
-  url::Origin origin_b = url::Origin::Create(GURL("http://b.com"));
-  url::Origin origin_data =
-      url::Origin::Create(GURL("data:text/html,<body>Hello World</body>"));
+  SchemefulSite site_a(GURL("http://a.com"));
+  SchemefulSite site_b(GURL("http://b.com"));
+  SchemefulSite site_data(GURL("data:text/html,<body>Hello World</body>"));
 
   MockHttpRequest trans_info = MockHttpRequest(kSimpleGET_Transaction);
   // Request with a.com as the top frame and subframe origins. It shouldn't be
   // cached.
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_a, site_a);
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
   EXPECT_FALSE(response.was_cached);
@@ -10486,7 +10486,7 @@
   EXPECT_TRUE(response.was_cached);
 
   // Now request with b.com as the subframe origin. It shouldn't be cached.
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_a, origin_b);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_a, site_b);
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
   EXPECT_FALSE(response.was_cached);
@@ -10497,14 +10497,14 @@
   EXPECT_TRUE(response.was_cached);
 
   // a.com should still be cached.
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_a, site_a);
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
   EXPECT_TRUE(response.was_cached);
 
-  // Now make a request with an opaque subframe origin.  It shouldn't be
+  // Now make a request with an opaque subframe site.  It shouldn't be
   // cached.
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_a, origin_data);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_a, site_data);
   EXPECT_TRUE(trans_info.network_isolation_key.ToString().empty());
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
@@ -10543,7 +10543,7 @@
                                               kUploadId);
 
   MockHttpRequest post_info = MockHttpRequest(kSimplePOST_Transaction);
-  post_info.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  post_info.network_isolation_key = NetworkIsolationKey(site_a, site_a);
   post_info.upload_data_stream = &upload_data_stream;
 
   RunTransactionTestWithRequest(cache.http_cache(), kSimplePOST_Transaction,
@@ -10558,15 +10558,17 @@
 
   url::Origin origin_a = url::Origin::Create(GURL(kSimpleGET_Transaction.url));
   url::Origin origin_b = url::Origin::Create(GURL("http://b.com"));
+  SchemefulSite site_a(origin_a);
+  SchemefulSite site_b(origin_b);
 
   ScopedMockTransaction transaction(kSimpleGET_Transaction);
   transaction.response_headers = "Content-Type: text/css\n";
 
   MockHttpRequest trans_info = MockHttpRequest(transaction);
 
-  // Requesting with the same top-frame origin should not count as third-party
+  // Requesting with the same top-frame site should not count as third-party
   // but should still be recorded as CSS
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_a, site_a);
   trans_info.possibly_top_frame_origin = origin_a;
 
   RunTransactionTestWithRequest(cache.http_cache(), transaction, trans_info,
@@ -10576,9 +10578,9 @@
   histograms.ExpectTotalCount("HttpCache.Pattern.CSS", 1);
   histograms.ExpectTotalCount("HttpCache.Pattern.CSSThirdParty", 0);
 
-  // Requesting with a different top-frame origin should count as third-party
+  // Requesting with a different top-frame site should count as third-party
   // and recorded as CSS
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_b, origin_b);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_b, site_b);
   trans_info.possibly_top_frame_origin = origin_b;
 
   RunTransactionTestWithRequest(cache.http_cache(), transaction, trans_info,
@@ -10595,15 +10597,17 @@
 
   url::Origin origin_a = url::Origin::Create(GURL(kSimpleGET_Transaction.url));
   url::Origin origin_b = url::Origin::Create(GURL("http://b.com"));
+  SchemefulSite site_a(origin_a);
+  SchemefulSite site_b(origin_b);
 
   ScopedMockTransaction transaction(kSimpleGET_Transaction);
   transaction.response_headers = "Content-Type: application/javascript\n";
 
   MockHttpRequest trans_info = MockHttpRequest(transaction);
 
-  // Requesting with the same top-frame origin should not count as third-party
+  // Requesting with the same top-frame site should not count as third-party
   // but should still be recorded as JavaScript
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_a, site_a);
   trans_info.possibly_top_frame_origin = origin_a;
 
   RunTransactionTestWithRequest(cache.http_cache(), transaction, trans_info,
@@ -10613,9 +10617,9 @@
   histograms.ExpectTotalCount("HttpCache.Pattern.JavaScript", 1);
   histograms.ExpectTotalCount("HttpCache.Pattern.JavaScriptThirdParty", 0);
 
-  // Requesting with a different top-frame origin should count as third-party
+  // Requesting with a different top-frame site should count as third-party
   // and recorded as JavaScript
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_b, origin_b);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_b, site_b);
   trans_info.possibly_top_frame_origin = origin_b;
 
   RunTransactionTestWithRequest(cache.http_cache(), transaction, trans_info,
@@ -10632,15 +10636,17 @@
 
   url::Origin origin_a = url::Origin::Create(GURL(kSimpleGET_Transaction.url));
   url::Origin origin_b = url::Origin::Create(GURL("http://b.com"));
+  SchemefulSite site_a(origin_a);
+  SchemefulSite site_b(origin_b);
 
   ScopedMockTransaction transaction(kSimpleGET_Transaction);
   transaction.response_headers = "Content-Type: font/otf\n";
 
   MockHttpRequest trans_info = MockHttpRequest(transaction);
 
-  // Requesting with the same top-frame origin should not count as third-party
+  // Requesting with the same top-frame site should not count as third-party
   // but should still be recorded as a font
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_a, site_a);
   trans_info.possibly_top_frame_origin = origin_a;
 
   RunTransactionTestWithRequest(cache.http_cache(), transaction, trans_info,
@@ -10650,9 +10656,9 @@
   histograms.ExpectTotalCount("HttpCache.Pattern.Font", 1);
   histograms.ExpectTotalCount("HttpCache.Pattern.FontThirdParty", 0);
 
-  // Requesting with a different top-frame origin should count as third-party
+  // Requesting with a different top-frame site should count as third-party
   // and recorded as a font
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_b, origin_b);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_b, site_b);
   trans_info.possibly_top_frame_origin = origin_b;
 
   RunTransactionTestWithRequest(cache.http_cache(), transaction, trans_info,
@@ -10671,10 +10677,9 @@
   MockHttpCache cache;
   HttpResponseInfo response;
 
-  url::Origin origin_a = url::Origin::Create(GURL("http://a.com"));
-  url::Origin origin_b = url::Origin::Create(GURL("http://b.com"));
-  url::Origin origin_data =
-      url::Origin::Create(GURL("data:text/html,<body>Hello World</body>"));
+  SchemefulSite site_a(GURL("http://a.com"));
+  SchemefulSite site_b(GURL("http://b.com"));
+  SchemefulSite site_data(GURL("data:text/html,<body>Hello World</body>"));
 
   // A request without a top frame origin is not cached at all.
   MockHttpRequest trans_info = MockHttpRequest(kSimpleGET_Transaction);
@@ -10694,7 +10699,7 @@
 
   // Now request with a.com as the top frame origin. It shouldn't be cached
   // since the cached resource has a different top frame origin.
-  net::NetworkIsolationKey key_a(origin_a, origin_a);
+  net::NetworkIsolationKey key_a(site_a, site_a);
   trans_info.network_isolation_key = key_a;
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
@@ -10723,7 +10728,7 @@
   EXPECT_TRUE(response.was_cached);
 
   // Now request with b.com as the top frame origin. It shouldn't be cached.
-  trans_info.network_isolation_key = NetworkIsolationKey(origin_b, origin_b);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_b, site_b);
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
   EXPECT_FALSE(response.was_cached);
@@ -10741,8 +10746,7 @@
 
   // Now make a request with an opaque top frame origin.  It shouldn't be
   // cached.
-  trans_info.network_isolation_key =
-      NetworkIsolationKey(origin_data, origin_data);
+  trans_info.network_isolation_key = NetworkIsolationKey(site_data, site_data);
   EXPECT_TRUE(trans_info.network_isolation_key.ToString().empty());
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
@@ -10763,7 +10767,7 @@
                                               kUploadId);
 
   MockHttpRequest post_info = MockHttpRequest(kSimplePOST_Transaction);
-  post_info.network_isolation_key = NetworkIsolationKey(origin_a, origin_a);
+  post_info.network_isolation_key = NetworkIsolationKey(site_a, site_a);
   post_info.upload_data_stream = &upload_data_stream;
 
   RunTransactionTestWithRequest(cache.http_cache(), kSimplePOST_Transaction,
@@ -10779,10 +10783,10 @@
   MockHttpCache cache;
   HttpResponseInfo response;
 
-  url::Origin origin_a = url::Origin::Create(GURL("http://a.com"));
-  url::Origin origin_b = url::Origin::Create(GURL("http://b.com"));
+  SchemefulSite site_a(GURL("http://a.com"));
+  SchemefulSite site_b(GURL("http://b.com"));
   MockHttpRequest trans_info = MockHttpRequest(kSimpleGET_Transaction);
-  net::NetworkIsolationKey key_a(origin_a, origin_a);
+  net::NetworkIsolationKey key_a(site_a, site_a);
   trans_info.network_isolation_key = key_a;
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
@@ -10794,7 +10798,7 @@
                                 trans_info, &response);
   EXPECT_TRUE(response.was_cached);
 
-  net::NetworkIsolationKey key_b(origin_b, origin_b);
+  net::NetworkIsolationKey key_b(site_b, site_b);
   trans_info.network_isolation_key = key_b;
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
@@ -10822,10 +10826,10 @@
   HttpResponseInfo response;
   MockHttpRequest trans_info = MockHttpRequest(kSimpleGET_Transaction);
 
-  url::Origin origin_a = url::Origin::Create(GURL("http://a.foo.com"));
-  url::Origin origin_b = url::Origin::Create(GURL("http://b.foo.com"));
+  SchemefulSite site_a(GURL("http://a.foo.com"));
+  SchemefulSite site_b(GURL("http://b.foo.com"));
 
-  net::NetworkIsolationKey key_a(origin_a, origin_a);
+  net::NetworkIsolationKey key_a(site_a, site_a);
   trans_info.network_isolation_key = key_a;
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
@@ -10836,15 +10840,15 @@
 
   // The second request with a different origin but the same registrable domain
   // should be a cache hit.
-  net::NetworkIsolationKey key_b(origin_b, origin_b);
+  net::NetworkIsolationKey key_b(site_b, site_b);
   trans_info.network_isolation_key = key_b;
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
   EXPECT_TRUE(response.was_cached);
 
   // Request with a different registrable domain. It should be a cache miss.
-  url::Origin new_origin_a = url::Origin::Create(GURL("http://a.bar.com"));
-  net::NetworkIsolationKey new_key_a(new_origin_a, new_origin_a);
+  SchemefulSite new_site_a(GURL("http://a.bar.com"));
+  net::NetworkIsolationKey new_key_a(new_site_a, new_site_a);
   trans_info.network_isolation_key = new_key_a;
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
@@ -10874,8 +10878,8 @@
 
   // Now request with a.com as the top frame origin. It should use the same
   // cached object.
-  const auto kOriginA = url::Origin::Create(GURL("http://a.com/"));
-  trans_info.network_isolation_key = NetworkIsolationKey(kOriginA, kOriginA);
+  const SchemefulSite kSiteA(GURL("http://a.com/"));
+  trans_info.network_isolation_key = NetworkIsolationKey(kSiteA, kSiteA);
   RunTransactionTestWithRequest(cache.http_cache(), kSimpleGET_Transaction,
                                 trans_info, &response);
   EXPECT_TRUE(response.was_cached);
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index f3e9c972..a63c0c08 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -52,6 +52,7 @@
 #include "net/base/proxy_delegate.h"
 #include "net/base/proxy_server.h"
 #include "net/base/request_priority.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/base/test_proxy_delegate.h"
 #include "net/base/upload_bytes_element_reader.h"
@@ -580,8 +581,8 @@
   const CommonConnectJobParams dummy_connect_job_params_;
 
   const net::NetworkIsolationKey kNetworkIsolationKey =
-      NetworkIsolationKey(url::Origin::Create(GURL("https://foo.test/")),
-                          url::Origin::Create(GURL("https://bar.test/")));
+      NetworkIsolationKey(SchemefulSite(GURL("https://foo.test/")),
+                          SchemefulSite(GURL("https://bar.test/")));
 
   // These clocks are defined here, even though they're only used in the
   // Reporting tests below, since they need to be destroyed after
@@ -4405,10 +4406,10 @@
 // affects server credentials, not proxy credentials.
 TEST_F(HttpNetworkTransactionTest,
        BasicAuthProxyMatchesServerAuthWithNetworkIsolationKeyNoTunnel) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   // This test would need to use a single socket without this option enabled.
   // Best to use this option when it would affect a test, as it will eventually
@@ -4656,10 +4657,10 @@
 // Much like the test above, but uses tunnelled connections.
 TEST_F(HttpNetworkTransactionTest,
        BasicAuthProxyMatchesServerAuthWithNetworkIsolationKeyWithTunnel) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   // This test would need to use a single socket without this option enabled.
   // Best to use this option when it would affect a test, as it will eventually
@@ -6285,7 +6286,7 @@
 
 // Make sure that NetworkIsolationKeys are passed down to the proxy layer.
 TEST_F(HttpNetworkTransactionTest, ProxyResolvedWithNetworkIsolationKey) {
-  const url::Origin kOrigin = url::Origin::Create(GURL("https://foo.test/"));
+  const SchemefulSite kSite(GURL("https://foo.test/"));
 
   ProxyConfig proxy_config;
   proxy_config.set_auto_detect(true);
@@ -13343,10 +13344,10 @@
   session_deps_.http_server_properties =
       std::make_unique<HttpServerProperties>();
 
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   MockRead data_reads[] = {
       MockRead("HTTP/1.1 200 OK\r\n"),
@@ -22158,10 +22159,10 @@
 // same key, the second a different one. Checks that the requests are
 // partitioned across sockets as expected.
 TEST_F(HttpNetworkTransactionTest, NetworkIsolation) {
-  const auto kOrigin1 = url::Origin::Create(GURL("http://origin1/"));
-  const auto kOrigin2 = url::Origin::Create(GURL("http://origin2/"));
-  NetworkIsolationKey network_isolation_key1(kOrigin1, kOrigin1);
-  NetworkIsolationKey network_isolation_key2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("http://origin1/"));
+  const SchemefulSite kSite2(GURL("http://origin2/"));
+  NetworkIsolationKey network_isolation_key1(kSite1, kSite1);
+  NetworkIsolationKey network_isolation_key2(kSite2, kSite2);
 
   for (bool partition_connections : {false, true}) {
     SCOPED_TRACE(partition_connections);
@@ -22306,10 +22307,10 @@
 }
 
 TEST_F(HttpNetworkTransactionTest, NetworkIsolationH2) {
-  const auto kOrigin1 = url::Origin::Create(GURL("http://origin1/"));
-  const auto kOrigin2 = url::Origin::Create(GURL("http://origin2/"));
-  NetworkIsolationKey network_isolation_key1(kOrigin1, kOrigin1);
-  NetworkIsolationKey network_isolation_key2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("http://origin1/"));
+  const SchemefulSite kSite2(GURL("http://origin2/"));
+  NetworkIsolationKey network_isolation_key1(kSite1, kSite1);
+  NetworkIsolationKey network_isolation_key2(kSite2, kSite2);
 
   // Whether to use an H2 proxy. When false, uses HTTPS H2 requests without a
   // proxy, when true, uses HTTP requests over an H2 proxy. It's unnecessary to
@@ -22536,12 +22537,12 @@
     kDontUsePreconnect,
   };
 
-  const auto kOrigin1 = url::Origin::Create(GURL("http://origin1/"));
-  const auto kOrigin2 = url::Origin::Create(GURL("http://origin2/"));
-  const auto kOrigin3 = url::Origin::Create(GURL("http://origin3/"));
-  NetworkIsolationKey preconnect1_isolation_key(kOrigin1, kOrigin1);
-  NetworkIsolationKey preconnect2_isolation_key(kOrigin2, kOrigin2);
-  NetworkIsolationKey not_preconnected_isolation_key(kOrigin3, kOrigin3);
+  const SchemefulSite kSite1(GURL("http://origin1/"));
+  const SchemefulSite kSite2(GURL("http://origin2/"));
+  const SchemefulSite kSite3(GURL("http://origin3/"));
+  NetworkIsolationKey preconnect1_isolation_key(kSite1, kSite1);
+  NetworkIsolationKey preconnect2_isolation_key(kSite2, kSite2);
+  NetworkIsolationKey not_preconnected_isolation_key(kSite3, kSite3);
 
   // Test that only preconnects with
   for (TestCase test_case :
@@ -22656,10 +22657,10 @@
        features::kPartitionSSLSessionsByNetworkIsolationKey},
       {});
 
-  const auto kOrigin1 = url::Origin::Create(GURL("http://origin1/"));
-  const auto kOrigin2 = url::Origin::Create(GURL("http://origin2/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("http://origin1/"));
+  const SchemefulSite kSite2(GURL("http://origin2/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
   std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
 
   // The server always sends Connection: close, so each request goes over a
@@ -22778,10 +22779,10 @@
       ConfiguredProxyResolutionService::CreateFixed(
           "https://myproxy:70", TRAFFIC_ANNOTATION_FOR_TESTS);
 
-  const auto kOrigin1 = url::Origin::Create(GURL("http://origin1/"));
-  const auto kOrigin2 = url::Origin::Create(GURL("http://origin2/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("http://origin1/"));
+  const SchemefulSite kSite2(GURL("http://origin2/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
   std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
 
   // Make both a tunneled and non-tunneled request.
diff --git a/net/http/http_server_properties_manager_unittest.cc b/net/http/http_server_properties_manager_unittest.cc
index 39c91d0..9a49069 100644
--- a/net/http/http_server_properties_manager_unittest.cc
+++ b/net/http/http_server_properties_manager_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/values.h"
 #include "net/base/features.h"
 #include "net/base/ip_address.h"
+#include "net/base/schemeful_site.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_server_properties.h"
 #include "net/test/test_with_task_environment.h"
@@ -2080,10 +2081,9 @@
 }
 
 TEST_F(HttpServerPropertiesManagerTest, NetworkIsolationKeyServerInfo) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const url::Origin kOpaqueOrigin =
-      url::Origin::Create(GURL("data:text/plain,Hello World"));
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const SchemefulSite kOpaqueSite(GURL("data:text/plain,Hello World"));
   const url::SchemeHostPort kServer("https", "baz.test", 443);
   const url::SchemeHostPort kServer2("https", "zab.test", 443);
 
@@ -2114,7 +2114,7 @@
       // to make sure to call the constructor after setting up the feature
       // above.
       HttpServerProperties::ServerInfoMapKey server_info_key(
-          kServer, NetworkIsolationKey(kOrigin1, kOrigin2),
+          kServer, NetworkIsolationKey(kSite1, kSite2),
           use_network_isolation_key);
       server_info_map.Put(server_info_key, server_info);
 
@@ -2123,7 +2123,7 @@
       // are only meaningful within a browsing session.
       if (use_network_isolation_key) {
         HttpServerProperties::ServerInfoMapKey server_info_key2(
-            kServer2, NetworkIsolationKey(kOpaqueOrigin, kOpaqueOrigin),
+            kServer2, NetworkIsolationKey(kOpaqueSite, kOpaqueSite),
             use_network_isolation_key);
         server_info_map.Put(server_info_key2, server_info);
       }
@@ -2164,7 +2164,7 @@
         const HttpServerProperties::ServerInfo& server_info2 =
             server_info_map2->begin()->second;
         EXPECT_EQ(kServer, server_info_key2.server);
-        EXPECT_EQ(NetworkIsolationKey(kOrigin1, kOrigin2),
+        EXPECT_EQ(NetworkIsolationKey(kSite1, kSite2),
                   server_info_key2.network_isolation_key);
         EXPECT_EQ(server_info, server_info2);
       } else {
@@ -2179,14 +2179,13 @@
 // Tests a full round trip with a NetworkIsolationKey, using the
 // HttpServerProperties interface.
 TEST_F(HttpServerPropertiesManagerTest, NetworkIsolationKeyIntegration) {
-  const url::Origin kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
   const url::SchemeHostPort kServer("https", "baz.test", 443);
 
-  const url::Origin kOpaqueOrigin =
-      url::Origin::Create(GURL("data:text/plain,Hello World"));
-  const NetworkIsolationKey kOpaqueOriginNetworkIsolationKey(kOpaqueOrigin,
-                                                             kOpaqueOrigin);
+  const SchemefulSite kOpaqueSite(GURL("data:text/plain,Hello World"));
+  const NetworkIsolationKey kOpaqueSiteNetworkIsolationKey(kOpaqueSite,
+                                                           kOpaqueSite);
   const url::SchemeHostPort kServer2("https", "zab.test", 443);
 
   base::test::ScopedFeatureList feature_list;
@@ -2208,15 +2207,15 @@
   properties->SetSupportsSpdy(kServer, kNetworkIsolationKey, true);
   EXPECT_TRUE(properties->GetSupportsSpdy(kServer, kNetworkIsolationKey));
   EXPECT_FALSE(
-      properties->GetSupportsSpdy(kServer, kOpaqueOriginNetworkIsolationKey));
+      properties->GetSupportsSpdy(kServer, kOpaqueSiteNetworkIsolationKey));
   EXPECT_FALSE(properties->GetSupportsSpdy(kServer, NetworkIsolationKey()));
 
   // Opaque origins should works with HttpServerProperties, but not be persisted
   // to disk.
-  properties->SetSupportsSpdy(kServer2, kOpaqueOriginNetworkIsolationKey, true);
+  properties->SetSupportsSpdy(kServer2, kOpaqueSiteNetworkIsolationKey, true);
   EXPECT_FALSE(properties->GetSupportsSpdy(kServer2, kNetworkIsolationKey));
   EXPECT_TRUE(
-      properties->GetSupportsSpdy(kServer2, kOpaqueOriginNetworkIsolationKey));
+      properties->GetSupportsSpdy(kServer2, kOpaqueSiteNetworkIsolationKey));
   EXPECT_FALSE(properties->GetSupportsSpdy(kServer2, NetworkIsolationKey()));
 
   // Wait until the data's been written to prefs, and then tear down the
@@ -2238,14 +2237,14 @@
   // HttpServerProperties.
   EXPECT_TRUE(properties->GetSupportsSpdy(kServer, kNetworkIsolationKey));
   EXPECT_FALSE(
-      properties->GetSupportsSpdy(kServer, kOpaqueOriginNetworkIsolationKey));
+      properties->GetSupportsSpdy(kServer, kOpaqueSiteNetworkIsolationKey));
   EXPECT_FALSE(properties->GetSupportsSpdy(kServer, NetworkIsolationKey()));
 
-  // The information set using kOpaqueOriginNetworkIsolationKey should not have
+  // The information set using kOpaqueSiteNetworkIsolationKey should not have
   // been restored.
   EXPECT_FALSE(properties->GetSupportsSpdy(kServer2, kNetworkIsolationKey));
   EXPECT_FALSE(
-      properties->GetSupportsSpdy(kServer2, kOpaqueOriginNetworkIsolationKey));
+      properties->GetSupportsSpdy(kServer2, kOpaqueSiteNetworkIsolationKey));
   EXPECT_FALSE(properties->GetSupportsSpdy(kServer2, NetworkIsolationKey()));
 }
 
@@ -2254,10 +2253,10 @@
 // canonical suffix logic.
 TEST_F(HttpServerPropertiesManagerTest,
        CanonicalSuffixRoundTripWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
   // Three servers with the same canonical suffix (".c.youtube.com").
   const url::SchemeHostPort kServer1("https", "foo.c.youtube.com", 443);
   const url::SchemeHostPort kServer2("https", "bar.c.youtube.com", 443);
@@ -2421,8 +2420,8 @@
 // HttpServerProperties interface and setting alternative services as broken.
 TEST_F(HttpServerPropertiesManagerTest,
        NetworkIsolationKeyBrokenAltServiceRoundTrip) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo1.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://foo2.test/"));
+  const SchemefulSite kSite1(GURL("https://foo1.test/"));
+  const SchemefulSite kSite2(GURL("https://foo2.test/"));
 
   const AlternativeService kAlternativeService1(kProtoHTTP2,
                                                 "alt.service1.test", 443);
@@ -2441,8 +2440,8 @@
 
       // The NetworkIsolationKey constructor checks the field trial state, so
       // need to create the keys only after setting up the field trials.
-      const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-      const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+      const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+      const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
       // Create and initialize an HttpServerProperties, must be done after
       // setting the feature.
@@ -2520,8 +2519,8 @@
 
       // The NetworkIsolationKey constructor checks the field trial state, so
       // need to create the keys only after setting up the field trials.
-      const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-      const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+      const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+      const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
       // Create a new HttpServerProperties, loading the data from before.
       std::unique_ptr<MockPrefDelegate> pref_delegate =
@@ -2631,9 +2630,8 @@
 // Make sure broken alt services with opaque origins aren't saved.
 TEST_F(HttpServerPropertiesManagerTest,
        NetworkIsolationKeyBrokenAltServiceOpaqueOrigin) {
-  const url::Origin kOpaqueOrigin =
-      url::Origin::Create(GURL("data:text/plain,Hello World"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOpaqueOrigin, kOpaqueOrigin);
+  const SchemefulSite kOpaqueSite(GURL("data:text/plain,Hello World"));
+  const NetworkIsolationKey kNetworkIsolationKey(kOpaqueSite, kOpaqueSite);
   const AlternativeService kAlternativeService(kProtoHTTP2, "alt.service1.test",
                                                443);
 
@@ -2677,8 +2675,8 @@
 // HttpServerProperties interface and setting QuicServerInfo.
 TEST_F(HttpServerPropertiesManagerTest,
        NetworkIsolationKeyQuicServerInfoRoundTrip) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo1.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://foo2.test/"));
+  const SchemefulSite kSite1(GURL("https://foo1.test/"));
+  const SchemefulSite kSite2(GURL("https://foo2.test/"));
 
   const quic::QuicServerId kServer1("foo", 443,
                                     false /* privacy_mode_enabled */);
@@ -2701,8 +2699,8 @@
 
       // The NetworkIsolationKey constructor checks the field trial state, so
       // need to create the keys only after setting up the field trials.
-      const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-      const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+      const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+      const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
       // Create and initialize an HttpServerProperties, must be done after
       // setting the feature.
@@ -2766,8 +2764,8 @@
 
       // The NetworkIsolationKey constructor checks the field trial state, so
       // need to create the keys only after setting up the field trials.
-      const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-      const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+      const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+      const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
       // Create a new HttpServerProperties, loading the data from before.
       std::unique_ptr<MockPrefDelegate> pref_delegate =
@@ -2850,10 +2848,10 @@
 // interactions with the canonical suffix logic.
 TEST_F(HttpServerPropertiesManagerTest,
        NetworkIsolationKeyQuicServerInfoCanonicalSuffixRoundTrip) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   // Three servers with the same canonical suffix (".c.youtube.com").
   const quic::QuicServerId kServer1("foo.c.youtube.com", 443,
@@ -2963,9 +2961,8 @@
 // origins aren't saved.
 TEST_F(HttpServerPropertiesManagerTest,
        NetworkIsolationKeyQuicServerInfoOpaqueOrigin) {
-  const url::Origin kOpaqueOrigin =
-      url::Origin::Create(GURL("data:text/plain,Hello World"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOpaqueOrigin, kOpaqueOrigin);
+  const SchemefulSite kOpaqueSite(GURL("data:text/plain,Hello World"));
+  const NetworkIsolationKey kNetworkIsolationKey(kOpaqueSite, kOpaqueSite);
   const quic::QuicServerId kServer("foo", 443,
                                    false /* privacy_mode_enabled */);
 
diff --git a/net/http/http_server_properties_unittest.cc b/net/http/http_server_properties_unittest.cc
index b7f6901..910a3ad 100644
--- a/net/http/http_server_properties_unittest.cc
+++ b/net/http/http_server_properties_unittest.cc
@@ -20,6 +20,7 @@
 #include "net/base/features.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/ip_address.h"
+#include "net/base/schemeful_site.h"
 #include "net/http/http_network_session.h"
 #include "net/test/test_with_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -89,10 +90,10 @@
     // Set |test_clock_| to some random time.
     test_clock_.Advance(base::TimeDelta::FromSeconds(12345));
 
-    url::Origin origin1 = url::Origin::Create(GURL("https://foo.test/"));
-    network_isolation_key1_ = NetworkIsolationKey(origin1, origin1);
-    url::Origin origin2 = url::Origin::Create(GURL("https://bar.test/"));
-    network_isolation_key2_ = NetworkIsolationKey(origin2, origin2);
+    SchemefulSite site1(GURL("https://foo.test/"));
+    network_isolation_key1_ = NetworkIsolationKey(site1, site1);
+    SchemefulSite site2(GURL("https://bar.test/"));
+    network_isolation_key2_ = NetworkIsolationKey(site2, site2);
   }
 
   bool HasAlternativeService(const url::SchemeHostPort& origin,
diff --git a/net/http/http_stream_factory_job_controller_unittest.cc b/net/http/http_stream_factory_job_controller_unittest.cc
index 00ca59d..c3f150de 100644
--- a/net/http/http_stream_factory_job_controller_unittest.cc
+++ b/net/http/http_stream_factory_job_controller_unittest.cc
@@ -24,6 +24,7 @@
 #include "net/base/features.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/proxy_server.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_proxy_delegate.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/http/http_basic_stream.h"
@@ -54,7 +55,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "url/origin.h"
+#include "url/gurl.h"
 
 using ::testing::_;
 using ::testing::Contains;
@@ -2135,10 +2136,10 @@
   session_deps_.http_server_properties =
       std::make_unique<HttpServerProperties>();
 
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   tcp_data_ = std::make_unique<SequencedSocketData>();
   tcp_data_->set_connect_data(MockConnect(ASYNC, OK));
@@ -2350,10 +2351,10 @@
   session_deps_.http_server_properties =
       std::make_unique<HttpServerProperties>();
 
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   tcp_data_ = std::make_unique<SequencedSocketData>(
       MockConnect(SYNCHRONOUS, ERR_IO_PENDING), base::span<MockRead>(),
@@ -2955,8 +2956,8 @@
   Initialize(request_info);
   url::SchemeHostPort origin(request_info.url);
   NetworkIsolationKey network_isolation_key(
-      url::Origin::Create(GURL("https://example.com")),
-      url::Origin::Create(GURL("https://example.com")));
+      SchemefulSite(GURL("https://example.com")),
+      SchemefulSite(GURL("https://example.com")));
   scoped_refptr<HttpResponseHeaders> headers(
       base::MakeRefCounted<HttpResponseHeaders>(""));
   headers->AddHeader("alt-svc", alt_svc_header);
diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc
index b8af6f2..929ed191 100644
--- a/net/http/http_stream_factory_unittest.cc
+++ b/net/http/http_stream_factory_unittest.cc
@@ -26,6 +26,7 @@
 #include "net/base/port_util.h"
 #include "net/base/privacy_mode.h"
 #include "net/base/proxy_server.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/base/test_proxy_delegate.h"
 #include "net/cert/ct_policy_enforcer.h"
@@ -80,7 +81,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "url/origin.h"
+#include "url/gurl.h"
 
 // This file can be included from net/http even though
 // it is in net/websockets because it doesn't
@@ -657,10 +658,10 @@
   peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
   const GURL kURL("http://foo.test/");
-  const auto kOriginFoo = url::Origin::Create(GURL("http://foo.test"));
-  const auto kOriginBar = url::Origin::Create(GURL("http://bar.test"));
-  const NetworkIsolationKey kKey1(kOriginFoo, kOriginFoo);
-  const NetworkIsolationKey kKey2(kOriginBar, kOriginBar);
+  SchemefulSite kSiteFoo(GURL("http://foo.test"));
+  SchemefulSite kSiteBar(GURL("http://bar.test"));
+  const NetworkIsolationKey kKey1(kSiteFoo, kSiteFoo);
+  const NetworkIsolationKey kKey2(kSiteBar, kSiteBar);
   PreconnectHelperForURL(1, kURL, kKey1, false /* disable_secure_dns */,
                          session.get());
   EXPECT_EQ(1, transport_conn_pool->last_num_streams());
@@ -695,8 +696,8 @@
   peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
   const GURL kURL("http://foo.test/");
-  const auto kOriginFoo = url::Origin::Create(GURL("http://foo.test"));
-  const auto kOriginBar = url::Origin::Create(GURL("http://bar.test"));
+  SchemefulSite kSiteFoo(GURL("http://foo.test"));
+  SchemefulSite kSiteBar(GURL("http://bar.test"));
   PreconnectHelperForURL(1, kURL, NetworkIsolationKey(),
                          false /* disable_secure_dns */, session.get());
   EXPECT_EQ(1, transport_conn_pool->last_num_streams());
@@ -1665,10 +1666,10 @@
 // should probably be merged into the above test.
 TEST_F(HttpStreamFactoryTest,
        RequestSpdyHttpStreamHttpURLWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
@@ -3181,10 +3182,10 @@
   session_ =
       std::make_unique<HttpNetworkSession>(session_params_, session_context_);
   url::SchemeHostPort origin(url::kHttpsScheme, "example.com", 443);
-  ;
+
   NetworkIsolationKey network_isolation_key(
-      url::Origin::Create(GURL("https://example.com")),
-      url::Origin::Create(GURL("https://example.com")));
+      SchemefulSite(GURL("https://example.com")),
+      SchemefulSite(GURL("https://example.com")));
 
   http_server_properties_.SetAlternativeServices(
       origin, network_isolation_key,
@@ -3224,8 +3225,8 @@
   url::SchemeHostPort origin(url::kHttpsScheme, "example.com", 443);
 
   NetworkIsolationKey network_isolation_key(
-      url::Origin::Create(GURL("https://example.com")),
-      url::Origin::Create(GURL("https://example.com")));
+      SchemefulSite(GURL("https://example.com")),
+      SchemefulSite(GURL("https://example.com")));
 
   scoped_refptr<HttpResponseHeaders> headers(
       base::MakeRefCounted<HttpResponseHeaders>(""));
@@ -3260,8 +3261,8 @@
   url::SchemeHostPort origin(url::kHttpsScheme, "example.com", 443);
 
   NetworkIsolationKey network_isolation_key(
-      url::Origin::Create(GURL("https://example.com")),
-      url::Origin::Create(GURL("https://example.com")));
+      SchemefulSite(GURL("https://example.com")),
+      SchemefulSite(GURL("https://example.com")));
 
   scoped_refptr<HttpResponseHeaders> headers(
       base::MakeRefCounted<HttpResponseHeaders>(""));
@@ -3290,8 +3291,8 @@
   url::SchemeHostPort origin(url::kHttpsScheme, "example.com", 443);
 
   NetworkIsolationKey network_isolation_key(
-      url::Origin::Create(GURL("https://example.com")),
-      url::Origin::Create(GURL("https://example.com")));
+      SchemefulSite(GURL("https://example.com")),
+      SchemefulSite(GURL("https://example.com")));
 
   scoped_refptr<HttpResponseHeaders> headers(
       base::MakeRefCounted<HttpResponseHeaders>(""));
@@ -3328,8 +3329,8 @@
   url::SchemeHostPort origin(url::kHttpsScheme, "example.com", 443);
 
   NetworkIsolationKey network_isolation_key(
-      url::Origin::Create(GURL("https://example.com")),
-      url::Origin::Create(GURL("https://example.com")));
+      SchemefulSite(GURL("https://example.com")),
+      SchemefulSite(GURL("https://example.com")));
 
   scoped_refptr<HttpResponseHeaders> headers(
       base::MakeRefCounted<HttpResponseHeaders>(""));
diff --git a/net/http/http_transaction_test_util.cc b/net/http/http_transaction_test_util.cc
index 90235d2..2e4b0ef 100644
--- a/net/http/http_transaction_test_util.cc
+++ b/net/http/http_transaction_test_util.cc
@@ -23,6 +23,7 @@
 #include "net/base/load_timing_info.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/cert/x509_certificate.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/http_cache.h"
@@ -33,6 +34,7 @@
 #include "net/log/net_log_source.h"
 #include "net/log/net_log_with_source.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 namespace net {
 
@@ -192,8 +194,8 @@
   method = t.method;
   extra_headers.AddHeadersFromString(t.request_headers);
   load_flags = t.load_flags;
-  url::Origin origin = url::Origin::Create(url);
-  network_isolation_key = NetworkIsolationKey(origin, origin);
+  SchemefulSite site(url);
+  network_isolation_key = NetworkIsolationKey(site, site);
 }
 
 std::string MockHttpRequest::CacheKey() {
diff --git a/net/http/transport_security_persister_unittest.cc b/net/http/transport_security_persister_unittest.cc
index 6dbd628..3797a7c8 100644
--- a/net/http/transport_security_persister_unittest.cc
+++ b/net/http/transport_security_persister_unittest.cc
@@ -18,11 +18,11 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "net/base/features.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/http/transport_security_state.h"
 #include "net/test/test_with_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace net {
 
@@ -462,11 +462,10 @@
 
   const GURL report_uri(kReportUri);
   static const char kTestDomain[] = "example.test";
-  const url::Origin kOrigin =
-      url::Origin::Create(GURL("https://somewhere.else.test"));
+  const SchemefulSite kSite(GURL("https://somewhere.else.test"));
   const NetworkIsolationKey empty_network_isolation_key;
-  const NetworkIsolationKey network_isolation_key(kOrigin /* top_frame_origin*/,
-                                                  kOrigin /* frame_origin*/);
+  const NetworkIsolationKey network_isolation_key(kSite /* top_frame_site */,
+                                                  kSite /* frame_site */);
   const NetworkIsolationKey transient_network_isolation_key =
       NetworkIsolationKey::CreateTransient();
 
@@ -552,11 +551,10 @@
 
   const GURL report_uri(kReportUri);
   static const char kTestDomain[] = "example.test";
-  const url::Origin kOrigin =
-      url::Origin::Create(GURL("https://somewhere.else.test"));
+  const SchemefulSite kSite(GURL("https://somewhere.else.test"));
   const NetworkIsolationKey empty_network_isolation_key;
-  const NetworkIsolationKey network_isolation_key(kOrigin /* top_frame_origin*/,
-                                                  kOrigin /* frame_origin*/);
+  const NetworkIsolationKey network_isolation_key(kSite /* top_frame_site */,
+                                                  kSite /* frame_site */);
   const base::Time current_time(base::Time::Now());
   const base::Time expiry1 = current_time + base::TimeDelta::FromSeconds(1000);
   const base::Time expiry2 = current_time + base::TimeDelta::FromSeconds(2000);
diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc
index fda88d2..9ffa34a 100644
--- a/net/http/transport_security_state_unittest.cc
+++ b/net/http/transport_security_state_unittest.cc
@@ -29,6 +29,7 @@
 #include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/cert/asn1_util.h"
 #include "net/cert/cert_verifier.h"
@@ -49,6 +50,7 @@
 #include "net/tools/huffman_trie/trie/trie_bit_buffer.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/origin.h"
 
 namespace net {
 
@@ -340,10 +342,9 @@
 NetworkIsolationKey CreateUniqueNetworkIsolationKey(bool is_transient) {
   if (is_transient)
     return NetworkIsolationKey::CreateTransient();
-  url::Origin origin = url::Origin::CreateFromNormalizedTuple(
-      "https", CreateUniqueHostName(), 443);
-  return NetworkIsolationKey(origin /* top_frame_origin */,
-                             origin /* frame_origin */);
+  SchemefulSite site = SchemefulSite(url::Origin::CreateFromNormalizedTuple(
+      "https", CreateUniqueHostName(), 443));
+  return NetworkIsolationKey(site /* top_frame_site */, site /* frame_site */);
 }
 
 }  // namespace
diff --git a/net/network_error_logging/mock_persistent_nel_store_unittest.cc b/net/network_error_logging/mock_persistent_nel_store_unittest.cc
index 8914640..bbc1e8d 100644
--- a/net/network_error_logging/mock_persistent_nel_store_unittest.cc
+++ b/net/network_error_logging/mock_persistent_nel_store_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/strings/strcat.h"
 #include "base/test/bind.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/network_error_logging/mock_persistent_nel_store.h"
 #include "net/network_error_logging/network_error_logging_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -57,8 +58,8 @@
   const url::Origin origin_ =
       url::Origin::Create(GURL("https://example.test/"));
   const NetworkIsolationKey network_isolation_key_ =
-      NetworkIsolationKey(url::Origin::Create(GURL("https://foo.test/")),
-                          url::Origin::Create(GURL("https://bar.test/")));
+      NetworkIsolationKey(SchemefulSite(GURL("https://foo.test/")),
+                          SchemefulSite(GURL("https://bar.test/")));
   const NetworkErrorLoggingService::NelPolicy nel_policy_ =
       MakePolicy(origin_, network_isolation_key_);
 };
diff --git a/net/network_error_logging/network_error_logging_service_unittest.cc b/net/network_error_logging/network_error_logging_service_unittest.cc
index 147f6f2..12057dec 100644
--- a/net/network_error_logging/network_error_logging_service_unittest.cc
+++ b/net/network_error_logging/network_error_logging_service_unittest.cc
@@ -18,6 +18,7 @@
 #include "net/base/features.h"
 #include "net/base/ip_address.h"
 #include "net/base/net_errors.h"
+#include "net/base/schemeful_site.h"
 #include "net/network_error_logging/mock_persistent_nel_store.h"
 #include "net/network_error_logging/network_error_logging_service.h"
 #include "net/reporting/reporting_test_util.h"
@@ -124,9 +125,9 @@
     return url::Origin::Create(url);
   }
   NetworkIsolationKey MakeNetworkIsolationKey(size_t index) {
-    url::Origin origin = url::Origin::Create(
+    SchemefulSite site(
         GURL(base::StringPrintf("https://example%zd.com/", (index + 1) / 2)));
-    return NetworkIsolationKey(origin, origin);
+    return NetworkIsolationKey(site, site);
   }
 
   NetworkErrorLoggingService::NelPolicy MakePolicy(
@@ -181,9 +182,11 @@
   const url::Origin kOriginDifferentHost_ =
       url::Origin::Create(kUrlDifferentHost_);
   const url::Origin kOriginEtld_ = url::Origin::Create(kUrlEtld_);
-  const NetworkIsolationKey kNik_ = NetworkIsolationKey(kOrigin_, kOrigin_);
+  const NetworkIsolationKey kNik_ =
+      NetworkIsolationKey(SchemefulSite(kOrigin_), SchemefulSite(kOrigin_));
   const NetworkIsolationKey kOtherNik_ =
-      NetworkIsolationKey(kOriginDifferentHost_, kOriginDifferentHost_);
+      NetworkIsolationKey(SchemefulSite(kOriginDifferentHost_),
+                          SchemefulSite(kOriginDifferentHost_));
 
   const std::string kHeader_ = "{\"report_to\":\"group\",\"max_age\":86400}";
   const std::string kHeaderSuccessFraction0_ =
diff --git a/net/nqe/network_quality_estimator_util_unittest.cc b/net/nqe/network_quality_estimator_util_unittest.cc
index cfe043a..1457fe7 100644
--- a/net/nqe/network_quality_estimator_util_unittest.cc
+++ b/net/nqe/network_quality_estimator_util_unittest.cc
@@ -14,6 +14,7 @@
 #include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/context_host_resolver.h"
 #include "net/dns/host_resolver.h"
@@ -21,7 +22,6 @@
 #include "net/log/test_net_log.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace net {
 
@@ -143,8 +143,8 @@
 // provided to it.
 TEST(NetworkQualityEstimatorUtilTest,
      MAYBE_ReservedHostUncachedWithNetworkIsolationKey) {
-  const url::Origin kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(
diff --git a/net/nqe/throughput_analyzer_unittest.cc b/net/nqe/throughput_analyzer_unittest.cc
index 5bd2bdc..2df3d1d 100644
--- a/net/nqe/throughput_analyzer_unittest.cc
+++ b/net/nqe/throughput_analyzer_unittest.cc
@@ -30,6 +30,7 @@
 #include "build/build_config.h"
 #include "net/base/features.h"
 #include "net/base/isolation_info.h"
+#include "net/base/schemeful_site.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/log/test_net_log.h"
 #include "net/nqe/network_quality_estimator.h"
@@ -42,7 +43,6 @@
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace net {
 
@@ -181,8 +181,8 @@
 // Make sure that the NetworkIsolationKey is respected when resolving a host
 // from the cache.
 TEST_F(ThroughputAnalyzerTest, MAYBE_MaximumRequestsWithNetworkIsolationKey) {
-  const url::Origin kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
   const GURL kUrl = GURL("http://foo.test/test.html");
 
   base::test::ScopedFeatureList feature_list;
diff --git a/net/proxy_resolution/multi_threaded_proxy_resolver_unittest.cc b/net/proxy_resolution/multi_threaded_proxy_resolver_unittest.cc
index 2a7150a..41930536 100644
--- a/net/proxy_resolution/multi_threaded_proxy_resolver_unittest.cc
+++ b/net/proxy_resolution/multi_threaded_proxy_resolver_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/threading/thread_checker_impl.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_with_source.h"
@@ -32,7 +33,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 using net::test::IsError;
 using net::test::IsOk;
@@ -489,8 +489,8 @@
 
 // Make sure the NetworkIsolationKey makes it to the resolver.
 TEST_F(MultiThreadedProxyResolverTest, SingleThread_WithNetworkIsolationKey) {
-  const url::Origin kOrigin(url::Origin::Create(GURL("https://origin.test/")));
-  const net::NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://origin.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
   const GURL kUrl("https://url.test/");
 
   const size_t kNumThreads = 1u;
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index 7141f8c..92f44ac 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -17,6 +17,7 @@
 #include "build/build_config.h"
 #include "net/base/features.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/cert/cert_verify_result.h"
 #include "net/http/transport_security_state.h"
@@ -68,6 +69,7 @@
 #include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "url/gurl.h"
 
 using testing::_;
 
@@ -1615,7 +1617,7 @@
       QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
                      NetworkIsolationKey(), false /* disable_secure_dns */)));
 
-  const auto kOriginFoo = url::Origin::Create(GURL("http://foo.test/"));
+  const SchemefulSite kSiteFoo(GURL("http://foo.test/"));
 
   // Check that NetworkIsolationKey is respected when feature is enabled.
   {
@@ -1625,7 +1627,7 @@
     EXPECT_TRUE(session_->CanPool(
         "mail.example.com",
         QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
-                       NetworkIsolationKey(kOriginFoo, kOriginFoo),
+                       NetworkIsolationKey(kSiteFoo, kSiteFoo),
                        false /* disable_secure_dns */)));
   }
   {
@@ -1635,7 +1637,7 @@
     EXPECT_FALSE(session_->CanPool(
         "mail.example.com",
         QuicSessionKey("foo", 1234, PRIVACY_MODE_DISABLED, SocketTag(),
-                       NetworkIsolationKey(kOriginFoo, kOriginFoo),
+                       NetworkIsolationKey(kSiteFoo, kSiteFoo),
                        false /* disable_secure_dns */)));
   }
 }
@@ -1725,10 +1727,10 @@
   feature_list.InitAndEnableFeature(
       features::kPartitionConnectionsByNetworkIsolationKey);
 
-  const auto kOriginFoo = url::Origin::Create(GURL("http://foo.test/"));
-  const auto kOriginBar = url::Origin::Create(GURL("http://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOriginFoo, kOriginFoo);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOriginBar, kOriginBar);
+  const SchemefulSite kSiteFoo(GURL("http://foo.test/"));
+  const SchemefulSite kSiteBar(GURL("http://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSiteFoo, kSiteFoo);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSiteBar, kSiteBar);
 
   session_key_ = QuicSessionKey(
       kServerHostname, kServerPort, PRIVACY_MODE_DISABLED, SocketTag(),
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index ae710d1..3776bdbe 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -25,6 +25,7 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/mock_network_change_notifier.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/base/test_proxy_delegate.h"
 #include "net/cert/ct_policy_enforcer.h"
@@ -92,7 +93,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 using ::testing::ElementsAre;
 using ::testing::Key;
@@ -2032,10 +2032,10 @@
   // one.
   http_server_properties_ = std::make_unique<HttpServerProperties>();
 
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   MockRead http_reads[] = {
       MockRead("HTTP/1.1 200 OK\r\n"), MockRead(kQuicAlternativeServiceHeader),
@@ -2855,10 +2855,10 @@
     return;
   }
 
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
@@ -3601,10 +3601,10 @@
 // Much like above test, but verifies that NetworkIsolationKey is respected.
 TEST_P(QuicNetworkTransactionTest,
        ProtocolErrorAfterHandshakeConfirmedThenBrokenWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
@@ -4597,10 +4597,10 @@
 
 TEST_P(QuicNetworkTransactionTest,
        ConfirmAlternativeServiceWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
@@ -5448,10 +5448,10 @@
 
 TEST_P(QuicNetworkTransactionTest,
        BrokenAlternateProtocolWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
@@ -5776,10 +5776,10 @@
   // one.
   http_server_properties_ = std::make_unique<HttpServerProperties>();
 
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   // Alternate-protocol job
   MockRead quic_reads[] = {
@@ -8984,10 +8984,10 @@
 // Test that NetworkIsolationKey is respected by QUIC connections, when
 // kPartitionConnectionsByNetworkIsolationKey is enabled.
 TEST_P(QuicNetworkTransactionTest, NetworkIsolation) {
-  const auto kOrigin1 = url::Origin::Create(GURL("http://origin1/"));
-  const auto kOrigin2 = url::Origin::Create(GURL("http://origin2/"));
-  NetworkIsolationKey network_isolation_key1(kOrigin1, kOrigin1);
-  NetworkIsolationKey network_isolation_key2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("http://origin1/"));
+  const SchemefulSite kSite2(GURL("http://origin2/"));
+  NetworkIsolationKey network_isolation_key1(kSite1, kSite1);
+  NetworkIsolationKey network_isolation_key2(kSite2, kSite2);
 
   context_.params()->origins_to_force_quic_on.insert(
       HostPortPair::FromString("mail.example.org:443"));
@@ -9375,8 +9375,8 @@
   CheckResponseData(&trans, "0123456789");
 
   HttpRequestInfo request2;
-  const auto kOrigin1 = url::Origin::Create(GURL("http://origin1/"));
-  request_.network_isolation_key = NetworkIsolationKey(kOrigin1, kOrigin1);
+  const SchemefulSite kSite1(GURL("http://origin1/"));
+  request_.network_isolation_key = NetworkIsolationKey(kSite1, kSite1);
   HttpNetworkTransaction trans2(DEFAULT_PRIORITY, session_.get());
   RunTransaction(&trans2);
   CheckResponseData(&trans2, "0123456789");
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 5461653a..7839158 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -24,6 +24,7 @@
 #include "net/base/load_flags.h"
 #include "net/base/mock_network_change_notifier.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/do_nothing_ct_verifier.h"
 #include "net/cert/mock_cert_verifier.h"
@@ -564,10 +565,10 @@
   // NetworkIsolationKeys, but the same server. If false, stores data for two
   // different servers, using the same NetworkIsolationKey.
   void VerifyInitialization(bool vary_network_isolation_key) {
-    const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-    const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
+    const SchemefulSite kSite1(GURL("https://foo.test/"));
+    const SchemefulSite kSite2(GURL("https://bar.test/"));
 
-    NetworkIsolationKey network_isolation_key1(kOrigin1, kOrigin1);
+    NetworkIsolationKey network_isolation_key1(kSite1, kSite1);
     quic::QuicServerId quic_server_id1(
         kDefaultServerHostName, kDefaultServerPort, PRIVACY_MODE_DISABLED);
 
@@ -575,7 +576,7 @@
     quic::QuicServerId quic_server_id2;
 
     if (vary_network_isolation_key) {
-      network_isolation_key2 = NetworkIsolationKey(kOrigin2, kOrigin2);
+      network_isolation_key2 = NetworkIsolationKey(kSite2, kSite2);
       quic_server_id2 = quic_server_id1;
     } else {
       network_isolation_key2 = network_isolation_key1;
@@ -1265,10 +1266,10 @@
 // Test that QUIC sessions use the cached RTT from HttpServerProperties for the
 // correct NetworkIsolationKey.
 TEST_P(QuicStreamFactoryTest, CachedInitialRttWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
@@ -1497,10 +1498,10 @@
 // Makes sure that setting and clearing ServerNetworkStats respects the
 // NetworkIsolationKey.
 TEST_P(QuicStreamFactoryTest, ServerNetworkStatsWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   const NetworkIsolationKey kNetworkIsolationKeys[] = {
       kNetworkIsolationKey1, kNetworkIsolationKey2, NetworkIsolationKey()};
@@ -10873,14 +10874,14 @@
   feature_list.InitAndEnableFeature(
       features::kPartitionConnectionsByNetworkIsolationKey);
 
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
 
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
-  const url::Origin kOrigin3 = url::Origin::Create(GURL("https://baz.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey3(kOrigin3, kOrigin3);
+  const SchemefulSite kSite3(GURL("https://baz.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey3(kSite3, kSite3);
 
   Initialize();
 
@@ -10928,14 +10929,14 @@
       // disabled_features
       {});
 
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
 
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
-  const url::Origin kOrigin3 = url::Origin::Create(GURL("https://baz.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey3(kOrigin3, kOrigin3);
+  const SchemefulSite kSite3(GURL("https://baz.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey3(kSite3, kSite3);
 
   Initialize();
 
@@ -11022,9 +11023,8 @@
       crypto_config_handles;
   std::vector<NetworkIsolationKey> network_isolation_keys;
   for (int i = 0; i < kNumSessionsToMake; ++i) {
-    url::Origin origin =
-        url::Origin::Create(GURL(base::StringPrintf("https://foo%i.test/", i)));
-    network_isolation_keys.push_back(NetworkIsolationKey(origin, origin));
+    SchemefulSite site(GURL(base::StringPrintf("https://foo%i.test/", i)));
+    network_isolation_keys.push_back(NetworkIsolationKey(site, site));
 
     std::unique_ptr<QuicCryptoClientConfigHandle> crypto_config_handle =
         QuicStreamFactoryPeer::GetCryptoConfig(factory_.get(),
@@ -11092,9 +11092,8 @@
 
   std::vector<NetworkIsolationKey> network_isolation_keys;
   for (int i = 0; i < kNumSessionsToMake; ++i) {
-    url::Origin origin =
-        url::Origin::Create(GURL(base::StringPrintf("https://foo%i.test/", i)));
-    network_isolation_keys.push_back(NetworkIsolationKey(origin, origin));
+    SchemefulSite site(GURL(base::StringPrintf("https://foo%i.test/", i)));
+    network_isolation_keys.push_back(NetworkIsolationKey(site, site));
   }
 
   const quic::QuicServerId kQuicServerId(
@@ -12145,9 +12144,9 @@
 // Verifies that the host resolver uses the disable secure DNS setting and
 // NetworkIsolationKey passed to QuicStreamRequest::Request().
 TEST_P(QuicStreamFactoryTest, HostResolverUsesParams) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin1, kOrigin1);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite1, kSite1);
   base::test::ScopedFeatureList feature_list;
   feature_list.InitWithFeatures(
       // enabled_features
diff --git a/net/quic/quic_transport_end_to_end_test.cc b/net/quic/quic_transport_end_to_end_test.cc
index 4ab0a246..dc7f191a 100644
--- a/net/quic/quic_transport_end_to_end_test.cc
+++ b/net/quic/quic_transport_end_to_end_test.cc
@@ -8,6 +8,7 @@
 
 #include "base/strings/strcat.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "net/base/schemeful_site.h"
 #include "net/cert/mock_cert_verifier.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/log/test_net_log.h"
@@ -22,6 +23,8 @@
 #include "net/url_request/url_request_context_builder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace net {
 namespace test {
@@ -84,7 +87,8 @@
       quic::QuicEnableVersion(version);
     }
     origin_ = url::Origin::Create(GURL{"https://example.org"});
-    isolation_key_ = NetworkIsolationKey(origin_, origin_);
+    isolation_key_ =
+        NetworkIsolationKey(SchemefulSite(origin_), SchemefulSite(origin_));
 
     URLRequestContextBuilder builder;
     builder.set_proxy_resolution_service(
diff --git a/net/reporting/reporting_cache_unittest.cc b/net/reporting/reporting_cache_unittest.cc
index 5ac623ea..143b54ad 100644
--- a/net/reporting/reporting_cache_unittest.cc
+++ b/net/reporting/reporting_cache_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/values.h"
 #include "net/base/features.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/reporting/mock_persistent_reporting_store.h"
 #include "net/reporting/reporting_cache_impl.h"
 #include "net/reporting/reporting_cache_observer.h"
@@ -186,7 +187,7 @@
   const url::Origin kOrigin2_ = url::Origin::Create(GURL("https://origin2/"));
   const NetworkIsolationKey kNik_;
   const NetworkIsolationKey kOtherNik_ =
-      NetworkIsolationKey(kOrigin1_, kOrigin2_);
+      NetworkIsolationKey(SchemefulSite(kOrigin1_), SchemefulSite(kOrigin2_));
   const GURL kEndpoint1_ = GURL("https://endpoint1/");
   const GURL kEndpoint2_ = GURL("https://endpoint2/");
   const GURL kEndpoint3_ = GURL("https://endpoint3/");
diff --git a/net/reporting/reporting_delivery_agent_unittest.cc b/net/reporting/reporting_delivery_agent_unittest.cc
index 078ab9d..46462c8 100644
--- a/net/reporting/reporting_delivery_agent_unittest.cc
+++ b/net/reporting/reporting_delivery_agent_unittest.cc
@@ -16,6 +16,7 @@
 #include "net/base/backoff_entry.h"
 #include "net/base/features.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/reporting/reporting_cache.h"
 #include "net/reporting/reporting_report.h"
 #include "net/reporting/reporting_test_util.h"
@@ -85,9 +86,11 @@
   const url::Origin kOrigin_ = url::Origin::Create(GURL("https://origin/"));
   const url::Origin kOtherOrigin_ =
       url::Origin::Create(GURL("https://other-origin/"));
-  const NetworkIsolationKey kNik_ = NetworkIsolationKey(kOrigin_, kOrigin_);
+  const NetworkIsolationKey kNik_ =
+      NetworkIsolationKey(SchemefulSite(kOrigin_), SchemefulSite(kOrigin_));
   const NetworkIsolationKey kOtherNik_ =
-      NetworkIsolationKey(kOtherOrigin_, kOtherOrigin_);
+      NetworkIsolationKey(SchemefulSite(kOtherOrigin_),
+                          SchemefulSite(kOtherOrigin_));
   const GURL kEndpoint_ = GURL("https://endpoint/");
   const std::string kUserAgent_ = "Mozilla/1.0";
   const std::string kGroup_ = "group";
diff --git a/net/reporting/reporting_endpoint_manager_unittest.cc b/net/reporting/reporting_endpoint_manager_unittest.cc
index 0e02093..9fd8652e 100644
--- a/net/reporting/reporting_endpoint_manager_unittest.cc
+++ b/net/reporting/reporting_endpoint_manager_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/time/time.h"
 #include "net/base/backoff_entry.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/reporting/reporting_cache.h"
 #include "net/reporting/reporting_endpoint.h"
 #include "net/reporting/reporting_policy.h"
@@ -218,6 +219,7 @@
 
   const NetworkIsolationKey kNik;
   const url::Origin kOrigin = url::Origin::Create(GURL("https://origin/"));
+  const SchemefulSite kSite = SchemefulSite(kOrigin);
   const std::string kGroup = "group";
   const ReportingEndpointGroupKey kGroupKey =
       ReportingEndpointGroupKey(kNik, kOrigin, kGroup);
@@ -446,10 +448,10 @@
 
 // Check that ReportingEndpointManager distinguishes NetworkIsolationKeys.
 TEST_F(ReportingEndpointManagerTest, NetworkIsolationKey) {
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://origin2/"));
+  const SchemefulSite kSite2(GURL("https://origin2/"));
 
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin, kOrigin);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite, kSite);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
   const ReportingEndpointGroupKey kGroupKey1(kNetworkIsolationKey1, kOrigin,
                                              kGroup);
   const ReportingEndpointGroupKey kGroupKey2(kNetworkIsolationKey2, kOrigin,
@@ -491,10 +493,10 @@
 }
 
 TEST_F(ReportingEndpointManagerTest, NetworkIsolationKeyWithMultipleEndpoints) {
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://origin2/"));
+  const SchemefulSite kSite2(GURL("https://origin2/"));
 
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin, kOrigin);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite, kSite);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
   const ReportingEndpointGroupKey kGroupKey1(kNetworkIsolationKey1, kOrigin,
                                              kGroup);
   const ReportingEndpointGroupKey kGroupKey2(kNetworkIsolationKey2, kOrigin,
@@ -570,7 +572,7 @@
   EXPECT_FALSE(endpoint_manager_->FindEndpointForDelivery(kGroupKey));
 
   // Add another endpoint with a different NetworkIsolationKey;
-  const NetworkIsolationKey kDifferentNetworkIsolationKey(kOrigin, kOrigin);
+  const NetworkIsolationKey kDifferentNetworkIsolationKey(kSite, kSite);
   const ReportingEndpointGroupKey kDifferentGroupKey(
       kDifferentNetworkIsolationKey, kOrigin, kGroup);
   SetEndpoint(kEndpoint, ReportingEndpoint::EndpointInfo::kDefaultPriority,
diff --git a/net/reporting/reporting_header_parser_unittest.cc b/net/reporting/reporting_header_parser_unittest.cc
index 20b2f1f9..70dbe8e 100644
--- a/net/reporting/reporting_header_parser_unittest.cc
+++ b/net/reporting/reporting_header_parser_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "net/base/features.h"
+#include "net/base/schemeful_site.h"
 #include "net/reporting/mock_persistent_reporting_store.h"
 #include "net/reporting/reporting_cache.h"
 #include "net/reporting/reporting_endpoint.h"
@@ -152,9 +153,10 @@
   const url::Origin kOrigin1_ = url::Origin::Create(kUrl1_);
   const GURL kUrl2_ = GURL("https://origin2.test/path");
   const url::Origin kOrigin2_ = url::Origin::Create(kUrl2_);
-  const NetworkIsolationKey kNik_ = NetworkIsolationKey(kOrigin1_, kOrigin1_);
+  const NetworkIsolationKey kNik_ =
+      NetworkIsolationKey(SchemefulSite(kOrigin1_), SchemefulSite(kOrigin1_));
   const NetworkIsolationKey kOtherNik_ =
-      NetworkIsolationKey(kOrigin2_, kOrigin2_);
+      NetworkIsolationKey(SchemefulSite(kOrigin2_), SchemefulSite(kOrigin2_));
   const GURL kUrlEtld_ = GURL("https://co.uk/foo.html/");
   const url::Origin kOriginEtld_ = url::Origin::Create(kUrlEtld_);
   const GURL kEndpoint1_ = GURL("https://endpoint1.test/");
diff --git a/net/reporting/reporting_service_unittest.cc b/net/reporting/reporting_service_unittest.cc
index 99e226d..e6864ed0 100644
--- a/net/reporting/reporting_service_unittest.cc
+++ b/net/reporting/reporting_service_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/values.h"
 #include "net/base/features.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/reporting/mock_persistent_reporting_store.h"
 #include "net/reporting/reporting_browsing_data_remover.h"
 #include "net/reporting/reporting_cache.h"
@@ -26,6 +27,8 @@
 #include "net/test/test_with_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace net {
 namespace {
@@ -45,8 +48,10 @@
   const std::string kUserAgent_ = "Mozilla/1.0";
   const std::string kGroup_ = "group";
   const std::string kType_ = "type";
-  const NetworkIsolationKey kNik_ = NetworkIsolationKey(kOrigin_, kOrigin_);
-  const NetworkIsolationKey kNik2_ = NetworkIsolationKey(kOrigin2_, kOrigin2_);
+  const NetworkIsolationKey kNik_ =
+      NetworkIsolationKey(SchemefulSite(kOrigin_), SchemefulSite(kOrigin_));
+  const NetworkIsolationKey kNik2_ =
+      NetworkIsolationKey(SchemefulSite(kOrigin2_), SchemefulSite(kOrigin2_));
   const ReportingEndpointGroupKey kGroupKey_ =
       ReportingEndpointGroupKey(kNik_, kOrigin_, kGroup_);
   const ReportingEndpointGroupKey kGroupKey2_ =
diff --git a/net/reporting/reporting_uploader_unittest.cc b/net/reporting/reporting_uploader_unittest.cc
index 62bdda7..0c184d0 100644
--- a/net/reporting/reporting_uploader_unittest.cc
+++ b/net/reporting/reporting_uploader_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "net/base/features.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/cookies/cookie_access_result.h"
 #include "net/cookies/cookie_store.h"
 #include "net/cookies/cookie_store_test_callbacks.h"
@@ -25,6 +26,8 @@
 #include "net/test/test_with_task_environment.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
 
 namespace net {
 namespace {
@@ -546,10 +549,11 @@
   feature_list.InitAndEnableFeature(
       features::kPartitionConnectionsByNetworkIsolationKey);
 
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://origin2/"));
-  ASSERT_NE(kOrigin, kOrigin2);
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin, kOrigin);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1 = SchemefulSite(kOrigin);
+  const SchemefulSite kSite2(GURL("https://origin2/"));
+  ASSERT_NE(kSite1, kSite2);
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   MockClientSocketFactory socket_factory;
   TestURLRequestContext context(true /* delay_initialization */);
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index c0c35618..bcdbd8e 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -34,6 +34,7 @@
 #include "net/base/privacy_mode.h"
 #include "net/base/proxy_server.h"
 #include "net/base/request_priority.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/public/resolve_error_info.h"
 #include "net/http/http_response_headers.h"
@@ -861,11 +862,11 @@
   const PrivacyMode kPrivacyModes[] = {PrivacyMode::PRIVACY_MODE_DISABLED,
                                        PrivacyMode::PRIVACY_MODE_ENABLED};
 
-  const auto kOriginA = url::Origin::Create(GURL("http://a.test/"));
-  const auto kOriginB = url::Origin::Create(GURL("http://b.test/"));
+  const SchemefulSite kSiteA(GURL("http://a.test/"));
+  const SchemefulSite kSiteB(GURL("http://b.test/"));
   const NetworkIsolationKey kNetworkIsolationKeys[] = {
-      NetworkIsolationKey(kOriginA, kOriginA),
-      NetworkIsolationKey(kOriginB, kOriginB),
+      NetworkIsolationKey(kSiteA, kSiteA),
+      NetworkIsolationKey(kSiteB, kSiteB),
   };
 
   const bool kDisableSecureDnsValues[] = {false, true};
@@ -5653,8 +5654,8 @@
   static ClientSocketPool::GroupId GetGroupIdInPartition() {
     // Note this GroupId will match GetGroupId() unless
     // kPartitionConnectionsByNetworkIsolationKey is enabled.
-    const auto kOrigin = url::Origin::Create(GURL("https://b/"));
-    const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+    const SchemefulSite kSite(GURL("https://b/"));
+    const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
     return TestGroupId("a", 443, ClientSocketPool::SocketType::kSsl,
                        PrivacyMode::PRIVACY_MODE_DISABLED,
                        kNetworkIsolationKey);
diff --git a/net/socket/client_socket_pool_unittest.cc b/net/socket/client_socket_pool_unittest.cc
index 916cd6c..051c0c3 100644
--- a/net/socket/client_socket_pool_unittest.cc
+++ b/net/socket/client_socket_pool_unittest.cc
@@ -12,9 +12,9 @@
 #include "net/base/host_port_pair.h"
 #include "net/base/network_isolation_key.h"
 #include "net/base/privacy_mode.h"
+#include "net/base/schemeful_site.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace net {
 
@@ -43,11 +43,11 @@
       PrivacyMode::PRIVACY_MODE_ENABLED,
   };
 
-  const auto kOriginA = url::Origin::Create(GURL("http://a.test/"));
-  const auto kOriginB = url::Origin::Create(GURL("http://b.test/"));
+  const SchemefulSite kSiteA(GURL("http://a.test/"));
+  const SchemefulSite kSiteB(GURL("http://b.test/"));
   const NetworkIsolationKey kNetworkIsolationKeys[] = {
-      NetworkIsolationKey(kOriginA, kOriginA),
-      NetworkIsolationKey(kOriginB, kOriginB),
+      NetworkIsolationKey(kSiteA, kSiteA),
+      NetworkIsolationKey(kSiteB, kSiteB),
   };
 
   const bool kDisableSecureDnsValues[] = {false, true};
@@ -134,15 +134,14 @@
                 false /* disable_secure_dns */)
                 .ToString());
 
-  EXPECT_EQ(
-      "ssl/foo:443 <https://foo.com>",
-      ClientSocketPool::GroupId(
-          HostPortPair("foo", 443), ClientSocketPool::SocketType::kSsl,
-          PrivacyMode::PRIVACY_MODE_DISABLED,
-          NetworkIsolationKey(url::Origin::Create(GURL("https://foo.com")),
-                              url::Origin::Create(GURL("https://foo.com"))),
-          false /* disable_secure_dns */)
-          .ToString());
+  EXPECT_EQ("ssl/foo:443 <https://foo.com>",
+            ClientSocketPool::GroupId(
+                HostPortPair("foo", 443), ClientSocketPool::SocketType::kSsl,
+                PrivacyMode::PRIVACY_MODE_DISABLED,
+                NetworkIsolationKey(SchemefulSite(GURL("https://foo.com")),
+                                    SchemefulSite(GURL("https://foo.com"))),
+                false /* disable_secure_dns */)
+                .ToString());
 
   EXPECT_EQ("dsd/pm/ssl/bar:80 <null>",
             ClientSocketPool::GroupId(
@@ -155,8 +154,8 @@
 TEST(ClientSocketPool, PartitionConnectionsByNetworkIsolationKeyDisabled) {
   // Partitioning connections by NetworkIsolationKey is disabled by default, so
   // test both the explicitly and implicitly disabled cases.
-  const auto kOriginFoo = url::Origin::Create(GURL("https://foo.com"));
-  const auto kOriginBar = url::Origin::Create(GURL("https://bar.com"));
+  const SchemefulSite kSiteFoo(GURL("https://foo.com"));
+  const SchemefulSite kSiteBar(GURL("https://bar.com"));
   for (bool explicitly_disabled : {false, true}) {
     base::test::ScopedFeatureList feature_list;
     if (explicitly_disabled) {
@@ -164,17 +163,17 @@
           features::kPartitionConnectionsByNetworkIsolationKey);
     }
 
-    ClientSocketPool::GroupId group_id1(
-        HostPortPair("foo", 443), ClientSocketPool::SocketType::kSsl,
-        PrivacyMode::PRIVACY_MODE_DISABLED,
-        NetworkIsolationKey(kOriginFoo, kOriginFoo),
-        false /* disable_secure_dns */);
+    ClientSocketPool::GroupId group_id1(HostPortPair("foo", 443),
+                                        ClientSocketPool::SocketType::kSsl,
+                                        PrivacyMode::PRIVACY_MODE_DISABLED,
+                                        NetworkIsolationKey(kSiteFoo, kSiteFoo),
+                                        false /* disable_secure_dns */);
 
-    ClientSocketPool::GroupId group_id2(
-        HostPortPair("foo", 443), ClientSocketPool::SocketType::kSsl,
-        PrivacyMode::PRIVACY_MODE_DISABLED,
-        NetworkIsolationKey(kOriginBar, kOriginBar),
-        false /* disable_secure_dns */);
+    ClientSocketPool::GroupId group_id2(HostPortPair("foo", 443),
+                                        ClientSocketPool::SocketType::kSsl,
+                                        PrivacyMode::PRIVACY_MODE_DISABLED,
+                                        NetworkIsolationKey(kSiteBar, kSiteBar),
+                                        false /* disable_secure_dns */);
 
     EXPECT_FALSE(group_id1.network_isolation_key().IsFullyPopulated());
     EXPECT_FALSE(group_id2.network_isolation_key().IsFullyPopulated());
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 7cca819b..a1f5b51 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -37,6 +37,7 @@
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/cert/asn1_util.h"
 #include "net/cert/ct_policy_enforcer.h"
@@ -94,7 +95,6 @@
 #include "third_party/boringssl/src/include/openssl/pem.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 using net::test::IsError;
 using net::test::IsOk;
@@ -3262,8 +3262,8 @@
 
   // Using a different NetworkIsolationKey shares session cache key because
   // sharding is disabled.
-  const auto kOriginA = url::Origin::Create(GURL("https://a.test"));
-  ssl_config.network_isolation_key = NetworkIsolationKey(kOriginA, kOriginA);
+  const SchemefulSite kSiteA(GURL("https://a.test"));
+  ssl_config.network_isolation_key = NetworkIsolationKey(kSiteA, kSiteA);
   ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
   ASSERT_THAT(rv, IsOk());
   ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
@@ -3271,8 +3271,8 @@
   EXPECT_THAT(MakeHTTPRequest(sock_.get()), IsOk());
   sock_.reset();
 
-  const auto kOriginB = url::Origin::Create(GURL("https://a.test"));
-  ssl_config.network_isolation_key = NetworkIsolationKey(kOriginB, kOriginB);
+  const SchemefulSite kSiteB(GURL("https://a.test"));
+  ssl_config.network_isolation_key = NetworkIsolationKey(kSiteB, kSiteB);
   ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
   ASSERT_THAT(rv, IsOk());
   ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
@@ -3289,10 +3289,10 @@
   feature_list.InitAndEnableFeature(
       features::kPartitionSSLSessionsByNetworkIsolationKey);
 
-  const auto kOriginA = url::Origin::Create(GURL("https://a.test"));
-  const auto kOriginB = url::Origin::Create(GURL("https://b.test"));
-  const NetworkIsolationKey kNetworkIsolationKeyA(kOriginA, kOriginA);
-  const NetworkIsolationKey kNetworkIsolationKeyB(kOriginB, kOriginB);
+  const SchemefulSite kSiteA(GURL("https://a.test"));
+  const SchemefulSite kSiteB(GURL("https://b.test"));
+  const NetworkIsolationKey kNetworkIsolationKeyA(kSiteA, kSiteA);
+  const NetworkIsolationKey kNetworkIsolationKeyB(kSiteB, kSiteB);
 
   ASSERT_TRUE(
       StartEmbeddedTestServer(EmbeddedTestServer::CERT_OK, GetServerConfig()));
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc
index 1ef24e0..883c3ce5 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -25,6 +25,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/privacy_mode.h"
 #include "net/base/proxy_server.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/mock_cert_verifier.h"
@@ -55,7 +56,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 using net::test::IsError;
 using net::test::IsOk;
@@ -1616,8 +1616,8 @@
 }
 
 TEST_F(TransportClientSocketPoolTest, NetworkIsolationKey) {
-  const auto kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
   const char kHost[] = "bar.test";
 
   base::test::ScopedFeatureList scoped_feature_list;
@@ -1654,8 +1654,8 @@
 }
 
 TEST_F(TransportClientSocketPoolTest, NetworkIsolationKeySsl) {
-  const auto kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
   const char kHost[] = "bar.test";
 
   base::test::ScopedFeatureList scoped_feature_list;
@@ -1693,8 +1693,8 @@
 
 // Test that, in the case of an HTTP proxy, the NetworkIsolationKey is not used.
 TEST_F(TransportClientSocketPoolTest, NetworkIsolationKeyHttpProxy) {
-  const auto kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
   const char kHost[] = "bar.test";
   const ProxyServer kProxyServer = ProxyServer::FromURI(
       "http://proxy.test", ProxyServer::SCHEME_HTTP /* default_scheme */);
@@ -1740,8 +1740,8 @@
 // Test that, in the case of an HTTPS proxy, the NetworkIsolationKey is not
 // used.
 TEST_F(TransportClientSocketPoolTest, NetworkIsolationKeyHttpsProxy) {
-  const auto kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
   const char kHost[] = "bar.test";
   const ProxyServer kProxyServer = ProxyServer::FromURI(
       "https://proxy.test", ProxyServer::SCHEME_HTTP /* default_scheme */);
@@ -1787,8 +1787,8 @@
 // Test that, in the case of a SOCKS5 proxy, the NetworkIsolationKey is only
 // used for the destination DNS lookup, not the proxy DNS lookup.
 TEST_F(TransportClientSocketPoolTest, NetworkIsolationKeySocks4Proxy) {
-  const auto kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
   const char kHost[] = "bar.test";
   const ProxyServer kProxyServer = ProxyServer::FromURI(
       "socks4://proxy.test", ProxyServer::SCHEME_HTTP /* default_scheme */);
@@ -1849,8 +1849,8 @@
 // Test that, in the case of a SOCKS5 proxy, the NetworkIsolationKey is not
 // used.
 TEST_F(TransportClientSocketPoolTest, NetworkIsolationKeySocks5Proxy) {
-  const auto kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
   const char kHost[] = "bar.test";
   const ProxyServer kProxyServer = ProxyServer::FromURI(
       "socks5://proxy.test", ProxyServer::SCHEME_HTTP /* default_scheme */);
diff --git a/net/socket/websocket_transport_client_socket_pool_unittest.cc b/net/socket/websocket_transport_client_socket_pool_unittest.cc
index 002b7c2..63a19f9a 100644
--- a/net/socket/websocket_transport_client_socket_pool_unittest.cc
+++ b/net/socket/websocket_transport_client_socket_pool_unittest.cc
@@ -25,6 +25,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/privacy_mode.h"
 #include "net/base/proxy_server.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_completion_callback.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/log/test_net_log.h"
@@ -42,7 +43,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 using net::test::IsError;
 using net::test::IsOk;
@@ -1132,8 +1132,8 @@
 
 // Make sure that WebSocket requests use the correct NetworkIsolationKey.
 TEST_F(WebSocketTransportClientSocketPoolTest, NetworkIsolationKey) {
-  const auto kOrigin = url::Origin::Create(GURL("https://foo.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey(kOrigin, kOrigin);
+  const SchemefulSite kSite(GURL("https://foo.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
 
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures(
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index af95a159..f32dd92 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -29,6 +29,7 @@
 #include "net/base/proxy_delegate.h"
 #include "net/base/proxy_server.h"
 #include "net/base/request_priority.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_proxy_delegate.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_file_element_reader.h"
@@ -68,6 +69,7 @@
 #include "net/websockets/websocket_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/platform_test.h"
+#include "url/gurl.h"
 
 using net::test::IsError;
 using net::test::IsOk;
@@ -5302,10 +5304,10 @@
 
 // Same as above test, but checks that NetworkIsolationKeys are respected.
 TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredRetryWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   const NetworkIsolationKey kNetworkIsolationKeys[] = {
       kNetworkIsolationKey1, kNetworkIsolationKey2, NetworkIsolationKey()};
@@ -5507,10 +5509,10 @@
 // Same as above, but also test that NetworkIsolationKeys are respected.
 TEST_F(SpdyNetworkTransactionTest,
        HTTP11RequiredProxyRetryWithNetworkIsolationKey) {
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
 
   const NetworkIsolationKey kNetworkIsolationKeys[] = {
       kNetworkIsolationKey1, kNetworkIsolationKey2, NetworkIsolationKey()};
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
index bb7faa4..10a403a5e 100644
--- a/net/spdy/spdy_session_unittest.cc
+++ b/net/spdy/spdy_session_unittest.cc
@@ -27,6 +27,7 @@
 #include "net/base/proxy_delegate.h"
 #include "net/base/proxy_server.h"
 #include "net/base/request_priority.h"
+#include "net/base/schemeful_site.h"
 #include "net/base/test_data_stream.h"
 #include "net/cert/ct_policy_status.h"
 #include "net/http/http_request_info.h"
@@ -55,6 +56,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/platform_test.h"
+#include "url/gurl.h"
 
 using net::test::IsError;
 using net::test::IsOk;
@@ -6622,10 +6624,10 @@
   session_deps_.http_server_properties =
       std::make_unique<HttpServerProperties>();
 
-  const url::Origin kOrigin1 = url::Origin::Create(GURL("https://foo.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey1(kOrigin1, kOrigin1);
-  const url::Origin kOrigin2 = url::Origin::Create(GURL("https://bar.test/"));
-  const net::NetworkIsolationKey kNetworkIsolationKey2(kOrigin2, kOrigin2);
+  const SchemefulSite kSite1(GURL("https://foo.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
+  const SchemefulSite kSite2(GURL("https://bar.test/"));
+  const net::NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
   key_ = SpdySessionKey(HostPortPair::FromURL(test_url_), ProxyServer::Direct(),
                         PRIVACY_MODE_DISABLED,
                         SpdySessionKey::IsProxySession::kFalse, SocketTag(),
diff --git a/net/ssl/ssl_client_session_cache_unittest.cc b/net/ssl/ssl_client_session_cache_unittest.cc
index fb358a1c..cdf1ea7 100644
--- a/net/ssl/ssl_client_session_cache_unittest.cc
+++ b/net/ssl/ssl_client_session_cache_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/traced_value.h"
 #include "net/base/network_isolation_key.h"
+#include "net/base/schemeful_site.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/boringssl/src/include/openssl/ssl.h"
@@ -415,8 +416,8 @@
   SSLClientSessionCache::Config config;
   SSLClientSessionCache cache(config);
 
-  const url::Origin kOriginA = url::Origin::Create(GURL("https://a.test"));
-  const url::Origin kOriginB = url::Origin::Create(GURL("https://b.test"));
+  const SchemefulSite kSiteA(GURL("https://a.test"));
+  const SchemefulSite kSiteB(GURL("https://b.test"));
 
   // Insert a number of cache entries.
   SSLClientSessionCache::Key key1;
@@ -427,7 +428,7 @@
   SSLClientSessionCache::Key key2;
   key2.server = HostPortPair("a.test", 443);
   key2.dest_ip_addr = IPAddress::IPv4Localhost();
-  key2.network_isolation_key = NetworkIsolationKey(kOriginB, kOriginB);
+  key2.network_isolation_key = NetworkIsolationKey(kSiteB, kSiteB);
   key2.privacy_mode = PRIVACY_MODE_ENABLED;
   auto session2 = NewSSLSession();
   cache.Insert(key2, bssl::UpRef(session2));
@@ -444,7 +445,7 @@
 
   SSLClientSessionCache::Key key5;
   key5.server = HostPortPair("b.test", 443);
-  key5.network_isolation_key = NetworkIsolationKey(kOriginA, kOriginA);
+  key5.network_isolation_key = NetworkIsolationKey(kSiteA, kSiteA);
   auto session5 = NewSSLSession();
   cache.Insert(key5, bssl::UpRef(session5));
 
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 033238b..4d5967e 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -1176,52 +1176,7 @@
           "ignore_task_failure": false,
           "io_timeout": 21600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 1
-        },
-        "trigger_script": {
-          "args": [
-            "--multiple-dimension-script-verbose",
-            "True"
-          ],
-          "requires_simultaneous_shard_dispatch": true,
-          "script": "//testing/trigger_scripts/perf_device_trigger.py"
-        }
-      }
-    ]
-  },
-  "mac-arm_dtk_x86-perf": {
-    "isolated_scripts": [
-      {
-        "args": [
-          "-v",
-          "--browser=release",
-          "--upload-results",
-          "--test-shard-map-filename=mac-arm_dtk_x86-perf_map.json",
-          "--assert-gpu-compositing"
-        ],
-        "isolate_name": "performance_test_suite",
-        "merge": {
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "performance_test_suite",
-        "override_compile_targets": [
-          "performance_test_suite"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "arm",
-              "os": "Mac",
-              "pool": "chrome.tests.perf"
-            }
-          ],
-          "expiration": 7200,
-          "hard_timeout": 21600,
-          "ignore_task_failure": false,
-          "io_timeout": 21600,
-          "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 1
+          "shards": 8
         },
         "trigger_script": {
           "args": [
diff --git a/testing/clusterfuzz/OWNERS b/testing/clusterfuzz/OWNERS
index 70d27b6..9685177 100644
--- a/testing/clusterfuzz/OWNERS
+++ b/testing/clusterfuzz/OWNERS
@@ -1,3 +1,2 @@
 inferno@chromium.org
-mmoroz@chromium.org
 ochang@chromium.org
diff --git a/testing/libfuzzer/OWNERS b/testing/libfuzzer/OWNERS
index 85e0aa4..a4bcaf3 100644
--- a/testing/libfuzzer/OWNERS
+++ b/testing/libfuzzer/OWNERS
@@ -2,5 +2,4 @@
 kcc@chromium.org
 mbarbella@chromium.org
 metzman@chromium.org
-mmoroz@chromium.org
 ochang@chromium.org
diff --git a/third_party/afl/OWNERS b/third_party/afl/OWNERS
index ef3a3872..971934d8 100644
--- a/third_party/afl/OWNERS
+++ b/third_party/afl/OWNERS
@@ -1,7 +1,6 @@
 inferno@chromium.org
 kcc@chromium.org
 metzman@chromium.org
-mmoroz@chromium.org
 ochang@chromium.org
 # COMPONENT: Tools>Stability>AFL
 # TEAM: chrome-security-bugs--@chromium.org
diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc
index 0ad731c2..ef21556 100644
--- a/third_party/blink/common/web_preferences/web_preferences.cc
+++ b/third_party/blink/common/web_preferences/web_preferences.cc
@@ -100,7 +100,7 @@
       available_pointer_types(0),
       primary_pointer_type(blink::mojom::PointerType::kPointerNone),
       available_hover_types(0),
-      primary_hover_type(ui::HOVER_TYPE_NONE),
+      primary_hover_type(blink::mojom::HoverType::kHoverNone),
       dont_send_key_events_to_javascript(false),
       sync_xhr_in_documents_enabled(true),
       number_of_cpu_cores(1),
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
index 8ea4701..63d5d4e5 100644
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
@@ -13,35 +13,6 @@
 namespace mojo {
 
 // static
-blink::mojom::HoverType EnumTraits<blink::mojom::HoverType,
-                                   ui::HoverType>::ToMojom(ui::HoverType type) {
-  switch (type) {
-    case ui::HoverType::HOVER_TYPE_FIRST:
-      return blink::mojom::HoverType::kHoverFirstType;
-    case ui::HoverType::HOVER_TYPE_HOVER:
-      return blink::mojom::HoverType::kHoverHoverType;
-  }
-  NOTREACHED();
-  return blink::mojom::HoverType::kMinValue;
-}
-
-// static
-bool EnumTraits<blink::mojom::HoverType, ui::HoverType>::FromMojom(
-    blink::mojom::HoverType input,
-    ui::HoverType* out) {
-  switch (input) {
-    case blink::mojom::HoverType::kHoverFirstType:
-      *out = ui::HoverType::HOVER_TYPE_FIRST;
-      return true;
-    case blink::mojom::HoverType::kHoverHoverType:
-      *out = ui::HoverType::HOVER_TYPE_HOVER;
-      return true;
-  }
-  NOTREACHED();
-  return false;
-}
-
-// static
 bool StructTraits<blink::mojom::WebPreferencesDataView,
                   blink::web_pref::WebPreferences>::
     Read(blink::mojom::WebPreferencesDataView data,
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
index 578ad8c..95ab020 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
@@ -18,7 +18,6 @@
 #include "third_party/blink/public/mojom/css/preferred_contrast.mojom-shared.h"
 #include "third_party/blink/public/mojom/v8_cache_options.mojom-forward.h"
 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-shared.h"
-#include "ui/base/pointer/pointer_device.h"
 #include "url/gurl.h"
 
 namespace blink {
@@ -132,7 +131,7 @@
   int available_pointer_types;
   blink::mojom::PointerType primary_pointer_type;
   int available_hover_types;
-  ui::HoverType primary_hover_type;
+  blink::mojom::HoverType primary_hover_type;
   bool dont_send_key_events_to_javascript;
   bool barrel_button_for_drag_enabled = false;
   bool sync_xhr_in_documents_enabled;
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
index 11c903b..145106c 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
@@ -11,18 +11,10 @@
 #include "third_party/blink/public/common/common_export.h"
 #include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom.h"
-#include "ui/base/pointer/pointer_device.h"
 
 namespace mojo {
 
 template <>
-struct BLINK_COMMON_EXPORT EnumTraits<blink::mojom::HoverType, ui::HoverType> {
-  static blink::mojom::HoverType ToMojom(ui::HoverType type);
-
-  static bool FromMojom(blink::mojom::HoverType input, ui::HoverType* out);
-};
-
-template <>
 struct BLINK_COMMON_EXPORT StructTraits<blink::mojom::WebPreferencesDataView,
                                         blink::web_pref::WebPreferences> {
   static const std::map<std::string, base::string16>& standard_font_family_map(
@@ -348,7 +340,7 @@
     return r.available_hover_types;
   }
 
-  static ui::HoverType primary_hover_type(
+  static blink::mojom::HoverType primary_hover_type(
       const blink::web_pref::WebPreferences& r) {
     return r.primary_hover_type;
   }
diff --git a/third_party/blink/public/mojom/font_access/font_access.mojom b/third_party/blink/public/mojom/font_access/font_access.mojom
index ea78daf..aa1d006 100644
--- a/third_party/blink/public/mojom/font_access/font_access.mojom
+++ b/third_party/blink/public/mojom/font_access/font_access.mojom
@@ -19,6 +19,8 @@
   kNotVisible,
   // The site doesn't have permission for the requested operation.
   kPermissionDenied,
+  // The user canceled the operation.
+  kCanceled,
 };
 
 // Bag of data representing a font, used to pass structured data from
@@ -36,4 +38,7 @@
   // Enumerate locally installed fonts. Results will be gated by a permission
   // check.
   EnumerateLocalFonts() => (FontEnumerationStatus enumeration_status, mojo_base.mojom.ReadOnlySharedMemoryRegion? enumeration_table);
+  // Begins a UX that gives affordances for a user to choose fonts to be shared
+  // with the page.
+  ChooseLocalFonts() => (FontEnumerationStatus status, array<FontMetadata> selection);
 };
diff --git a/third_party/blink/public/mojom/use_counter/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
index 467f469..95dba63 100644
--- a/third_party/blink/public/mojom/use_counter/css_property_id.mojom
+++ b/third_party/blink/public/mojom/use_counter/css_property_id.mojom
@@ -38,6 +38,8 @@
     kInternalVisitedTextStrokeColor = 0,
     kInternalFontSizeDelta = 0,
     kInternalForcedBackgroundColor = 0,
+    kInternalForcedBorderColor = 0,
+    kInternalForcedOutlineColor = 0,
 
     // This CSSSampleId represents page load for CSS histograms. It is recorded once
     // per page visit for each CSS histogram being logged on the blink side and the
diff --git a/third_party/blink/public/mojom/webauthn/authenticator.mojom b/third_party/blink/public/mojom/webauthn/authenticator.mojom
index d7820afd..0d773226 100644
--- a/third_party/blink/public/mojom/webauthn/authenticator.mojom
+++ b/third_party/blink/public/mojom/webauthn/authenticator.mojom
@@ -236,15 +236,23 @@
   uint8 version;
 
   // A 16-byte ephemeral identifier that the browser will advertise.
-  array<uint8, 16> client_eid;
+  // (Present iff version == 1.)
+  array<uint8, 16>? client_eid;
 
   // A 16-byte ephemeral identifier that the browser expects to receive from a
   // responding authenticator.
-  array<uint8, 16> authenticator_eid;
+  // (Present iff version == 1.)
+  array<uint8, 16>? authenticator_eid;
 
   // A 32-byte pre-key used to compute a session key to encrypt messages between
   // a paired client and authenticator following a successful discovery.
-  array<uint8, 32> session_pre_key;
+  // (Present iff version == 1.)
+  array<uint8, 32>? session_pre_key;
+
+  // caBLEv2 server-link data, used to find and communicate with a caBLEv2
+  // device.
+  // (Present iff version == 2.)
+  array<uint8>? server_link_data;
 };
 
 // Cloud-assisted BLE extension data for makeCredential.
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
index 38b05b0..df3ffb49 100644
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
@@ -18,8 +18,9 @@
 };
 
 enum HoverType {
-  kHoverFirstType                            = 1,  // 1 << 0
-  kHoverHoverType                            = 2   // 1 << 1
+  kHoverNone                                 = 1,            // 1 << 0
+  kHoverFirstType                            = kHoverNone,
+  kHoverHoverType                            = 2             // 1 << 1
 };
 
 // There are multiple editing details that are different on Windows than
diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h
index 15dfd99..7e97b5df 100644
--- a/third_party/blink/public/web/web_settings.h
+++ b/third_party/blink/public/web/web_settings.h
@@ -183,7 +183,7 @@
   virtual void SetAvailablePointerTypes(int) = 0;
   virtual void SetPrimaryPointerType(blink::mojom::PointerType) = 0;
   virtual void SetAvailableHoverTypes(int) = 0;
-  virtual void SetPrimaryHoverType(ui::HoverType) = 0;
+  virtual void SetPrimaryHoverType(blink::mojom::HoverType) = 0;
   virtual void SetPreferHiddenVolumeControls(bool) = 0;
   virtual void SetShouldProtectAgainstIpcFlooding(bool) = 0;
   virtual void SetRenderVSyncNotificationEnabled(bool) = 0;
diff --git a/third_party/blink/renderer/bindings/core/v8/script_controller.cc b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
index b572c7f..f63faebc 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
@@ -36,8 +36,6 @@
 #include <utility>
 
 #include "base/callback_helpers.h"
-#include "third_party/blink/public/mojom/v8_cache_options.mojom-blink.h"
-#include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
@@ -51,7 +49,6 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
-#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/html_plugin_element.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
@@ -91,15 +88,9 @@
     const KURL& base_url,
     SanitizeScriptErrors sanitize_script_errors,
     const ScriptFetchOptions& fetch_options) {
-    mojom::blink::V8CacheOptions v8_cache_options =
-        mojom::blink::V8CacheOptions::kDefault;
-    if (const Settings* settings = window_->GetFrame()->GetSettings())
-      v8_cache_options = settings->GetV8CacheOptions();
-
     ScriptEvaluationResult result = V8ScriptRunner::CompileAndRunScript(
         GetIsolate(), ScriptState::From(context), window_.Get(), source,
         base_url, sanitize_script_errors, fetch_options,
-        std::move(v8_cache_options),
         V8ScriptRunner::RethrowErrorsOption::DoNotRethrow());
 
     if (result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess)
@@ -287,15 +278,20 @@
     return v8::Local<v8::Value>();
   }
 
-  // |context| should be initialized already due to the
-  // MainWorldProxy() call.
-  v8::Local<v8::Context> context =
-      window_proxy_manager_->MainWorldProxy()->ContextIfInitialized();
-  v8::Context::Scope scope(context);
+  // |script_state->GetContext()| should be initialized already due to the
+  // WindowProxy() call inside ToScriptStateForMainWorld().
+  ScriptState* script_state = ToScriptStateForMainWorld(window_->GetFrame());
+  if (!script_state) {
+    return v8::Local<v8::Value>();
+  }
+  DCHECK_EQ(script_state->GetIsolate(), GetIsolate());
+
+  v8::Context::Scope scope(script_state->GetContext());
   v8::EscapableHandleScope handle_scope(GetIsolate());
 
   v8::Local<v8::Value> object = ExecuteScriptAndReturnValue(
-      context, source_code, base_url, sanitize_script_errors, fetch_options);
+      script_state->GetContext(), source_code, base_url, sanitize_script_errors,
+      fetch_options);
 
   if (object.IsEmpty())
     return v8::Local<v8::Value>();
@@ -313,18 +309,21 @@
     return v8::Local<v8::Value>();
   }
 
-  // |context| should be initialized already due to the
-  // MainWorldProxy() call.
-  v8::Local<v8::Context> context =
-      window_proxy_manager_->MainWorldProxy()->ContextIfInitialized();
-  v8::Context::Scope scope(context);
+  // |script_state->GetContext()| should be initialized already due to the
+  // WindowProxy() call inside ToScriptStateForMainWorld().
+  ScriptState* script_state = ToScriptStateForMainWorld(window_->GetFrame());
+  if (!script_state) {
+    return v8::Local<v8::Value>();
+  }
+  DCHECK_EQ(script_state->GetIsolate(), GetIsolate());
+
+  v8::Context::Scope scope(script_state->GetContext());
   v8::EscapableHandleScope handle_scope(GetIsolate());
 
   v8::TryCatch try_catch(GetIsolate());
   try_catch.SetVerbose(true);
 
-  ExecutionContext* executionContext =
-      ExecutionContext::From(ScriptState::From(context));
+  ExecutionContext* executionContext = ExecutionContext::From(script_state);
 
   v8::MaybeLocal<v8::Value> resultObj = V8ScriptRunner::CallFunction(
       function, executionContext, receiver, argc,
@@ -354,17 +353,20 @@
     SanitizeScriptErrors sanitize_script_errors) {
   DCHECK_GT(world_id, 0);
 
-  scoped_refptr<DOMWrapperWorld> world =
-      DOMWrapperWorld::EnsureIsolatedWorld(GetIsolate(), world_id);
-  LocalWindowProxy* isolated_world_window_proxy = WindowProxy(*world);
+  ScriptState* script_state = ToScriptState(
+      window_, *DOMWrapperWorld::EnsureIsolatedWorld(GetIsolate(), world_id));
+  if (!script_state) {
+    return v8::Local<v8::Value>();
+  }
+
   // TODO(dcheng): Context must always be initialized here, due to the call to
-  // windowProxy() on the previous line. Add a helper which makes that obvious?
-  v8::Local<v8::Context> context =
-      isolated_world_window_proxy->ContextIfInitialized();
-  v8::Context::Scope scope(context);
+  // WindowProxy() inside ToScriptState() above. Add a helper which makes that
+  // obvious?
+
+  v8::Context::Scope scope(script_state->GetContext());
 
   v8::Local<v8::Value> evaluation_result = ExecuteScriptAndReturnValue(
-      context, source, base_url, sanitize_script_errors);
+      script_state->GetContext(), source, base_url, sanitize_script_errors);
   if (!evaluation_result.IsEmpty())
     return evaluation_result;
   return v8::Local<v8::Value>::New(GetIsolate(), v8::Undefined(GetIsolate()));
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index 09f87118..ff68180 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -377,7 +377,6 @@
     const KURL& base_url,
     SanitizeScriptErrors sanitize_script_errors,
     const ScriptFetchOptions& fetch_options,
-    mojom::blink::V8CacheOptions v8_cache_options,
     RethrowErrorsOption rethrow_errors) {
   DCHECK_EQ(isolate, script_state->GetIsolate());
 
@@ -432,7 +431,8 @@
     V8CodeCache::ProduceCacheOptions produce_cache_options;
     v8::ScriptCompiler::NoCacheReason no_cache_reason;
     std::tie(compile_options, produce_cache_options, no_cache_reason) =
-        V8CodeCache::GetCompileOptions(v8_cache_options, source);
+        V8CodeCache::GetCompileOptions(execution_context->GetV8CacheOptions(),
+                                       source);
 
     v8::MaybeLocal<v8::Value> maybe_result;
     if (V8ScriptRunner::CompileScript(script_state, source,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
index 65b8867..62d2c0f 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
@@ -26,7 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_SCRIPT_RUNNER_H_
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_SCRIPT_RUNNER_H_
 
-#include "third_party/blink/public/mojom/v8_cache_options.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -129,7 +128,6 @@
       const KURL&,
       SanitizeScriptErrors,
       const ScriptFetchOptions&,
-      mojom::blink::V8CacheOptions,
       RethrowErrorsOption);
   static v8::MaybeLocal<v8::Value> CompileAndRunInternalScript(
       v8::Isolate*,
diff --git a/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h b/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h
index ea9dd33..ec27fcd6 100644
--- a/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h
+++ b/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h
@@ -39,11 +39,6 @@
   void CORE_EXPORT ReleaseGlobalProxies(GlobalProxyVector&);
   void CORE_EXPORT SetGlobalProxies(const GlobalProxyVector&);
 
-  WindowProxy* MainWorldProxy() {
-    window_proxy_->InitializeIfNeeded();
-    return window_proxy_;
-  }
-
   WindowProxy* GetWindowProxy(DOMWrapperWorld& world) {
     WindowProxy* window_proxy = WindowProxyMaybeUninitialized(world);
     window_proxy->InitializeIfNeeded();
@@ -76,9 +71,6 @@
   using Base = WindowProxyManager;
 
  public:
-  ProxyType* MainWorldProxy() {
-    return static_cast<ProxyType*>(Base::MainWorldProxy());
-  }
   ProxyType* WindowProxy(DOMWrapperWorld& world) {
     return static_cast<ProxyType*>(Base::GetWindowProxy(world));
   }
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
index 4f3b691b..bad464b 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
@@ -316,7 +316,6 @@
 ScriptEvaluationResult WorkerOrWorkletScriptController::EvaluateAndReturnValue(
     const ScriptSourceCode& source_code,
     SanitizeScriptErrors sanitize_script_errors,
-    mojom::blink::V8CacheOptions v8_cache_options,
     V8ScriptRunner::RethrowErrorsOption rethrow_errors) {
   if (IsExecutionForbidden())
     return ScriptEvaluationResult::FromClassicNotRun();
@@ -333,8 +332,7 @@
   // TODO(crbug/1114989): Plumb ScriptFetchOptions from ClassicScript.
   ScriptEvaluationResult result = V8ScriptRunner::CompileAndRunScript(
       isolate_, script_state_, global_scope_, source_code, base_url,
-      sanitize_script_errors, ScriptFetchOptions(), v8_cache_options,
-      std::move(rethrow_errors));
+      sanitize_script_errors, ScriptFetchOptions(), std::move(rethrow_errors));
 
   if (result.GetResultType() == ScriptEvaluationResult::ResultType::kAborted)
     ForbidExecution();
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
index 24286a79..0342df3 100644
--- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
+++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h
@@ -32,7 +32,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_WORKER_OR_WORKLET_SCRIPT_CONTROLLER_H_
 
 #include "base/macros.h"
-#include "third_party/blink/public/mojom/v8_cache_options.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/rejected_promises.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_evaluation_result.h"
@@ -62,7 +61,6 @@
   ScriptEvaluationResult EvaluateAndReturnValue(
       const ScriptSourceCode&,
       SanitizeScriptErrors sanitize_script_errors,
-      mojom::blink::V8CacheOptions = mojom::blink::V8CacheOptions::kDefault,
       V8ScriptRunner::RethrowErrorsOption =
           V8ScriptRunner::RethrowErrorsOption::DoNotRethrow());
 
diff --git a/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc
index 7276cf1..fbb39af 100644
--- a/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_color_interpolation_type.cc
@@ -141,8 +141,9 @@
                                *state.Style())
               .Access();
     }
-    AddPremultipliedColor(red, green, blue, alpha, currentcolor_fraction,
-                          current_style_color.GetColor());
+    AddPremultipliedColor(
+        red, green, blue, alpha, currentcolor_fraction,
+        current_style_color.Resolve(Color(), state.Style()->UsedColorScheme()));
   }
   const TextLinkColors& colors = state.GetDocument().GetTextLinkColors();
   if (double webkit_activelink_fraction =
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 34a22e0..8d5026d3 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -6082,6 +6082,32 @@
       valid_for_first_letter: true,
       valid_for_cue: true,
     },
+    {
+      name: "-internal-forced-border-color",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "ColorIncludingFallback"],
+      field_group: "*",
+      field_template: "external",
+      include_paths: ["third_party/blink/renderer/core/css/style_color.h"],
+      default_value: "StyleColor::CurrentColor()",
+      type_name: "StyleColor",
+      computed_style_custom_functions: ["getter"],
+      converter: "ConvertStyleColor",
+      style_builder_template: "color",
+      valid_for_first_letter: true,
+    },
+    {
+      name: "-internal-forced-outline-color",
+      property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal", "ColorIncludingFallback"],
+      field_group: "*",
+      field_template: "external",
+      include_paths: ["third_party/blink/renderer/core/css/style_color.h"],
+      default_value: "StyleColor::CurrentColor()",
+      type_name: "StyleColor",
+      computed_style_custom_functions: ["getter"],
+      converter: "ConvertStyleColor",
+      style_builder_template: "color",
+      valid_for_cue: true,
+    },
 
     // Name: -internal-empty-line-height:
     // Value: none | fabricated
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index ede8e49..9fae71a 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -64,6 +64,7 @@
 
 namespace blink {
 
+using mojom::blink::HoverType;
 using mojom::blink::PointerType;
 
 namespace {
@@ -698,16 +699,17 @@
 static bool HoverMediaFeatureEval(const MediaQueryExpValue& value,
                                   MediaFeaturePrefix,
                                   const MediaValues& media_values) {
-  ui::HoverType hover = media_values.PrimaryHoverType();
+  HoverType hover = media_values.PrimaryHoverType();
 
   if (!value.IsValid())
-    return hover != ui::HOVER_TYPE_NONE;
+    return hover != HoverType::kHoverNone;
 
   if (!value.is_id)
     return false;
 
-  return (hover == ui::HOVER_TYPE_NONE && value.id == CSSValueID::kNone) ||
-         (hover == ui::HOVER_TYPE_HOVER && value.id == CSSValueID::kHover);
+  return (hover == HoverType::kHoverNone && value.id == CSSValueID::kNone) ||
+         (hover == HoverType::kHoverHoverType &&
+          value.id == CSSValueID::kHover);
 }
 
 static bool AnyHoverMediaFeatureEval(const MediaQueryExpValue& value,
@@ -716,16 +718,17 @@
   int available_hover_types = media_values.AvailableHoverTypes();
 
   if (!value.IsValid())
-    return available_hover_types & ~ui::HOVER_TYPE_NONE;
+    return available_hover_types & ~static_cast<int>(HoverType::kHoverNone);
 
   if (!value.is_id)
     return false;
 
   switch (value.id) {
     case CSSValueID::kNone:
-      return available_hover_types & ui::HOVER_TYPE_NONE;
+      return available_hover_types & static_cast<int>(HoverType::kHoverNone);
     case CSSValueID::kHover:
-      return available_hover_types & ui::HOVER_TYPE_HOVER;
+      return available_hover_types &
+             static_cast<int>(HoverType::kHoverHoverType);
     default:
       NOTREACHED();
       return false;
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
index bc728d0..c900d3e 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
@@ -312,7 +312,7 @@
   data.color_bits_per_component = 24;
   data.monochrome_bits_per_component = 0;
   data.primary_pointer_type = mojom::blink::PointerType::kPointerFineType;
-  data.primary_hover_type = ui::HOVER_TYPE_HOVER;
+  data.primary_hover_type = mojom::blink::HoverType::kHoverHoverType;
   data.default_font_size = 16;
   data.three_d_enabled = true;
   data.media_type = media_type_names::kScreen;
diff --git a/third_party/blink/renderer/core/css/media_values.cc b/third_party/blink/renderer/core/css/media_values.cc
index add012b..61791fb6 100644
--- a/third_party/blink/renderer/core/css/media_values.cc
+++ b/third_party/blink/renderer/core/css/media_values.cc
@@ -163,7 +163,8 @@
   return frame->GetSettings()->GetAvailablePointerTypes();
 }
 
-ui::HoverType MediaValues::CalculatePrimaryHoverType(LocalFrame* frame) {
+mojom::blink::HoverType MediaValues::CalculatePrimaryHoverType(
+    LocalFrame* frame) {
   DCHECK(frame);
   DCHECK(frame->GetSettings());
   return frame->GetSettings()->GetPrimaryHoverType();
diff --git a/third_party/blink/renderer/core/css/media_values.h b/third_party/blink/renderer/core/css/media_values.h
index 6aae445..88640e7 100644
--- a/third_party/blink/renderer/core/css/media_values.h
+++ b/third_party/blink/renderer/core/css/media_values.h
@@ -72,7 +72,7 @@
   virtual int MonochromeBitsPerComponent() const = 0;
   virtual mojom::blink::PointerType PrimaryPointerType() const = 0;
   virtual int AvailablePointerTypes() const = 0;
-  virtual ui::HoverType PrimaryHoverType() const = 0;
+  virtual mojom::blink::HoverType PrimaryHoverType() const = 0;
   virtual int AvailableHoverTypes() const = 0;
   virtual bool ThreeDEnabled() const = 0;
   virtual bool InImmersiveMode() const = 0;
@@ -109,7 +109,7 @@
   static bool CalculateInImmersiveMode(LocalFrame*);
   static mojom::blink::PointerType CalculatePrimaryPointerType(LocalFrame*);
   static int CalculateAvailablePointerTypes(LocalFrame*);
-  static ui::HoverType CalculatePrimaryHoverType(LocalFrame*);
+  static mojom::blink::HoverType CalculatePrimaryHoverType(LocalFrame*);
   static int CalculateAvailableHoverTypes(LocalFrame*);
   static ColorSpaceGamut CalculateColorGamut(LocalFrame*);
   static mojom::blink::PreferredColorScheme CalculatePreferredColorScheme(
diff --git a/third_party/blink/renderer/core/css/media_values_cached.cc b/third_party/blink/renderer/core/css/media_values_cached.cc
index 6b19f17..7420c85 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.cc
+++ b/third_party/blink/renderer/core/css/media_values_cached.cc
@@ -27,7 +27,7 @@
       monochrome_bits_per_component(0),
       primary_pointer_type(mojom::blink::PointerType::kPointerNone),
       available_pointer_types(ui::POINTER_TYPE_NONE),
-      primary_hover_type(ui::HOVER_TYPE_NONE),
+      primary_hover_type(mojom::blink::HoverType::kHoverNone),
       available_hover_types(ui::HOVER_TYPE_NONE),
       default_font_size(16),
       three_d_enabled(false),
@@ -149,7 +149,7 @@
   return data_.available_pointer_types;
 }
 
-ui::HoverType MediaValuesCached::PrimaryHoverType() const {
+mojom::blink::HoverType MediaValuesCached::PrimaryHoverType() const {
   return data_.primary_hover_type;
 }
 
diff --git a/third_party/blink/renderer/core/css/media_values_cached.h b/third_party/blink/renderer/core/css/media_values_cached.h
index 71225e7..4e152a3 100644
--- a/third_party/blink/renderer/core/css/media_values_cached.h
+++ b/third_party/blink/renderer/core/css/media_values_cached.h
@@ -26,7 +26,7 @@
     int monochrome_bits_per_component;
     mojom::blink::PointerType primary_pointer_type;
     int available_pointer_types;
-    ui::HoverType primary_hover_type;
+    mojom::blink::HoverType primary_hover_type;
     int available_hover_types;
     int default_font_size;
     bool three_d_enabled;
@@ -98,7 +98,7 @@
   int MonochromeBitsPerComponent() const override;
   mojom::blink::PointerType PrimaryPointerType() const override;
   int AvailablePointerTypes() const override;
-  ui::HoverType PrimaryHoverType() const override;
+  mojom::blink::HoverType PrimaryHoverType() const override;
   int AvailableHoverTypes() const override;
   bool ThreeDEnabled() const override;
   bool InImmersiveMode() const override;
diff --git a/third_party/blink/renderer/core/css/media_values_dynamic.cc b/third_party/blink/renderer/core/css/media_values_dynamic.cc
index aab69b6d..9aaf3f35 100644
--- a/third_party/blink/renderer/core/css/media_values_dynamic.cc
+++ b/third_party/blink/renderer/core/css/media_values_dynamic.cc
@@ -107,7 +107,7 @@
   return CalculateAvailablePointerTypes(frame_);
 }
 
-ui::HoverType MediaValuesDynamic::PrimaryHoverType() const {
+mojom::blink::HoverType MediaValuesDynamic::PrimaryHoverType() const {
   return CalculatePrimaryHoverType(frame_);
 }
 
diff --git a/third_party/blink/renderer/core/css/media_values_dynamic.h b/third_party/blink/renderer/core/css/media_values_dynamic.h
index 6c3e369f..56ed154 100644
--- a/third_party/blink/renderer/core/css/media_values_dynamic.h
+++ b/third_party/blink/renderer/core/css/media_values_dynamic.h
@@ -39,7 +39,7 @@
   int MonochromeBitsPerComponent() const override;
   mojom::blink::PointerType PrimaryPointerType() const override;
   int AvailablePointerTypes() const override;
-  ui::HoverType PrimaryHoverType() const override;
+  mojom::blink::HoverType PrimaryHoverType() const override;
   int AvailableHoverTypes() const override;
   bool ThreeDEnabled() const override;
   bool InImmersiveMode() const override;
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 5a37d3ba..fde07c0 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
@@ -819,9 +819,13 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(!visited_link);
-  return ComputedStyleUtils::BorderSideColor(style, style.BorderBottomColor(),
-                                             style.BorderBottomStyle(),
-                                             visited_link);
+  StyleColor border_bottom_color = style.BorderBottomColor();
+  if (style.ShouldForceColor(border_bottom_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedBorderColor())
+        .ColorIncludingFallback(false, style);
+  }
+  return ComputedStyleUtils::BorderSideColor(
+      style, border_bottom_color, style.BorderBottomStyle(), visited_link);
 }
 
 const CSSValue* BorderBottomColor::CSSValueFromComputedStyleInternal(
@@ -829,13 +833,18 @@
     const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
+  StyleColor border_bottom_color = style.BorderBottomColor();
+  if (style.ShouldForceColor(border_bottom_color)) {
+    return GetCSSPropertyInternalForcedBorderColor().CSSValueFromComputedStyle(
+        style, nullptr, allow_visited_style);
+  }
   // https://drafts.csswg.org/cssom/#resolved-values
   // For this property, the resolved value is the used value.
   return allow_visited_style
              ? cssvalue::CSSColorValue::Create(
                    style.VisitedDependentColor(*this).Rgb())
              : ComputedStyleUtils::CurrentColorOrValidColor(
-                   style, style.BorderBottomColor(), CSSValuePhase::kUsedValue);
+                   style, border_bottom_color, CSSValuePhase::kUsedValue);
 }
 
 const CSSValue* BorderBottomLeftRadius::ParseSingleValue(
@@ -1074,8 +1083,13 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(!visited_link);
+  StyleColor border_left_color = style.BorderLeftColor();
+  if (style.ShouldForceColor(border_left_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedBorderColor())
+        .ColorIncludingFallback(false, style);
+  }
   return ComputedStyleUtils::BorderSideColor(
-      style, style.BorderLeftColor(), style.BorderLeftStyle(), visited_link);
+      style, border_left_color, style.BorderLeftStyle(), visited_link);
 }
 
 const CSSValue* BorderLeftColor::CSSValueFromComputedStyleInternal(
@@ -1083,13 +1097,18 @@
     const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
+  StyleColor border_left_color = style.BorderLeftColor();
+  if (style.ShouldForceColor(border_left_color)) {
+    return GetCSSPropertyInternalForcedBorderColor().CSSValueFromComputedStyle(
+        style, nullptr, allow_visited_style);
+  }
   // https://drafts.csswg.org/cssom/#resolved-values
   // For this property, the resolved value is the used value.
   return allow_visited_style
              ? cssvalue::CSSColorValue::Create(
                    style.VisitedDependentColor(*this).Rgb())
              : ComputedStyleUtils::CurrentColorOrValidColor(
-                   style, style.BorderLeftColor(), CSSValuePhase::kUsedValue);
+                   style, border_left_color, CSSValuePhase::kUsedValue);
 }
 
 const CSSValue* BorderLeftStyle::CSSValueFromComputedStyleInternal(
@@ -1127,8 +1146,13 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(!visited_link);
-  return ComputedStyleUtils::BorderSideColor(
-      style, style.BorderRightColor(), style.BorderRightStyle(), visited_link);
+  StyleColor border_right_color = style.BorderRightColor();
+  if (style.ShouldForceColor(border_right_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedBorderColor())
+        .ColorIncludingFallback(false, style);
+  }
+  return ComputedStyleUtils::BorderSideColor(style, border_right_color,
+                                             style.BorderRightStyle(), false);
 }
 
 const CSSValue* BorderRightColor::CSSValueFromComputedStyleInternal(
@@ -1136,13 +1160,18 @@
     const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
+  StyleColor border_right_color = style.BorderRightColor();
+  if (style.ShouldForceColor(border_right_color)) {
+    return GetCSSPropertyInternalForcedBorderColor().CSSValueFromComputedStyle(
+        style, nullptr, allow_visited_style);
+  }
   // https://drafts.csswg.org/cssom/#resolved-values
   // For this property, the resolved value is the used value.
   return allow_visited_style
              ? cssvalue::CSSColorValue::Create(
                    style.VisitedDependentColor(*this).Rgb())
              : ComputedStyleUtils::CurrentColorOrValidColor(
-                   style, style.BorderRightColor(), CSSValuePhase::kUsedValue);
+                   style, border_right_color, CSSValuePhase::kUsedValue);
 }
 
 const CSSValue* BorderRightStyle::CSSValueFromComputedStyleInternal(
@@ -1180,8 +1209,13 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(!visited_link);
+  StyleColor border_top_color = style.BorderTopColor();
+  if (style.ShouldForceColor(border_top_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedBorderColor())
+        .ColorIncludingFallback(false, style);
+  }
   return ComputedStyleUtils::BorderSideColor(
-      style, style.BorderTopColor(), style.BorderTopStyle(), visited_link);
+      style, border_top_color, style.BorderTopStyle(), visited_link);
 }
 
 const CSSValue* BorderTopColor::CSSValueFromComputedStyleInternal(
@@ -1189,13 +1223,18 @@
     const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
+  StyleColor border_top_color = style.BorderTopColor();
+  if (style.ShouldForceColor(border_top_color)) {
+    return GetCSSPropertyInternalForcedBorderColor().CSSValueFromComputedStyle(
+        style, nullptr, allow_visited_style);
+  }
   // https://drafts.csswg.org/cssom/#resolved-values
   // For this property, the resolved value is the used value.
   return allow_visited_style
              ? cssvalue::CSSColorValue::Create(
                    style.VisitedDependentColor(*this).Rgb())
              : ComputedStyleUtils::ComputedStyleUtils::CurrentColorOrValidColor(
-                   style, style.BorderTopColor(), CSSValuePhase::kUsedValue);
+                   style, border_top_color, CSSValuePhase::kUsedValue);
 }
 
 const CSSValue* BorderTopLeftRadius::ParseSingleValue(
@@ -1727,8 +1766,11 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(!visited_link);
-  return style.ColumnRuleColor().Resolve(style.GetCurrentColor(),
-                                         style.UsedColorScheme());
+  StyleColor column_rule_color = style.ColumnRuleColor();
+  if (style.ShouldForceColor(column_rule_color))
+    return style.GetCurrentColor();
+  return column_rule_color.Resolve(style.GetCurrentColor(),
+                                   style.UsedColorScheme());
 }
 
 const CSSValue* ColumnRuleColor::CSSValueFromComputedStyleInternal(
@@ -2566,6 +2608,8 @@
   DCHECK(!visited_link);
   DCHECK(style.SvgStyle().FillPaint().HasColor());
   StyleColor fill_color = style.SvgStyle().FillPaint().GetColor();
+  if (style.ShouldForceColor(fill_color))
+    return style.GetCurrentColor();
   return fill_color.Resolve(style.GetCurrentColor(), style.UsedColorScheme());
 }
 
@@ -3528,7 +3572,12 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(visited_link);
-  return style.InternalVisitedBorderLeftColor().Resolve(
+  StyleColor visited_border_left_color = style.InternalVisitedBorderLeftColor();
+  if (style.ShouldForceColor(visited_border_left_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedBorderColor())
+        .ColorIncludingFallback(true, style);
+  }
+  return visited_border_left_color.Resolve(
       style.GetInternalVisitedCurrentColor(), style.UsedColorScheme());
 }
 
@@ -3544,7 +3593,12 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(visited_link);
-  return style.InternalVisitedBorderTopColor().Resolve(
+  StyleColor visited_border_top_color = style.InternalVisitedBorderTopColor();
+  if (style.ShouldForceColor(visited_border_top_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedBorderColor())
+        .ColorIncludingFallback(true, style);
+  }
+  return visited_border_top_color.Resolve(
       style.GetInternalVisitedCurrentColor(), style.UsedColorScheme());
 }
 
@@ -3579,7 +3633,13 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(visited_link);
-  return style.InternalVisitedBorderRightColor().Resolve(
+  StyleColor visited_border_right_color =
+      style.InternalVisitedBorderRightColor();
+  if (style.ShouldForceColor(visited_border_right_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedBorderColor())
+        .ColorIncludingFallback(true, style);
+  }
+  return visited_border_right_color.Resolve(
       style.GetInternalVisitedCurrentColor(), style.UsedColorScheme());
 }
 
@@ -3595,7 +3655,13 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(visited_link);
-  return style.InternalVisitedBorderBottomColor().Resolve(
+  StyleColor visited_border_bottom_color =
+      style.InternalVisitedBorderBottomColor();
+  if (style.ShouldForceColor(visited_border_bottom_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedBorderColor())
+        .ColorIncludingFallback(true, style);
+  }
+  return visited_border_bottom_color.Resolve(
       style.GetInternalVisitedCurrentColor(), style.UsedColorScheme());
 }
 
@@ -3659,6 +3725,8 @@
         .ColorIncludingFallback(false, style);
   }
   StyleColor visited_fill_color = paint.GetColor();
+  if (style.ShouldForceColor(visited_fill_color))
+    return style.GetInternalVisitedCurrentColor();
   return visited_fill_color.Resolve(style.GetInternalVisitedCurrentColor(),
                                     style.UsedColorScheme());
 }
@@ -3667,7 +3735,10 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(visited_link);
-  return style.InternalVisitedColumnRuleColor().Resolve(
+  StyleColor visited_column_rule_color = style.InternalVisitedColumnRuleColor();
+  if (style.ShouldForceColor(visited_column_rule_color))
+    return style.GetInternalVisitedCurrentColor();
+  return visited_column_rule_color.Resolve(
       style.GetInternalVisitedCurrentColor(), style.UsedColorScheme());
 }
 
@@ -3682,8 +3753,13 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(visited_link);
-  return style.InternalVisitedOutlineColor().Resolve(
-      style.GetInternalVisitedCurrentColor(), style.UsedColorScheme());
+  StyleColor visited_outline_color = style.InternalVisitedOutlineColor();
+  if (style.ShouldForceColor(visited_outline_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedOutlineColor())
+        .ColorIncludingFallback(true, style);
+  }
+  return visited_outline_color.Resolve(style.GetInternalVisitedCurrentColor(),
+                                       style.UsedColorScheme());
 }
 
 const CSSValue* InternalVisitedOutlineColor::ParseSingleValue(
@@ -3714,6 +3790,8 @@
         .ColorIncludingFallback(false, style);
   }
   StyleColor visited_stroke_color = paint.GetColor();
+  if (style.ShouldForceColor(visited_stroke_color))
+    return style.GetInternalVisitedCurrentColor();
   return visited_stroke_color.Resolve(style.GetInternalVisitedCurrentColor(),
                                       style.UsedColorScheme());
 }
@@ -3722,8 +3800,12 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(visited_link);
-  return style.DecorationColorIncludingFallback(visited_link)
-      .Resolve(style.GetInternalVisitedCurrentColor(), style.UsedColorScheme());
+  StyleColor visited_decoration_color =
+      style.DecorationColorIncludingFallback(visited_link);
+  if (style.ShouldForceColor(visited_decoration_color))
+    return style.GetInternalVisitedCurrentColor();
+  return visited_decoration_color.Resolve(
+      style.GetInternalVisitedCurrentColor(), style.UsedColorScheme());
 }
 
 const CSSValue* InternalVisitedTextDecorationColor::ParseSingleValue(
@@ -3737,7 +3819,11 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(visited_link);
-  return style.InternalVisitedTextEmphasisColor().Resolve(
+  StyleColor visited_text_emphasis_color =
+      style.InternalVisitedTextEmphasisColor();
+  if (style.ShouldForceColor(visited_text_emphasis_color))
+    return style.GetInternalVisitedCurrentColor();
+  return visited_text_emphasis_color.Resolve(
       style.GetInternalVisitedCurrentColor(), style.UsedColorScheme());
 }
 
@@ -3819,6 +3905,66 @@
                                          IsQuirksModeBehavior(context.Mode()));
 }
 
+const blink::Color InternalForcedBorderColor::ColorIncludingFallback(
+    bool visited_link,
+    const ComputedStyle& style) const {
+  blink::Color current_color = visited_link
+                                   ? style.GetInternalVisitedCurrentColor()
+                                   : style.GetCurrentColor();
+
+  return style.InternalForcedBorderColor().Resolve(current_color,
+                                                   style.UsedColorScheme());
+}
+
+const CSSValue* InternalForcedBorderColor::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const SVGComputedStyle&,
+    const LayoutObject*,
+    bool allow_visited_style) const {
+  bool visited_link = allow_visited_style &&
+                      style.InsideLink() == EInsideLink::kInsideVisitedLink;
+  return cssvalue::CSSColorValue::Create(
+      ColorIncludingFallback(visited_link, style).Rgb());
+}
+
+const CSSValue* InternalForcedBorderColor::ParseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext& context,
+    const CSSParserLocalContext& local_context) const {
+  return css_parsing_utils::ConsumeColor(range, context,
+                                         IsQuirksModeBehavior(context.Mode()));
+}
+
+const blink::Color InternalForcedOutlineColor::ColorIncludingFallback(
+    bool visited_link,
+    const ComputedStyle& style) const {
+  blink::Color current_color = visited_link
+                                   ? style.GetInternalVisitedCurrentColor()
+                                   : style.GetCurrentColor();
+
+  return style.InternalForcedOutlineColor().Resolve(current_color,
+                                                    style.UsedColorScheme());
+}
+
+const CSSValue* InternalForcedOutlineColor::CSSValueFromComputedStyleInternal(
+    const ComputedStyle& style,
+    const SVGComputedStyle&,
+    const LayoutObject*,
+    bool allow_visited_style) const {
+  bool visited_link = allow_visited_style &&
+                      style.InsideLink() == EInsideLink::kInsideVisitedLink;
+  return cssvalue::CSSColorValue::Create(
+      ColorIncludingFallback(visited_link, style).Rgb());
+}
+
+const CSSValue* InternalForcedOutlineColor::ParseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext& context,
+    const CSSParserLocalContext& local_context) const {
+  return css_parsing_utils::ConsumeColor(range, context,
+                                         IsQuirksModeBehavior(context.Mode()));
+}
+
 const CSSValue* Isolation::CSSValueFromComputedStyleInternal(
     const ComputedStyle& style,
     const SVGComputedStyle&,
@@ -4723,8 +4869,13 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(!visited_link);
-  return style.OutlineColor().Resolve(style.GetCurrentColor(),
-                                      style.UsedColorScheme());
+  StyleColor outline_color = style.OutlineColor();
+  if (style.ShouldForceColor(outline_color)) {
+    return To<Longhand>(GetCSSPropertyInternalForcedOutlineColor())
+        .ColorIncludingFallback(false, style);
+  }
+  return outline_color.Resolve(style.GetCurrentColor(),
+                               style.UsedColorScheme());
 }
 
 const CSSValue* OutlineColor::CSSValueFromComputedStyleInternal(
@@ -4732,13 +4883,18 @@
     const SVGComputedStyle&,
     const LayoutObject*,
     bool allow_visited_style) const {
+  StyleColor outline_color = style.OutlineColor();
+  if (style.ShouldForceColor(outline_color)) {
+    return GetCSSPropertyInternalForcedOutlineColor().CSSValueFromComputedStyle(
+        style, nullptr, allow_visited_style);
+  }
   // https://drafts.csswg.org/cssom/#resolved-values
   // For this property, the resolved value is the used value.
   return allow_visited_style
              ? cssvalue::CSSColorValue::Create(
                    style.VisitedDependentColor(*this).Rgb())
              : ComputedStyleUtils::CurrentColorOrValidColor(
-                   style, style.OutlineColor(), CSSValuePhase::kUsedValue);
+                   style, outline_color, CSSValuePhase::kUsedValue);
 }
 
 const CSSValue* OutlineOffset::ParseSingleValue(
@@ -6192,6 +6348,8 @@
   DCHECK(!visited_link);
   DCHECK(style.SvgStyle().StrokePaint().HasColor());
   StyleColor stroke_color = style.SvgStyle().StrokePaint().GetColor();
+  if (style.ShouldForceColor(stroke_color))
+    return style.GetCurrentColor();
   return stroke_color.Resolve(style.GetCurrentColor(), style.UsedColorScheme());
 }
 
@@ -6435,8 +6593,12 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(!visited_link);
-  return style.DecorationColorIncludingFallback(visited_link)
-      .Resolve(style.GetCurrentColor(), style.UsedColorScheme());
+  StyleColor decoration_color =
+      style.DecorationColorIncludingFallback(visited_link);
+  if (style.ShouldForceColor(decoration_color))
+    return style.GetCurrentColor();
+  return decoration_color.Resolve(style.GetCurrentColor(),
+                                  style.UsedColorScheme());
 }
 
 const CSSValue* TextDecorationColor::CSSValueFromComputedStyleInternal(
@@ -7878,6 +8040,11 @@
 const blink::Color WebkitTapHighlightColor::ColorIncludingFallback(
     bool visited_link,
     const ComputedStyle& style) const {
+  StyleColor highlight_color = style.TapHighlightColor();
+  if (style.ShouldForceColor(highlight_color)) {
+    return visited_link ? style.GetInternalVisitedCurrentColor()
+                        : style.GetCurrentColor();
+  }
   return style.ResolvedColor(style.TapHighlightColor());
 }
 
@@ -7928,8 +8095,11 @@
     bool visited_link,
     const ComputedStyle& style) const {
   DCHECK(!visited_link);
-  return style.TextEmphasisColor().Resolve(style.GetCurrentColor(),
-                                           style.UsedColorScheme());
+  StyleColor text_emphasis_color = style.TextEmphasisColor();
+  if (style.ShouldForceColor(text_emphasis_color))
+    return style.GetCurrentColor();
+  return text_emphasis_color.Resolve(style.GetCurrentColor(),
+                                     style.UsedColorScheme());
 }
 
 const CSSValue* WebkitTextEmphasisColor::CSSValueFromComputedStyleInternal(
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade.cc b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
index 6d5866f..1279ffd 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
@@ -574,46 +574,9 @@
       style->ForcedColorAdjust() == EForcedColorAdjust::kNone)
     return;
 
-  const SVGComputedStyle& svg_style = style->SvgStyle();
-
   MaybeForceColor(GetCSSPropertyColor(), style->GetColor());
-  MaybeForceColor(GetCSSPropertyBorderBottomColor(),
-                  style->BorderBottomColor());
-  MaybeForceColor(GetCSSPropertyBorderLeftColor(), style->BorderLeftColor());
-  MaybeForceColor(GetCSSPropertyBorderRightColor(), style->BorderRightColor());
-  MaybeForceColor(GetCSSPropertyBorderTopColor(), style->BorderTopColor());
-  MaybeForceColor(GetCSSPropertyFill(), svg_style.FillPaint().GetColor());
-  MaybeForceColor(GetCSSPropertyOutlineColor(), style->OutlineColor());
-  MaybeForceColor(GetCSSPropertyStroke(), svg_style.StrokePaint().GetColor());
-  MaybeForceColor(GetCSSPropertyTextDecorationColor(),
-                  style->TextDecorationColor());
-  MaybeForceColor(GetCSSPropertyColumnRuleColor(), style->ColumnRuleColor());
-  MaybeForceColor(GetCSSPropertyWebkitTapHighlightColor(),
-                  style->TapHighlightColor());
-  MaybeForceColor(GetCSSPropertyWebkitTextEmphasisColor(),
-                  style->TextEmphasisColor());
   MaybeForceColor(GetCSSPropertyInternalVisitedColor(),
                   style->InternalVisitedColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedBorderBottomColor(),
-                  style->InternalVisitedBorderBottomColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedBorderLeftColor(),
-                  style->InternalVisitedBorderLeftColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedBorderRightColor(),
-                  style->InternalVisitedBorderRightColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedBorderTopColor(),
-                  style->InternalVisitedBorderTopColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedFill(),
-                  svg_style.InternalVisitedFillPaint().GetColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedOutlineColor(),
-                  style->InternalVisitedOutlineColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedStroke(),
-                  svg_style.InternalVisitedStrokePaint().GetColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedTextDecorationColor(),
-                  style->InternalVisitedTextDecorationColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedColumnRuleColor(),
-                  style->InternalVisitedColumnRuleColor());
-  MaybeForceColor(GetCSSPropertyInternalVisitedTextEmphasisColor(),
-                  style->InternalVisitedTextEmphasisColor());
 
   ScopedCSSValue scoped_none(*CSSIdentifierValue::Create(CSSValueID::kNone),
                              nullptr);
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index 351d803..d5e1a23 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -3483,7 +3483,9 @@
 
 // Properties stored for forced colors mode should only be usable by the UA.
 TEST_F(StyleEngineTest, InternalForcedProperties) {
-  String properties_to_test[] = {"-internal-forced-background-color"};
+  String properties_to_test[] = {"-internal-forced-background-color",
+                                 "-internal-forced-border-color",
+                                 "-internal-forced-outline-color"};
   for (auto property : properties_to_test) {
     String declaration = property + ":red";
     ASSERT_TRUE(
diff --git a/third_party/blink/renderer/core/css/svg.css b/third_party/blink/renderer/core/css/svg.css
index ed746bbc..557df4ed 100644
--- a/third_party/blink/renderer/core/css/svg.css
+++ b/third_party/blink/renderer/core/css/svg.css
@@ -90,10 +90,6 @@
    https://drafts.csswg.org/css-color-adjust-1/#forced-colors-properties
 */
 @media ua-forced-colors {
-  svg:root {
-    color: CanvasText;
-  }
-
   svg {
       forced-color-adjust: none;
   }
diff --git a/third_party/blink/renderer/core/execution_context/execution_context.h b/third_party/blink/renderer/core/execution_context/execution_context.h
index 240e3642..373d4c45 100644
--- a/third_party/blink/renderer/core/execution_context/execution_context.h
+++ b/third_party/blink/renderer/core/execution_context/execution_context.h
@@ -42,6 +42,7 @@
 #include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/feature_policy/policy_disposition.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/v8_cache_options.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
@@ -212,6 +213,9 @@
   virtual bool CanExecuteScripts(ReasonForCallingCanExecuteScripts) {
     return false;
   }
+  virtual mojom::blink::V8CacheOptions GetV8CacheOptions() const {
+    return mojom::blink::V8CacheOptions::kDefault;
+  }
 
   void DispatchErrorEvent(ErrorEvent*, SanitizeScriptErrors);
 
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.cc b/third_party/blink/renderer/core/exported/web_settings_impl.cc
index f125056f..69157fea 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc
@@ -267,7 +267,7 @@
   dev_tools_emulator_->SetAvailableHoverTypes(types);
 }
 
-void WebSettingsImpl::SetPrimaryHoverType(ui::HoverType type) {
+void WebSettingsImpl::SetPrimaryHoverType(mojom::blink::HoverType type) {
   dev_tools_emulator_->SetPrimaryHoverType(type);
 }
 
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.h b/third_party/blink/renderer/core/exported/web_settings_impl.h
index be41a03..2971a655 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.h
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.h
@@ -132,7 +132,7 @@
   void SetAvailablePointerTypes(int) override;
   void SetPrimaryPointerType(mojom::blink::PointerType) override;
   void SetAvailableHoverTypes(int) override;
-  void SetPrimaryHoverType(ui::HoverType) override;
+  void SetPrimaryHoverType(mojom::blink::HoverType) override;
   void SetPreferHiddenVolumeControls(bool) override;
   void SetShouldProtectAgainstIpcFlooding(bool) override;
   void SetRenderVSyncNotificationEnabled(bool) override;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 114ca7d..ff4ec73 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -239,6 +239,15 @@
   return blink::ToLocalDOMWindow(script_state->GetContext());
 }
 
+mojom::blink::V8CacheOptions LocalDOMWindow::GetV8CacheOptions() const {
+  if (LocalFrame* frame = GetFrame()) {
+    if (const Settings* settings = frame->GetSettings())
+      return settings->GetV8CacheOptions();
+  }
+
+  return mojom::blink::V8CacheOptions::kDefault;
+}
+
 bool LocalDOMWindow::IsContextThread() const {
   return IsMainThread();
 }
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h
index 07101cf..86d2cb62 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.h
+++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -128,6 +128,8 @@
 
   void ResetWindowAgent(WindowAgent*);
 
+  mojom::blink::V8CacheOptions GetV8CacheOptions() const override;
+
   // Bind Content Security Policy to this window. This will cause the
   // CSP to resolve the 'self' attribute and all policies will then be
   // applied to this document.
diff --git a/third_party/blink/renderer/core/frame/settings.h b/third_party/blink/renderer/core/frame/settings.h
index f4fdbbc..af8e5f4 100644
--- a/third_party/blink/renderer/core/frame/settings.h
+++ b/third_party/blink/renderer/core/frame/settings.h
@@ -49,6 +49,7 @@
 #include "third_party/blink/renderer/platform/geometry/int_size.h"
 #include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "ui/base/pointer/pointer_device.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5
index 863e8bb..3af88df 100644
--- a/third_party/blink/renderer/core/frame/settings.json5
+++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -642,9 +642,9 @@
     },
     {
       name: "primaryHoverType",
-      initial: "ui::HOVER_TYPE_NONE",
+      initial: "mojom::blink::HoverType::kHoverNone",
       invalidate: "MediaQuery",
-      type: "ui::HoverType",
+      type: "mojom::blink::HoverType",
     },
 
     // If true, the value in password fields is exposed to assistive technologies.
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 f1431d0..400f7498 100644
--- a/third_party/blink/renderer/core/html/canvas/image_data.cc
+++ b/third_party/blink/renderer/core/html/canvas/image_data.cc
@@ -272,13 +272,6 @@
 }
 
 ImageData* ImageData::Create(const IntSize& size,
-                             const CanvasColorParams& color_params) {
-  ImageDataColorSettings* color_settings =
-      CanvasColorParamsToImageDataColorSettings(color_params);
-  return ImageData::Create(size, color_settings);
-}
-
-ImageData* ImageData::Create(const IntSize& size,
                              CanvasColorSpace color_space,
                              ImageDataStorageFormat storage_format) {
   ImageDataColorSettings* color_settings = ImageDataColorSettings::Create();
@@ -332,7 +325,8 @@
     }
   }
 
-  ImageData* image_data = Create(image->Size(), color_params);
+  ImageData* image_data = Create(
+      image->Size(), CanvasColorParamsToImageDataColorSettings(color_params));
   if (!image_data)
     return nullptr;
 
@@ -667,7 +661,13 @@
   return kUint8ClampedArrayStorageFormat;
 }
 
-ImageDataStorageFormat ImageData::GetImageDataStorageFormat() {
+CanvasColorSpace ImageData::GetCanvasColorSpace() const {
+  if (!RuntimeEnabledFeatures::CanvasColorManagementEnabled())
+    return CanvasColorSpace::kSRGB;
+  return CanvasColorSpaceFromName(color_settings_->colorSpace());
+}
+
+ImageDataStorageFormat ImageData::GetImageDataStorageFormat() const {
   if (data_u16_)
     return kUint16ArrayStorageFormat;
   if (data_f32_)
@@ -782,15 +782,17 @@
       kNonOpaque);
 }
 
-SkImageInfo ImageData::GetSkImageInfo() {
-  SkColorType color_type = kN32_SkColorType;
+SkPixmap ImageData::GetSkPixmap() const {
+  SkColorType color_type = kRGBA_8888_SkColorType;
   if (data_u16_) {
     color_type = kR16G16B16A16_unorm_SkColorType;
   } else if (data_f32_) {
     color_type = kRGBA_F32_SkColorType;
   }
-  return SkImageInfo::Make(width(), height(), color_type,
-                           kUnpremul_SkAlphaType);
+  SkImageInfo info =
+      SkImageInfo::Make(width(), height(), color_type, kUnpremul_SkAlphaType,
+                        CanvasColorSpaceToSkColorSpace(GetCanvasColorSpace()));
+  return SkPixmap(info, BufferBase()->Data(), info.minRowBytes());
 }
 
 bool ImageData::ImageDataInCanvasColorSettings(
@@ -836,7 +838,8 @@
   skcms_ICCProfile* src_profile_ptr = nullptr;
   skcms_ICCProfile* dst_profile_ptr = nullptr;
   skcms_ICCProfile src_profile, dst_profile;
-  GetCanvasColorParams().GetSkColorSpace()->toProfile(&src_profile);
+  CanvasColorSpaceToSkColorSpace(GetCanvasColorSpace())
+      ->toProfile(&src_profile);
   canvas_color_params.GetSkColorSpace()->toProfile(&dst_profile);
   // If the profiles are similar, we better leave them as nullptr, since
   // skcms_Transform() only checks for profile pointer equality for the fast
diff --git a/third_party/blink/renderer/core/html/canvas/image_data.h b/third_party/blink/renderer/core/html/canvas/image_data.h
index 53f636c..529af055 100644
--- a/third_party/blink/renderer/core/html/canvas/image_data.h
+++ b/third_party/blink/renderer/core/html/canvas/image_data.h
@@ -71,7 +71,6 @@
  public:
   static ImageData* Create(const IntSize&,
                            const ImageDataColorSettings* = nullptr);
-  static ImageData* Create(const IntSize&, const CanvasColorParams&);
   static ImageData* Create(const IntSize&,
                            CanvasColorSpace,
                            ImageDataStorageFormat);
@@ -127,7 +126,6 @@
 
   ImageData* CropRect(const IntRect&, bool flip_y = false);
 
-  ImageDataStorageFormat GetImageDataStorageFormat();
   static String CanvasColorSpaceName(CanvasColorSpace);
   static ImageDataStorageFormat GetImageDataStorageFormat(const String&);
   static unsigned StorageFormatBytesPerPixel(const String&);
@@ -148,7 +146,11 @@
 
   DOMArrayBufferBase* BufferBase() const;
   CanvasColorParams GetCanvasColorParams();
-  SkImageInfo GetSkImageInfo();
+  CanvasColorSpace GetCanvasColorSpace() const;
+  ImageDataStorageFormat GetImageDataStorageFormat() const;
+
+  // Return an SkPixmap that references this data directly.
+  SkPixmap GetSkPixmap() const;
 
   // DataU8ColorType param specifies if the converted pixels in uint8 pixel
   // format should respect the "native" 32bit ARGB format of Skia's blitters.
diff --git a/third_party/blink/renderer/core/html/resources/forced_colors.css b/third_party/blink/renderer/core/html/resources/forced_colors.css
index cd123c4..4b3d9a00 100644
--- a/third_party/blink/renderer/core/html/resources/forced_colors.css
+++ b/third_party/blink/renderer/core/html/resources/forced_colors.css
@@ -17,11 +17,10 @@
 @media ua-forced-colors {
   html {
     color: CanvasText;
-    fill: currentColor;
   }
 
   :focus {
-    outline-color: Highlight;
+    -internal-forced-outline-color: Highlight;
   }
 
   a:link,
@@ -34,7 +33,7 @@
   }
 
   fieldset {
-    border-color: CanvasText;
+    -internal-forced-border-color: CanvasText;
   }
 
   mark {
@@ -48,9 +47,9 @@
   input,
   textarea {
     background-color: Canvas;
-    border-color: ButtonText;
     color: CanvasText;
     -internal-forced-background-color: Canvas;
+    -internal-forced-border-color: ButtonText;
   }
 
   input:hover,
@@ -58,7 +57,7 @@
   input[type="file"]:hover::-webkit-file-upload-button,
   input:focus,
   textarea:focus {
-    border-color: Highlight;
+    -internal-forced-border-color: Highlight;
   }
 
   input[type="text"]:disabled,
@@ -75,9 +74,9 @@
   input[type="datetime-local"]:disabled,
   textarea:disabled {
     background-color: ButtonFace;
-    border-color: GrayText;
     color: GrayText;
     -internal-forced-background-color: ButtonFace;
+    -internal-forced-border-color: GrayText;
   }
 
   input::-webkit-calendar-picker-indicator {
@@ -118,10 +117,10 @@
   input[type="submit"],
   input[type="reset"],
   input[type="file"]::-webkit-file-upload-button {
-    border-color: ButtonText;
     background-color: ButtonFace;
     color: ButtonText;
     -internal-forced-background-color: ButtonFace;
+    -internal-forced-border-color: ButtonText;
   }
 
   button:hover,
@@ -132,15 +131,15 @@
   input[type="button"]:focus,
   input[type="submit"]:focus,
   input[type="reset"]:focus {
-    border-color: Highlight;
+    -internal-forced-border-color: Highlight;
   }
 
   button:disabled,
   input[type="button"]:disabled,
   input[type="submit"]:disabled,
   input[type="reset"]:disabled {
-    border-color: GrayText;
     color: GrayText;
+    -internal-forced-border-color: GrayText;
   }
 
   /* same color as hyperlinks */
@@ -150,8 +149,8 @@
 
   select:-internal-list-box {
     background-color: Canvas !important;
-    border-color: ButtonText;
     -internal-forced-background-color: Canvas !important;
+    -internal-forced-border-color: ButtonText;
   }
 
   /* option disabled */
@@ -196,9 +195,9 @@
 
   select {
     background-color: Canvas;
-    border-color: CanvasText;
     color: CanvasText;
     -internal-forced-background-color: Canvas;
+    -internal-forced-border-color: CanvasText;
   }
 
   select:not(:-internal-list-box) {
@@ -207,23 +206,23 @@
   }
 
   select:hover {
-    border-color: Highlight;
+    -internal-forced-border-color: Highlight;
   }
 
   select:focus {
-    border-color: Highlight;
+    -internal-forced-border-color: Highlight;
   }
 
   select:disabled {
-    border-color: GrayText;
     color: GrayText;
     opacity: 1;
+    -internal-forced-border-color: GrayText;
   }
 
   meter::-webkit-meter-bar {
     background-color: ButtonFace;
-    border-color: CanvasText;
     -internal-forced-background-color: ButtonFace;
+    -internal-forced-border-color: CanvasText;
   }
 
   meter::-webkit-meter-even-less-good-value,
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
index 922ddfc..5179aed 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
@@ -784,32 +784,25 @@
   }
 
   // Copy / color convert the pixels
-  size_t image_pixels_size;
-  if (!base::CheckMul(parsed_options.color_params.BytesPerPixel(),
-                      static_cast<unsigned>(src_rect.Width()),
-                      static_cast<unsigned>(src_rect.Height()))
-           .AssignIfValid(&image_pixels_size)) {
-    return;
-  }
-  sk_sp<SkData> image_pixels = TryAllocateSkData(image_pixels_size);
-  if (!image_pixels)
-    return;
-  if (!data->ImageDataInCanvasColorSettings(
-          parsed_options.color_params.ColorSpace(),
-          parsed_options.color_params.PixelFormat(),
-          static_cast<unsigned char*>(image_pixels->writable_data()),
-          kN32ColorType, &src_rect,
-          parsed_options.premultiply_alpha ? kPremultiplyAlpha
-                                           : kUnpremultiplyAlpha))
-    return;
-
-  // Create Image object
   SkImageInfo info = SkImageInfo::Make(
       src_rect.Width(), src_rect.Height(),
       parsed_options.color_params.GetSkColorType(),
       parsed_options.premultiply_alpha ? kPremul_SkAlphaType
                                        : kUnpremul_SkAlphaType,
       parsed_options.color_params.GetSkColorSpace());
+  size_t image_pixels_size = info.computeMinByteSize();
+  if (SkImageInfo::ByteSizeOverflowed(image_pixels_size))
+    return;
+  sk_sp<SkData> image_pixels = TryAllocateSkData(image_pixels_size);
+  if (!image_pixels)
+    return;
+  if (!data->GetSkPixmap().readPixels(info, image_pixels->writable_data(),
+                                      info.minRowBytes(), src_rect.X(),
+                                      src_rect.Y())) {
+    return;
+  }
+
+  // Create Image object
   image_ = StaticBitmapImage::Create(std::move(image_pixels), info);
   if (!image_)
     return;
diff --git a/third_party/blink/renderer/core/inspector/dev_tools_emulator.cc b/third_party/blink/renderer/core/inspector/dev_tools_emulator.cc
index b5b8373..10c1c63 100644
--- a/third_party/blink/renderer/core/inspector/dev_tools_emulator.cc
+++ b/third_party/blink/renderer/core/inspector/dev_tools_emulator.cc
@@ -212,7 +212,7 @@
     web_view_->GetPage()->GetSettings().SetAvailableHoverTypes(types);
 }
 
-void DevToolsEmulator::SetPrimaryHoverType(ui::HoverType hover_type) {
+void DevToolsEmulator::SetPrimaryHoverType(mojom::blink::HoverType hover_type) {
   embedder_primary_hover_type_ = hover_type;
   if (!touch_event_emulation_enabled_)
     web_view_->GetPage()->GetSettings().SetPrimaryHoverType(hover_type);
@@ -463,9 +463,11 @@
       enabled ? mojom::blink::PointerType::kPointerCoarseType
               : embedder_primary_pointer_type_);
   web_view_->GetPage()->GetSettings().SetAvailableHoverTypes(
-      enabled ? ui::HOVER_TYPE_NONE : embedder_available_hover_types_);
+      enabled ? static_cast<int>(mojom::blink::HoverType::kHoverNone)
+              : embedder_available_hover_types_);
   web_view_->GetPage()->GetSettings().SetPrimaryHoverType(
-      enabled ? ui::HOVER_TYPE_NONE : embedder_primary_hover_type_);
+      enabled ? mojom::blink::HoverType::kHoverNone
+              : embedder_primary_hover_type_);
   WebLocalFrameImpl* frame = web_view_->MainFrameImpl();
   if (enabled && frame)
     frame->GetFrame()->GetEventHandler().ClearMouseEventManager();
diff --git a/third_party/blink/renderer/core/inspector/dev_tools_emulator.h b/third_party/blink/renderer/core/inspector/dev_tools_emulator.h
index eb8b2cfc..18f432d 100644
--- a/third_party/blink/renderer/core/inspector/dev_tools_emulator.h
+++ b/third_party/blink/renderer/core/inspector/dev_tools_emulator.h
@@ -13,7 +13,6 @@
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
-#include "ui/base/pointer/pointer_device.h"
 
 namespace gfx {
 class PointF;
@@ -44,7 +43,7 @@
   void SetAvailablePointerTypes(int);
   void SetPrimaryPointerType(mojom::blink::PointerType);
   void SetAvailableHoverTypes(int);
-  void SetPrimaryHoverType(ui::HoverType);
+  void SetPrimaryHoverType(mojom::blink::HoverType);
   void SetMainFrameResizesAreOrientationChanges(bool);
 
   // Enables and/or sets the parameters for emulation. Returns the emulation
@@ -127,7 +126,7 @@
   int embedder_available_pointer_types_;
   mojom::blink::PointerType embedder_primary_pointer_type_;
   int embedder_available_hover_types_;
-  ui::HoverType embedder_primary_hover_type_;
+  mojom::blink::HoverType embedder_primary_hover_type_;
   bool embedder_main_frame_resizes_are_orientation_changes_;
 
   bool touch_event_emulation_enabled_;
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index e4bc91a7..26d5db6ed 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -546,8 +546,6 @@
       }
     }
   }
-
-  ApplyOverflowClipToLayoutOverflowRect();
 }
 
 void LayoutBlock::AddVisualOverflowFromBlockChildren() {
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 79de9ade..f1ed3479 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -7271,7 +7271,23 @@
   if (!ShouldApplyLayoutContainment() &&
       (!ShouldClipOverflowAlongBothAxis() ||
        StyleRef().OverflowClipMargin() != LayoutUnit())) {
-    rect.Unite(LayoutOverflowRect());
+    LayoutRect overflow = LayoutOverflowRect();
+    if (HasNonVisibleOverflow()) {
+      const OverflowClipAxes overflow_clip_axes = GetOverflowClipAxes();
+      const LayoutUnit overflow_clip_margin = StyleRef().OverflowClipMargin();
+      LayoutRect clip_rect = rect;
+      if (overflow_clip_margin != LayoutUnit()) {
+        // overflow_clip_margin should only be set if 'overflow' is 'clip' along
+        // both axis.
+        DCHECK_EQ(overflow_clip_axes, kOverflowClipBothAxis);
+        clip_rect.Contract(BorderBoxOutsets());
+        clip_rect.Inflate(overflow_clip_margin);
+        overflow.Intersect(clip_rect);
+      } else {
+        ApplyOverflowClip(overflow_clip_axes, clip_rect, overflow);
+      }
+    }
+    rect.Unite(overflow);
   }
 
   bool has_transform = HasLayer() && Layer()->Transform();
@@ -7784,32 +7800,6 @@
   return PhysicalRect(PhysicalLocation(), Size());
 }
 
-void LayoutBox::ApplyOverflowClipToLayoutOverflowRect() {
-  NOT_DESTROYED();
-  if (!HasNonVisibleOverflow() || IsScrollContainer() ||
-      !LayoutOverflowIsSet()) {
-    return;
-  }
-
-  const OverflowClipAxes overflow_clip_axes = GetOverflowClipAxes();
-  if (overflow_clip_axes == kNoOverflowClip)
-    return;
-
-  LayoutRect no_overflow_rect = NoOverflowRect();
-  LayoutRect overflow_rect = overflow_->layout_overflow->LayoutOverflowRect();
-  const LayoutUnit overflow_clip_margin = StyleRef().OverflowClipMargin();
-  if (overflow_clip_margin != LayoutUnit()) {
-    // overflow_clip_margin should only be set if 'overflow' is 'clip' along
-    // both axis.
-    DCHECK_EQ(overflow_clip_axes, kOverflowClipBothAxis);
-    no_overflow_rect.Inflate(overflow_clip_margin);
-    overflow_rect.Intersect(no_overflow_rect);
-  } else {
-    ApplyOverflowClip(overflow_clip_axes, no_overflow_rect, overflow_rect);
-  }
-  overflow_->layout_overflow->SetLayoutOverflow(overflow_rect);
-}
-
 OverflowClipAxes LayoutBox::ComputeOverflowClipAxes() const {
   NOT_DESTROYED();
   if (ShouldApplyPaintContainment() || HasControlClip())
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 5238437..4e53247 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -2062,11 +2062,6 @@
  protected:
   ~LayoutBox() override;
 
-  // Applies 'overflow:clip' as necessary to the accumulated layout overflow.
-  // During layout overflow is accumulated, once all the overflow has been
-  // accumulated 'overflow:clip' is then applied.
-  void ApplyOverflowClipToLayoutOverflowRect();
-
   virtual OverflowClipAxes ComputeOverflowClipAxes() const;
 
   void WillBeDestroyed() override;
diff --git a/third_party/blink/renderer/core/layout/layout_box_test.cc b/third_party/blink/renderer/core/layout/layout_box_test.cc
index 8193669..201558a6 100644
--- a/third_party/blink/renderer/core/layout/layout_box_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_test.cc
@@ -563,12 +563,14 @@
   LayoutBox* clip1 = GetLayoutBoxByElementId("clip1");
   EXPECT_FALSE(clip1->IsScrollContainer());
   EXPECT_TRUE(clip1->ShouldClipOverflowAlongBothAxis());
-  EXPECT_EQ(LayoutRect(-4, -4, 108, 58), clip1->LayoutOverflowRect());
+  EXPECT_EQ(LayoutRect(-4, -4, 108, 58),
+            clip1->LayoutOverflowRectForPropagation(clip1->Parent()));
 
   LayoutBox* clip2 = GetLayoutBoxByElementId("clip2");
   EXPECT_FALSE(clip2->IsScrollContainer());
   EXPECT_TRUE(clip2->ShouldClipOverflowAlongBothAxis());
-  EXPECT_EQ(LayoutRect(-6, -5, 110, 65), clip2->LayoutOverflowRect());
+  EXPECT_EQ(LayoutRect(-6, -5, 110, 65),
+            clip2->LayoutOverflowRectForPropagation(clip2->Parent()));
 }
 
 TEST_P(LayoutBoxTest, ContentsVisualOverflowPropagation) {
diff --git a/third_party/blink/renderer/core/layout/layout_table_test.cc b/third_party/blink/renderer/core/layout/layout_table_test.cc
index 6cebb6f..3d0699b 100644
--- a/third_party/blink/renderer/core/layout/layout_table_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_test.cc
@@ -80,9 +80,16 @@
                                             LayoutUnit(0), LayoutUnit(10));
   EXPECT_EQ(expected_self_visual_overflow,
             table->PhysicalSelfVisualOverflowRect());
-  // For this table, its layout overflow equals self visual overflow.
-  EXPECT_EQ(expected_self_visual_overflow, table->PhysicalLayoutOverflowRect());
-
+  if (RuntimeEnabledFeatures::LayoutNGTableEnabled()) {
+    EXPECT_EQ(table->PhysicalContentBoxRect(),
+              table->PhysicalLayoutOverflowRect());
+  } else {
+    // In Legacy, visual overflow incorrectly does not include borders
+    // that extend beyond table boundaries.
+    // For this table, its layout overflow equals self visual overflow.
+    EXPECT_EQ(expected_self_visual_overflow,
+              table->PhysicalLayoutOverflowRect());
+  }
   // The table's visual overflow covers self visual overflow and content visual
   // overflows.
   auto expected_visual_overflow = table->PhysicalContentBoxRect();
@@ -133,14 +140,25 @@
 
   // Cells have wider borders.
   auto* table3 = GetTableByElementId("table3");
-  // Cell E's border-top won.
-  EXPECT_EQ(7, table3->BorderBefore());
-  // Cell H's border-bottom won.
-  EXPECT_EQ(20, table3->BorderAfter());
-  // Cell E's border-left won.
-  EXPECT_EQ(10, table3->BorderStart());
-  // Cell F's border-bottom won.
-  EXPECT_EQ(13, table3->BorderEnd());
+  if (RuntimeEnabledFeatures::LayoutNGTableEnabled()) {
+    // Cell E's border-top won.
+    EXPECT_EQ(LayoutUnit(7.5), table3->BorderBefore());
+    // Cell H's border-bottom won.
+    EXPECT_EQ(20, table3->BorderAfter());
+    // Cell E's border-left won.
+    EXPECT_EQ(LayoutUnit(10.5), table3->BorderStart());
+    // Cell F's border-bottom won.
+    EXPECT_EQ(LayoutUnit(12.5), table3->BorderEnd());
+  } else {
+    // Cell E's border-top won.
+    EXPECT_EQ(7, table3->BorderBefore());
+    // Cell H's border-bottom won.
+    EXPECT_EQ(20, table3->BorderAfter());
+    // Cell E's border-left won.
+    EXPECT_EQ(10, table3->BorderStart());
+    // Cell F's border-bottom won.
+    EXPECT_EQ(13, table3->BorderEnd());
+  }
 }
 
 TEST_F(LayoutTableTest, CollapsedBordersWithCol) {
@@ -224,7 +242,11 @@
 
   // Table width should be TableLayoutAlgorithm::kMaxTableWidth
   auto* table = GetTableByElementId("onlyTable");
-  EXPECT_EQ(1000000, table->OffsetWidth());
+  // TablesNG will grow up to LayoutUnit::Max()
+  if (RuntimeEnabledFeatures::LayoutNGTableEnabled())
+    EXPECT_EQ(2000000, table->OffsetWidth());
+  else
+    EXPECT_EQ(1000000, table->OffsetWidth());
 }
 
 TEST_F(LayoutTableTest, CloseToMaxWidth) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc
index db87551..dde2465 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.cc
@@ -91,29 +91,6 @@
 
 const PhysicalRect NGLayoutOverflowCalculator::Result(
     const base::Optional<PhysicalRect> inflow_bounds) {
-  // Adjust the layout-overflow if we have "overflow: clip" present.
-  if (!is_scroll_container_ && has_non_visible_overflow_) {
-    const OverflowClipAxes overflow_clip_axes = node_.GetOverflowClipAxes();
-    const LayoutUnit overflow_clip_margin = node_.Style().OverflowClipMargin();
-    if (overflow_clip_margin != LayoutUnit()) {
-      // overflow_clip_margin should only be set if 'overflow' is 'clip' along
-      // both axis.
-      DCHECK_EQ(overflow_clip_axes, kOverflowClipBothAxis);
-      PhysicalRect expanded_padding_rect = padding_rect_;
-      expanded_padding_rect.Inflate(overflow_clip_margin);
-      layout_overflow_.Intersect(expanded_padding_rect);
-    } else {
-      if (overflow_clip_axes & kOverflowClipX) {
-        layout_overflow_.offset.left = padding_rect_.offset.left;
-        layout_overflow_.size.width = padding_rect_.size.width;
-      }
-      if (overflow_clip_axes & kOverflowClipY) {
-        layout_overflow_.offset.top = padding_rect_.offset.top;
-        layout_overflow_.size.height = padding_rect_.size.height;
-      }
-    }
-  }
-
   if (!inflow_bounds || !is_scroll_container_)
     return layout_overflow_;
 
@@ -281,14 +258,38 @@
   if (!child_fragment.IsCSSBox())
     return child_fragment.LayoutOverflow();
 
-  // Children with overflow clip (e.g. a scrollable child) don't propagate any
-  // layout overflow.
   PhysicalRect overflow = {{}, child_fragment.Size()};
+  const auto& child_style = child_fragment.Style();
   if (!child_fragment.ShouldApplyLayoutContainment() &&
       (!child_fragment.ShouldClipOverflowAlongBothAxis() ||
-       child_fragment.Style().OverflowClipMargin() != LayoutUnit()) &&
-      !child_fragment.IsInlineBox())
-    overflow.UniteEvenIfEmpty(child_fragment.LayoutOverflow());
+       child_style.OverflowClipMargin() != LayoutUnit()) &&
+      !child_fragment.IsInlineBox()) {
+    PhysicalRect child_overflow = child_fragment.LayoutOverflow();
+    if (child_fragment.HasNonVisibleOverflow()) {
+      const OverflowClipAxes overflow_clip_axes =
+          child_fragment.GetOverflowClipAxes();
+      const LayoutUnit overflow_clip_margin = child_style.OverflowClipMargin();
+      if (overflow_clip_margin != LayoutUnit()) {
+        // overflow_clip_margin should only be set if 'overflow' is 'clip' along
+        // both axis.
+        DCHECK_EQ(overflow_clip_axes, kOverflowClipBothAxis);
+        PhysicalRect child_padding_rect({}, child_fragment.Size());
+        child_padding_rect.Contract(child_fragment.Borders());
+        child_padding_rect.Inflate(overflow_clip_margin);
+        child_overflow.Intersect(child_padding_rect);
+      } else {
+        if (overflow_clip_axes & kOverflowClipX) {
+          child_overflow.offset.left = LayoutUnit();
+          child_overflow.size.width = child_fragment.Size().width;
+        }
+        if (overflow_clip_axes & kOverflowClipY) {
+          child_overflow.offset.top = LayoutUnit();
+          child_overflow.size.height = child_fragment.Size().height;
+        }
+      }
+    }
+    overflow.UniteEvenIfEmpty(child_overflow);
+  }
 
   // Apply any transforms to the overflow.
   if (base::Optional<TransformationMatrix> transform =
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
index 7aacdab85..7d97804 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -198,6 +198,12 @@
   PhysicalRect ScrollableOverflow(TextHeightType height_type) const;
   PhysicalRect ScrollableOverflowFromChildren(TextHeightType height_type) const;
 
+  OverflowClipAxes GetOverflowClipAxes() const {
+    if (const auto* layout_object = GetLayoutObject())
+      return layout_object->GetOverflowClipAxes();
+    return kNoOverflowClip;
+  }
+
   // TODO(layout-dev): These three methods delegate to legacy layout for now,
   // update them to use LayoutNG based overflow information from the fragment
   // and change them to use NG geometry types once LayoutNG supports overflow.
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
index 9fb8bcf..8c8290d 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
@@ -225,17 +225,23 @@
   if (const NGPhysicalBoxFragment* fragment = GetPhysicalFragment(0)) {
     DCHECK_EQ(PhysicalFragmentCount(), 1u);
     // Table's collapsed borders contribute to visual overflow.
-    // Table border side can be composed of multiple border segments.
-    // Inline visual overflow uses size of the largest border segment.
-    // Block visual overflow uses size of first border segment.
+    // In the inline direction, table's border box does not include
+    // visual border width (largest border), but does include
+    // layout border width (border of first cell).
+    // Expands border box to include visual border width.
     if (const NGTableBorders* collapsed_borders =
             fragment->TableCollapsedBorders()) {
       PhysicalRect borders_overflow = PhysicalBorderBoxRect();
       NGBoxStrut table_borders = collapsed_borders->TableBorder();
       auto visual_inline_strut =
           collapsed_borders->GetCollapsedBorderVisualInlineStrut();
-      table_borders.inline_start = visual_inline_strut.first;
-      table_borders.inline_end = visual_inline_strut.second;
+      // Expand by difference between visual and layout border width.
+      table_borders.inline_start =
+          visual_inline_strut.first - table_borders.inline_start;
+      table_borders.inline_end =
+          visual_inline_strut.second - table_borders.inline_end;
+      table_borders.block_start = LayoutUnit();
+      table_borders.block_end = LayoutUnit();
       borders_overflow.Expand(
           table_borders.ConvertToPhysical(StyleRef().GetWritingDirection()));
       AddSelfVisualOverflow(borders_overflow);
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
index b3b9483..af7237f 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
@@ -108,6 +108,10 @@
 
   LayoutRectOutsets BorderBoxOutsets() const override;
 
+  // TODO(1151101)
+  // ClientLeft/Top are incorrect for tables, but cannot be fixed
+  // by subclassing ClientLeft/Top.
+
   PhysicalRect OverflowClipRect(const PhysicalOffset&,
                                 OverlayScrollbarClipBehavior) const override;
 
@@ -177,8 +181,8 @@
     return absolute_column_index;
   }
 
-  // Legacy caches sections. Might not be needed by NG.
-  void RecalcSectionsIfNeeded() const final { NOTIMPLEMENTED(); }
+  // NG does not need this method. Sections are not cached.
+  void RecalcSectionsIfNeeded() const final {}
 
   // Legacy caches sections. Might not be needed by NG.
   void ForceSectionsRecalc() final { NOTIMPLEMENTED(); }
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc
index c57bcf6..6a4c861 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc
@@ -37,8 +37,13 @@
 
 LayoutRectOutsets LayoutNGTableCell::BorderBoxOutsets() const {
   NOT_DESTROYED();
-  DCHECK_GE(PhysicalFragmentCount(), 0u);
-  return GetPhysicalFragment(0)->Borders().ToLayoutRectOutsets();
+  // TODO(1061423) This function should not be called before layout.
+  // ScrollAnchor::Examine does. Example trigger:
+  // ScrollTimelineTest.TimelineInvalidationWhenScrollerDisplayPropertyChanges
+  // DCHECK_GE(PhysicalFragmentCount(), 0u);
+  if (PhysicalFragmentCount() > 0)
+    return GetPhysicalFragment(0)->Borders().ToLayoutRectOutsets();
+  return LayoutNGBlockFlowMixin<LayoutBlockFlow>::BorderBoxOutsets();
 }
 
 LayoutUnit LayoutNGTableCell::BorderTop() const {
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
index e1f6ceb..30e47a5 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
@@ -855,8 +855,8 @@
       } else {
         minmax.min_size += *column.min_inline_size;
       }
-      if (column.percent) {
-        if (*column.max_inline_size > LayoutUnit() && *column.percent > 0) {
+      if (column.percent && *column.percent > 0) {
+        if (*column.max_inline_size > LayoutUnit()) {
           LayoutUnit estimate = LayoutUnit(
               100 / *column.percent *
               (*column.max_inline_size - column.percent_border_padding));
diff --git a/third_party/blink/renderer/core/layout/overflow_model.h b/third_party/blink/renderer/core/layout/overflow_model.h
index 5e700d3..da33cc8 100644
--- a/third_party/blink/renderer/core/layout/overflow_model.h
+++ b/third_party/blink/renderer/core/layout/overflow_model.h
@@ -167,7 +167,6 @@
   BoxLayoutOverflowModel& operator=(const BoxLayoutOverflowModel&) = delete;
 
   const LayoutRect& LayoutOverflowRect() const { return layout_overflow_; }
-  void SetLayoutOverflow(const LayoutRect& rect) { layout_overflow_ = rect; }
   void AddLayoutOverflow(const LayoutRect& rect) {
     UniteLayoutOverflowRect(layout_overflow_, rect);
   }
diff --git a/third_party/blink/renderer/core/script/classic_script.cc b/third_party/blink/renderer/core/script/classic_script.cc
index 742bf3c..882c9ff1 100644
--- a/third_party/blink/renderer/core/script/classic_script.cc
+++ b/third_party/blink/renderer/core/script/classic_script.cc
@@ -58,8 +58,7 @@
   ScriptState::Scope scope(global_scope.ScriptController()->GetScriptState());
   ScriptEvaluationResult result =
       global_scope.ScriptController()->EvaluateAndReturnValue(
-          GetScriptSourceCode(), sanitize_script_errors_,
-          global_scope.GetV8CacheOptions());
+          GetScriptSourceCode(), sanitize_script_errors_);
   return result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess;
 }
 
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index c23e6ec2..319ea11 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -111,6 +111,8 @@
 class Float;
 class FloodColor;
 class InternalForcedBackgroundColor;
+class InternalForcedBorderColor;
+class InternalForcedOutlineColor;
 class InternalVisitedBackgroundColor;
 class InternalVisitedBorderBottomColor;
 class InternalVisitedBorderLeftColor;
@@ -218,6 +220,8 @@
   friend class css_longhand::Float;
   friend class css_longhand::FloodColor;
   friend class css_longhand::InternalForcedBackgroundColor;
+  friend class css_longhand::InternalForcedBorderColor;
+  friend class css_longhand::InternalForcedOutlineColor;
   friend class css_longhand::InternalVisitedBackgroundColor;
   friend class css_longhand::InternalVisitedBorderBottomColor;
   friend class css_longhand::InternalVisitedBorderLeftColor;
@@ -2845,6 +2849,12 @@
   const StyleColor& InternalForcedBackgroundColor() const {
     return InternalForcedBackgroundColorInternal();
   }
+  const StyleColor& InternalForcedBorderColor() const {
+    return InternalForcedBorderColorInternal();
+  }
+  const StyleColor& InternalForcedOutlineColor() const {
+    return InternalForcedOutlineColorInternal();
+  }
 
   StyleColor DecorationColorIncludingFallback(bool visited_link) const;
 
diff --git a/third_party/blink/renderer/core/testing/internal_settings.cc b/third_party/blink/renderer/core/testing/internal_settings.cc
index bd7a0829..c82cc8b 100644
--- a/third_party/blink/renderer/core/testing/internal_settings.cc
+++ b/third_party/blink/renderer/core/testing/internal_settings.cc
@@ -57,6 +57,7 @@
 
 namespace blink {
 
+using mojom::blink::HoverType;
 using mojom::blink::PointerType;
 
 InternalSettings::Backup::Backup(Settings* settings)
@@ -450,9 +451,9 @@
   for (const String& split_token : tokens) {
     String token = split_token.StripWhiteSpace();
     if (token == "none") {
-      hover_types |= ui::HOVER_TYPE_NONE;
+      hover_types |= static_cast<int>(HoverType::kHoverNone);
     } else if (token == "hover") {
-      hover_types |= ui::HOVER_TYPE_HOVER;
+      hover_types |= static_cast<int>(HoverType::kHoverHoverType);
     } else {
       exception_state.ThrowDOMException(
           DOMExceptionCode::kSyntaxError,
@@ -469,11 +470,11 @@
   InternalSettingsGuardForSettings();
   String token = type.StripWhiteSpace();
 
-  ui::HoverType hover_type = ui::HOVER_TYPE_NONE;
+  HoverType hover_type = HoverType::kHoverNone;
   if (token == "none") {
-    hover_type = ui::HOVER_TYPE_NONE;
+    hover_type = HoverType::kHoverNone;
   } else if (token == "hover") {
-    hover_type = ui::HOVER_TYPE_HOVER;
+    hover_type = HoverType::kHoverHoverType;
   } else {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kSyntaxError,
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index 2a34728..92377e35 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -339,7 +339,7 @@
                          handler,
                          ScriptSourceCode::UsePostRedirectURL() ? response_url
                                                                 : complete_url),
-        sanitize_script_errors, GetV8CacheOptions(),
+        sanitize_script_errors,
         V8ScriptRunner::RethrowErrorsOption::Rethrow(error_message));
 
     // Step 5.2: "If an exception was thrown or if the script was prematurely
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
index 571f1fe..9722f73e 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -140,7 +140,7 @@
   WorkerOrWorkletScriptController* ScriptController() {
     return script_controller_.Get();
   }
-  mojom::blink::V8CacheOptions GetV8CacheOptions() const {
+  mojom::blink::V8CacheOptions GetV8CacheOptions() const override {
     return v8_cache_options_;
   }
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index c7c8b2b..c93bda1 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -1905,7 +1905,7 @@
   // in sRGB color space and use uint8 pixel storage format. We use RGBA pixel
   // order for both ImageData and CanvasResourceProvider, therefore no
   // additional swizzling is needed.
-  SkImageInfo data_info = data->GetSkImageInfo();
+  SkPixmap data_pixmap = data->GetSkPixmap();
   CanvasColorParams data_color_params = data->GetCanvasColorParams();
   CanvasColorParams context_color_params = CanvasColorParams(
       GetCanvas2DColorParams().ColorSpace(), PixelFormat(), kNonOpaque);
@@ -1913,30 +1913,28 @@
   if (data_color_params.ColorSpace() != context_color_params.ColorSpace() ||
       data_color_params.PixelFormat() != context_color_params.PixelFormat() ||
       PixelFormat() == CanvasPixelFormat::kF16) {
-    size_t data_length;
-    if (!base::CheckMul(data->Size().Area(),
-                        context_color_params.BytesPerPixel())
-             .AssignIfValid(&data_length)) {
-      return;
-    }
-    std::unique_ptr<uint8_t[]> converted_pixels(new uint8_t[data_length]);
+    SkImageInfo converted_info = data_pixmap.info();
+    converted_info =
+        converted_info.makeColorType(GetCanvas2DColorParams().GetSkColorType());
+    converted_info = converted_info.makeColorSpace(
+        GetCanvas2DColorParams().GetSkColorSpace());
+    if (converted_info.colorType() == kN32_SkColorType)
+      converted_info = converted_info.makeColorType(kRGBA_8888_SkColorType);
 
-    if (data->ImageDataInCanvasColorSettings(
-            GetCanvas2DColorParams().ColorSpace(), PixelFormat(),
-            converted_pixels.get(), kRGBAColorType)) {
-      SkImageInfo converted_info = data_info;
-      converted_info = converted_info.makeColorType(
-          GetCanvas2DColorParams().GetSkColorType());
-      converted_info = converted_info.makeColorSpace(
-          GetCanvas2DColorParams().GetSkColorSpace());
-      PutByteArray(SkPixmap(converted_info, converted_pixels.get(),
-                            converted_info.minRowBytes()),
-                   source_rect, IntPoint(dest_offset));
+    const size_t converted_data_bytes = converted_info.computeMinByteSize();
+    const size_t converted_row_bytes = converted_info.minRowBytes();
+    if (SkImageInfo::ByteSizeOverflowed(converted_data_bytes))
+      return;
+    std::unique_ptr<uint8_t[]> converted_pixels(
+        new uint8_t[converted_data_bytes]);
+    if (data_pixmap.readPixels(converted_info, converted_pixels.get(),
+                               converted_row_bytes)) {
+      PutByteArray(
+          SkPixmap(converted_info, converted_pixels.get(), converted_row_bytes),
+          source_rect, IntPoint(dest_offset));
     }
   } else {
-    PutByteArray(SkPixmap(data_info, data->BufferBase()->Data(),
-                          data_info.minRowBytes()),
-                 source_rect, IntPoint(dest_offset));
+    PutByteArray(data_pixmap, source_rect, IntPoint(dest_offset));
   }
 
   if (!IsPaint2D()) {
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
index 5ac82e37..af2bb0c 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
@@ -585,14 +585,31 @@
     const blink::CableAuthenticationData& data) {
   auto entity = CableAuthentication::New();
   entity->version = data.version();
-  entity->client_eid = ConvertFixedSizeArray(data.clientEid(), 16);
-  entity->authenticator_eid =
-      ConvertFixedSizeArray(data.authenticatorEid(), 16);
-  entity->session_pre_key = ConvertFixedSizeArray(data.sessionPreKey(), 32);
-  if (entity->client_eid.IsEmpty() || entity->authenticator_eid.IsEmpty() ||
-      entity->session_pre_key.IsEmpty()) {
-    return nullptr;
+  switch (entity->version) {
+    case 1:
+      entity->client_eid = ConvertFixedSizeArray(data.clientEid(), 16);
+      entity->authenticator_eid =
+          ConvertFixedSizeArray(data.authenticatorEid(), 16);
+      entity->session_pre_key = ConvertFixedSizeArray(data.sessionPreKey(), 32);
+      if (entity->client_eid->IsEmpty() ||
+          entity->authenticator_eid->IsEmpty() ||
+          entity->session_pre_key->IsEmpty()) {
+        return nullptr;
+      }
+      break;
+
+    case 2:
+      entity->server_link_data =
+          ConvertTo<Vector<uint8_t>>(data.sessionPreKey());
+      if (entity->server_link_data->IsEmpty()) {
+        return nullptr;
+      }
+      break;
+
+    default:
+      return nullptr;
   }
+
   return entity;
 }
 
@@ -651,7 +668,7 @@
     if (extensions->hasCableAuthentication()) {
       Vector<CableAuthenticationPtr> mojo_data;
       for (auto& data : extensions->cableAuthentication()) {
-        if (data->version() != 1) {
+        if (data->version() < 1 || data->version() > 2) {
           continue;
         }
         CableAuthenticationPtr mojo_cable = CableAuthentication::From(*data);
diff --git a/third_party/blink/renderer/modules/font_access/font_manager.cc b/third_party/blink/renderer/modules/font_access/font_manager.cc
index 1f185d5..e5e93f0 100644
--- a/third_party/blink/renderer/modules/font_access/font_manager.cc
+++ b/third_party/blink/renderer/modules/font_access/font_manager.cc
@@ -6,13 +6,18 @@
 
 #include <algorithm>
 
+#include "base/feature_list.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/font_access/font_iterator.h"
+#include "third_party/blink/renderer/modules/font_access/font_metadata.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 
 namespace blink {
@@ -25,6 +30,18 @@
 
 }  // namespace
 
+FontManager::FontManager(ExecutionContext* context)
+    : ExecutionContextLifecycleObserver(context) {
+  // Only connect if the feature is enabled. Otherwise, there will
+  // be no service to connect to on the end.
+  if (base::FeatureList::IsEnabled(blink::features::kFontAccess)) {
+    context->GetBrowserInterfaceBroker().GetInterface(
+        remote_manager_.BindNewPipeAndPassReceiver());
+    remote_manager_.set_disconnect_handler(
+        WTF::Bind(&FontManager::OnDisconnect, WrapWeakPersistent(this)));
+  }
+}
+
 ScriptValue FontManager::query(ScriptState* script_state,
                                ExceptionState& exception_state) {
   if (exception_state.HadException())
@@ -49,14 +66,69 @@
 
 ScriptPromise FontManager::showFontChooser(ScriptState* script_state,
                                            const QueryOptions* options) {
-  return ScriptPromise::RejectWithDOMException(
-      script_state,
-      MakeGarbageCollected<DOMException>(DOMExceptionCode::kNotSupportedError,
-                                         "Not implemented yet"));
+  // TODO(crbug.com/1149621): Queue up font chooser requests.
+  if (!pending_resolver_) {
+    remote_manager_->ChooseLocalFonts(
+        WTF::Bind(&FontManager::DidShowFontChooser, WrapWeakPersistent(this)));
+    pending_resolver_ =
+        MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  }
+
+  return pending_resolver_->Promise();
 }
 
 void FontManager::Trace(blink::Visitor* visitor) const {
   ScriptWrappable::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
+  visitor->Trace(pending_resolver_);
+}
+
+void FontManager::DidShowFontChooser(
+    mojom::blink::FontEnumerationStatus status,
+    Vector<mojom::blink::FontMetadataPtr> fonts) {
+  switch (status) {
+    case mojom::blink::FontEnumerationStatus::kOk:
+      break;
+    case mojom::blink::FontEnumerationStatus::kUnimplemented:
+      pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNotSupportedError,
+          "Not yet supported on this platform."));
+      pending_resolver_.Clear();
+      return;
+    case mojom::blink::FontEnumerationStatus::kCanceled:
+      pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kAbortError, "The user canceled the operation."));
+      pending_resolver_.Clear();
+      return;
+    case mojom::blink::FontEnumerationStatus::kNeedsUserActivation:
+      pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kSecurityError, "User activation is required."));
+      pending_resolver_.Clear();
+      return;
+    case mojom::blink::FontEnumerationStatus::kUnexpectedError:
+    default:
+      pending_resolver_->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kUnknownError, "An unexpected error occured."));
+      pending_resolver_.Clear();
+      return;
+  }
+
+  auto entries = HeapVector<Member<FontMetadata>>();
+  for (const auto& font : fonts) {
+    auto entry = FontEnumerationEntry{font->postscript_name, font->full_name,
+                                      font->family};
+    entries.push_back(FontMetadata::Create(std::move(entry)));
+  }
+  pending_resolver_->Resolve(std::move(entries));
+  pending_resolver_.Clear();
+}
+
+void FontManager::ContextDestroyed() {
+  remote_manager_.reset();
+}
+
+void FontManager::OnDisconnect() {
+  remote_manager_.reset();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/font_access/font_manager.h b/third_party/blink/renderer/modules/font_access/font_manager.h
index bf0b74f..34e9344 100644
--- a/third_party/blink/renderer/modules/font_access/font_manager.h
+++ b/third_party/blink/renderer/modules/font_access/font_manager.h
@@ -5,6 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FONT_ACCESS_FONT_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FONT_ACCESS_FONT_MANAGER_H_
 
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/mojom/font_access/font_access.mojom-blink.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -15,13 +18,15 @@
 class ScriptState;
 class ScriptValue;
 class ScriptPromise;
+class ScriptPromiseResolver;
 class QueryOptions;
 
-class FontManager final : public ScriptWrappable {
+class FontManager final : public ScriptWrappable,
+                          public ExecutionContextLifecycleObserver {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  FontManager() = default;
+  explicit FontManager(ExecutionContext* context);
 
   // Disallow copy and assign.
   FontManager(const FontManager&) = delete;
@@ -32,6 +37,15 @@
   ScriptPromise showFontChooser(ScriptState*, const QueryOptions* options);
 
   void Trace(blink::Visitor*) const override;
+
+ private:
+  void DidShowFontChooser(mojom::blink::FontEnumerationStatus status,
+                          Vector<mojom::blink::FontMetadataPtr> fonts);
+  void ContextDestroyed() override;
+  void OnDisconnect();
+
+  mojo::Remote<mojom::blink::FontAccessManager> remote_manager_;
+  Member<ScriptPromiseResolver> pending_resolver_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/font_access/font_manager.idl b/third_party/blink/renderer/modules/font_access/font_manager.idl
index 9d0a3293..39fa7d6 100644
--- a/third_party/blink/renderer/modules/font_access/font_manager.idl
+++ b/third_party/blink/renderer/modules/font_access/font_manager.idl
@@ -9,5 +9,5 @@
     RuntimeEnabled=FontAccess
 ] interface FontManager {
     [CallWith=ScriptState, RaisesException, Measure] object query();
-    [CallWith=ScriptState, RuntimeEnabled=FontAccessChooser] Promise<FontIterator> showFontChooser(optional QueryOptions options = {});
+    [CallWith=ScriptState, RuntimeEnabled=FontAccessChooser] Promise<sequence<FontMetadata>> showFontChooser(optional QueryOptions options = {});
 };
diff --git a/third_party/blink/renderer/modules/font_access/navigator_fonts.cc b/third_party/blink/renderer/modules/font_access/navigator_fonts.cc
index 777410f7..0d5e82c 100644
--- a/third_party/blink/renderer/modules/font_access/navigator_fonts.cc
+++ b/third_party/blink/renderer/modules/font_access/navigator_fonts.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/font_access/navigator_fonts.h"
 
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/frame/navigator.h"
 #include "third_party/blink/renderer/core/workers/worker_navigator.h"
@@ -36,9 +37,9 @@
 
   explicit NavigatorFontsImpl(T& navigator) : Supplement<T>(navigator) {}
 
-  FontManager* GetFontManager() const {
+  FontManager* GetFontManager(ExecutionContext* context) const {
     if (!font_manager_) {
-      font_manager_ = MakeGarbageCollected<FontManager>();
+      font_manager_ = MakeGarbageCollected<FontManager>(context);
     }
     return font_manager_.Get();
   }
@@ -66,7 +67,8 @@
                                    Navigator& navigator,
                                    ExceptionState& exception_state) {
   DCHECK(ExecutionContext::From(script_state)->IsContextThread());
-  return NavigatorFontsImpl<Navigator>::From(navigator).GetFontManager();
+  ExecutionContext* context = ExecutionContext::From(script_state);
+  return NavigatorFontsImpl<Navigator>::From(navigator).GetFontManager(context);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/shapedetection/shape_detector.cc b/third_party/blink/renderer/modules/shapedetection/shape_detector.cc
index 8f99decf..0a6cf92 100644
--- a/third_party/blink/renderer/modules/shapedetection/shape_detector.cc
+++ b/third_party/blink/renderer/modules/shapedetection/shape_detector.cc
@@ -119,18 +119,21 @@
     return promise;
   }
 
+  SkPixmap image_data_pixmap = image_data->GetSkPixmap();
   SkBitmap sk_bitmap;
-  SkImageInfo sk_image_info = image_data->GetSkImageInfo();
-  if (!sk_bitmap.tryAllocPixels(sk_image_info, sk_image_info.minRowBytes())) {
+  if (!sk_bitmap.tryAllocPixels(image_data_pixmap.info(),
+                                image_data_pixmap.rowBytes())) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidStateError,
         "Failed to allocate pixels for current frame."));
     return promise;
   }
-
-  size_t byte_size = sk_bitmap.computeByteSize();
-  CHECK_EQ(byte_size, image_data->BufferBase()->ByteLength());
-  memcpy(sk_bitmap.getPixels(), image_data->BufferBase()->Data(), byte_size);
+  if (!sk_bitmap.writePixels(image_data_pixmap, 0, 0)) {
+    resolver->Reject(MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError,
+        "Failed to copy pixels for current frame."));
+    return promise;
+  }
 
   return DoDetect(resolver, std::move(sk_bitmap));
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
index d446b29..6fef242b 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
@@ -200,11 +200,11 @@
   return requested_encodes_;
 }
 
-std::unique_ptr<VideoEncoder::ParsedConfig> VideoEncoder::ParseConfig(
+VideoEncoder::ParsedConfig* VideoEncoder::ParseConfig(
     const VideoEncoderConfig* config,
     ExceptionState& exception_state) {
   constexpr int kMaxSupportedFrameSize = 8000;
-  auto parsed = std::make_unique<ParsedConfig>();
+  auto* parsed = MakeGarbageCollected<ParsedConfig>();
 
   parsed->options.height = config->height();
   if (parsed->options.height == 0 ||
@@ -362,6 +362,17 @@
   }
 }
 
+bool VideoEncoder::CanReconfigure(ParsedConfig& original_config,
+                                  ParsedConfig& new_config) {
+  // Reconfigure is intended for things that don't require changing underlying
+  // codec implementatio and can be changed on the fly.
+  return original_config.codec == new_config.codec &&
+         original_config.profile == new_config.profile &&
+         original_config.level == new_config.level &&
+         original_config.color_space == new_config.color_space &&
+         original_config.acc_pref == new_config.acc_pref;
+}
+
 void VideoEncoder::configure(const VideoEncoderConfig* config,
                              ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -369,27 +380,28 @@
   if (ThrowIfCodecStateClosed(state_, "configure", exception_state))
     return;
 
-  auto parsed_config = ParseConfig(config, exception_state);
-
+  auto* parsed_config = ParseConfig(config, exception_state);
   if (!parsed_config) {
     DCHECK(exception_state.HadException());
     return;
   }
 
-  if (!VerifyCodecSupport(parsed_config.get(), exception_state)) {
+  if (!VerifyCodecSupport(parsed_config, exception_state)) {
     DCHECK(exception_state.HadException());
     return;
   }
 
-  // TODO(https://crbug.com/1119892): flush |media_encoder_| if it already
-  // exists, otherwise might could lose frames in flight.
-
-  state_ = V8CodecState(V8CodecState::Enum::kConfigured);
-
   Request* request = MakeGarbageCollected<Request>();
   request->reset_count = reset_count_;
-  request->type = Request::Type::kConfigure;
-  active_config_ = std::move(parsed_config);
+  if (media_encoder_ && active_config_ &&
+      state_.AsEnum() == V8CodecState::Enum::kConfigured &&
+      CanReconfigure(*active_config_, *parsed_config)) {
+    request->type = Request::Type::kReconfigure;
+  } else {
+    state_ = V8CodecState(V8CodecState::Enum::kConfigured);
+    request->type = Request::Type::kConfigure;
+  }
+  active_config_ = parsed_config;
   EnqueueRequest(request);
 }
 
@@ -478,6 +490,7 @@
 
   state_ = V8CodecState(V8CodecState::Enum::kUnconfigured);
   ResetInternal();
+  media_encoder_.reset();
 }
 
 void VideoEncoder::ResetInternal() {
@@ -559,6 +572,9 @@
       case Request::Type::kConfigure:
         ProcessConfigure(request);
         break;
+      case Request::Type::kReconfigure:
+        ProcessReconfigure(request);
+        break;
       case Request::Type::kEncode:
         ProcessEncode(request);
         break;
@@ -603,9 +619,10 @@
   bool keyframe = request->encodeOpts->hasKeyFrameNonNull() &&
                   request->encodeOpts->keyFrameNonNull();
   --requested_encodes_;
-  media_encoder_->Encode(frame, keyframe,
-                         WTF::Bind(done_callback, WrapWeakPersistent(this),
-                                   WrapPersistentIfNeeded(request)));
+  media_encoder_->Encode(
+      frame, keyframe,
+      WTF::Bind(done_callback, WrapCrossThreadWeakPersistent(this),
+                WrapCrossThreadPersistent(request)));
 
   // We passed a copy of frame() above, so this should be safe to destroy
   // here.
@@ -628,8 +645,11 @@
     return;
   }
 
-  auto output_cb = WTF::BindRepeating(&VideoEncoder::CallOutputCallback,
-                                      WrapWeakPersistent(this), reset_count_);
+  auto output_cb = WTF::BindRepeating(
+      &VideoEncoder::CallOutputCallback, WrapCrossThreadWeakPersistent(this),
+      // We can't use |active_config_| from |this| because it can change by
+      // the time the callback is executed.
+      WrapCrossThreadPersistent(active_config_.Get()), reset_count_);
 
   auto done_callback = [](VideoEncoder* self, Request* req,
                           media::Status status) {
@@ -647,10 +667,67 @@
   };
 
   stall_request_processing_ = true;
-  media_encoder_->Initialize(active_config_->profile, active_config_->options,
-                             std::move(output_cb),
-                             WTF::Bind(done_callback, WrapWeakPersistent(this),
-                                       WrapPersistent(request)));
+  media_encoder_->Initialize(
+      active_config_->profile, active_config_->options, std::move(output_cb),
+      WTF::Bind(done_callback, WrapCrossThreadWeakPersistent(this),
+                WrapCrossThreadPersistent(request)));
+}
+
+void VideoEncoder::ProcessReconfigure(Request* request) {
+  DCHECK_EQ(state_.AsEnum(), V8CodecState::Enum::kConfigured);
+  DCHECK_EQ(request->type, Request::Type::kReconfigure);
+  DCHECK(active_config_);
+  DCHECK(media_encoder_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  auto reconf_done_callback = [](VideoEncoder* self, Request* req,
+                                 media::Status status) {
+    if (!self || self->reset_count_ != req->reset_count)
+      return;
+    DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_);
+    DCHECK(self->active_config_);
+
+    if (status.is_ok()) {
+      self->stall_request_processing_ = false;
+      self->ProcessRequests();
+    } else {
+      // Reconfiguration failed. Either encoder doesn't support changing options
+      // or it didn't like this particular change. Let's try to configure it
+      // from scratch.
+      req->type = Request::Type::kConfigure;
+      self->ProcessConfigure(req);
+    }
+  };
+
+  auto flush_done_callback = [](VideoEncoder* self, Request* req,
+                                decltype(reconf_done_callback) reconf_callback,
+                                media::Status status) {
+    if (!self || self->reset_count_ != req->reset_count)
+      return;
+    DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_);
+    if (!status.is_ok()) {
+      self->HandleError("Encoder reconfiguration error.", status);
+      self->stall_request_processing_ = false;
+      return;
+    }
+
+    auto output_cb = WTF::BindRepeating(
+        &VideoEncoder::CallOutputCallback, WrapCrossThreadWeakPersistent(self),
+        // We can't use |active_config_| from |this| because it can change by
+        // the time the callback is executed.
+        WrapCrossThreadPersistent(self->active_config_.Get()),
+        self->reset_count_);
+
+    self->media_encoder_->ChangeOptions(
+        self->active_config_->options, std::move(output_cb),
+        WTF::Bind(reconf_callback, WrapCrossThreadWeakPersistent(self),
+                  WrapCrossThreadPersistent(req)));
+  };
+
+  stall_request_processing_ = true;
+  media_encoder_->Flush(WTF::Bind(
+      flush_done_callback, WrapCrossThreadWeakPersistent(this),
+      WrapCrossThreadPersistent(request), std::move(reconf_done_callback)));
 }
 
 void VideoEncoder::ProcessFlush(Request* request) {
@@ -684,14 +761,17 @@
 
   stall_request_processing_ = true;
 
-  media_encoder_->Flush(WTF::Bind(done_callback, WrapWeakPersistent(this),
-                                  WrapPersistentIfNeeded(request)));
+  media_encoder_->Flush(WTF::Bind(done_callback,
+                                  WrapCrossThreadWeakPersistent(this),
+                                  WrapCrossThreadPersistent(request)));
 }
 
 void VideoEncoder::CallOutputCallback(
+    ParsedConfig* active_config,
     uint32_t reset_count,
     media::VideoEncoderOutput output,
     base::Optional<media::VideoEncoder::CodecDescription> codec_desc) {
+  DCHECK(active_config);
   if (!script_state_->ContextIsValid() || !output_callback_ ||
       state_.AsEnum() != V8CodecState::Enum::kConfigured ||
       reset_count != reset_count_)
@@ -707,12 +787,11 @@
   auto* dom_array = MakeGarbageCollected<DOMArrayBuffer>(std::move(data));
   auto* chunk = MakeGarbageCollected<EncodedVideoChunk>(metadata, dom_array);
 
-  DCHECK(active_config_);
   VideoDecoderConfig* decoder_config =
       MakeGarbageCollected<VideoDecoderConfig>();
-  decoder_config->setCodec(active_config_->codec_string);
-  decoder_config->setCodedHeight(active_config_->options.height);
-  decoder_config->setCodedWidth(active_config_->options.width);
+  decoder_config->setCodec(active_config->codec_string);
+  decoder_config->setCodedHeight(active_config->options.height);
+  decoder_config->setCodedWidth(active_config->options.width);
   if (codec_desc.has_value()) {
     auto* desc_array_buf = DOMArrayBuffer::Create(codec_desc.value().data(),
                                                   codec_desc.value().size());
@@ -732,6 +811,7 @@
 }
 
 void VideoEncoder::Trace(Visitor* visitor) const {
+  visitor->Trace(active_config_);
   visitor->Trace(script_state_);
   visitor->Trace(output_callback_);
   visitor->Trace(error_callback_);
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.h b/third_party/blink/renderer/modules/webcodecs/video_encoder.h
index 30b9485..0d521a1 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.h
@@ -80,7 +80,7 @@
   enum class AccelerationPreference { kAllow, kDeny, kRequire };
 
   // TODO(ezemtsov): Replace this with a {Audio|Video}EncoderConfig.
-  struct ParsedConfig final {
+  struct ParsedConfig final : public GarbageCollected<ParsedConfig> {
     media::VideoCodec codec;
     media::VideoCodecProfile profile;
     uint8_t level;
@@ -90,11 +90,16 @@
 
     media::VideoEncoder::Options options;
     String codec_string;
+
+    void Trace(Visitor*) const {}
   };
 
   struct Request final : public GarbageCollected<Request> {
     enum class Type {
+      // Configure an encoder from scratch, possibly replacing the existing one.
       kConfigure,
+      // Adjust options in the already configured encoder.
+      kReconfigure,
       kEncode,
       kFlush,
     };
@@ -110,6 +115,7 @@
   };
 
   void CallOutputCallback(
+      ParsedConfig* active_config,
       uint32_t reset_count,
       media::VideoEncoderOutput output,
       base::Optional<media::VideoEncoder::CodecDescription> codec_desc);
@@ -119,6 +125,7 @@
   void ProcessRequests();
   void ProcessEncode(Request* request);
   void ProcessConfigure(Request* request);
+  void ProcessReconfigure(Request* request);
   void ProcessFlush(Request* request);
 
   void UpdateEncoderLog(std::string encoder_name, bool is_hw_accelerated);
@@ -128,13 +135,12 @@
   void ResolvePromise(Request* req);
   void RejectPromise(Request* req, DOMException* ex = nullptr);
 
-  std::unique_ptr<ParsedConfig> ParseConfig(const VideoEncoderConfig*,
-                                            ExceptionState&);
+  ParsedConfig* ParseConfig(const VideoEncoderConfig*, ExceptionState&);
   bool VerifyCodecSupport(ParsedConfig*, ExceptionState&);
   std::unique_ptr<media::VideoEncoder> CreateMediaVideoEncoder(
       const ParsedConfig& config);
+  bool CanReconfigure(ParsedConfig& original_config, ParsedConfig& new_config);
 
-  std::unique_ptr<ParsedConfig> active_config_;
   std::unique_ptr<media::VideoEncoder> media_encoder_;
   bool is_hw_accelerated_ = false;
 
@@ -150,6 +156,7 @@
 
   V8CodecState state_;
 
+  Member<ParsedConfig> active_config_;
   Member<ScriptState> script_state_;
   Member<V8VideoEncoderOutputCallback> output_callback_;
   Member<V8WebCodecsErrorCallback> error_callback_;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 59eb6e5..2e083b8 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1370,7 +1370,7 @@
     {
       name: "OriginIsolationHeader",
       origin_trial_feature_name: "OriginIsolationHeader",
-      status: "stable",
+      status: "experimental",
     },
     {
       name: "OriginPolicy",
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.cc b/third_party/blink/renderer/platform/weborigin/security_origin.cc
index 7c44a0b..9c4dfc1 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -515,9 +515,8 @@
   builder.Append("://");
   builder.Append(host_);
 
-  // TODO(crbug.com/1136966): Fix this, and url::Origin's serialization logic,
-  // to make origins with port 0 serialize their ports.
-  if (port_ && port_ != DefaultPortForProtocol(protocol_)) {
+  if (DefaultPortForProtocol(protocol_) &&
+      port_ != DefaultPortForProtocol(protocol_)) {
     builder.Append(':');
     builder.AppendNumber(port_);
   }
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
index 489ead1..62551dc 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -571,7 +571,7 @@
     const char* origin;
   } cases[] = {
       {"http", "example.com", 80, "http://example.com"},
-      {"http", "example.com", 0, "http://example.com"},
+      {"http", "example.com", 0, "http://example.com:0"},
       {"http", "example.com", 81, "http://example.com:81"},
       {"https", "example.com", 443, "https://example.com"},
       {"https", "example.com", 444, "https://example.com:444"},
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index de68ae6..a5fe5aa 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -2215,13 +2215,10 @@
 # Remove from virtual tests when FontAccess is turned on by default.
 external/wpt/font-access/font_access-blob.tentative.https.window.html [ Skip ]
 external/wpt/font-access/font_access-enumeration.tentative.https.window.html [ Skip ]
-external/wpt/font-access/font_access-chooser.tentative.https.window.html [ Skip ]
+external/wpt/font-access/font_access-chooser.tentative.manual.https.html [ Skip ]
 virtual/font-access/external/wpt/font-access/font_access-blob.tentative.https.window.html [ Pass ]
-virtual/font-access/external/wpt/font-access/font_access-chooser.tentative.https.window.html [ Skip ]
+virtual/font-access/external/wpt/font-access/font_access-chooser.tentative.manual.https.html [ Skip ]
 virtual/font-access/external/wpt/font-access/font_access-enumeration.tentative.https.window.html [ Pass ]
-virtual/font-access-chooser/external/wpt/font-access/font_access-blob.tentative.https.window.html [ Skip ]
-virtual/font-access-chooser/external/wpt/font-access/font_access-chooser.tentative.https.window.html [ Pass ]
-virtual/font-access-chooser/external/wpt/font-access/font_access-enumeration.tentative.https.window.html [ Skip ]
 
 # text-orientation:upright
 crbug.com/1005518 external/wpt/css/css-writing-modes/table-progression-vlr-003.html [ Skip ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 00f0126..7b2fe8c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1258,7 +1258,6 @@
 crbug.com/626703 external/wpt/css/css-text/text-transform/math/text-transform-math-sans-serif-001.tentative.html [ Failure ]
 crbug.com/1043295 [ Fuchsia ] virtual/font-access/external/wpt/font-access/font_access-blob.tentative.https.window.html [ Skip ]
 crbug.com/1043295 [ Fuchsia ] virtual/font-access/external/wpt/font-access/font_access-enumeration.tentative.https.window.html [ Skip ]
-crbug.com/1043295 [ Fuchsia ] virtual/font-access-chooser/external/wpt/font-access/font_access-chooser.tentative.https.window.html [ Skip ]
 
 # ====== Style team owned tests from here ======
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 100b448..fea00fb 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -213,11 +213,6 @@
     "args": ["--enable-features=FontAccess"]
   },
   {
-    "prefix": "font-access-chooser",
-    "bases": ["external/wpt/font-access"],
-    "args": ["--enable-features=FontAccess,FontAccessChooser"]
-  },
-  {
     "prefix": "highdpi-threaded",
     "bases": ["external/wpt/css/css-paint-api/hidpi"],
     "args": ["--force-device-scale-factor=2",
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-scroll-size.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-scroll-size.html
new file mode 100644
index 0000000..1f2c2236a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-scroll-size.html
@@ -0,0 +1,80 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>overflow: scroll width/height should return overflow size</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#valdef-overflow-clip">
+<link rel="author" title="Scott Violet" href="mailto:sky@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  .parent {
+    width: 100px;
+    height: 101px;
+  }
+  .child {
+    width: 200px;
+    height: 201px;
+  }
+  .overflow_clip_and_border {
+    width: 100px;
+    height: 101px;
+    overflow: clip;
+    border-width: 2px;
+    border-style: solid;
+  }
+</style>
+<div id="parent-clip-both" class="parent" style="overflow: clip">
+  <div class="child"></div>
+</div>
+<div id="parent-clip-x" class="parent" style="overflow: clip-x">
+  <div class="child"></div>
+</div>
+<div id="parent-clip-y" class="parent" style="overflow: clip-y">
+  <div class="child"></div>
+</div>
+
+<div id="border-equal-clip" class="parent">
+  <div class="overflow_clip_and_border"
+       style="overflow-clip-margin: 2px">
+    <div class="child"></div>
+  </div>
+</div>
+<div id="border-smaller-clip" class="parent">
+  <div class="overflow_clip_and_border"
+       style="overflow-clip-margin: 3px">
+    <div class="child"></div>
+  </div>
+</div>
+<div id="border-greater-clip" class="parent">
+  <div class="overflow_clip_and_border"
+       style="overflow-clip-margin: 1px">
+    <div class="child"></div>
+  </div>
+</div>
+
+<script>
+test(() => {
+  var pClipBoth = document.getElementById("parent-clip-both");
+  assert_equals(pClipBoth.scrollWidth, 200);
+  assert_equals(pClipBoth.scrollHeight, 201);
+
+  var pClipX = document.getElementById("parent-clip-x");
+  assert_equals(pClipX.scrollWidth, 200);
+  assert_equals(pClipX.scrollHeight, 201);
+
+  var pClipY = document.getElementById("parent-clip-y");
+  assert_equals(pClipY.scrollWidth, 200);
+  assert_equals(pClipY.scrollHeight, 201);
+}, "scroll size should match that of size specified by overflow: clip");
+
+test(() => {
+    assert_equals(document.getElementById("border-equal-clip").scrollWidth,
+                  104);
+
+    assert_equals(document.getElementById("border-smaller-clip").scrollWidth,
+                  105);
+
+    assert_equals(document.getElementById("border-greater-clip").scrollWidth,
+                  104);
+}, "scroll size should take into account border size and overflow-clip-margin");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/font-access/font_access-chooser.tentative.https.window.js b/third_party/blink/web_tests/external/wpt/font-access/font_access-chooser.tentative.https.window.js
deleted file mode 100644
index 047521a7..0000000
--- a/third_party/blink/web_tests/external/wpt/font-access/font_access-chooser.tentative.https.window.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// META: script=/resources/testdriver.js
-// META: script=/resources/testdriver-vendor.js
-// META: script=resources/test-expectations.js
-// META: script=resources/window-tests-chooser.js
diff --git a/third_party/blink/web_tests/external/wpt/font-access/font_access-chooser.tentative.manual.https.html b/third_party/blink/web_tests/external/wpt/font-access/font_access-chooser.tentative.manual.https.html
new file mode 100644
index 0000000..8f623da
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/font-access/font_access-chooser.tentative.manual.https.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset=utf-8>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script>
+  promise_test(async t => {
+    await new Promise(resolve => {
+      window.addEventListener('DOMContentLoaded', resolve);
+    });
+    // Small delay to give chrome's test automation a chance to actually install
+    // itself.
+    await new Promise(resolve => step_timeout(resolve, 100));
+
+    await window.test_driver.bless('show a font chooser.<br />Please select at least one font.');
+    const fonts = await navigator.fonts.showFontChooser();
+    assert_true(Array.isArray(fonts));
+    assert_greater_than_equal(fonts.length, 1);
+
+    const postscriptName = fonts[0].postscriptName;
+    assert_equals(typeof postscriptName, "string");
+    assert_greater_than(postscriptName.length, 0);
+  }, 'showFontChooser works');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/font-access/resources/window-tests-chooser.js b/third_party/blink/web_tests/external/wpt/font-access/resources/window-tests-chooser.js
deleted file mode 100644
index bb9e0bf1..0000000
--- a/third_party/blink/web_tests/external/wpt/font-access/resources/window-tests-chooser.js
+++ /dev/null
@@ -1,8 +0,0 @@
-'use strict';
-
-font_access_test(async t => {
-  await promise_rejects_dom(
-      t, 'NotSupportedError', navigator.fonts.showFontChooser());
-  await promise_rejects_dom(
-      t, 'NotSupportedError', navigator.fonts.showFontChooser({all: false}));
-});
diff --git a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-04.html b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-04.html
deleted file mode 100644
index 42b4efd..0000000
--- a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-04.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>Forced colors mode - webkit-tap-highlight-color.</title>
-<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#forced-colors-properties">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<style>
-  a {
-    -webkit-tap-highlight-color: rgb(0, 0, 255);
-  }
-</style>
-<body>
-  <p>
-    <a href="https://www.wikipedia.org" id="link">
-      This link color should be overridden when forced colors mode is enabled.
-    </a>
-  </p>
-</body>
-
-<script>
-  test(function(){
-    assert_equals(getComputedStyle(link).webkitTapHighlightColor, "rgba(0, 0, 0, 0.18)");
-  }, "Checks hyperlinks are overridden in forced colors mode.");
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-26-ref.html b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-26-ref.html
index 39caee4..617d425 100644
--- a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-26-ref.html
+++ b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-26-ref.html
@@ -8,8 +8,12 @@
   }
 </style>
 <body>
-  The triangle below should be currentColor when forced colors mode.
-  <svg height="600" width="600">
-    <path fill="currentColor" d="M150 0 L75 200 L225 200 Z" />
+  The triangle below should NOT get overridden in forced colors mode.
+  <svg height="200" width="350">
+    <path fill="red" stroke="blue" d="M150 0 L75 200 L225 200 Z" />
+  </svg>
+  The triangle below should be currentColor in forced colors mode.
+  <svg height="200" width="350">
+    <path fill="currentColor" stroke="currentColor" d="M150 0 L75 200 L225 200 Z" />
   </svg>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-26.html b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-26.html
index a9aa590..53795645 100644
--- a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-26.html
+++ b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-26.html
@@ -12,8 +12,12 @@
 </style>
 <body>
   <div>
-    The triangle below should be currentColor when forced colors mode.
-    <svg height="600" width="600">
+    The triangle below should NOT get overridden in forced colors mode.
+    <svg height="200" width="350">
+      <path d="M150 0 L75 200 L225 200 Z" />
+    </svg>
+    The triangle below should be currentColor in forced colors mode.
+    <svg height="200" width="350" style="forced-color-adjust: auto;">
       <path d="M150 0 L75 200 L225 200 Z" />
     </svg>
   </div>
diff --git a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-32.html b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-32.html
deleted file mode 100644
index bd3595b..0000000
--- a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-32.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<title>Forced colors mode - webkit-tap-highlight-color with sys color.</title>
-<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#forced-colors-properties">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<style>
-  a {
-    -webkit-tap-highlight-color: GrayText;
-  }
-</style>
-<a href="https://www.wikipedia.org" id="link">
-  This link color should not be overridden when forced colors mode is enabled.
-</a>
-<a href="" id="ref" style="forced-color-adjust:none"></a>
-
-<script>
-  var gray_text = getComputedStyle(ref).webkitTapHighlightColor;
-  test(function(){
-    assert_equals(getComputedStyle(link).webkitTapHighlightColor, gray_text);
-  }, "Checks hyperlinks are not overridden if a sys color is used.");
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-35-ref.html b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-35-ref.html
index ba38e48..2b397e9 100644
--- a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-35-ref.html
+++ b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-35-ref.html
@@ -4,14 +4,17 @@
 </title>
 <style>
   a {
-    fill: VisitedText;
+    color: VisitedText;
     forced-color-adjust: none;
   }
 </style>
 <a href="">
-  The triangle below should not preserve the blue/green fill/stroke colors
-  in forced colors mode.
-  <svg height="600" width="600">
-    <path d="M150 0 L75 200 L225 200 Z" />
+  The triangle below should NOT get overridden in forced colors mode.
+  <svg height="200" width="350">
+    <path fill="blue" stroke="green" d="M150 0 L75 200 L225 200 Z" />
+  </svg>
+  The triangle below should be currentColor in forced colors mode.
+  <svg height="200" width="350">
+    <path fill="currentColor" stroke="currentColor" d="M150 0 L75 200 L225 200 Z" />
   </svg>
 </a>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-35.html b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-35.html
index 098a9ce..5d80e80 100644
--- a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-35.html
+++ b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-35.html
@@ -15,9 +15,12 @@
   }
 </style>
 <a href="">
-  The triangle below should not preserve the blue/green fill/stroke colors
-  in forced colors mode.
-  <svg height="600" width="600">
+  The triangle below should NOT get overridden in forced colors mode.
+  <svg height="200" width="350">
+    <path d="M150 0 L75 200 L225 200 Z" />
+  </svg>
+  The triangle below should be currentColor in forced colors mode.
+  <svg height="200" width="350" style="forced-color-adjust: auto;">
     <path d="M150 0 L75 200 L225 200 Z" />
   </svg>
 </a>
diff --git a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-40.html b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-40.html
index 235fb42f..84d16ec 100644
--- a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-40.html
+++ b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-40.html
@@ -9,6 +9,14 @@
 <style>
   div {
     background-color: green;
+    border-color: green;
+    column-rule-color: green;
+    fill: green;
+    outline-color: green;
+    stroke: green;
+    text-decoration-color: green;
+    -webkit-tap-highlight-color: green;
+    -webkit-text-emphasis-color: green;
   }
 </style>
 <div id="div">
@@ -18,6 +26,17 @@
 <script>
   const properties_to_test = [
     "background-color",
+    "border-bottom-color",
+    "border-left-color",
+    "border-right-color",
+    "border-top-color",
+    "column-rule-color",
+    "fill",
+    "outline-color",
+    "stroke",
+    "text-decoration-color",
+    "-webkit-tap-highlight-color",
+    "-webkit-text-emphasis-color"
   ];
   for (let property of properties_to_test) {
     test(function() {
diff --git a/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-41.html b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-41.html
new file mode 100644
index 0000000..64bc6cc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/forced-colors-mode/forced-colors-mode-41.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Forced colors mode - resolved value.
+  Forced colors happens at used value time. The resolved values of certain color
+  properties are used values. This test ensures that those values are forced in
+  forced colors mode.
+</title>
+<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#forced-colors-properties">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  div {
+    background-color: green;
+    border-color: green;
+    outline-color: green;
+  }
+</style>
+<div id="div"></div>
+
+<script>
+  const properties_to_test = [
+    "background-color",
+    "border-bottom-color",
+    "border-left-color",
+    "border-right-color",
+    "border-top-color",
+    "outline-color"
+  ];
+  for (let property of properties_to_test) {
+    test(function() {
+      let value =
+        window.getComputedStyle(document.getElementById("div")).getPropertyValue(property);
+        assert_not_equals(value, "rgb(0, 128, 0)")
+    }, "Forced colors affects the resolved value of " + property);
+  }
+</script>
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-expected.txt
index 3ff0350..5ca7d99 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 309 tests; 303 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 309 tests; 305 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -15,8 +15,8 @@
 PASS Parsing origin: <a:	 foo.com> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:/c> against <http://example.org/foo/bar>
-FAIL Parsing origin: <http://f:0/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
-FAIL Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
+PASS Parsing origin: <http://f:0/c> against <http://example.org/foo/bar>
+PASS Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:
 /c> against <http://example.org/foo/bar>
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-xhtml-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-xhtml-expected.txt
index 3ff0350..5ca7d99 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 309 tests; 303 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 309 tests; 305 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -15,8 +15,8 @@
 PASS Parsing origin: <a:	 foo.com> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:/c> against <http://example.org/foo/bar>
-FAIL Parsing origin: <http://f:0/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
-FAIL Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
+PASS Parsing origin: <http://f:0/c> against <http://example.org/foo/bar>
+PASS Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:
 /c> against <http://example.org/foo/bar>
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-origin-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-origin-expected.txt
index df2e7b8..e6b9139 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 309 tests; 300 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 309 tests; 302 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Origin parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -15,8 +15,8 @@
 PASS Origin parsing: <a:	 foo.com> against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:/c> against <http://example.org/foo/bar>
-FAIL Origin parsing: <http://f:0/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
-FAIL Origin parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
+PASS Origin parsing: <http://f:0/c> against <http://example.org/foo/bar>
+PASS Origin parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:
 /c> against <http://example.org/foo/bar>
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-expected.txt
index 3ff0350..5ca7d99 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 309 tests; 303 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 309 tests; 305 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -15,8 +15,8 @@
 PASS Parsing origin: <a:	 foo.com> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:/c> against <http://example.org/foo/bar>
-FAIL Parsing origin: <http://f:0/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
-FAIL Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
+PASS Parsing origin: <http://f:0/c> against <http://example.org/foo/bar>
+PASS Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:
 /c> against <http://example.org/foo/bar>
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-xhtml-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-xhtml-expected.txt
index 3ff0350..5ca7d99 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 309 tests; 303 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 309 tests; 305 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -15,8 +15,8 @@
 PASS Parsing origin: <a:	 foo.com> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:/c> against <http://example.org/foo/bar>
-FAIL Parsing origin: <http://f:0/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
-FAIL Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
+PASS Parsing origin: <http://f:0/c> against <http://example.org/foo/bar>
+PASS Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:
 /c> against <http://example.org/foo/bar>
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-origin-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-origin-expected.txt
index df2e7b8..e6b9139 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 309 tests; 300 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 309 tests; 302 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Origin parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -15,8 +15,8 @@
 PASS Origin parsing: <a:	 foo.com> against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:/c> against <http://example.org/foo/bar>
-FAIL Origin parsing: <http://f:0/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
-FAIL Origin parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
+PASS Origin parsing: <http://f:0/c> against <http://example.org/foo/bar>
+PASS Origin parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:
 /c> against <http://example.org/foo/bar>
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-expected.txt
index 98f52901..9548ad4e 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 309 tests; 301 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 309 tests; 303 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -15,8 +15,8 @@
 FAIL Parsing origin: <a:	 foo.com> against <http://example.org/foo/bar> assert_equals: origin expected "null" but got "file://"
 PASS Parsing origin: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:/c> against <http://example.org/foo/bar>
-FAIL Parsing origin: <http://f:0/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
-FAIL Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
+PASS Parsing origin: <http://f:0/c> against <http://example.org/foo/bar>
+PASS Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:
 /c> against <http://example.org/foo/bar>
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-xhtml-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-xhtml-expected.txt
index 98f52901..9548ad4e 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 309 tests; 301 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 309 tests; 303 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -15,8 +15,8 @@
 FAIL Parsing origin: <a:	 foo.com> against <http://example.org/foo/bar> assert_equals: origin expected "null" but got "file://"
 PASS Parsing origin: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:/c> against <http://example.org/foo/bar>
-FAIL Parsing origin: <http://f:0/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
-FAIL Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
+PASS Parsing origin: <http://f:0/c> against <http://example.org/foo/bar>
+PASS Parsing origin: <http://f:00000000000000/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
 PASS Parsing origin: <http://f:
 /c> against <http://example.org/foo/bar>
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/url-origin-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/url-origin-expected.txt
index 5c82897..e1d5a678 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/url-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/url-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 309 tests; 298 PASS, 11 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 309 tests; 300 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Origin parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -15,8 +15,8 @@
 FAIL Origin parsing: <a:	 foo.com> against <http://example.org/foo/bar> assert_equals: origin expected "null" but got "file://"
 PASS Origin parsing: <http://f:21/ b ? d # e > against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:/c> against <http://example.org/foo/bar>
-FAIL Origin parsing: <http://f:0/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
-FAIL Origin parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar> assert_equals: origin expected "http://f:0" but got "http://f"
+PASS Origin parsing: <http://f:0/c> against <http://example.org/foo/bar>
+PASS Origin parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>
 PASS Origin parsing: <http://f:
 /c> against <http://example.org/foo/bar>
diff --git a/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-04-expected.html b/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-04-expected.html
new file mode 100644
index 0000000..cf49350
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-04-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+  #link {
+    background-color: LinkText;
+    color: transparent !important;
+    forced-color-adjust: none;
+  }
+</style>
+<body>
+  <span href="" id="link">
+    In forced colors mode, the used value for -webkit-tap-highlight-color
+    should be overridden to currentColor.
+  </span>
+</body>
diff --git a/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-04.html b/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-04.html
new file mode 100644
index 0000000..93485b3
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-04.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Forced colors mode - webkit-tap-highlight-color.</title>
+<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#forced-colors-properties">
+<script src="../../../../../compositing/gestures/resources/link-highlight-helper.js"></script>
+<style>
+  a {
+    -webkit-tap-highlight-color: red;
+  }
+</style>
+<body onload="runTest();">
+  <a href="https://www.wikipedia.org" id="link">
+    In forced colors mode, the used value for -webkit-tap-highlight-color
+    should be overridden to currentColor.
+  </a>
+  <script>
+    function runTest() {
+        useMockHighlight();
+
+        var clientRect = document.getElementById('link').getBoundingClientRect();
+        x = (clientRect.left + clientRect.right) / 2;
+        y = (clientRect.top + clientRect.bottom) / 2;
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        if (window.eventSender) {
+            eventSender.gestureShowPress(x, y);
+            requestAnimationFrame(function() { testRunner.notifyDone(); });
+        } else {
+            debug("This test requires DumpRenderTree.");
+        }
+      }
+    </script>
+</body>
diff --git a/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-32-expected.html b/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-32-expected.html
new file mode 100644
index 0000000..c06341e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-32-expected.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+  #link {
+    background-color: GrayText;
+    color: GrayText !important;
+    forced-color-adjust: none;
+  }
+</style>
+<body>
+  <span href="" id="link">
+    In forced colors mode, the used value for -webkit-tap-highlight-color
+    should remain GrayText.
+  </span>
+</body>
diff --git a/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-32.html b/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-32.html
new file mode 100644
index 0000000..a3dd694
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/forced-high-contrast-colors/fast/css/forced-colors-mode/forced-colors-mode-32.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Forced colors mode - webkit-tap-highlight-color with sys color.</title>
+<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#forced-colors-properties">
+<script src="../../../../../compositing/gestures/resources/link-highlight-helper.js"></script>
+<style>
+  a {
+    -webkit-tap-highlight-color: GrayText;
+  }
+</style>
+<body onload="runTest();"></body>
+  <a href="https://www.wikipedia.org" id="link">
+    In forced colors mode, the used value for -webkit-tap-highlight-color
+    should remain GrayText.
+  </a>
+  <script>
+    function runTest() {
+        useMockHighlight();
+
+        var clientRect = document.getElementById('link').getBoundingClientRect();
+        x = (clientRect.left + clientRect.right) / 2;
+        y = (clientRect.top + clientRect.bottom) / 2;
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        if (window.eventSender) {
+            eventSender.gestureShowPress(x, y);
+            requestAnimationFrame(function() { testRunner.notifyDone(); });
+        } else {
+            debug("This test requires DumpRenderTree.");
+        }
+      }
+    </script>
+</body>
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index fcf2bb4f3..f83a746 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -171,7 +171,6 @@
 PASS oldChildWindow.onwheel is newChildWindow.onwheel
 PASS oldChildWindow.opener is newChildWindow.opener
 PASS oldChildWindow.origin is newChildWindow.origin
-PASS oldChildWindow.originIsolated is newChildWindow.originIsolated
 PASS oldChildWindow.outerHeight is newChildWindow.outerHeight
 PASS oldChildWindow.outerWidth is newChildWindow.outerWidth
 PASS oldChildWindow.pageXOffset is newChildWindow.pageXOffset
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
index 1f6025be..8365fb6 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -146,7 +146,6 @@
 PASS childWindow.onwheel is null
 PASS childWindow.opener is null
 PASS childWindow.origin is 'file://'
-PASS childWindow.originIsolated is false
 PASS childWindow.outerHeight is 0
 PASS childWindow.outerWidth is 0
 PASS childWindow.pageXOffset is 0
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
index e6f27d0..0afc4889 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -146,7 +146,6 @@
 PASS childWindow.onwheel is null
 PASS childWindow.opener is null
 PASS childWindow.origin is 'file://'
-PASS childWindow.originIsolated is false
 PASS childWindow.outerHeight is 0
 PASS childWindow.outerWidth is 0
 PASS childWindow.pageXOffset is 0
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 47b642e..fd31a84 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
@@ -9708,7 +9708,6 @@
     getter onwheel
     getter opener
     getter origin
-    getter originIsolated
     getter outerHeight
     getter outerWidth
     getter pageXOffset
diff --git a/third_party/blink/web_tests/webcodecs/reconfiguring_encooder.html b/third_party/blink/web_tests/webcodecs/reconfiguring_encooder.html
new file mode 100644
index 0000000..db9afcf
--- /dev/null
+++ b/third_party/blink/web_tests/webcodecs/reconfiguring_encooder.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <script src="../resources/testharness.js"></script>
+  <script src="../resources/testharnessreport.js"></script>
+</head>
+
+<body>
+  <img id='frame_image' style='display: none;' src="pattern.png">
+  <script>
+
+    'use strict';
+
+    async function waitTillLoaded(img) {
+      if (img.complete)
+        return Promise.resolve();
+      return new Promise((resolve, reject) => {
+        img.onload = () => resolve()
+      });
+    }
+
+    async function generateBitmap(width, height, text) {
+      let img = document.getElementById('frame_image');
+      await waitTillLoaded(img);
+      let cnv = document.createElement("canvas");
+      cnv.height = height;
+      cnv.width = width;
+      var ctx = cnv.getContext('2d');
+      ctx.drawImage(img, 0, 0, width, height);
+      ctx.font = '30px fantasy';
+      ctx.fillText(text, 5, 40);
+      return createImageBitmap(cnv);
+    }
+
+    async function createFrame(width, height, ts) {
+      let imageBitmap = await generateBitmap(width, height, ts.toString());
+      let frame = new VideoFrame(imageBitmap, { timestamp: ts });
+      imageBitmap.close();
+      return frame;
+    }
+
+    function delay(time_ms) {
+      return new Promise((resolve, reject) => {
+        setTimeout(resolve, time_ms);
+      });
+    };
+
+    async function change_encoding_params_test(codec, acc) {
+      let original_w = 800;
+      let original_h = 600;
+      let original_bitrate = 5_000_000;
+
+      let new_w = 640;
+      let new_h = 480;
+      let new_bitrate = 3_000_000;
+
+      let next_ts = 0
+      let reconf_ts = 0;
+      let frames_to_encode = 16;
+      let before_reconf_frames = 0;
+      let after_reconf_frames = 0;
+      let errors = 0;
+
+      let process_video_chunk = function (chunk, config) {
+        var data = new Uint8Array(chunk.data);
+        assert_greater_than_equal(data.length, 0);
+        let after_reconf = (reconf_ts != 0) && (chunk.timestamp >= reconf_ts);
+        if (after_reconf) {
+          after_reconf_frames++;
+          if (config) {
+            assert_equals(config.codedWidth, new_w);
+            assert_equals(config.codedHeight, new_h);
+          }
+        } else {
+          before_reconf_frames++;
+          if (config) {
+            assert_equals(config.codedWidth, original_w);
+            assert_equals(config.codedHeight, original_h);
+          }
+        }
+      };
+
+      const init = {
+        output: process_video_chunk,
+        error: (e) => {
+          errors++;
+          console.log(e.message);
+        },
+      };
+      const params = {
+        codec: codec,
+        acceleration: acc,
+        width: original_w,
+        height: original_h,
+        bitrate: original_bitrate,
+        framerate: 30,
+      };
+      let encoder = new VideoEncoder(init);
+      encoder.configure(params);
+
+      for (let i = 0; i < frames_to_encode; i++) {
+        var frame = await createFrame(original_w, original_h, next_ts++);
+        encoder.encode(frame, {});
+        await delay(1);
+      }
+
+      params.width = new_w;
+      params.height = new_h;
+      params.bitrate = new_bitrate;
+
+      encoder.configure(params);
+      reconf_ts = next_ts;
+
+      for (let i = 0; i < frames_to_encode; i++) {
+        var frame = await createFrame(new_w, new_h, next_ts++);
+        encoder.encode(frame, {});
+        await delay(1);
+      }
+
+      await encoder.flush();
+      encoder.close();
+      assert_equals(before_reconf_frames, frames_to_encode);
+      assert_equals(after_reconf_frames, frames_to_encode);
+      assert_equals(errors, 0);
+    }
+
+    promise_test(change_encoding_params_test.bind(null, "vp8", "allow"),
+      "reconfiguring vp8");
+
+    promise_test(change_encoding_params_test.bind(null, "vp09.00.10.08", "allow"),
+      "reconfiguring vp9 profile0");
+
+    promise_test(change_encoding_params_test.bind(null, "vp09.02.10.10", "allow"),
+      "reconfiguring vp9 profile2");
+
+    promise_test(change_encoding_params_test.bind(null, "avc1.42001E", "allow"),
+      "reconfiguring avc1.42001E");
+
+    /* Uncomment this for manual testing, before we have GPU tests for that */
+    //promise_test(change_encoding_params_test.bind(null, "avc1.42001E", "require"),
+    //  "reconfiguring avc1.42001E hw");
+
+
+  </script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/third_party/expat/fuzz/OWNERS b/third_party/expat/fuzz/OWNERS
index 7ab46b14c..4116061 100644
--- a/third_party/expat/fuzz/OWNERS
+++ b/third_party/expat/fuzz/OWNERS
@@ -1,2 +1 @@
-mmoroz@chromium.org
 ochang@chromium.org
diff --git a/third_party/libFuzzer/OWNERS b/third_party/libFuzzer/OWNERS
index 292df45..12379aa 100644
--- a/third_party/libFuzzer/OWNERS
+++ b/third_party/libFuzzer/OWNERS
@@ -1,7 +1,6 @@
 inferno@chromium.org
 kcc@chromium.org
 metzman@chromium.org
-mmoroz@chromium.org
 ochang@chromium.org
 # COMPONENT: Tools>Stability>libFuzzer
 # TEAM: chrome-security-bugs--@chromium.org
diff --git a/third_party/libprotobuf-mutator/OWNERS b/third_party/libprotobuf-mutator/OWNERS
index b718fa0..a53d99c1 100644
--- a/third_party/libprotobuf-mutator/OWNERS
+++ b/third_party/libprotobuf-mutator/OWNERS
@@ -1,6 +1,5 @@
 kcc@chromium.org
 metzman@chromium.org
-mmoroz@chromium.org
 vitalybuka@chromium.org
 # COMPONENT: Tools>Stability>ClusterFuzz
 # TEAM: chrome-security-bugs--@chromium.org
diff --git a/third_party/re2/OWNERS b/third_party/re2/OWNERS
index d4c5914a..bae5c864 100644
--- a/third_party/re2/OWNERS
+++ b/third_party/re2/OWNERS
@@ -1,3 +1,2 @@
-mmoroz@chromium.org
 thakis@chromium.org
 # COMPONENT: Internals
diff --git a/third_party/zlib/contrib/tests/fuzzers/OWNERS b/third_party/zlib/contrib/tests/fuzzers/OWNERS
index 6397ce6..9a2fb6f 100644
--- a/third_party/zlib/contrib/tests/fuzzers/OWNERS
+++ b/third_party/zlib/contrib/tests/fuzzers/OWNERS
@@ -1,2 +1 @@
 cblume@chromium.org
-mmoroz@chromium.org
diff --git a/tools/code_coverage/OWNERS b/tools/code_coverage/OWNERS
index 69ae6f43f..56f84aae4 100644
--- a/tools/code_coverage/OWNERS
+++ b/tools/code_coverage/OWNERS
@@ -1,5 +1,4 @@
 liaoyuke@chromium.org
 robertocn@chromium.org
 nodir@chromium.org
-mmoroz@chromium.org
-inferno@chromium.org
\ No newline at end of file
+inferno@chromium.org
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 7762e43..d471bc3 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2403,6 +2403,9 @@
 </enum>
 
 <enum name="AnnouncementNotificationEvent">
+  <obsolete>
+    Deprecated 11/2020
+  </obsolete>
   <int value="0" label="Start"/>
   <int value="1" label="Show notification"/>
   <int value="2" label="Click"/>
diff --git a/tools/metrics/histograms/histograms_xml/assistant/histograms.xml b/tools/metrics/histograms/histograms_xml/assistant/histograms.xml
index fb04262..970e1ec01 100644
--- a/tools/metrics/histograms/histograms_xml/assistant/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/assistant/histograms.xml
@@ -62,7 +62,7 @@
 </histogram>
 
 <histogram name="Assistant.DspHotwordDetection"
-    enum="DspHotwordDetectionStatus" expires_after="2020-05-23">
+    enum="DspHotwordDetectionStatus" expires_after="2021-05-23">
   <owner>meilinw@chromium.org</owner>
   <owner>xiaohuic@chromium.org</owner>
   <summary>
@@ -90,7 +90,7 @@
 </histogram>
 
 <histogram name="Assistant.HotwordEnableNotification" enum="BooleanHit"
-    expires_after="2020-12-31">
+    expires_after="2021-05-31">
   <owner>updowndota@chromium.org</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
@@ -121,8 +121,8 @@
   </summary>
 </histogram>
 
-<histogram name="Assistant.OptInFlow.LoadingTimeoutCount" units="units"
-    expires_after="2020-12-31">
+<histogram name="Assistant.OptInFlow.LoadingTimeoutCount" units="timeouts"
+    expires_after="2021-05-31">
   <owner>updowndota@chromium.org</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/obsolete_histograms.xml b/tools/metrics/histograms/histograms_xml/obsolete_histograms.xml
index 4cb320fb..d5ac6c0b 100644
--- a/tools/metrics/histograms/histograms_xml/obsolete_histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/obsolete_histograms.xml
@@ -29317,6 +29317,23 @@
   <summary>The Linux distro used. Logged on each start up.</summary>
 </histogram>
 
+<histogram name="Linux.X11.ServerRTT" units="microseconds" expires_after="M77">
+  <obsolete>
+    Removed 11/20 due to lack of usage.
+  </obsolete>
+  <owner>thomasanderson@chromium.org</owner>
+  <summary>
+    RTT between Chrome and the X11 server. Tracked in X11EventSource by
+    measuring the latency to receive a property event after changing a property.
+
+    Warning: This metric may include reports from clients with low-resolution
+    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
+    will cause this metric to have an abnormal distribution. When considering
+    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
+    solution.
+  </summary>
+</histogram>
+
 <histogram name="LoadingPredictor.SubresourceConnectDuration" units="ms"
     expires_after="2018-02-28">
   <obsolete>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 3d2b94c..40a14978 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -8115,20 +8115,6 @@
   </summary>
 </histogram>
 
-<histogram name="Linux.X11.ServerRTT" units="microseconds" expires_after="M77">
-  <owner>thomasanderson@chromium.org</owner>
-  <summary>
-    RTT between Chrome and the X11 server. Tracked in X11EventSource by
-    measuring the latency to receive a property event after changing a property.
-
-    Warning: This metric may include reports from clients with low-resolution
-    clocks (i.e. on Windows, ref. |TimeTicks::IsHighResolution()|). Such reports
-    will cause this metric to have an abnormal distribution. When considering
-    revising this histogram, see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES for the
-    solution.
-  </summary>
-</histogram>
-
 <histogram name="LiteVideo.CanApplyLiteVideo.HintCache.HasHint"
     enum="BooleanAvailable" expires_after="M90">
   <owner>mcrouse@chromium.org</owner>
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index 970d0b9..4cf872df 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -331,10 +331,16 @@
     _load_library_perf_tests(),
     _performance_browser_tests(210),
 ])
-_MAC_ARM_DTK_BENCHMARK_CONFIGS = PerfSuite([
-    'loading.desktop',
-]).Abridge([
-    'loading.desktop',
+_MAC_ARM_DTK_BENCHMARK_CONFIGS = PerfSuite(OFFICIAL_BENCHMARK_CONFIGS).Remove([
+    'blink_perf.display_locking',
+    'v8.runtime_stats.top_25',
+])
+_MAC_ARM_DTK_EXECUTABLE_CONFIGS = frozenset([
+    _base_perftests(300),
+    _dawn_perf_tests(330),
+    _media_perftests(),
+    _performance_browser_tests(190),
+    _views_perftests(),
 ])
 
 _WIN_10_BENCHMARK_CONFIGS = PerfSuite(OFFICIAL_BENCHMARK_CONFIGS).Remove([
@@ -464,18 +470,12 @@
     26,
     'mac',
     executables=_MAC_LOW_END_EXECUTABLE_CONFIGS)
-MAC_ARM_DTK_X86 = PerfPlatform(
-    'mac-arm_dtk_x86-perf',
-    'Mac ARM DTK (X86 Chrome)',
-    _MAC_ARM_DTK_BENCHMARK_CONFIGS,
-    1,
-    'mac')
-MAC_ARM_DTK_ARM = PerfPlatform(
-    'mac-arm_dtk_arm-perf',
-    'Mac ARM DTK (ARM Chrome)',
-    _MAC_ARM_DTK_BENCHMARK_CONFIGS,
-    1,
-    'mac')
+MAC_ARM_DTK_ARM = PerfPlatform('mac-arm_dtk_arm-perf',
+                               'Mac ARM DTK (ARM Chrome)',
+                               _MAC_ARM_DTK_BENCHMARK_CONFIGS,
+                               8,
+                               'mac',
+                               executables=_MAC_ARM_DTK_EXECUTABLE_CONFIGS)
 
 # Win
 WIN_10_LOW_END = PerfPlatform(
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index dbce5ad..8b2bd45 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -632,23 +632,6 @@
             'MacBookAir7,2_x86-64-i5-5350U_Intel Broadwell HD Graphics 6000_8192_APPLE SSD SM0128G'
         },
     },
-    'mac-arm_dtk_x86-perf': {
-        'tests': [
-            {
-                'isolate': 'performance_test_suite',
-                'extra_args': [
-                    '--assert-gpu-compositing',
-                ],
-            },
-        ],
-        'platform':
-        'mac',
-        'dimension': {
-            'cpu': 'arm',
-            'os': 'Mac',
-            'pool': 'chrome.tests.perf',
-        },
-    },
     'mac-arm_dtk_arm-perf': {
         'tests': [
             {
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 28f623d..9a7a3e7 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "bfdb6057bef6817a2dde5a68acd2e3852a05ff62",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/16973d613fed5c24d63bc4bf6cc9ffe267708ea5/trace_processor_shell.exe"
+            "hash": "589c99eb2e8e954600fa441597fc26b6fb46d498",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/8f8e2dc7d50fac8d40137e49c39d1a0c2cc10ed7/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "fc50aba2bda1b92a35ddce6e7cfa0fcba40b010d",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/16973d613fed5c24d63bc4bf6cc9ffe267708ea5/trace_processor_shell"
+            "hash": "f5e047022dcbe6fa044a7da9739ac8623d0b1efb",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/8f8e2dc7d50fac8d40137e49c39d1a0c2cc10ed7/trace_processor_shell"
         },
         "linux": {
             "hash": "d62b8d8400fd5124b7e6ead6eae14a6e1dfa58c3",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/2f47910c9ce32bb4cca529a10f0ce3ea05871876/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/8f8e2dc7d50fac8d40137e49c39d1a0c2cc10ed7/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/core/shard_maps/mac-arm_dtk_arm-perf_map.json b/tools/perf/core/shard_maps/mac-arm_dtk_arm-perf_map.json
index 91063898..e2dbe2e 100644
--- a/tools/perf/core/shard_maps/mac-arm_dtk_arm-perf_map.json
+++ b/tools/perf/core/shard_maps/mac-arm_dtk_arm-perf_map.json
@@ -1,17 +1,240 @@
 {
     "0": {
         "benchmarks": {
+            "blink_perf.accessibility": {
+                "abridged": false
+            },
+            "blink_perf.bindings": {
+                "abridged": false
+            },
+            "blink_perf.css": {
+                "abridged": false
+            },
+            "blink_perf.dom": {
+                "abridged": false
+            },
+            "blink_perf.events": {
+                "abridged": false
+            },
+            "blink_perf.image_decoder": {
+                "end": 4,
+                "abridged": false
+            }
+        },
+        "executables": {
+            "base_perftests": {
+                "path": "base_perftests",
+                "arguments": [
+                    "--test-launcher-jobs=1",
+                    "--test-launcher-retry-limit=0"
+                ]
+            }
+        }
+    },
+    "1": {
+        "benchmarks": {
+            "blink_perf.image_decoder": {
+                "begin": 4,
+                "abridged": false
+            },
+            "blink_perf.layout": {
+                "abridged": false
+            },
+            "blink_perf.owp_storage": {
+                "abridged": false
+            },
+            "blink_perf.paint": {
+                "abridged": false
+            },
+            "blink_perf.parser": {
+                "end": 26,
+                "abridged": false
+            }
+        }
+    },
+    "2": {
+        "benchmarks": {
+            "blink_perf.parser": {
+                "begin": 26,
+                "abridged": false
+            },
+            "blink_perf.shadow_dom": {
+                "abridged": false
+            },
+            "blink_perf.svg": {
+                "abridged": false
+            },
+            "blink_perf.webaudio": {
+                "abridged": false
+            },
+            "dromaeo": {
+                "abridged": false
+            },
+            "dummy_benchmark.noisy_benchmark_1": {
+                "abridged": false
+            },
+            "dummy_benchmark.stable_benchmark_1": {
+                "abridged": false
+            },
+            "jetstream": {
+                "abridged": false
+            },
+            "jetstream2": {
+                "abridged": false
+            },
+            "kraken": {
+                "abridged": false
+            },
             "loading.desktop": {
-                "abridged": true
+                "end": 20,
+                "abridged": false
+            }
+        },
+        "executables": {
+            "dawn_perf_tests": {
+                "path": "dawn_perf_tests",
+                "arguments": [
+                    "--test-launcher-jobs=1",
+                    "--test-launcher-retry-limit=0"
+                ]
+            }
+        }
+    },
+    "3": {
+        "benchmarks": {
+            "loading.desktop": {
+                "begin": 20,
+                "abridged": false
+            },
+            "media.desktop": {
+                "abridged": false
+            },
+            "memory.desktop": {
+                "abridged": false
+            },
+            "octane": {
+                "abridged": false
+            }
+        },
+        "executables": {
+            "media_perftests": {
+                "path": "media_perftests",
+                "arguments": [
+                    "--single-process-tests",
+                    "--test-launcher-retry-limit=0",
+                    "--isolated-script-test-filter=*::-*_unoptimized::*_unaligned::*unoptimized_aligned"
+                ]
+            }
+        }
+    },
+    "4": {
+        "benchmarks": {
+            "power.desktop": {
+                "abridged": false
+            },
+            "rasterize_and_record_micro.top_25": {
+                "abridged": false
+            },
+            "rendering.desktop": {
+                "end": 90,
+                "abridged": false
+            }
+        },
+        "executables": {
+            "performance_browser_tests": {
+                "path": "browser_tests",
+                "arguments": [
+                    "--full-performance-run",
+                    "--test-launcher-jobs=1",
+                    "--test-launcher-retry-limit=0",
+                    "--ui-test-action-timeout=60000",
+                    "--ui-test-action-max-timeout=60000",
+                    "--test-launcher-timeout=60000",
+                    "--gtest_filter=*/TabCapturePerformanceTest.*:*/CastV2PerformanceTest.*"
+                ]
+            }
+        }
+    },
+    "5": {
+        "benchmarks": {
+            "rendering.desktop": {
+                "begin": 90,
+                "end": 240,
+                "abridged": false
+            }
+        }
+    },
+    "6": {
+        "benchmarks": {
+            "rendering.desktop": {
+                "begin": 240,
+                "abridged": false
+            },
+            "speedometer": {
+                "abridged": false
+            },
+            "speedometer-future": {
+                "abridged": false
+            },
+            "speedometer2": {
+                "abridged": false
+            },
+            "speedometer2-future": {
+                "abridged": false
+            },
+            "system_health.common_desktop": {
+                "abridged": false
+            },
+            "system_health.memory_desktop": {
+                "end": 2,
+                "abridged": false
+            }
+        }
+    },
+    "7": {
+        "benchmarks": {
+            "system_health.memory_desktop": {
+                "begin": 2,
+                "abridged": false
+            },
+            "tab_switching.typical_25": {
+                "abridged": false
+            },
+            "tracing.tracing_with_background_memory_infra": {
+                "abridged": false
+            },
+            "v8.browsing_desktop": {
+                "abridged": false
+            },
+            "v8.browsing_desktop-future": {
+                "abridged": false
+            },
+            "webrtc": {
+                "abridged": false
+            }
+        },
+        "executables": {
+            "views_perftests": {
+                "path": "views_perftests",
+                "arguments": [
+                    "--xvfb"
+                ]
             }
         }
     },
     "extra_infos": {
-        "num_stories": 10,
-        "predicted_min_shard_time": 492.0,
-        "predicted_min_shard_index": 0,
-        "predicted_max_shard_time": 492.0,
-        "predicted_max_shard_index": 0,
-        "shard #0": 492.0
+        "num_stories": 1063,
+        "predicted_min_shard_time": 1418.0,
+        "predicted_min_shard_index": 3,
+        "predicted_max_shard_time": 1500,
+        "predicted_max_shard_index": 5,
+        "shard #0": 1480.0,
+        "shard #1": 1480,
+        "shard #2": 1482.0,
+        "shard #3": 1418.0,
+        "shard #4": 1490.0,
+        "shard #5": 1500,
+        "shard #6": 1490,
+        "shard #7": 1497.0
     }
 }
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/mac-arm_dtk_x86-perf_map.json b/tools/perf/core/shard_maps/mac-arm_dtk_x86-perf_map.json
deleted file mode 100644
index 79b9bc43..0000000
--- a/tools/perf/core/shard_maps/mac-arm_dtk_x86-perf_map.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-    "0": {
-        "benchmarks": {
-            "loading.desktop": {
-                "abridged": true
-            }
-        }
-    },
-    "extra_infos": {
-        "num_stories": 10,
-        "predicted_min_shard_time": 616.0,
-        "predicted_min_shard_index": 0,
-        "predicted_max_shard_time": 616.0,
-        "predicted_max_shard_index": 0,
-        "shard #0": 616.0
-    }
-}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/mac-arm_dtk_arm-perf_timing.json b/tools/perf/core/shard_maps/timing_data/mac-arm_dtk_arm-perf_timing.json
index 54a9ae7..7429693 100644
--- a/tools/perf/core/shard_maps/timing_data/mac-arm_dtk_arm-perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/mac-arm_dtk_arm-perf_timing.json
@@ -1,14 +1,14 @@
 [
     {
-        "duration": "33.0",
+        "duration": "35.0",
         "name": "loading.desktop/AirBnB_warm"
     },
     {
-        "duration": "32.0",
+        "duration": "33.0",
         "name": "loading.desktop/Aljayyash_cold"
     },
     {
-        "duration": "25.0",
+        "duration": "26.0",
         "name": "loading.desktop/AllRecipes_cold"
     },
     {
@@ -16,15 +16,15 @@
         "name": "loading.desktop/Baidu_warm"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "loading.desktop/Naver_cold"
     },
     {
-        "duration": "20.0",
+        "duration": "22.0",
         "name": "loading.desktop/Orange_cold"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "loading.desktop/Orange_warm"
     },
     {
@@ -32,11 +32,31 @@
         "name": "loading.desktop/Taobao_warm"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "loading.desktop/TheOnion_cold"
     },
     {
-        "duration": "22.0",
+        "duration": "24.0",
         "name": "loading.desktop/ru.wikipedia_warm"
+    },
+    {
+        "duration": "300.0",
+        "name": "base_perftests/_gtest_"
+    },
+    {
+        "duration": "330.0",
+        "name": "dawn_perf_tests/_gtest_"
+    },
+    {
+        "duration": "16.0",
+        "name": "media_perftests/_gtest_"
+    },
+    {
+        "duration": "190.0",
+        "name": "performance_browser_tests/_gtest_"
+    },
+    {
+        "duration": "7.0",
+        "name": "views_perftests/_gtest_"
     }
 ]
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/mac-arm_dtk_x86-perf_timing.json b/tools/perf/core/shard_maps/timing_data/mac-arm_dtk_x86-perf_timing.json
deleted file mode 100644
index 9e65041..0000000
--- a/tools/perf/core/shard_maps/timing_data/mac-arm_dtk_x86-perf_timing.json
+++ /dev/null
@@ -1,42 +0,0 @@
-[
-    {
-        "duration": "44.0",
-        "name": "loading.desktop/AirBnB_warm"
-    },
-    {
-        "duration": "43.0",
-        "name": "loading.desktop/Aljayyash_cold"
-    },
-    {
-        "duration": "31.0",
-        "name": "loading.desktop/AllRecipes_cold"
-    },
-    {
-        "duration": "25.0",
-        "name": "loading.desktop/Baidu_warm"
-    },
-    {
-        "duration": "24.0",
-        "name": "loading.desktop/Naver_cold"
-    },
-    {
-        "duration": "24.0",
-        "name": "loading.desktop/Orange_cold"
-    },
-    {
-        "duration": "26.0",
-        "name": "loading.desktop/Orange_warm"
-    },
-    {
-        "duration": "34.0",
-        "name": "loading.desktop/Taobao_warm"
-    },
-    {
-        "duration": "29.0",
-        "name": "loading.desktop/TheOnion_cold"
-    },
-    {
-        "duration": "28.0",
-        "name": "loading.desktop/ru.wikipedia_warm"
-    }
-]
\ No newline at end of file
diff --git a/ui/base/x/test/x11_property_change_waiter.cc b/ui/base/x/test/x11_property_change_waiter.cc
index eaeb106..fa88854e 100644
--- a/ui/base/x/test/x11_property_change_waiter.cc
+++ b/ui/base/x/test/x11_property_change_waiter.cc
@@ -24,12 +24,14 @@
   x_window_events_ = std::make_unique<XScopedEventSelector>(
       x_window_, x11::EventMask::PropertyChange);
 
-  // Override the dispatcher so that we get events before X11Window does. We
-  // must do this because X11Window stops propagation.
-  dispatcher_ = X11EventSource::GetInstance()->OverrideXEventDispatcher(this);
+  // Add ourselves as an event observer so that we get events before X11Window
+  // does. We must do this because X11Window stops propagation.
+  X11EventSource::GetInstance()->AddXEventObserver(this);
 }
 
-X11PropertyChangeWaiter::~X11PropertyChangeWaiter() = default;
+X11PropertyChangeWaiter::~X11PropertyChangeWaiter() {
+  X11EventSource::GetInstance()->RemoveXEventObserver(this);
+}
 
 void X11PropertyChangeWaiter::Wait() {
   if (!wait_)
@@ -38,8 +40,6 @@
   base::RunLoop run_loop;
   quit_closure_ = run_loop.QuitClosure();
   run_loop.Run();
-
-  dispatcher_.reset();
 }
 
 bool X11PropertyChangeWaiter::ShouldKeepOnWaiting(x11::Event* event) {
@@ -47,17 +47,16 @@
   return true;
 }
 
-bool X11PropertyChangeWaiter::DispatchXEvent(x11::Event* x11_event) {
+void X11PropertyChangeWaiter::WillProcessXEvent(x11::Event* x11_event) {
   auto* prop = x11_event->As<x11::PropertyNotifyEvent>();
   if (!wait_ || !prop || prop->window != x_window_ ||
       prop->atom != gfx::GetAtom(property_) || ShouldKeepOnWaiting(x11_event)) {
-    return false;
+    return;
   }
 
   wait_ = false;
   if (!quit_closure_.is_null())
     std::move(quit_closure_).Run();
-  return false;
 }
 
 }  // namespace ui
diff --git a/ui/base/x/test/x11_property_change_waiter.h b/ui/base/x/test/x11_property_change_waiter.h
index 85c427f0..a02d2e7 100644
--- a/ui/base/x/test/x11_property_change_waiter.h
+++ b/ui/base/x/test/x11_property_change_waiter.h
@@ -18,7 +18,7 @@
 namespace ui {
 
 // Blocks till the value of |property| on |window| changes.
-class X11PropertyChangeWaiter : public XEventDispatcher {
+class X11PropertyChangeWaiter : public XEventObserver {
  public:
   X11PropertyChangeWaiter(x11::Window window, const char* property);
   ~X11PropertyChangeWaiter() override;
@@ -33,8 +33,8 @@
   x11::Window xwindow() const { return x_window_; }
 
  private:
-  // XEventDispatcher:
-  bool DispatchXEvent(x11::Event* event) override;
+  // XEventObserver:
+  void WillProcessXEvent(x11::Event* event) override;
 
   x11::Window x_window_;
   const char* property_;
@@ -47,8 +47,6 @@
   // Ends the run loop.
   base::OnceClosure quit_closure_;
 
-  std::unique_ptr<ScopedXEventDispatcher> dispatcher_;
-
   DISALLOW_COPY_AND_ASSIGN(X11PropertyChangeWaiter);
 };
 
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 eb014db..9dc6f68 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,7 +6,7 @@
 
 #include <linux/input.h>
 
-#include "base/system/sys_info.h"
+#include "base/test/scoped_chromeos_version_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"
@@ -53,20 +53,26 @@
 
 #if defined(OS_CHROMEOS)
 TEST_F(PalmDetectionFilterFactoryTest, RadiusesFromLSBRelease) {
-  std::string lsb_release = "CHROMEOS_RELEASE_BOARD=hatch\n";
-  base::SysInfo::SetChromeOSVersionInfoForTest(lsb_release, base::Time());
-  EXPECT_EQ("0.1010944, 3.51837568", 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_, ""));
+  {
+    base::test::ScopedChromeOSVersionInfo version(
+        "CHROMEOS_RELEASE_BOARD=hatch\n", base::Time());
+    EXPECT_EQ("0.1010944, 3.51837568",
+              internal::FetchNeuralPalmRadiusPolynomial(
+                  kohaku_touchscreen_info_, ""));
+  }
+  {
+    base::test::ScopedChromeOSVersionInfo version(
+        "CHROMEOS_RELEASE_BOARD=reef\n", base::Time());
+    EXPECT_EQ("0.17889799, 4.22584412",
+              internal::FetchNeuralPalmRadiusPolynomial(
+                  kohaku_touchscreen_info_, ""));
+  }
+  {
+    base::test::ScopedChromeOSVersionInfo version(
+        "CHROMEOS_RELEASE_BOARD=octopus\n", base::Time());
+    EXPECT_EQ("", internal::FetchNeuralPalmRadiusPolynomial(
+                      kohaku_touchscreen_info_, ""));
+  }
 }
 #endif
 
diff --git a/ui/events/platform/x11/x11_event_source.cc b/ui/events/platform/x11/x11_event_source.cc
index 010e1c7..3907c739 100644
--- a/ui/events/platform/x11/x11_event_source.cc
+++ b/ui/events/platform/x11/x11_event_source.cc
@@ -123,8 +123,7 @@
     : watcher_(std::make_unique<X11EventWatcherImpl>(this)),
       connection_(connection),
       dispatching_event_(nullptr),
-      dummy_initialized_(false),
-      distribution_(0, 999) {
+      dummy_initialized_(false) {
   DCHECK(connection_);
   DeviceDataManagerX11::CreateInstance();
   InitializeXkb(connection_);
@@ -174,14 +173,6 @@
     dummy_initialized_ = true;
   }
 
-  // No need to measure Linux.X11.ServerRTT on every call.
-  // base::TimeTicks::Now() itself has non-trivial overhead.
-  bool measure_rtt = distribution_(generator_) == 0;
-
-  base::TimeTicks start;
-  if (measure_rtt)
-    start = base::TimeTicks::Now();
-
   // Make a no-op property change on |dummy_window_|.
   std::vector<uint8_t> data{0};
   connection_->ChangeProperty(x11::ChangePropertyRequest{
@@ -195,12 +186,6 @@
 
   // Observe the resulting PropertyNotify event to obtain the timestamp.
   connection_->Sync();
-  if (measure_rtt) {
-    UMA_HISTOGRAM_CUSTOM_COUNTS(
-        "Linux.X11.ServerRTT",
-        (base::TimeTicks::Now() - start).InMicroseconds(), 1,
-        base::TimeDelta::FromMilliseconds(50).InMicroseconds(), 50);
-  }
   connection_->ReadResponses();
 
   auto time = x11::Time::CurrentTime;
@@ -287,19 +272,6 @@
   observers_.RemoveObserver(observer);
 }
 
-std::unique_ptr<ScopedXEventDispatcher>
-X11EventSource::OverrideXEventDispatcher(XEventDispatcher* dispatcher) {
-  CHECK(dispatcher);
-  overridden_dispatcher_restored_ = false;
-  return std::make_unique<ScopedXEventDispatcher>(&overridden_dispatcher_,
-                                                  dispatcher);
-}
-
-void X11EventSource::RestoreOverridenXEventDispatcher() {
-  CHECK(overridden_dispatcher_);
-  overridden_dispatcher_restored_ = true;
-}
-
 void X11EventSource::DispatchPlatformEvent(const PlatformEvent& event,
                                            x11::Event* xevent) {
   DCHECK(event);
@@ -321,34 +293,16 @@
 }
 
 void X11EventSource::DispatchXEventToXEventDispatchers(x11::Event* xevent) {
-  bool stop_dispatching = false;
-
   for (auto& observer : observers_)
     observer.WillProcessXEvent(xevent);
 
-  if (overridden_dispatcher_) {
-    stop_dispatching = overridden_dispatcher_->DispatchXEvent(xevent);
-  }
-
-  if (!stop_dispatching) {
-    for (XEventDispatcher& dispatcher : dispatchers_xevent_) {
-      if (dispatcher.DispatchXEvent(xevent))
-        break;
-    }
+  for (XEventDispatcher& dispatcher : dispatchers_xevent_) {
+    if (dispatcher.DispatchXEvent(xevent))
+      break;
   }
 
   for (auto& observer : observers_)
     observer.DidProcessXEvent(xevent);
-
-  // If an overridden dispatcher has been destroyed, then the event source
-  // should halt dispatching the current stream of events, and wait until the
-  // next message-loop iteration for dispatching events. This lets any nested
-  // message-loop to unwind correctly and any new dispatchers to receive the
-  // correct sequence of events.
-  if (overridden_dispatcher_restored_)
-    StopCurrentEventStream();
-
-  overridden_dispatcher_restored_ = false;
 }
 
 void XEventDispatcher::CheckCanDispatchNextPlatformEvent(x11::Event* xev) {}
@@ -449,18 +403,6 @@
   dispatching_event_ = nullptr;
 }
 
-// ScopedXEventDispatcher implementation
-ScopedXEventDispatcher::ScopedXEventDispatcher(
-    XEventDispatcher** scoped_dispatcher,
-    XEventDispatcher* new_dispatcher)
-    : original_(*scoped_dispatcher),
-      restore_(scoped_dispatcher, new_dispatcher) {}
-
-ScopedXEventDispatcher::~ScopedXEventDispatcher() {
-  DCHECK(X11EventSource::HasInstance());
-  X11EventSource::GetInstance()->RestoreOverridenXEventDispatcher();
-}
-
 // static
 #if defined(USE_X11)
 std::unique_ptr<PlatformEventSource> PlatformEventSource::CreateDefault() {
diff --git a/ui/events/platform/x11/x11_event_source.h b/ui/events/platform/x11/x11_event_source.h
index b9fc9a0..95176ac 100644
--- a/ui/events/platform/x11/x11_event_source.h
+++ b/ui/events/platform/x11/x11_event_source.h
@@ -8,7 +8,6 @@
 #include <stdint.h>
 
 #include <memory>
-#include <random>
 
 #include "base/auto_reset.h"
 #include "base/callback.h"
@@ -28,7 +27,6 @@
 class X11HotplugEventHandler;
 class XScopedEventSelector;
 class PlatformEventDispatcher;
-class ScopedXEventDispatcher;
 
 // The XEventDispatcher interface is used in two different ways: the first is
 // when classes want to receive x11::Event directly and second is to say if
@@ -65,10 +63,10 @@
 class EVENTS_EXPORT XEventObserver {
  public:
   // Called before the dispatchers receive the event.
-  virtual void WillProcessXEvent(x11::Event* event) = 0;
+  virtual void WillProcessXEvent(x11::Event* event) {}
 
   // Called after the event has been dispatched.
-  virtual void DidProcessXEvent(x11::Event* event) = 0;
+  virtual void DidProcessXEvent(x11::Event* event) {}
 
  protected:
   virtual ~XEventObserver() = default;
@@ -92,26 +90,6 @@
   DISALLOW_COPY_AND_ASSIGN(X11EventWatcher);
 };
 
-// A temporary XEventDispatcher can be installed on a X11EventSource that
-// overrides all installed event dispatchers, and always gets a chance to
-// dispatch the event first, similar to what PlatformEventSource does with
-// ScopedEventDispatcher. When this object is destroyed, it removes the
-// override-dispatcher, and restores the previous override-dispatcher.
-class EVENTS_EXPORT ScopedXEventDispatcher {
- public:
-  ScopedXEventDispatcher(XEventDispatcher** scoped_dispatcher,
-                         XEventDispatcher* new_dispatcher);
-  ~ScopedXEventDispatcher();
-
-  operator XEventDispatcher*() const { return original_; }
-
- private:
-  XEventDispatcher* original_;
-  base::AutoReset<XEventDispatcher*> restore_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedXEventDispatcher);
-};
-
 // PlatformEventSource implementation for X11, both Ozone and non-Ozone.
 // Receives X11 events from X11EventWatcher and sends them to registered
 // {Platform,X}EventDispatchers. Handles receiving, pre-process, translation
@@ -163,15 +141,6 @@
   void AddXEventObserver(XEventObserver* observer);
   void RemoveXEventObserver(XEventObserver* observer);
 
-  // Installs a XEventDispatcher that receives all the events. The dispatcher
-  // can process the event, or request that the default dispatchers be invoked
-  // by returning false from its DispatchXEvent() override. The returned
-  // ScopedXEventDispatcher object is a handler for the overridden dispatcher.
-  // When this handler is destroyed, it removes the overridden dispatcher, and
-  // restores the previous override-dispatcher (or null if there wasn't any).
-  std::unique_ptr<ScopedXEventDispatcher> OverrideXEventDispatcher(
-      XEventDispatcher* dispatcher);
-
   void ProcessXEvent(x11::Event* xevent);
 
   // x11::Connection::Delegate:
@@ -183,8 +152,6 @@
   void PostDispatchEvent(x11::Event* xevent);
 
  private:
-  friend class ScopedXEventDispatcher;
-
   // Tells XEventDispatchers, which can also have PlatformEventDispatchers, that
   // a translated event is going to be sent next, then dispatches the event and
   // notifies XEventDispatchers the event has been sent out and, most probably,
@@ -198,8 +165,6 @@
   void StopCurrentEventStream() override;
   void OnDispatcherListChanged() override;
 
-  void RestoreOverridenXEventDispatcher();
-
   std::unique_ptr<X11EventWatcher> watcher_;
 
   // The connection to the X11 server used to receive the events.
@@ -220,18 +185,11 @@
 
   std::unique_ptr<X11HotplugEventHandler> hotplug_event_handler_;
 
-  // Used to sample RTT measurements, with frequency 1/1000.
-  std::default_random_engine generator_;
-  std::uniform_int_distribution<int> distribution_;
-
   // Keep track of all XEventDispatcher to send XEvents directly to.
   base::ObserverList<XEventDispatcher>::Unchecked dispatchers_xevent_;
 
   base::ObserverList<XEventObserver>::Unchecked observers_;
 
-  XEventDispatcher* overridden_dispatcher_ = nullptr;
-  bool overridden_dispatcher_restored_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(X11EventSource);
 };
 
diff --git a/ui/events/test/x11_event_waiter.h b/ui/events/test/x11_event_waiter.h
index 9a11768c..e16c7b4c 100644
--- a/ui/events/test/x11_event_waiter.h
+++ b/ui/events/test/x11_event_waiter.h
@@ -13,8 +13,6 @@
 
 namespace ui {
 
-class ScopedXEventDispatcher;
-
 // X11 Event Waiter class
 class XEventWaiter : public ui::XEventObserver {
  public:
@@ -32,7 +30,6 @@
   static x11::Atom MarkerEventAtom();
 
   base::OnceClosure success_callback_;
-  std::unique_ptr<ui::ScopedXEventDispatcher> dispatcher_;
 };
 
 }  // namespace ui
diff --git a/ui/gfx/x/BUILD.gn b/ui/gfx/x/BUILD.gn
index 96f72a4..e64338d 100644
--- a/ui/gfx/x/BUILD.gn
+++ b/ui/gfx/x/BUILD.gn
@@ -34,6 +34,7 @@
     "XFlush",
     "XSynchronize",
     "XSetErrorHandler",
+    "XFree",
   ]
 }
 
diff --git a/ui/gfx/x/xlib.h b/ui/gfx/x/xlib.h
index 68f4d3e..a2deed23 100644
--- a/ui/gfx/x/xlib.h
+++ b/ui/gfx/x/xlib.h
@@ -12,6 +12,7 @@
 int XFlush(struct _XDisplay*);
 int XSynchronize(struct _XDisplay*, int);
 int XSetErrorHandler(int (*)(void*, void*));
+void XFree(void*);
 }
 
 #endif  // UI_GFX_X_XLIB_H_
diff --git a/ui/gfx/x/xlib_support.cc b/ui/gfx/x/xlib_support.cc
index fe7846d..37817ee 100644
--- a/ui/gfx/x/xlib_support.cc
+++ b/ui/gfx/x/xlib_support.cc
@@ -50,6 +50,11 @@
 }
 
 DISABLE_CFI_ICALL
+void XlibFree(void* data) {
+  GetXlibLoader()->XFree(data);
+}
+
+DISABLE_CFI_ICALL
 XlibDisplay::XlibDisplay(const std::string& address) {
   InitXlib();
 
diff --git a/ui/gfx/x/xlib_support.h b/ui/gfx/x/xlib_support.h
index 2d50399..312430e 100644
--- a/ui/gfx/x/xlib_support.h
+++ b/ui/gfx/x/xlib_support.h
@@ -32,6 +32,9 @@
 // Sets an async error handler which only logs an error message.
 COMPONENT_EXPORT(X11) void SetXlibErrorHandler();
 
+// Wraps XFree().
+COMPONENT_EXPORT(X11) void XlibFree(void* data);
+
 // A scoped Xlib display.
 class COMPONENT_EXPORT(X11) XlibDisplay {
  public:
diff --git a/ui/gl/glx_util.cc b/ui/gl/glx_util.cc
index 397e807a..b85a1418 100644
--- a/ui/gl/glx_util.cc
+++ b/ui/gl/glx_util.cc
@@ -62,13 +62,6 @@
   return {};
 }
 
-NO_SANITIZE("cfi-icall")
-void XlibFree(void* data) {
-  using xfree_type = void (*)(void*);
-  auto* xfree = reinterpret_cast<xfree_type>(dlsym(RTLD_DEFAULT, "XFree"));
-  xfree(data);
-}
-
 }  // namespace
 
 GLXFBConfig GetFbConfigForWindow(x11::Connection* connection,
@@ -89,7 +82,7 @@
   DCHECK_EQ(nitems, 1);
   DCHECK(glx_configs);
   GLXFBConfig glx_config = glx_configs[0];
-  XlibFree(glx_configs);
+  x11::XlibFree(glx_configs);
   return glx_config;
 }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
index 96616b9..d6994f11 100644
--- a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
+++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
@@ -743,7 +743,9 @@
 }
 
 bool WaylandBufferManagerHost::SupportsAcquireFence() const {
-  return !!connection_->linux_explicit_synchronization_v1();
+  // TODO(fangzhoug@): Re-enable usage of linux_explicit_synchronization once
+  // crbug.com/1144179 is fixed.
+  return !!connection_->linux_explicit_synchronization_v1() && false;
 }
 
 void WaylandBufferManagerHost::SetWaylandBufferManagerGpu(
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
index f54d77d..b975f8c 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_auralinux.cc
@@ -51,7 +51,7 @@
 // Return the toplevel widget ancestor of |widget|, including widgets of
 // parents of transient windows.
 Widget* GetToplevelWidgetIncludingTransientWindows(Widget* widget) {
-  widget = widget = widget->GetTopLevelWidget();
+  widget = widget->GetTopLevelWidget();
   if (Widget* parent_widget = GetWidgetOfParentWindowIncludingTransient(widget))
     return GetToplevelWidgetIncludingTransientWindows(parent_widget);
   return widget;
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn b/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn
index 9f6f29e..b328eed 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/BUILD.gn
@@ -113,6 +113,7 @@
     ":profile_discovery_list_item",
     ":profile_discovery_list_page",
     ":subflow_behavior",
+    "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior",
   ]
 }
@@ -330,6 +331,7 @@
     ":profile_discovery_list_page.m",
     ":subflow_behavior.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
   extra_deps = [ ":esim_flow_ui_module" ]
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/esim_flow_ui.html b/ui/webui/resources/cr_components/chromeos/cellular_setup/esim_flow_ui.html
index 3ec9ff57..3b7d681 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/esim_flow_ui.html
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/esim_flow_ui.html
@@ -4,6 +4,7 @@
 <link rel="import" href="subflow_behavior.html">
 <link rel="import" href="cellular_types.html">
 <link rel="import" href="cellular_setup_delegate.html">
+<link rel="import" href="chrome://resources/html/assert.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html">
 <link rel="import" href="setup_loading_page.html">
 <link rel="import" href="activation_code_page.html">
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/esim_flow_ui.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/esim_flow_ui.js
index f757fcd..ddac58bc 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/esim_flow_ui.js
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/esim_flow_ui.js
@@ -7,9 +7,19 @@
   /* #export */ const ESimPageName = {
     PROFILE_LOADING: 'profileLoadingPage',
     PROFILE_DISCOVERY: 'profileDiscoveryPage',
-    ESIM: 'activationCodePage',
+    ACTIVATION_CODE: 'activationCodePage',
     FINAL: 'finalPage',
   };
+
+  /** @enum{string} */
+  /* #export */ const ESimUiState = {
+    PROFILE_SEARCH: 'profile-search',
+    ACTIVATION_CODE_ENTRY: 'activation-code-entry',
+    MULTI_PROFILE_SELECTION: 'multi-profile-selection',
+    SETUP_SUCCESS: 'setup-success',
+    SETUP_FAILURE: 'setup-failure',
+  };
+
   /**
    * Root element for the eSIM cellular setup flow. This element interacts with
    * the CellularSetup service to carry out the esim activation flow.
@@ -27,14 +37,22 @@
       delegate: Object,
 
       /**
+       * @type {!cellular_setup.ESimUiState}
+       * @private
+       */
+      state_: {
+        type: String,
+        value: ESimUiState.PROFILE_SEARCH,
+      },
+
+      /**
        * Element name of the current selected sub-page.
        * @type {!cellular_setup.ESimPageName}
        * @private
        */
       selectedESimPageName_: {
         type: String,
-        // TODO(crbug.com/1093185) Make initial page PROFILE_LOADING.
-        value: ESimPageName.PROFILE_DISCOVERY,
+        value: ESimPageName.PROFILE_LOADING,
       },
 
       /**
@@ -55,23 +73,158 @@
       },
     },
 
+    /**
+     * Provides an interface to the ESimManager Mojo service.
+     * @private {?chromeos.cellularSetup.mojom.ESimManagerRemote}
+     */
+    eSimManagerRemote_: null,
+
     listeners: {
       'activation-code-updated': 'onActivationCodeUpdated_',
     },
 
-    observers: ['onSelectedProfilesChanged_(selectedProfiles_.splices)'],
+    observers: [
+      'updateSelectedPage_(state_)', 'updateButtonBarState_(state_)',
+      'onSelectedProfilesChanged_(selectedProfiles_.splices)'
+    ],
+
+    /** @override */
+    created() {
+      this.eSimManagerRemote_ = cellular_setup.getESimManagerRemote();
+    },
 
     initSubflow() {
-      this.buttonState = {
-        backward: cellularSetup.ButtonState.HIDDEN,
-        cancel: this.delegate.shouldShowCancelButton() ?
-            cellularSetup.ButtonState.SHOWN_AND_ENABLED :
-            cellularSetup.ButtonState.HIDDEN,
-        done: cellularSetup.ButtonState.HIDDEN,
-        next: cellularSetup.ButtonState.HIDDEN,
-        tryAgain: cellularSetup.ButtonState.HIDDEN,
-        skipDiscovery: cellularSetup.ButtonState.SHOWN_AND_ENABLED,
-      };
+      this.fetchProfiles_();
+    },
+
+    /** @private */
+    fetchProfiles_() {
+      let euicc;
+      this.eSimManagerRemote_.getAvailableEuiccs()
+          .then(response => {
+            // TODO(crbug.com/1093185) User should have at least 1 EUICC or
+            // we shouldn't have gotten to this flow. Add check for this in
+            // cellular_setup.
+            euicc = response.euiccs[0];
+            return euicc.requestPendingProfiles();
+          })
+          .then(response => {
+            if (response.result ===
+                chromeos.cellularSetup.mojom.ESimOperationResult.kFailure) {
+              console.error('Error requesting pending profiles: ' + response);
+            }
+            return euicc.getProfileList();
+          })
+          .then(response => {
+            return this.filterForPendingProfiles_(response.profiles);
+          })
+          .then(profiles => {
+            switch (profiles.length) {
+              case 0:
+                this.state_ = ESimUiState.ACTIVATION_CODE_ENTRY;
+                break;
+              case 1:
+                // TODO(crbug.com/1093185) Install the
+                // profile. Handle error state. Handle
+                // confirmation code if needed.
+                this.state_ = ESimUiState.SETUP_SUCCESS;
+                break;
+              default:
+                // TODO(crbug.com/1093185) Populate the profile discovery with
+                // profiles.
+                this.state_ = ESimUiState.MULTI_PROFILE_SELECTION;
+                break;
+            }
+          });
+    },
+
+    /**
+     * @private
+     * @param {!Array<!chromeos.cellularSetup.mojom.ESimProfileRemote>} profiles
+     * @return {!Promise<Array<!chromeos.cellularSetup.mojom.ESimProfileRemote>>}
+     */
+    filterForPendingProfiles_(profiles) {
+      const profilePromises = profiles.map(profile => {
+        return profile.getProperties().then(response => {
+          if (response.properties.state !==
+              chromeos.cellularSetup.mojom.ProfileState.kPending) {
+            return null;
+          }
+          return profile;
+        });
+      });
+      return Promise.all(profilePromises).then(profiles => {
+        return profiles.filter(profile => {
+          return profile !== null;
+        });
+      });
+    },
+
+    /** @private */
+    updateSelectedPage_() {
+      switch (this.state_) {
+        case ESimUiState.PROFILE_SEARCH:
+          this.selectedESimPageName_ = ESimPageName.PROFILE_LOADING;
+          break;
+        case ESimUiState.ACTIVATION_CODE_ENTRY:
+          this.selectedESimPageName_ = ESimPageName.ACTIVATION_CODE;
+          break;
+        case ESimUiState.MULTI_PROFILE_SELECTION:
+          this.selectedESimPageName_ = ESimPageName.PROFILE_DISCOVERY;
+          break;
+        case ESimUiState.SETUP_SUCCESS:
+          this.selectedESimPageName_ = ESimPageName.FINAL;
+          break;
+        default:
+          assertNotReached();
+          break;
+      }
+    },
+
+    /** @private */
+    updateButtonBarState_() {
+      let buttonState;
+      switch (this.state_) {
+        case ESimUiState.PROFILE_SEARCH:
+        case ESimUiState.ACTIVATION_CODE_ENTRY:
+          buttonState = {
+            backward: cellularSetup.ButtonState.SHOWN_AND_ENABLED,
+            cancel: this.delegate.shouldShowCancelButton() ?
+                cellularSetup.ButtonState.SHOWN_AND_ENABLED :
+                cellularSetup.ButtonState.HIDDEN,
+            done: cellularSetup.ButtonState.HIDDEN,
+            next: cellularSetup.ButtonState.SHOWN_BUT_DISABLED,
+            tryAgain: cellularSetup.ButtonState.HIDDEN,
+            skipDiscovery: cellularSetup.ButtonState.HIDDEN,
+          };
+          break;
+        case ESimUiState.MULTI_PROFILE_SELECTION:
+          buttonState = {
+            backward: cellularSetup.ButtonState.HIDDEN,
+            cancel: this.delegate.shouldShowCancelButton() ?
+                cellularSetup.ButtonState.SHOWN_AND_ENABLED :
+                cellularSetup.ButtonState.HIDDEN,
+            done: cellularSetup.ButtonState.HIDDEN,
+            next: cellularSetup.ButtonState.HIDDEN,
+            tryAgain: cellularSetup.ButtonState.HIDDEN,
+            skipDiscovery: cellularSetup.ButtonState.SHOWN_AND_ENABLED,
+          };
+          break;
+        case ESimUiState.SETUP_SUCCESS:
+          buttonState = {
+            backward: cellularSetup.ButtonState.HIDDEN,
+            cancel: cellularSetup.ButtonState.HIDDEN,
+            done: cellularSetup.ButtonState.SHOWN_AND_ENABLED,
+            next: cellularSetup.ButtonState.HIDDEN,
+            tryAgain: cellularSetup.ButtonState.HIDDEN,
+            skipDiscovery: cellularSetup.ButtonState.HIDDEN,
+          };
+          break;
+        default:
+          assertNotReached();
+          break;
+      }
+      this.set('buttonState', buttonState);
     },
 
     /** @private */
@@ -87,9 +240,9 @@
 
     /** @private */
     onSelectedProfilesChanged_() {
-      // TODO(crbug.com/1093185): Add navigation logic.
       if (this.selectedProfiles_.length > 0) {
         this.set('buttonState.skipDiscovery', cellularSetup.ButtonState.HIDDEN);
+        // TODO(crbug.com/1093185): Install the profiles when 'Done' is pressed.
         this.set(
             'buttonState.done', cellularSetup.ButtonState.SHOWN_AND_ENABLED);
       } else {
@@ -101,7 +254,19 @@
     },
 
     navigateForward() {
-      this.selectedESimPageName_ = ESimPageName.FINAL;
+      switch (this.state_) {
+        case ESimUiState.ACTIVATION_CODE_ENTRY:
+          // TODO(crbug.com/1093185) Install the profile. Handle error state.
+          // Handle confirmation code if needed.
+          this.state_ = ESimUiState.SETUP_SUCCESS;
+          break;
+        case ESimUiState.MULTI_PROFILE_SELECTION:
+          this.state_ = ESimUiState.ACTIVATION_CODE_ENTRY;
+          break;
+        default:
+          assertNotReached();
+          break;
+      }
     },
 
     /**
@@ -114,5 +279,5 @@
   });
 
   // #cr_define_end
-  return {ESimPageName: ESimPageName};
+  return {ESimPageName: ESimPageName, ESimUiState: ESimUiState};
 });
\ No newline at end of file
diff --git a/ui/webui/resources/cr_components/chromeos/os_cr_components.gni b/ui/webui/resources/cr_components/chromeos/os_cr_components.gni
index e2af10a..b051c4b 100644
--- a/ui/webui/resources/cr_components/chromeos/os_cr_components.gni
+++ b/ui/webui/resources/cr_components/chromeos/os_cr_components.gni
@@ -11,6 +11,7 @@
   "cellularSetup.ButtonBarState|ButtonBarState",
   "cellularSetup.CellularSetupPageName|CellularSetupPageName",
   "cellular_setup.ESimPageName|ESimPageName",
+  "cellular_setup.ESimUiState|ESimUiState",
   "cellularSetup.PSimPageName|PSimPageName",
   "cellularSetup.PSimUIState|PSimUIState",
   "cellularSetup.getTimeoutMsForPSimUIState|getTimeoutMsForPSimUIState",
diff --git a/url/scheme_host_port.cc b/url/scheme_host_port.cc
index 2e73141..006b2ee 100644
--- a/url/scheme_host_port.cc
+++ b/url/scheme_host_port.cc
@@ -242,9 +242,6 @@
     result.append(host_);
   }
 
-  if (port_ == 0)
-    return result;
-
   // Omit the port component if the port matches with the default port
   // defined for the scheme, if any.
   int default_port = DefaultPortForScheme(scheme_.data(),
diff --git a/url/scheme_host_port_unittest.cc b/url/scheme_host_port_unittest.cc
index 9f41f5b..0f3913b 100644
--- a/url/scheme_host_port_unittest.cc
+++ b/url/scheme_host_port_unittest.cc
@@ -224,6 +224,7 @@
       {"https://example.com:123/", "https://example.com:123"},
       {"file:///etc/passwd", "file://"},
       {"file://example.com/etc/passwd", "file://example.com"},
+      {"https://example.com:0/", "https://example.com:0"},
   };
 
   for (const auto& test : cases) {