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..f6e0845 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..7b97e6d 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 b634ba0..d9ef55e 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..56c23a8 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..7f9efcb
--- /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..05b0281 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 e9f16cd..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..79f8dc6 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 a03eec9..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 2d04c66..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..e229b6b 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..61df0ad 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..35ef9fa
--- /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..1b4da38
--- /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..1df7bf7 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..f6a122b 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..66fdedf 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..66fdedf 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 39669b3..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..0f76c1a 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..2980c4c 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 c810c5a..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..c1bb978 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..c8f601a 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..3fefe05 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 773d074..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 7f12d47..4d83810 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 e915a6c..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..41f8b68 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 73144f4..2410e74 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 dee3561..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..00e8194 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..5addd5c
--- /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..5addd5c
--- /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..5addd5c
--- /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..356d0d2
--- /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..2ee8508 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 6414c65..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..e0e1f27 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 c90bb54..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..9e77c9e 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 95d6a41..99200b3 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..7e936b3 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 c1ebe5a..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 a4ddb8c..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..e6ac317
--- /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..5cafa3f 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..b135e43 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 1a0288c..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 ba6f0a3..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..81af39f 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 90ba34e..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 ba0845b..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 c725348..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 2c8f902..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..f357afe 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 7ed4c75..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 482213b..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 8e1ce21..3fafcf5 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 a30f446..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 2f01472..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..181cc5b 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 3f1b513..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..28bc273
--- /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..3c22d77 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 f2b9df7..5008ae5 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..281dbd4 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..58aa8ec 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 7e63a64..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..a9bbeef 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 8167ab6..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 de8441c..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 a910787..50d395c 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 0882d02..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..6742d22 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 24ab010..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 7504c81d..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..b630ab5 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 6010e29..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..6678365 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 8a19cb0..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..1d47549 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..d29057f 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 b9e232c..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 a6be885..d0b3bc3 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 b83de7b..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..72b5741 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..417f9d1 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 8317abe..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 4da73d3..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..255407e
--- /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 f2c0617..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 de8755c..7e56d38 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..311715e 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..c5e2399 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..4f5c9dd 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..104f947 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..01c78d6 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..bbd07be 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..6eb6475 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..fcee5db 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 e2c4851..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..cb933df 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..57ae617 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..e251c69 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..0c5e7e9 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..f5696db 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..0b8ba06 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