diff --git a/.gn b/.gn
index 5356717..5a11496 100644
--- a/.gn
+++ b/.gn
@@ -82,6 +82,12 @@
   "//v8:cppgc_base",  # 1 error
   "//v8:v8_internal_headers",  # 11 errors
   "//v8:v8_libplatform",  # 2 errors
+
+  # After making partition_alloc a standalone library, remove partition_alloc
+  # target from the skip list, because partition_aloc will depend on its own
+  # base.
+  # partition alloc standalone library bug is https://crbug.com/1151236.
+  "//base/allocator/partition_allocator:partition_alloc",  # 292 errors
 ]
 
 # These are the list of GN files that run exec_script. This whitelist exists
diff --git a/DEPS b/DEPS
index 9904bed..df91280 100644
--- a/DEPS
+++ b/DEPS
@@ -253,7 +253,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'ae5984082b5e7aa4e7c4c8f9201f83bca067f66e',
+  'skia_revision': '371b3a68b254b89536efbf3a059fa128a4aa5156',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -261,7 +261,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'cbbf5b5dafeda4191b0f41914127b4ccf14d7a5b',
+  'angle_revision': '45543295b75842b499c137a9ed3a727e818ef185',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -328,7 +328,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '393848420284a7c004748a4ec078c51605337cbd',
+  'devtools_frontend_revision': '23ad1b50d79b1545d2b68b2157536e796de89716',
   # 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.
@@ -724,16 +724,16 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '326bd0e693eef75d6b10294e6acbfd3bfed3636b',
+    'url': Var('chromium_git') + '/website.git' + '@' + '9e03811dcd09e0b382264f94108f4b3840cd7fbd',
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '0c6bc2b507d0efe5091094099086de7b9035e924',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'aaf6cd0daad5e447754d89141fb75a2a0c4cee9a',
       'condition': 'checkout_ios',
   },
 
     'src/ios/third_party/edo/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + 'f7dea1a5bdc745493aeffece692a4883e85c0e78',
+      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + '727e556705278598fce683522beedbb9946bfda0',
       'condition': 'checkout_ios',
   },
 
@@ -1814,7 +1814,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ca7a17a9c855382eae3465f5a846679c5273d47d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0a380bc2473917740e92bdd1e1fb56eb57873c9d',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/ambient/test/test_ambient_client.h b/ash/ambient/test/test_ambient_client.h
index 9056168..8e81885 100644
--- a/ash/ambient/test/test_ambient_client.h
+++ b/ash/ambient/test/test_ambient_client.h
@@ -28,6 +28,7 @@
 
   // AmbientClient:
   bool IsAmbientModeAllowed() override;
+  void SetAmbientModeAllowedForTesting(bool allowed) override {}
   void RequestAccessToken(GetAccessTokenCallback callback) override;
   void DownloadImage(const std::string& url,
                      ash::ImageDownloader::DownloadCallback callback) override;
diff --git a/ash/components/arc/compat_mode/compat_mode_button_controller.cc b/ash/components/arc/compat_mode/compat_mode_button_controller.cc
index 5a456cf..418e0e8a 100644
--- a/ash/components/arc/compat_mode/compat_mode_button_controller.cc
+++ b/ash/components/arc/compat_mode/compat_mode_button_controller.cc
@@ -168,6 +168,8 @@
       frame_view->GetHeaderView()->GetFrameHeader()->GetCenterButton();
   if (!compat_mode_button || !compat_mode_button->GetEnabled())
     return;
+  if (resize_toggle_menu_ && resize_toggle_menu_->IsBubbleShown())
+    return;
   resize_toggle_menu_.reset();
   resize_toggle_menu_ =
       std::make_unique<ResizeToggleMenu>(frame_view->frame(), pref_delegate);
diff --git a/ash/components/arc/compat_mode/resize_toggle_menu.cc b/ash/components/arc/compat_mode/resize_toggle_menu.cc
index 1d7fa04..ad0153c 100644
--- a/ash/components/arc/compat_mode/resize_toggle_menu.cc
+++ b/ash/components/arc/compat_mode/resize_toggle_menu.cc
@@ -49,8 +49,14 @@
       frame->SetCornerRadius(corner_radius_);
   }
 
+  base::WeakPtr<RoundedCornerBubbleDialogDelegateView> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
  private:
   const int corner_radius_;
+  base::WeakPtrFactory<RoundedCornerBubbleDialogDelegateView> weak_factory_{
+      this};
 };
 
 }  // namespace
@@ -230,6 +236,7 @@
 
   auto delegate_view =
       std::make_unique<RoundedCornerBubbleDialogDelegateView>(kCornerRadius);
+  bubble_view_ = delegate_view->GetWeakPtr();
 
   // Setup delegate.
   delegate_view->SetArrow(views::BubbleBorder::Arrow::TOP_CENTER);
@@ -301,6 +308,10 @@
       FROM_HERE, auto_close_closure_.callback(), kAutoCloseDelay);
 }
 
+bool ResizeToggleMenu::IsBubbleShown() const {
+  return bubble_view_ && bubble_view_->GetWidget();
+}
+
 void ResizeToggleMenu::CloseBubble() {
   if (!bubble_widget_ || bubble_widget_->IsClosed())
     return;
diff --git a/ash/components/arc/compat_mode/resize_toggle_menu.h b/ash/components/arc/compat_mode/resize_toggle_menu.h
index b3eb77b..76b4bea5 100644
--- a/ash/components/arc/compat_mode/resize_toggle_menu.h
+++ b/ash/components/arc/compat_mode/resize_toggle_menu.h
@@ -79,6 +79,8 @@
                                intptr_t old) override;
   void OnWindowDestroying(aura::Window* window) override;
 
+  bool IsBubbleShown() const;
+
  private:
   friend class ResizeToggleMenuTest;
 
@@ -88,6 +90,8 @@
 
   gfx::Rect GetAnchorRect() const;
 
+  base::WeakPtr<views::BubbleDialogDelegateView> bubble_view_;
+
   std::unique_ptr<views::BubbleDialogDelegateView> MakeBubbleDelegateView(
       views::Widget* parent,
       gfx::Rect anchor_rect,
@@ -106,8 +110,9 @@
 
   base::CancelableOnceClosure auto_close_closure_;
 
-  // Store only for testing.
   views::Widget* bubble_widget_{nullptr};
+
+  // Store only for testing.
   MenuButtonView* phone_button_{nullptr};
   MenuButtonView* tablet_button_{nullptr};
   MenuButtonView* resizable_button_{nullptr};
diff --git a/ash/components/arc/compat_mode/resize_toggle_menu_unittest.cc b/ash/components/arc/compat_mode/resize_toggle_menu_unittest.cc
index 69ca134..f380049 100644
--- a/ash/components/arc/compat_mode/resize_toggle_menu_unittest.cc
+++ b/ash/components/arc/compat_mode/resize_toggle_menu_unittest.cc
@@ -61,7 +61,10 @@
     SyncResizeLockPropertyWithMojoState(widget());
   }
 
+  void CloseBubble() { resize_toggle_menu_->CloseBubble(); }
+
   views::Widget* widget() { return widget_.get(); }
+  ResizeToggleMenu* resize_toggle_menu() { return resize_toggle_menu_.get(); }
 
  private:
   views::Button* GetButtonByCommandId(ResizeCompatMode command_id) {
@@ -265,4 +268,13 @@
   widget()->SetFullscreen(false);
 }
 
+// Test that IsBubbleOpen returns the correct state of the bubble
+TEST_F(ResizeToggleMenuTest, TestIsBubbleShown) {
+  EXPECT_TRUE(IsMenuRunning());
+  EXPECT_TRUE(resize_toggle_menu()->IsBubbleShown());
+  CloseBubble();
+  RunPendingMessages();
+  EXPECT_FALSE(resize_toggle_menu()->IsBubbleShown());
+}
+
 }  // namespace arc
diff --git a/ash/components/cryptohome/OWNERS b/ash/components/cryptohome/OWNERS
index e1627725..911c45d4 100644
--- a/ash/components/cryptohome/OWNERS
+++ b/ash/components/cryptohome/OWNERS
@@ -1 +1,2 @@
+antrim@chromium.org
 hashimoto@chromium.org
diff --git a/ash/display/overscan_calibrator.cc b/ash/display/overscan_calibrator.cc
index afb834e6..9b0a377 100644
--- a/ash/display/overscan_calibrator.cc
+++ b/ash/display/overscan_calibrator.cc
@@ -63,7 +63,7 @@
   gfx::Transform move_transform;
   move_transform.Translate(x_offset, y_offset);
   rotate_transform.ConcatTransform(move_transform);
-  base_path.transform(SkMatrix(rotate_transform.matrix()), &path);
+  base_path.transform(rotate_transform.matrix().asM33(), &path);
 
   canvas->DrawPath(path, content_flags);
   canvas->DrawPath(path, border_flags);
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc
index 7d60498e..99b7af0 100644
--- a/ash/drag_drop/drag_drop_controller_unittest.cc
+++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -137,12 +137,6 @@
 
   void OnDragExited() override { num_drag_exits_++; }
 
-  DragOperation OnPerformDrop(const ui::DropTargetEvent& event) override {
-    DragOperation output_drag_op = DragOperation::kNone;
-    PerformDrop(event, output_drag_op);
-    return output_drag_op;
-  }
-
   DropCallback GetDropCallback(const ui::DropTargetEvent& event) override {
     return base::BindOnce(&DragTestView::PerformDrop, base::Unretained(this));
   }
@@ -310,15 +304,6 @@
                 State::kDragUpdateInvoked == state_);
     state_ = State::kDragExitInvoked;
   }
-  DragOperation OnPerformDrop(
-      const ui::DropTargetEvent& event,
-      std::unique_ptr<ui::OSExchangeData> data) override {
-    DragOperation output_drag_op = DragOperation::kNone;
-    PerformDrop(std::move(data), output_drag_op);
-
-    return output_drag_op;
-  }
-
   DropCallback GetDropCallback(const ui::DropTargetEvent& event) override {
     return base::BindOnce(&EventTargetTestDelegate::PerformDrop,
                           base::Unretained(this));
diff --git a/ash/drag_drop/drag_drop_unittest.cc b/ash/drag_drop/drag_drop_unittest.cc
index 043feea..53d042e 100644
--- a/ash/drag_drop/drag_drop_unittest.cc
+++ b/ash/drag_drop/drag_drop_unittest.cc
@@ -63,13 +63,6 @@
   int OnDragUpdated(const ui::DropTargetEvent& event) override {
     return ui::DragDropTypes::DRAG_MOVE;
   }
-  ui::mojom::DragOperation OnPerformDrop(
-      const ui::DropTargetEvent& event) override {
-    ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
-    PerformDrop(event, output_drag_op);
-    return output_drag_op;
-  }
-
   DropCallback GetDropCallback(const ui::DropTargetEvent& event) override {
     return base::BindOnce(&TargetView::PerformDrop, base::Unretained(this));
   }
diff --git a/ash/fast_ink/view_tree_host_root_view.cc b/ash/fast_ink/view_tree_host_root_view.cc
index b787339..d8714ba 100644
--- a/ash/fast_ink/view_tree_host_root_view.cc
+++ b/ash/fast_ink/view_tree_host_root_view.cc
@@ -344,7 +344,7 @@
   int stride = resource->gpu_memory_buffer->stride(0);
   std::unique_ptr<SkCanvas> canvas =
       SkCanvas::MakeRasterDirect(info, data, stride);
-  canvas->setMatrix(static_cast<SkMatrix>(rotate_transform_.matrix()));
+  canvas->setMatrix(rotate_transform_.matrix().asM33());
   display_item_list->Raster(canvas.get());
 
   {
diff --git a/ash/public/cpp/ambient/ambient_client.h b/ash/public/cpp/ambient/ambient_client.h
index ba5cd368..afce249 100644
--- a/ash/public/cpp/ambient/ambient_client.h
+++ b/ash/public/cpp/ambient/ambient_client.h
@@ -41,6 +41,8 @@
   // Return whether the ambient mode is allowed for the user.
   virtual bool IsAmbientModeAllowed() = 0;
 
+  virtual void SetAmbientModeAllowedForTesting(bool allowed) = 0;
+
   // Return the gaia and access token associated with the active user's profile.
   virtual void RequestAccessToken(GetAccessTokenCallback callback) = 0;
 
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index a3776d8..934e52f 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -5,6 +5,7 @@
 #include "ash/webui/personalization_app/personalization_app_ui.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/public/cpp/ambient/ambient_client.h"
 #include "ash/webui/grit/ash_personalization_app_resources.h"
 #include "ash/webui/grit/ash_personalization_app_resources_map.h"
 #include "ash/webui/personalization_app/personalization_app_ambient_provider.h"
@@ -33,6 +34,11 @@
   return GURL(kGooglePhotosURL);
 }
 
+bool IsAmbientModeAllowed() {
+  return ash::AmbientClient::Get() &&
+         ash::AmbientClient::Get()->IsAmbientModeAllowed();
+}
+
 void AddResources(content::WebUIDataSource* source) {
   source->AddResourcePath("", IDR_ASH_PERSONALIZATION_APP_TRUSTED_INDEX_HTML);
   source->AddResourcePaths(base::make_span(
@@ -192,6 +198,8 @@
 
   source->AddBoolean("isDarkLightModeEnabled",
                      features::IsDarkLightModeEnabled());
+
+  source->AddBoolean("isAmbientModeAllowed", IsAmbientModeAllowed());
 }
 
 }  // namespace
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_main_element.html b/ash/webui/personalization_app/resources/trusted/personalization_main_element.html
index 9a7d01a..9a60406 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_main_element.html
+++ b/ash/webui/personalization_app/resources/trusted/personalization_main_element.html
@@ -62,13 +62,16 @@
         <personalization-theme></personalization-theme>
       </template>
     </wallpaper-preview>
-    <ambient-preview main-page>
-      <div id="ambientLabel">
-        <p>$i18n{screensaverLabel}</p>
-        <cr-button id="ambientSubpageLink" on-click="onClickAmbientSubpageLink_">
-          <iron-icon icon="cr:chevron-right" aria-hidden="true"></iron-icon>
-        </cr-button>
-      </div>
-    </ambient-preview>
+    <template is="dom-if" if="[[isAmbientModeAllowed_()]]">
+      <ambient-preview main-page>
+        <div id="ambientLabel">
+          <p>$i18n{screensaverLabel}</p>
+          <cr-button id="ambientSubpageLink"
+                on-click="onClickAmbientSubpageLink_">
+            <iron-icon icon="cr:chevron-right" aria-hidden="true"></iron-icon>
+          </cr-button>
+        </div>
+      </ambient-preview>
+    </template>
   </div>
 </div>
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_main_element.ts b/ash/webui/personalization_app/resources/trusted/personalization_main_element.ts
index e039007d..1a4a898 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_main_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_main_element.ts
@@ -9,7 +9,7 @@
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {Paths, PersonalizationRouter} from './personalization_router_element.js';
+import {isAmbientModeAllowed, Paths, PersonalizationRouter} from './personalization_router_element.js';
 import {WithPersonalizationStore} from './personalization_store.js';
 
 export class PersonalizationMain extends WithPersonalizationStore {
@@ -34,6 +34,10 @@
     return loadTimeData.getBoolean('isDarkLightModeEnabled');
   }
 
+  private isAmbientModeAllowed_(): boolean {
+    return isAmbientModeAllowed();
+  }
+
   private onClickUserSubpageLink_() {
     PersonalizationRouter.instance().goToRoute(Paths.User);
   }
diff --git a/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts b/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts
index 011852f..a4a4522 100644
--- a/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/personalization_router_element.ts
@@ -29,6 +29,10 @@
   return loadTimeData.getBoolean('isPersonalizationHubEnabled');
 }
 
+export function isAmbientModeAllowed(): boolean {
+  return loadTimeData.getBoolean('isAmbientModeAllowed');
+}
+
 export class PersonalizationRouter extends PolymerElement {
   static get is() {
     return 'personalization-router';
@@ -42,6 +46,7 @@
     return {
       path_: {
         type: String,
+        observer: 'onPathChanged_',
       },
 
       query_: {
@@ -119,11 +124,19 @@
   }
 
   private shouldShowRootPage_(path: string|null): boolean {
-    return isPersonalizationHubEnabled() && path === Paths.Root;
+    if (!isPersonalizationHubEnabled()) {
+      return false;
+    }
+
+    // If the ambient mode is not allowed, will not show Ambient/AmbientAlbums
+    // subpages.
+    return (path === Paths.Root) ||
+        (!!path && path.startsWith(Paths.Ambient) && !isAmbientModeAllowed());
   }
 
   private shouldShowAmbientSubpage_(path: string|null): boolean {
-    return isPersonalizationHubEnabled() && !!path?.startsWith(Paths.Ambient);
+    return isPersonalizationHubEnabled() && !!path?.startsWith(Paths.Ambient) &&
+        isAmbientModeAllowed();
   }
 
   private shouldShowUserSubpage_(path: string|null): boolean {
@@ -137,6 +150,18 @@
   private shouldShowBreadcrumb_(path: string|null): boolean {
     return path !== Paths.Root;
   }
+
+  /**
+   * When navigating to Ambient/AmbientAlbums subpages, but the ambient mode is
+   * not allowed, will not show Ambient/AmbientAlbums subpages. Reset path to
+   * root in this case.
+   */
+  private onPathChanged_(path: string|null) {
+    if (!!path && path.startsWith(Paths.Ambient) && !isAmbientModeAllowed()) {
+      // Reset the path to root.
+      this.setProperties({path_: Paths.Root, queryParams_: {}});
+    }
+  }
 }
 
 customElements.define(PersonalizationRouter.is, PersonalizationRouter);
diff --git a/ash/webui/personalization_app/test/personalization_app_browsertest.js b/ash/webui/personalization_app/test/personalization_app_browsertest.js
index caef8d9..83221918 100644
--- a/ash/webui/personalization_app/test/personalization_app_browsertest.js
+++ b/ash/webui/personalization_app/test/personalization_app_browsertest.js
@@ -9,6 +9,7 @@
 GEN('#include "ash/webui/personalization_app/test/personalization_app_browsertest_fixture.h"');
 
 GEN('#include "ash/constants/ash_features.h"');
+GEN('#include "ash/public/cpp/ambient/ambient_client.h"');
 GEN('#include "chromeos/constants/chromeos_features.h"');
 GEN('#include "content/public/test/browser_test.h"');
 
@@ -91,29 +92,8 @@
   const wallpaperPreview = document.querySelector('personalization-router')
                                .shadowRoot.querySelector('personalization-main')
                                .shadowRoot.querySelector('wallpaper-preview');
-  const ambientPreview = document.querySelector('personalization-router')
-                             .shadowRoot.querySelector('personalization-main')
-                             .shadowRoot.querySelector('ambient-preview');
   assertTrue(!!userPreview);
   assertTrue(!!wallpaperPreview);
-  assertTrue(!!ambientPreview);
-  testDone();
-});
-
-TEST_F('PersonalizationAppBrowserTest', 'ShowsAmbientPreview', () => {
-  const preview = document.querySelector('personalization-router')
-                      .shadowRoot.querySelector('personalization-main')
-                      .shadowRoot.querySelector('ambient-preview');
-  assertTrue(!!preview);
-  testDone();
-});
-
-TEST_F('PersonalizationAppBrowserTest', 'ShowsAmbientSubpageLink', () => {
-  const ambientSubpageLink =
-      document.querySelector('personalization-router')
-          .shadowRoot.querySelector('personalization-main')
-          .shadowRoot.querySelector('#ambientSubpageLink');
-  assertTrue(!!ambientSubpageLink);
   testDone();
 });
 
@@ -146,6 +126,74 @@
   testDone();
 });
 
+class PersonalizationAppAmbientModeAllowedBrowserTest extends
+    PersonalizationAppBrowserTest {
+  /** @override */
+  get testGenPreamble() {
+    return () => {
+      GEN('ash::AmbientClient::Get()->SetAmbientModeAllowedForTesting(true);');
+    };
+  }
+}
+
+this[PersonalizationAppAmbientModeAllowedBrowserTest.name] =
+    PersonalizationAppAmbientModeAllowedBrowserTest;
+
+TEST_F(
+    'PersonalizationAppAmbientModeAllowedBrowserTest', 'ShowsAmbientPreview',
+    () => {
+      const preview = document.querySelector('personalization-router')
+                          .shadowRoot.querySelector('personalization-main')
+                          .shadowRoot.querySelector('ambient-preview');
+      assertTrue(!!preview);
+      testDone();
+    });
+
+TEST_F(
+    'PersonalizationAppAmbientModeAllowedBrowserTest',
+    'ShowsAmbientSubpageLink', () => {
+      const ambientSubpageLink =
+          document.querySelector('personalization-router')
+              .shadowRoot.querySelector('personalization-main')
+              .shadowRoot.querySelector('#ambientSubpageLink');
+      assertTrue(!!ambientSubpageLink);
+      testDone();
+    });
+
+class PersonalizationAppAmbientModeDisllowedBrowserTest extends
+    PersonalizationAppBrowserTest {
+  /** @override */
+  get testGenPreamble() {
+    return () => {
+      GEN('ash::AmbientClient::Get()->SetAmbientModeAllowedForTesting(false);');
+    };
+  }
+}
+
+this[PersonalizationAppAmbientModeDisllowedBrowserTest.name] =
+    PersonalizationAppAmbientModeDisllowedBrowserTest;
+
+TEST_F(
+    'PersonalizationAppAmbientModeDisllowedBrowserTest',
+    'NotShowAmbientPreview', () => {
+      const preview = document.querySelector('personalization-router')
+                          .shadowRoot.querySelector('personalization-main')
+                          .shadowRoot.querySelector('ambient-preview');
+      assertFalse(!!preview);
+      testDone();
+    });
+
+TEST_F(
+    'PersonalizationAppAmbientModeDisllowedBrowserTest',
+    'NotShowAmbientSubpageLink', () => {
+      const ambientSubpageLink =
+          document.querySelector('personalization-router')
+              .shadowRoot.querySelector('personalization-main')
+              .shadowRoot.querySelector('#ambientSubpageLink');
+      assertFalse(!!ambientSubpageLink);
+      testDone();
+    });
+
 class WallpaperSubpageBrowserTest extends PersonalizationAppBrowserTest {
   /** @override */
   get browsePreload() {
diff --git a/ash/webui/shimless_rma/resources/onboarding_update_page.html b/ash/webui/shimless_rma/resources/onboarding_update_page.html
index cd6b793..9ae97d7 100644
--- a/ash/webui/shimless_rma/resources/onboarding_update_page.html
+++ b/ash/webui/shimless_rma/resources/onboarding_update_page.html
@@ -2,38 +2,45 @@
   #performUpdateButton {
     border-color: var(--google-blue-600);
   }
-</style>
 
+  #updateStatusDiv {
+    align-items: center;
+    display: flex;
+  }
+</style>
 <base-page>
   <div slot="left-pane">
     <h1>[[i18n('osUpdateTitleText')]]</h1>
-    <div hidden$="[[isCompliant_]]" class="instructions">
-      <div inner-h-t-m-l="[[verificationFailedMessage_]]"></div>
-      <div>[[i18n('osUpdateUnqualifiedComponentsBottomText')]]</div>
+    <div id="updateInstructionsDiv" hidden$="[[updateInProgress_]]">
+      <div hidden$="[[isCompliant_]]" class="instructions">
+        <div inner-h-t-m-l="[[verificationFailedMessage_]]"></div>
+        <div>[[i18n('osUpdateUnqualifiedComponentsBottomText')]]</div>
+      </div>
+      <div hidden$="[[!isCompliant_]]" class="instructions">
+        [[i18n('osUpdateOutOfDateDescriptionText')]]
+      </div>
+      <div id="versionInfo">
+        <iron-icon id="updateIcon" class="small-icon" icon="shimless-icon:info">
+        </iron-icon>
+        <span class="instructions">[[currentVersionText_]]</span>
+      </div>
+      <p id="networkUnavailable" hidden$="[[networkAvailable]]">
+        [[i18n('onboardingUpdateConnectToInternet')]]
+      </p>
+      <cr-button id="performUpdateButton" disabled="[[allButtonsDisabled]]"
+          on-click="onUpdateButtonClicked_">
+          [[updateVersionButtonLabel_]]
+        <iron-icon id="restartIcon" icon="shimless-icon:update"
+            class="small-icon">
+        </iron-icon>
+      </cr-button>
     </div>
-    <div hidden$="[[!isCompliant_]]" class="instructions">
-      [[i18n('osUpdateOutOfDateDescriptionText')]]
-    </div>
-    <div id="versionInfo">
-      <iron-icon id="updateIcon" class="small-icon" icon="shimless-icon:info">
-      </iron-icon>
-      <span class="instructions">[[currentVersionText_]]</span>
-    </div>
-    <div id="progressMessage">
-      <paper-spinner-lite hidden$="[[!updateInProgress_]]" active>
+    <div id="updateStatusDiv" class="instructions"
+        hidden$="[[!updateInProgress_]]">
+       <paper-spinner-lite class="small-spinner" active>
       </paper-spinner-lite>
-      <div class="instructions">[[updateProgressMessage_]]</div>
+      <span>[[i18n('updatingOsVersionText')]]</span>
     </div>
-    <p id="networkUnavailable" hidden$="[[networkAvailable]]">
-      [[i18n('onboardingUpdateConnectToInternet')]]
-    </p>
-    <cr-button id="performUpdateButton" disabled="[[allButtonsDisabled]]"
-        on-click="onUpdateButtonClicked_">
-        [[updateVersionButtonLabel_]]
-      <iron-icon id="restartIcon" icon="shimless-icon:update"
-          class="small-icon">
-      </iron-icon>
-    </cr-button>
   </div>
   <div slot="right-pane">
     <div class="illustration-wrapper">
diff --git a/ash/webui/shimless_rma/resources/onboarding_update_page.js b/ash/webui/shimless_rma/resources/onboarding_update_page.js
index 1fc815cf..e6538a6 100644
--- a/ash/webui/shimless_rma/resources/onboarding_update_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_update_page.js
@@ -77,12 +77,6 @@
         value: '',
       },
 
-      /** @protected */
-      updateProgressMessage_: {
-        type: String,
-        value: '',
-      },
-
       /**
        * TODO(joonbug): populate this and make private.
        */
@@ -179,7 +173,6 @@
     this.updateInProgress_ = true;
     this.shimlessRmaService_.updateOs().then((res) => {
       if (!res.updateStarted) {
-        this.updateProgressMessage_ = this.i18n('osUpdateFailedToStartText');
         this.updateInProgress_ = false;
       }
     });
@@ -200,15 +193,13 @@
     if (!this.updateInProgress_) {
       return;
     }
+
     if (operation === OsUpdateOperation.kIdle ||
         operation === OsUpdateOperation.kReportingErrorEvent ||
         operation === OsUpdateOperation.kNeedPermissionToUpdate ||
         operation === OsUpdateOperation.kDisabled) {
       this.updateInProgress_ = false;
     }
-    this.updateProgressMessage_ = this.i18n(
-        'onboardingUpdateProgress', this.i18n(operationNameKeys[operation]),
-        Math.round(progress * 100));
   }
 
   /**
diff --git a/ash/webui/shimless_rma/resources/shimless_rma_shared_css.html b/ash/webui/shimless_rma/resources/shimless_rma_shared_css.html
index 346cb75..03a47b36 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma_shared_css.html
+++ b/ash/webui/shimless_rma/resources/shimless_rma_shared_css.html
@@ -41,6 +41,12 @@
       width: 286px;
     }
 
+    .small-spinner {
+      height: 20px;
+      padding-inline-end: 8px;
+      width: 20px;
+    }
+
     h1 {
       color: var(--shimless-title-text-color);
       font-family: var(--shimless-title-font-family);
diff --git a/ash/webui/shimless_rma/shimless_rma.cc b/ash/webui/shimless_rma/shimless_rma.cc
index 141897f1..8559b1ca 100644
--- a/ash/webui/shimless_rma/shimless_rma.cc
+++ b/ash/webui/shimless_rma/shimless_rma.cc
@@ -130,6 +130,7 @@
        IDS_SHIMLESS_RMA_CURRENT_VERSION_UP_TO_DATE},
       {"updateVersionRestartLabel",
        IDS_SHIMLESS_RMA_UPDATE_VERSION_AND_RESTART},
+      {"updatingOsVersionText", IDS_SHIMLESS_RMA_UPDATING_OS_VERSION},
       // Choose WP disable method page
       {"chooseWpDisableMethodPageTitleText",
        IDS_SHIMLESS_RMA_CHOOSE_WP_DISABLE_METHOD_PAGE_TITLE},
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 4b4eb780..46d1d5c 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -117,22 +117,6 @@
   ]
 }
 
-config("memory_tagging") {
-  if (current_cpu == "arm64" && is_clang &&
-      (is_linux || is_chromeos || is_android || is_fuchsia)) {
-    # base/ has access to the MTE intrinsics because it needs to use them,
-    # but they're not backwards compatible. Use base::CPU::has_mte()
-    # beforehand to confirm or use indirect functions (ifuncs) to select
-    # an MTE-specific implementation at dynamic link-time.
-    cflags = [
-      "-Xclang",
-      "-target-feature",
-      "-Xclang",
-      "+mte",
-    ]
-  }
-}
-
 config("base_implementation") {
   defines = [ "BASE_IMPLEMENTATION" ]
   configs = [ "//build/config/compiler:wexit_time_destructors" ]
@@ -1418,7 +1402,6 @@
 
   configs += [
     ":base_implementation",
-    ":memory_tagging",
     "//build/config:precompiled_headers",
     "//build/config/compiler:wglobal_constructors",
   ]
@@ -1502,10 +1485,14 @@
     sources += [
       "allocator/allocator_shim.cc",
       "allocator/allocator_shim.h",
-      "allocator/allocator_shim_default_dispatch_to_partition_alloc.cc",
-      "allocator/allocator_shim_default_dispatch_to_partition_alloc.h",
       "allocator/allocator_shim_internals.h",
     ]
+    if (use_partition_alloc) {
+      sources += [
+        "allocator/allocator_shim_default_dispatch_to_partition_alloc.cc",
+        "allocator/allocator_shim_default_dispatch_to_partition_alloc.h",
+      ]
+    }
     if (is_android) {
       sources += [
         "allocator/allocator_shim_override_cpp_symbols.h",
@@ -2002,127 +1989,14 @@
         "allocator/partition_alloc_features.h",
         "allocator/partition_alloc_support.cc",
         "allocator/partition_alloc_support.h",
-        "allocator/partition_allocator/address_pool_manager.cc",
-        "allocator/partition_allocator/address_pool_manager.h",
-        "allocator/partition_allocator/address_pool_manager_bitmap.cc",
-        "allocator/partition_allocator/address_pool_manager_bitmap.h",
-        "allocator/partition_allocator/address_pool_manager_types.h",
-        "allocator/partition_allocator/address_space_randomization.cc",
-        "allocator/partition_allocator/address_space_randomization.h",
-        "allocator/partition_allocator/address_space_stats.h",
-        "allocator/partition_allocator/allocation_guard.cc",
-        "allocator/partition_allocator/allocation_guard.h",
-        "allocator/partition_allocator/dangling_raw_ptr_checks.cc",
-        "allocator/partition_allocator/dangling_raw_ptr_checks.h",
-        "allocator/partition_allocator/extended_api.cc",
-        "allocator/partition_allocator/extended_api.h",
-        "allocator/partition_allocator/memory_reclaimer.cc",
-        "allocator/partition_allocator/memory_reclaimer.h",
-        "allocator/partition_allocator/oom.cc",
-        "allocator/partition_allocator/oom.h",
-        "allocator/partition_allocator/oom_callback.cc",
-        "allocator/partition_allocator/oom_callback.h",
-        "allocator/partition_allocator/page_allocator.cc",
-        "allocator/partition_allocator/page_allocator.h",
-        "allocator/partition_allocator/page_allocator_constants.h",
-        "allocator/partition_allocator/page_allocator_internal.h",
-        "allocator/partition_allocator/partition_address_space.cc",
-        "allocator/partition_allocator/partition_address_space.h",
-        "allocator/partition_allocator/partition_alloc-inl.h",
-        "allocator/partition_allocator/partition_alloc.cc",
-        "allocator/partition_allocator/partition_alloc.h",
-        "allocator/partition_allocator/partition_alloc_check.h",
-        "allocator/partition_allocator/partition_alloc_config.h",
-        "allocator/partition_allocator/partition_alloc_constants.h",
-        "allocator/partition_allocator/partition_alloc_forward.h",
-        "allocator/partition_allocator/partition_alloc_hooks.cc",
-        "allocator/partition_allocator/partition_alloc_hooks.h",
-        "allocator/partition_allocator/partition_alloc_notreached.h",
-        "allocator/partition_allocator/partition_bucket.cc",
-        "allocator/partition_allocator/partition_bucket.h",
-        "allocator/partition_allocator/partition_bucket_lookup.h",
-        "allocator/partition_allocator/partition_cookie.h",
-        "allocator/partition_allocator/partition_direct_map_extent.h",
-        "allocator/partition_allocator/partition_freelist_entry.h",
-        "allocator/partition_allocator/partition_lock.h",
-        "allocator/partition_allocator/partition_oom.cc",
-        "allocator/partition_allocator/partition_oom.h",
-        "allocator/partition_allocator/partition_page.cc",
-        "allocator/partition_allocator/partition_page.h",
-        "allocator/partition_allocator/partition_ref_count.h",
-        "allocator/partition_allocator/partition_root.cc",
-        "allocator/partition_allocator/partition_root.h",
-        "allocator/partition_allocator/partition_stats.cc",
-        "allocator/partition_allocator/partition_stats.h",
-        "allocator/partition_allocator/partition_tag.h",
-        "allocator/partition_allocator/partition_tag_bitmap.h",
-        "allocator/partition_allocator/partition_tls.h",
-        "allocator/partition_allocator/random.cc",
-        "allocator/partition_allocator/random.h",
-        "allocator/partition_allocator/reservation_offset_table.cc",
-        "allocator/partition_allocator/reservation_offset_table.h",
-        "allocator/partition_allocator/spinning_mutex.cc",
-        "allocator/partition_allocator/spinning_mutex.h",
-        "allocator/partition_allocator/starscan/logging.h",
-        "allocator/partition_allocator/starscan/metadata_allocator.cc",
-        "allocator/partition_allocator/starscan/metadata_allocator.h",
-        "allocator/partition_allocator/starscan/pcscan.cc",
-        "allocator/partition_allocator/starscan/pcscan.h",
-        "allocator/partition_allocator/starscan/pcscan_internal.cc",
-        "allocator/partition_allocator/starscan/pcscan_internal.h",
-        "allocator/partition_allocator/starscan/pcscan_scheduling.cc",
-        "allocator/partition_allocator/starscan/pcscan_scheduling.h",
-        "allocator/partition_allocator/starscan/raceful_worklist.h",
-        "allocator/partition_allocator/starscan/scan_loop.h",
-        "allocator/partition_allocator/starscan/snapshot.cc",
-        "allocator/partition_allocator/starscan/snapshot.h",
-        "allocator/partition_allocator/starscan/stack/stack.cc",
-        "allocator/partition_allocator/starscan/stack/stack.h",
-        "allocator/partition_allocator/starscan/starscan_fwd.h",
-        "allocator/partition_allocator/starscan/state_bitmap.h",
-        "allocator/partition_allocator/starscan/stats_collector.cc",
-        "allocator/partition_allocator/starscan/stats_collector.h",
-        "allocator/partition_allocator/starscan/stats_reporter.h",
-        "allocator/partition_allocator/starscan/write_protector.cc",
-        "allocator/partition_allocator/starscan/write_protector.h",
-        "allocator/partition_allocator/tagging.cc",
-        "allocator/partition_allocator/tagging.h",
-        "allocator/partition_allocator/thread_cache.cc",
-        "allocator/partition_allocator/thread_cache.h",
-        "allocator/partition_allocator/yield_processor.h",
       ]
-      if (is_win) {
-        sources += [
-          "allocator/partition_allocator/page_allocator_internals_win.h",
-          "allocator/partition_allocator/partition_tls_win.cc",
-        ]
-      } else if (is_posix) {
-        sources += [
-          "allocator/partition_allocator/page_allocator_internals_posix.cc",
-          "allocator/partition_allocator/page_allocator_internals_posix.h",
-        ]
-      } else if (is_fuchsia) {
-        sources += [
-          "allocator/partition_allocator/page_allocator_internals_fuchsia.h",
-        ]
-      }
-
-      if (current_cpu == "x64") {
-        defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
-        sources += [ "allocator/partition_allocator/starscan/stack/asm/x64/push_registers_asm.cc" ]
-      } else if (current_cpu == "x86") {
-        defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
-        sources += [ "allocator/partition_allocator/starscan/stack/asm/x86/push_registers_asm.cc" ]
-      } else if (current_cpu == "arm") {
-        defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
-        sources += [ "allocator/partition_allocator/starscan/stack/asm/arm/push_registers_asm.cc" ]
-      } else if (current_cpu == "arm64") {
-        defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
-        sources += [ "allocator/partition_allocator/starscan/stack/asm/arm64/push_registers_asm.cc" ]
-      } else {
-        # To support a trampoline for another arch, please refer to v8/src/heap/base.
-      }
     }
+
+    # Need this to pass gn check, because gn check doesn't see
+    # BUILDFLAG(USE_PARTITION_ALLOC). A linker will remove all
+    # partition_alloc code if use_partition_alloc = false because no code uses
+    # partition_alloc.
+    public_deps += [ "allocator/partition_allocator:partition_alloc" ]
   }
 
   # Windows.
@@ -3934,8 +3808,6 @@
     ]
     deps += [ "//build/rust:cxx_cppdeps" ]
   }
-
-  configs += [ ":memory_tagging" ]
 }
 
 action("build_date") {
diff --git a/base/allocator/partition_allocator/BUILD.gn b/base/allocator/partition_allocator/BUILD.gn
new file mode 100644
index 0000000..cc5bf37
--- /dev/null
+++ b/base/allocator/partition_allocator/BUILD.gn
@@ -0,0 +1,206 @@
+# Copyright (c) 2022 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//base/allocator/allocator.gni")
+
+# Add partition_alloc.gni and import it for partition_alloc configs.
+
+config("partition_alloc_implementation") {
+  # After introducing partition_alloc_export, replace BASE_IMPLEMENTATION with
+  # PARTITION_ALLOC_IMPLEMENTATION.
+  defines = [ "BASE_IMPLEMENTATION" ]
+}
+
+config("memory_tagging") {
+  if (current_cpu == "arm64" && is_clang &&
+      (is_linux || is_chromeos || is_android || is_fuchsia)) {
+    # base/ has access to the MTE intrinsics because it needs to use them,
+    # but they're not backwards compatible. Use base::CPU::has_mte()
+    # beforehand to confirm or use indirect functions (ifuncs) to select
+    # an MTE-specific implementation at dynamic link-time.
+    cflags = [
+      "-Xclang",
+      "-target-feature",
+      "-Xclang",
+      "+mte",
+    ]
+  }
+}
+
+if (is_fuchsia) {
+  config("fuchsia_sync_lib") {
+    libs = [
+      "sync",  # Used by spinning_mutex.h.
+    ]
+  }
+}
+
+# TODO(crbug.com/1151236): Add component("base") for partition_alloc
+# base, and make partition_alloc standalone library depend on it.
+
+# TODO(crbug.com/1151236): Add component("partition_alloc") to build
+# partition_alloc as a standalone_library.
+source_set("partition_alloc") {
+  sources = [
+    "address_pool_manager.cc",
+    "address_pool_manager.h",
+    "address_pool_manager_bitmap.cc",
+    "address_pool_manager_bitmap.h",
+    "address_pool_manager_types.h",
+    "address_space_randomization.cc",
+    "address_space_randomization.h",
+    "address_space_stats.h",
+    "allocation_guard.cc",
+    "allocation_guard.h",
+    "dangling_raw_ptr_checks.cc",
+    "dangling_raw_ptr_checks.h",
+    "extended_api.cc",
+    "extended_api.h",
+    "memory_reclaimer.cc",
+    "memory_reclaimer.h",
+    "oom.cc",
+    "oom.h",
+    "oom_callback.cc",
+    "oom_callback.h",
+    "page_allocator.cc",
+    "page_allocator.h",
+    "page_allocator_constants.h",
+    "page_allocator_internal.h",
+    "partition_address_space.cc",
+    "partition_address_space.h",
+    "partition_alloc-inl.h",
+    "partition_alloc.cc",
+    "partition_alloc.h",
+    "partition_alloc_check.h",
+    "partition_alloc_config.h",
+    "partition_alloc_constants.h",
+    "partition_alloc_forward.h",
+    "partition_alloc_hooks.cc",
+    "partition_alloc_hooks.h",
+    "partition_alloc_notreached.h",
+    "partition_bucket.cc",
+    "partition_bucket.h",
+    "partition_bucket_lookup.h",
+    "partition_cookie.h",
+    "partition_direct_map_extent.h",
+    "partition_freelist_entry.h",
+    "partition_lock.h",
+    "partition_oom.cc",
+    "partition_oom.h",
+    "partition_page.cc",
+    "partition_page.h",
+    "partition_ref_count.h",
+    "partition_root.cc",
+    "partition_root.h",
+    "partition_stats.cc",
+    "partition_stats.h",
+    "partition_tag.h",
+    "partition_tag_bitmap.h",
+    "partition_tls.h",
+    "random.cc",
+    "random.h",
+    "reservation_offset_table.cc",
+    "reservation_offset_table.h",
+    "spinning_mutex.cc",
+    "spinning_mutex.h",
+    "starscan/logging.h",
+    "starscan/metadata_allocator.cc",
+    "starscan/metadata_allocator.h",
+    "starscan/pcscan.cc",
+    "starscan/pcscan.h",
+    "starscan/pcscan_internal.cc",
+    "starscan/pcscan_internal.h",
+    "starscan/pcscan_scheduling.cc",
+    "starscan/pcscan_scheduling.h",
+    "starscan/raceful_worklist.h",
+    "starscan/scan_loop.h",
+    "starscan/snapshot.cc",
+    "starscan/snapshot.h",
+    "starscan/stack/stack.cc",
+    "starscan/stack/stack.h",
+    "starscan/starscan_fwd.h",
+    "starscan/state_bitmap.h",
+    "starscan/stats_collector.cc",
+    "starscan/stats_collector.h",
+    "starscan/stats_reporter.h",
+    "starscan/write_protector.cc",
+    "starscan/write_protector.h",
+    "tagging.cc",
+    "tagging.h",
+    "thread_cache.cc",
+    "thread_cache.h",
+    "yield_processor.h",
+  ]
+  defines = []
+  if (is_win) {
+    sources += [
+      "page_allocator_internals_win.h",
+      "partition_tls_win.cc",
+    ]
+  } else if (is_posix) {
+    sources += [
+      "page_allocator_internals_posix.cc",
+      "page_allocator_internals_posix.h",
+    ]
+  } else if (is_fuchsia) {
+    sources += [ "page_allocator_internals_fuchsia.h" ]
+  }
+  if (current_cpu == "x64") {
+    defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
+    sources += [ "starscan/stack/asm/x64/push_registers_asm.cc" ]
+  } else if (current_cpu == "x86") {
+    defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
+    sources += [ "starscan/stack/asm/x86/push_registers_asm.cc" ]
+  } else if (current_cpu == "arm") {
+    defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
+    sources += [ "starscan/stack/asm/arm/push_registers_asm.cc" ]
+  } else if (current_cpu == "arm64") {
+    defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
+    sources += [ "starscan/stack/asm/arm64/push_registers_asm.cc" ]
+  } else {
+    # To support a trampoline for another arch, please refer to v8/src/heap/base.
+  }
+  public_deps = [
+    "//base:debugging_buildflags",
+    "//base:logging_buildflags",
+    "//base:synchronization_buildflags",
+    "//base:tracing_buildflags",
+    "//base/allocator:buildflags",
+    "//build:branding_buildflags",
+    "//build:chromecast_buildflags",
+    "//build:chromeos_buildflags",
+    "//build/config/compiler:compiler_buildflags",
+  ]
+  deps = []
+  configs += [
+    ":partition_alloc_implementation",
+    ":memory_tagging",
+  ]
+  public_configs = []
+  if (is_android) {
+    # tagging.cc requires __arm_mte_set_* functions.
+    deps += [ "//third_party/android_ndk:cpu_features" ]
+  }
+  if (is_fuchsia) {
+    public_deps += [
+      "//third_party/fuchsia-sdk/sdk/pkg/fit",
+      "//third_party/fuchsia-sdk/sdk/pkg/sync",
+      "//third_party/fuchsia-sdk/sdk/pkg/zx",
+    ]
+
+    # Needed for users of spinning_mutex.h, which for performance reasons,
+    # contains inlined calls to `libsync` inside the header file.
+    # It appends an entry to the "libs" section of the dependent target.
+    public_configs += [ ":fuchsia_sync_lib" ]
+  }
+
+  frameworks = []
+  if (is_mac) {
+    # SecTaskGetCodeSignStatus needs:
+    frameworks += [ "Security.framework" ]
+  }
+}
+# TODO(crbug.com/1151236): After making partition_alloc a standalone library,
+# move test code here. i.e. test("partition_alloc_tests") { ... } and
+# test("partition_alloc_perftests").
diff --git a/base/allocator/partition_allocator/allocation_guard.h b/base/allocator/partition_allocator/allocation_guard.h
index c05f5974..ebbe83bf 100644
--- a/base/allocator/partition_allocator/allocation_guard.h
+++ b/base/allocator/partition_allocator/allocation_guard.h
@@ -6,6 +6,7 @@
 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ALLOCATION_GUARD_H_
 
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
+#include "base/base_export.h"
 #include "build/build_config.h"
 
 namespace partition_alloc {
@@ -13,14 +14,14 @@
 #if defined(PA_HAS_ALLOCATION_GUARD)
 
 // Disallow allocations in the scope. Does not nest.
-class ScopedDisallowAllocations {
+class BASE_EXPORT ScopedDisallowAllocations {
  public:
   ScopedDisallowAllocations();
   ~ScopedDisallowAllocations();
 };
 
 // Disallow allocations in the scope. Does not nest.
-class ScopedAllowAllocations {
+class BASE_EXPORT ScopedAllowAllocations {
  public:
   ScopedAllowAllocations();
   ~ScopedAllowAllocations();
diff --git a/base/allocator/partition_allocator/partition_alloc_check.h b/base/allocator/partition_allocator/partition_alloc_check.h
index a4aef54..b60d978 100644
--- a/base/allocator/partition_allocator/partition_alloc_check.h
+++ b/base/allocator/partition_allocator/partition_alloc_check.h
@@ -12,6 +12,7 @@
 #include "base/check.h"
 #include "base/debug/alias.h"
 #include "base/immediate_crash.h"
+#include "build/build_config.h"
 
 #define PA_STRINGIFY_IMPL(s) #s
 #define PA_STRINGIFY(s) PA_STRINGIFY_IMPL(s)
@@ -96,10 +97,19 @@
 
 #endif
 
+// alignas(16) DebugKv causes breakpad_unittests and sandbox_linux_unittests
+// failures on android-marshmallow-x86-rel because of SIGSEGV.
+#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86_FAMILY) && \
+    defined(ARCH_CPU_32_BITS)
+#define PA_DEBUGKV_ALIGN alignas(8)
+#else
+#define PA_DEBUGKV_ALIGN alignas(16)
+#endif
+
 namespace partition_alloc::internal {
 
 // Used for PA_DEBUG_DATA_ON_STACK, below.
-struct alignas(16) DebugKv {
+struct PA_DEBUGKV_ALIGN DebugKv {
   // 16 bytes object aligned on 16 bytes, to make it easier to see in crash
   // reports.
   char k[8] = {};  // Not necessarily 0-terminated.
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index 163cad5..22edeec 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -100,7 +100,9 @@
     if (pmd) {
       MemoryAllocatorDump* win_heap_dump =
           pmd->CreateAllocatorDump("malloc/win_heap");
-      win_heap_dump->AddScalar("size", "bytes", main_heap_info.allocated_size);
+      win_heap_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                               MemoryAllocatorDump::kUnitsBytes,
+                               main_heap_info.allocated_size);
     }
   }
 }
@@ -146,6 +148,134 @@
 }
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
+#if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_APPLE)
+void ReportAppleAllocStats(size_t* total_virtual_size,
+                           size_t* resident_size,
+                           size_t* allocated_objects_size) {
+  malloc_statistics_t stats = {0};
+  malloc_zone_statistics(nullptr, &stats);
+  *total_virtual_size += stats.size_allocated;
+  *allocated_objects_size += stats.size_in_use;
+
+  // Resident size is approximated pretty well by stats.max_size_in_use.
+  // However, on macOS, freed blocks are both resident and reusable, which is
+  // semantically equivalent to deallocated. The implementation of libmalloc
+  // will also only hold a fixed number of freed regions before actually
+  // starting to deallocate them, so stats.max_size_in_use is also not
+  // representative of the peak size. As a result, stats.max_size_in_use is
+  // typically somewhere between actually resident [non-reusable] pages, and
+  // peak size. This is not very useful, so we just use stats.size_in_use for
+  // resident_size, even though it's an underestimate and fails to account for
+  // fragmentation. See
+  // https://bugs.chromium.org/p/chromium/issues/detail?id=695263#c1.
+  *resident_size += stats.size_in_use;
+}
+#endif
+
+#if (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_ANDROID)) || \
+    (!BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && !BUILDFLAG(IS_WIN) &&    \
+     !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA))
+void ReportMallinfoStats(ProcessMemoryDump* pmd,
+                         size_t* total_virtual_size,
+                         size_t* resident_size,
+                         size_t* allocated_objects_size,
+                         size_t* allocated_objects_count) {
+#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
+#if __GLIBC_PREREQ(2, 33)
+#define MALLINFO2_FOUND_IN_LIBC
+  struct mallinfo2 info = mallinfo2();
+#endif
+#endif  // defined(__GLIBC__) && defined(__GLIBC_PREREQ)
+#if !defined(MALLINFO2_FOUND_IN_LIBC)
+  struct mallinfo info = mallinfo();
+#endif
+#undef MALLINFO2_FOUND_IN_LIBC
+  // In case of Android's jemalloc |arena| is 0 and the outer pages size is
+  // reported by |hblkhd|. In case of dlmalloc the total is given by
+  // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF.
+  *total_virtual_size += info.arena + info.hblkhd;
+  *resident_size += info.uordblks;
+
+  // Total allocated space is given by |uordblks|.
+  *allocated_objects_size += info.uordblks;
+
+  if (pmd) {
+    MemoryAllocatorDump* sys_alloc_dump =
+        pmd->CreateAllocatorDump("malloc/sys_malloc");
+    sys_alloc_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                              MemoryAllocatorDump::kUnitsBytes, info.uordblks);
+  }
+}
+#endif
+
+#if BUILDFLAG(USE_PARTITION_ALLOC)
+void ReportPartitionAllocThreadCacheStats(ProcessMemoryDump* pmd,
+                                          MemoryAllocatorDump* dump,
+                                          const ThreadCacheStats& stats,
+                                          const std::string& metrics_suffix,
+                                          bool detailed) {
+  dump->AddScalar("alloc_count", MemoryAllocatorDump::kTypeScalar,
+                  stats.alloc_count);
+  dump->AddScalar("alloc_hits", MemoryAllocatorDump::kTypeScalar,
+                  stats.alloc_hits);
+  dump->AddScalar("alloc_misses", MemoryAllocatorDump::kTypeScalar,
+                  stats.alloc_misses);
+
+  dump->AddScalar("alloc_miss_empty", MemoryAllocatorDump::kTypeScalar,
+                  stats.alloc_miss_empty);
+  dump->AddScalar("alloc_miss_too_large", MemoryAllocatorDump::kTypeScalar,
+                  stats.alloc_miss_too_large);
+
+  dump->AddScalar("cache_fill_count", MemoryAllocatorDump::kTypeScalar,
+                  stats.cache_fill_count);
+  dump->AddScalar("cache_fill_hits", MemoryAllocatorDump::kTypeScalar,
+                  stats.cache_fill_hits);
+  dump->AddScalar("cache_fill_misses", MemoryAllocatorDump::kTypeScalar,
+                  stats.cache_fill_misses);
+
+  dump->AddScalar("batch_fill_count", MemoryAllocatorDump::kTypeScalar,
+                  stats.batch_fill_count);
+
+  dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                  MemoryAllocatorDump::kUnitsBytes, stats.bucket_total_memory);
+  dump->AddScalar("metadata_overhead", MemoryAllocatorDump::kUnitsBytes,
+                  stats.metadata_overhead);
+
+  if (stats.alloc_count) {
+    int hit_rate_percent =
+        static_cast<int>((100 * stats.alloc_hits) / stats.alloc_count);
+    base::UmaHistogramPercentage(
+        "Memory.PartitionAlloc.ThreadCache.HitRate" + metrics_suffix,
+        hit_rate_percent);
+    int batch_fill_rate_percent =
+        static_cast<int>((100 * stats.batch_fill_count) / stats.alloc_count);
+    base::UmaHistogramPercentage(
+        "Memory.PartitionAlloc.ThreadCache.BatchFillRate" + metrics_suffix,
+        batch_fill_rate_percent);
+
+#if defined(PA_THREAD_CACHE_ALLOC_STATS)
+    if (detailed) {
+      base::internal::BucketIndexLookup lookup{};
+      std::string name = dump->absolute_name();
+      for (size_t i = 0; i < kNumBuckets; i++) {
+        size_t bucket_size = lookup.bucket_sizes()[i];
+        if (bucket_size == kInvalidBucketSize)
+          continue;
+        // Covers all normal buckets, that is up to ~1MiB, so 7 digits.
+        std::string dump_name =
+            base::StringPrintf("%s/buckets_alloc/%07d", name.c_str(),
+                               static_cast<int>(bucket_size));
+        auto* buckets_alloc_dump = pmd->CreateAllocatorDump(dump_name);
+        buckets_alloc_dump->AddScalar("count",
+                                      MemoryAllocatorDump::kUnitsObjects,
+                                      stats.allocs_per_bucket_[i]);
+      }
+    }
+#endif  // defined(PA_THREAD_CACHE_ALLOC_STATS)
+  }
+}
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC)
+
 }  // namespace
 
 // static
@@ -166,16 +296,17 @@
                                       ProcessMemoryDump* pmd) {
   {
     base::AutoLock auto_lock(emit_metrics_on_memory_dump_lock_);
-    if (!emit_metrics_on_memory_dump_)
+    if (!emit_metrics_on_memory_dump_) {
       return true;
+    }
   }
 
   size_t total_virtual_size = 0;
   size_t resident_size = 0;
   size_t allocated_objects_size = 0;
   size_t allocated_objects_count = 0;
-#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
   uint64_t syscall_count = 0;
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
   uint64_t pa_only_resident_size;
   uint64_t pa_only_allocated_objects_size;
 #endif
@@ -188,32 +319,20 @@
   pa_only_resident_size = resident_size;
   pa_only_allocated_objects_size = allocated_objects_size;
 
-  // Even when PartitionAlloc is used, WinHeap is still used as well, report
-  // its statistics.
-#if OS_WIN
+  // Even when PartitionAlloc is used, WinHeap / System malloc is still used as
+  // well, report its statistics.
+#if BUILDFLAG(IS_ANDROID)
+  ReportMallinfoStats(pmd, &total_virtual_size, &resident_size,
+                      &allocated_objects_size, &allocated_objects_count);
+#elif BUILDFLAG(IS_WIN)
   ReportWinHeapStats(args.level_of_detail, pmd, &total_virtual_size,
                      &resident_size, &allocated_objects_size,
                      &allocated_objects_count);
-#endif
-  // TODO(keishi): Add glibc malloc on Android
-#elif BUILDFLAG(IS_APPLE)
-  malloc_statistics_t stats = {0};
-  malloc_zone_statistics(nullptr, &stats);
-  total_virtual_size = stats.size_allocated;
-  allocated_objects_size = stats.size_in_use;
+#endif  // BUILDFLAG(IS_ANDROID), BUILDFLAG(IS_WIN)
 
-  // Resident size is approximated pretty well by stats.max_size_in_use.
-  // However, on macOS, freed blocks are both resident and reusable, which is
-  // semantically equivalent to deallocated. The implementation of libmalloc
-  // will also only hold a fixed number of freed regions before actually
-  // starting to deallocate them, so stats.max_size_in_use is also not
-  // representative of the peak size. As a result, stats.max_size_in_use is
-  // typically somewhere between actually resident [non-reusable] pages, and
-  // peak size. This is not very useful, so we just use stats.size_in_use for
-  // resident_size, even though it's an underestimate and fails to account for
-  // fragmentation. See
-  // https://bugs.chromium.org/p/chromium/issues/detail?id=695263#c1.
-  resident_size = stats.size_in_use;
+#elif BUILDFLAG(IS_APPLE)
+  ReportAppleAllocStats(&total_virtual_size, &resident_size,
+                        &allocated_objects_size);
 #elif BUILDFLAG(IS_WIN)
   ReportWinHeapStats(args.level_of_detail, nullptr, &total_virtual_size,
                      &resident_size, &allocated_objects_size,
@@ -221,24 +340,8 @@
 #elif BUILDFLAG(IS_FUCHSIA)
 // TODO(fuchsia): Port, see https://crbug.com/706592.
 #else
-#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
-#if __GLIBC_PREREQ(2, 33)
-#define MALLINFO2_FOUND_IN_LIBC
-  struct mallinfo2 info = mallinfo2();
-#endif
-#endif  // defined(__GLIBC__) && defined(__GLIBC_PREREQ)
-#if !defined(MALLINFO2_FOUND_IN_LIBC)
-  struct mallinfo info = mallinfo();
-#endif
-#undef MALLINFO2_FOUND_IN_LIBC
-  // In case of Android's jemalloc |arena| is 0 and the outer pages size is
-  // reported by |hblkhd|. In case of dlmalloc the total is given by
-  // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF.
-  total_virtual_size = info.arena + info.hblkhd;
-  resident_size = info.uordblks;
-
-  // Total allocated space is given by |uordblks|.
-  allocated_objects_size = info.uordblks;
+  ReportMallinfoStats(/*pmd=*/nullptr, &total_virtual_size, &resident_size,
+                      &allocated_objects_size, &allocated_objects_count);
 #endif
 
   MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("malloc");
@@ -287,29 +390,24 @@
                           MemoryAllocatorDump::kUnitsBytes, waste);
   }
 
+  ReportSyscallCount(syscall_count, outer_dump);
+
+  return true;
+}
+
+void MallocDumpProvider::ReportSyscallCount(uint64_t syscall_count,
+                                            MemoryAllocatorDump* malloc_dump) {
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
   uint64_t new_syscalls = syscall_count - last_syscall_count_;
   base::TimeDelta time_since_last_dump =
       base::TimeTicks::Now() - last_memory_dump_time_;
   uint64_t syscalls_per_minute = static_cast<uint64_t>(
       (60 * new_syscalls) / time_since_last_dump.InSecondsF());
-  outer_dump->AddScalar("syscalls_per_minute", "count", syscalls_per_minute);
+  malloc_dump->AddScalar("syscalls_per_minute", "count", syscalls_per_minute);
 
   last_memory_dump_time_ = base::TimeTicks::Now();
   last_syscall_count_ = syscall_count;
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
-
-  return true;
-}
-
-void MallocDumpProvider::EnableMetrics() {
-  base::AutoLock auto_lock(emit_metrics_on_memory_dump_lock_);
-  emit_metrics_on_memory_dump_ = true;
-}
-
-void MallocDumpProvider::DisableMetrics() {
-  base::AutoLock auto_lock(emit_metrics_on_memory_dump_lock_);
-  emit_metrics_on_memory_dump_ = false;
 }
 
 #if BUILDFLAG(USE_PARTITION_ALLOC)
@@ -342,26 +440,34 @@
   size_t fragmentation =
       total_committed_bytes == 0 ? 0 : 100 * wasted / total_committed_bytes;
 
-  allocator_dump->AddScalar("size", "bytes",
+  allocator_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->total_resident_bytes);
-  allocator_dump->AddScalar("allocated_objects_size", "bytes",
+  allocator_dump->AddScalar("allocated_objects_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->total_active_bytes);
-  allocator_dump->AddScalar("virtual_size", "bytes",
+  allocator_dump->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->total_mmapped_bytes);
-  allocator_dump->AddScalar("virtual_committed_size", "bytes",
+  allocator_dump->AddScalar("virtual_committed_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->total_committed_bytes);
-  allocator_dump->AddScalar("max_committed_size", "bytes",
+  allocator_dump->AddScalar("max_committed_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->max_committed_bytes);
-  allocator_dump->AddScalar("allocated_size", "bytes",
+  allocator_dump->AddScalar("allocated_size", MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->total_allocated_bytes);
-  allocator_dump->AddScalar("max_allocated_size", "bytes",
+  allocator_dump->AddScalar("max_allocated_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->max_allocated_bytes);
-  allocator_dump->AddScalar("decommittable_size", "bytes",
+  allocator_dump->AddScalar("decommittable_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->total_decommittable_bytes);
-  allocator_dump->AddScalar("discardable_size", "bytes",
+  allocator_dump->AddScalar("discardable_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->total_discardable_bytes);
 #if BUILDFLAG(USE_BACKUP_REF_PTR)
-  allocator_dump->AddScalar("brp_quarantined_size", "bytes",
+  allocator_dump->AddScalar("brp_quarantined_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->total_brp_quarantined_bytes);
   allocator_dump->AddScalar("brp_quarantined_count", "count",
                             memory_stats->total_brp_quarantined_count);
@@ -371,7 +477,7 @@
   allocator_dump->AddScalar("syscall_total_time_ms", "ms",
                             memory_stats->syscall_total_time_ns / 1e6);
   allocator_dump->AddScalar("fragmentation", "percent", fragmentation);
-  allocator_dump->AddScalar("wasted", "bytes", wasted);
+  allocator_dump->AddScalar("wasted", MemoryAllocatorDump::kUnitsBytes, wasted);
 
   if (memory_stats->has_thread_cache) {
     const auto& thread_cache_stats = memory_stats->current_thread_cache_stats;
@@ -405,81 +511,37 @@
 
   MemoryAllocatorDump* allocator_dump =
       memory_dump_->CreateAllocatorDump(dump_name);
-  allocator_dump->AddScalar("size", "bytes", memory_stats->resident_bytes);
-  allocator_dump->AddScalar("allocated_objects_size", "bytes",
+  allocator_dump->AddScalar(MemoryAllocatorDump::kNameSize,
+                            MemoryAllocatorDump::kUnitsBytes,
+                            memory_stats->resident_bytes);
+  allocator_dump->AddScalar("allocated_objects_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->active_bytes);
-  allocator_dump->AddScalar("slot_size", "bytes",
+  allocator_dump->AddScalar("slot_size", MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->bucket_slot_size);
-  allocator_dump->AddScalar("decommittable_size", "bytes",
+  allocator_dump->AddScalar("decommittable_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->decommittable_bytes);
-  allocator_dump->AddScalar("discardable_size", "bytes",
+  allocator_dump->AddScalar("discardable_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->discardable_bytes);
   // TODO(bartekn): Rename the scalar names.
-  allocator_dump->AddScalar("total_slot_span_size", "bytes",
+  allocator_dump->AddScalar("total_slot_span_size",
+                            MemoryAllocatorDump::kUnitsBytes,
                             memory_stats->allocated_slot_span_size);
-  allocator_dump->AddScalar("active_slot_spans", "objects",
+  allocator_dump->AddScalar("active_slot_spans",
+                            MemoryAllocatorDump::kUnitsObjects,
                             memory_stats->num_active_slot_spans);
-  allocator_dump->AddScalar("full_slot_spans", "objects",
+  allocator_dump->AddScalar("full_slot_spans",
+                            MemoryAllocatorDump::kUnitsObjects,
                             memory_stats->num_full_slot_spans);
-  allocator_dump->AddScalar("empty_slot_spans", "objects",
+  allocator_dump->AddScalar("empty_slot_spans",
+                            MemoryAllocatorDump::kUnitsObjects,
                             memory_stats->num_empty_slot_spans);
-  allocator_dump->AddScalar("decommitted_slot_spans", "objects",
+  allocator_dump->AddScalar("decommitted_slot_spans",
+                            MemoryAllocatorDump::kUnitsObjects,
                             memory_stats->num_decommitted_slot_spans);
 }
-
-void ReportPartitionAllocThreadCacheStats(ProcessMemoryDump* pmd,
-                                          MemoryAllocatorDump* dump,
-                                          const ThreadCacheStats& stats,
-                                          const std::string& metrics_suffix,
-                                          bool detailed) {
-  dump->AddScalar("alloc_count", "scalar", stats.alloc_count);
-  dump->AddScalar("alloc_hits", "scalar", stats.alloc_hits);
-  dump->AddScalar("alloc_misses", "scalar", stats.alloc_misses);
-
-  dump->AddScalar("alloc_miss_empty", "scalar", stats.alloc_miss_empty);
-  dump->AddScalar("alloc_miss_too_large", "scalar", stats.alloc_miss_too_large);
-
-  dump->AddScalar("cache_fill_count", "scalar", stats.cache_fill_count);
-  dump->AddScalar("cache_fill_hits", "scalar", stats.cache_fill_hits);
-  dump->AddScalar("cache_fill_misses", "scalar", stats.cache_fill_misses);
-
-  dump->AddScalar("batch_fill_count", "scalar", stats.batch_fill_count);
-
-  dump->AddScalar("size", "bytes", stats.bucket_total_memory);
-  dump->AddScalar("metadata_overhead", "bytes", stats.metadata_overhead);
-
-  if (stats.alloc_count) {
-    int hit_rate_percent =
-        static_cast<int>((100 * stats.alloc_hits) / stats.alloc_count);
-    base::UmaHistogramPercentage(
-        "Memory.PartitionAlloc.ThreadCache.HitRate" + metrics_suffix,
-        hit_rate_percent);
-    int batch_fill_rate_percent =
-        static_cast<int>((100 * stats.batch_fill_count) / stats.alloc_count);
-    base::UmaHistogramPercentage(
-        "Memory.PartitionAlloc.ThreadCache.BatchFillRate" + metrics_suffix,
-        batch_fill_rate_percent);
-
-#if defined(PA_THREAD_CACHE_ALLOC_STATS)
-    if (detailed) {
-      base::internal::BucketIndexLookup lookup{};
-      std::string name = dump->absolute_name();
-      for (size_t i = 0; i < kNumBuckets; i++) {
-        size_t bucket_size = lookup.bucket_sizes()[i];
-        if (bucket_size == kInvalidBucketSize)
-          continue;
-        // Covers all normal buckets, that is up to ~1MiB, so 7 digits.
-        std::string dump_name =
-            base::StringPrintf("%s/buckets_alloc/%07d", name.c_str(),
-                               static_cast<int>(bucket_size));
-        auto* buckets_alloc_dump = pmd->CreateAllocatorDump(dump_name);
-        buckets_alloc_dump->AddScalar("count", "objects",
-                                      stats.allocs_per_bucket_[i]);
-      }
-    }
-#endif  // defined(PA_THREAD_CACHE_ALLOC_STATS)
-  }
-}
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC)
 
 }  // namespace trace_event
diff --git a/base/trace_event/malloc_dump_provider.h b/base/trace_event/malloc_dump_provider.h
index ca6c0c6..2f1625a 100644
--- a/base/trace_event/malloc_dump_provider.h
+++ b/base/trace_event/malloc_dump_provider.h
@@ -25,6 +25,8 @@
 namespace base {
 namespace trace_event {
 
+class MemoryAllocatorDump;
+
 // Dump provider which collects process-wide memory stats.
 class BASE_EXPORT MallocDumpProvider : public MemoryDumpProvider {
  public:
@@ -41,18 +43,15 @@
   bool OnMemoryDump(const MemoryDumpArgs& args,
                     ProcessMemoryDump* pmd) override;
 
-  // Used by out-of-process heap-profiling. When malloc is profiled by an
-  // external process, that process will be responsible for emitting metrics on
-  // behalf of this one. Thus, MallocDumpProvider should not do anything.
-  void EnableMetrics();
-  void DisableMetrics();
-
  private:
   friend struct DefaultSingletonTraits<MallocDumpProvider>;
 
   MallocDumpProvider();
   ~MallocDumpProvider() override;
 
+  void ReportSyscallCount(uint64_t syscall_count,
+                          MemoryAllocatorDump* malloc_dump);
+
   bool emit_metrics_on_memory_dump_
       GUARDED_BY(emit_metrics_on_memory_dump_lock_) = true;
   base::Lock emit_metrics_on_memory_dump_lock_;
@@ -105,14 +104,6 @@
   bool detailed_;
 };
 
-class MemoryAllocatorDump;
-
-BASE_EXPORT void ReportPartitionAllocThreadCacheStats(
-    ProcessMemoryDump* pmd,
-    MemoryAllocatorDump* dump,
-    const ThreadCacheStats& stats,
-    const std::string& metrics_suffix,
-    bool detailed);
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC)
 
 }  // namespace trace_event
diff --git a/base/trace_event/memory_infra_background_allowlist.cc b/base/trace_event/memory_infra_background_allowlist.cc
index 35d3410..f8a9e79a 100644
--- a/base/trace_event/memory_infra_background_allowlist.cc
+++ b/base/trace_event/memory_infra_background_allowlist.cc
@@ -156,6 +156,7 @@
     "malloc/partitions/original",
     "malloc/partitions/nonscannable",
     "malloc/partitions/nonquarantinable",
+    "malloc/sys_malloc",
     "malloc/win_heap",
 #endif
     "media/webmediaplayer/audio/player_0x?",
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 3dca68a4..f9c9e8e1 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20220324.3.1
+7.20220325.0.1
diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc
index b1bb5c7..51336ef 100644
--- a/cc/layers/render_surface_impl.cc
+++ b/cc/layers/render_surface_impl.cc
@@ -85,8 +85,8 @@
   gfx::Rect surface_content_rect = content_rect();
   const FilterOperations& filters = Filters();
   if (!filters.IsEmpty()) {
-    surface_content_rect = filters.MapRect(surface_content_rect,
-                                           SkMatrix(SurfaceScale().matrix()));
+    surface_content_rect =
+        filters.MapRect(surface_content_rect, SurfaceScale().matrix().asM33());
   }
   gfx::RectF drawable_content_rect = MathUtil::MapClippedRect(
       draw_transform(), gfx::RectF(surface_content_rect));
@@ -221,8 +221,8 @@
     const gfx::Transform& target_to_surface) {
   gfx::Rect clip_in_surface_space =
       MathUtil::ProjectEnclosingClippedRect(target_to_surface, clip_rect());
-  gfx::Rect expanded_clip_in_surface_space = Filters().MapRect(
-      clip_in_surface_space, SkMatrix(SurfaceScale().matrix()));
+  gfx::Rect expanded_clip_in_surface_space =
+      Filters().MapRect(clip_in_surface_space, SurfaceScale().matrix().asM33());
   gfx::Rect expanded_clip_in_target_space = MathUtil::MapEnclosingClippedRect(
       draw_transform(), expanded_clip_in_surface_space);
   return expanded_clip_in_target_space;
diff --git a/cc/paint/display_item_list_unittest.cc b/cc/paint/display_item_list_unittest.cc
index d198ccf..3246ef6 100644
--- a/cc/paint/display_item_list_unittest.cc
+++ b/cc/paint/display_item_list_unittest.cc
@@ -334,7 +334,7 @@
       SkRect::MakeLTRB(0.f + first_offset.x(), 0.f + first_offset.y(),
                        60.f + first_offset.x(), 60.f + first_offset.y()),
       red_paint);
-  expected_canvas.setMatrix(SkMatrix(transform.matrix()));
+  expected_canvas.setMatrix(transform.matrix().asM33());
   expected_canvas.drawRect(
       SkRect::MakeLTRB(50.f + second_offset.x(), 50.f + second_offset.y(),
                        75.f + second_offset.x(), 75.f + second_offset.y()),
diff --git a/cc/trees/clip_expander.cc b/cc/trees/clip_expander.cc
index 7fb0fa3..ee3b25603 100644
--- a/cc/trees/clip_expander.cc
+++ b/cc/trees/clip_expander.cc
@@ -28,7 +28,7 @@
   filter_draw_transform.Scale(effect_node->surface_contents_scale.x(),
                               effect_node->surface_contents_scale.y());
   return effect_node->filters.MapRect(rect,
-                                      SkMatrix(filter_draw_transform.matrix()));
+                                      filter_draw_transform.matrix().asM33());
 }
 
 gfx::Rect ClipExpander::MapRectReverse(
@@ -40,7 +40,7 @@
   filter_draw_transform.Scale(effect_node->surface_contents_scale.x(),
                               effect_node->surface_contents_scale.y());
   return effect_node->filters.MapRectReverse(
-      rect, SkMatrix(filter_draw_transform.matrix()));
+      rect, filter_draw_transform.matrix().asM33());
 }
 
 }  // namespace cc
diff --git a/cc/trees/damage_tracker.cc b/cc/trees/damage_tracker.cc
index 3101aa1..b3f0da73 100644
--- a/cc/trees/damage_tracker.cc
+++ b/cc/trees/damage_tracker.cc
@@ -216,7 +216,7 @@
     bool is_rect_valid = damage_for_this_update_.GetAsRect(&damage_rect);
     if (is_rect_valid && !damage_rect.IsEmpty()) {
       damage_rect = render_surface->Filters().MapRect(
-          damage_rect, SkMatrix(render_surface->SurfaceScale().matrix()));
+          damage_rect, render_surface->SurfaceScale().matrix().asM33());
       damage_for_this_update_ = DamageAccumulator();
       damage_for_this_update_.Union(damage_rect);
     }
diff --git a/cc/trees/property_tree_builder_unittest.cc b/cc/trees/property_tree_builder_unittest.cc
index 33535565..ee8c82f9 100644
--- a/cc/trees/property_tree_builder_unittest.cc
+++ b/cc/trees/property_tree_builder_unittest.cc
@@ -357,9 +357,8 @@
 
   gfx::Transform vertical_flip;
   vertical_flip.Scale(1, -1);
-  sk_sp<PaintFilter> flip_filter =
-      sk_make_sp<MatrixPaintFilter>(SkMatrix(vertical_flip.matrix()),
-                                    PaintFlags::FilterQuality::kLow, nullptr);
+  sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>(
+      vertical_flip.matrix().asM33(), PaintFlags::FilterQuality::kLow, nullptr);
   FilterOperations reflection_filter;
   reflection_filter.Append(
       FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>(
@@ -418,9 +417,8 @@
 
   gfx::Transform vertical_flip;
   vertical_flip.Scale(1, -1);
-  sk_sp<PaintFilter> flip_filter =
-      sk_make_sp<MatrixPaintFilter>(SkMatrix(vertical_flip.matrix()),
-                                    PaintFlags::FilterQuality::kLow, nullptr);
+  sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>(
+      vertical_flip.matrix().asM33(), PaintFlags::FilterQuality::kLow, nullptr);
   FilterOperations reflection_filter;
   reflection_filter.Append(
       FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>(
diff --git a/chrome/VERSION b/chrome/VERSION
index 5366000..5eaf009 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=102
 MINOR=0
-BUILD=4963
+BUILD=4964
 PATCH=0
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/attribution_reporting/AttributionProviderIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/attribution_reporting/AttributionProviderIntegrationTest.java
index 5e734d3..37dad2f0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/attribution_reporting/AttributionProviderIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/attribution_reporting/AttributionProviderIntegrationTest.java
@@ -48,8 +48,9 @@
 // enable-experimental-web-platform-features turns on the overall ConversionMeasurement Blink
 // feature.
 // conversions-debug-mode will send reports with no delay or noise.
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
-        "enable-experimental-web-platform-features", "conversions-debug-mode"})
+@CommandLineFlags.
+Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "enable-experimental-web-platform-features",
+        "enable-blink-test-features", "conversions-debug-mode"})
 @Features.EnableFeatures(ChromeFeatureList.APP_TO_WEB_ATTRIBUTION)
 public class AttributionProviderIntegrationTest {
     private static final String EVENT_ID = "12345";
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 5e1a2930..eb055e9 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-101.0.4951.7_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-101.0.4951.10_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 2467744..53191e2d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4553,6 +4553,8 @@
       "apps/app_discovery_service/app_discovery_util.h",
       "apps/app_discovery_service/app_fetcher_manager.cc",
       "apps/app_discovery_service/app_fetcher_manager.h",
+      "apps/app_discovery_service/game_extras.cc",
+      "apps/app_discovery_service/game_extras.h",
       "apps/app_discovery_service/play_extras.cc",
       "apps/app_discovery_service/play_extras.h",
       "apps/app_discovery_service/recommended_arc_app_fetcher.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6acb90e..02143169 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -7141,6 +7141,16 @@
          "disable-dialog-for-testing/true/show-sample-data/true,"
          "EnableFetchingAccountCapabilities")},
 
+    {"privacy-sandbox-ads-apis",
+     flag_descriptions::kPrivacySandboxAdsAPIsOverrideName,
+     flag_descriptions::kPrivacySandboxAdsAPIsOverrideDescription, kOsAll,
+     // Use a command-line parameter instead of a FEATURE_VALUE_TYPE to enable
+     // multiple related features when they are available.
+     SINGLE_VALUE_TYPE_AND_VALUE(switches::kEnableFeatures,
+                                 "PrivacySandboxAdsAPIsOverride"
+                                 "Fledge,BrowsingTopics,ConversionMeasurement"
+                                 "OverridePrivacySandboxSettingsLocalTesting")},
+
     {"animated-image-resume", flag_descriptions::kAnimatedImageResumeName,
      flag_descriptions::kAnimatedImageResumeDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kAnimatedImageResume)},
diff --git a/chrome/browser/apps/app_discovery_service/game_extras.cc b/chrome/browser/apps/app_discovery_service/game_extras.cc
new file mode 100644
index 0000000..72cc5cec
--- /dev/null
+++ b/chrome/browser/apps/app_discovery_service/game_extras.cc
@@ -0,0 +1,53 @@
+// Copyright 2022 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/apps/app_discovery_service/game_extras.h"
+
+namespace apps {
+namespace {
+
+using Source = GameExtras::Source;
+
+}  // namespace
+
+GameExtras::GameExtras(
+    const std::string& id,
+    const std::u16string& title,
+    Source source,
+    const absl::optional<std::vector<std::u16string>>& platforms,
+    const GURL& icon_url)
+    : id_(id),
+      title_(title),
+      source_(source),
+      platforms_(platforms),
+      icon_url_(icon_url) {}
+
+GameExtras::~GameExtras() = default;
+
+const std::string& GameExtras::GetId() const {
+  return id_;
+}
+
+const std::u16string& GameExtras::GetTitle() const {
+  return title_;
+}
+
+Source GameExtras::GetSource() const {
+  return source_;
+}
+
+const absl::optional<std::vector<std::u16string>>& GameExtras::GetPlatforms()
+    const {
+  return platforms_;
+}
+
+const GURL& GameExtras::GetIconUrl() const {
+  return icon_url_;
+}
+
+GameExtras* GameExtras::AsGameExtras() {
+  return this;
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/app_discovery_service/game_extras.h b/chrome/browser/apps/app_discovery_service/game_extras.h
new file mode 100644
index 0000000..8f48010
--- /dev/null
+++ b/chrome/browser/apps/app_discovery_service/game_extras.h
@@ -0,0 +1,54 @@
+// Copyright 2022 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_APPS_APP_DISCOVERY_SERVICE_GAME_EXTRAS_H_
+#define CHROME_BROWSER_APPS_APP_DISCOVERY_SERVICE_GAME_EXTRAS_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/browser/apps/app_discovery_service/result.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "url/gurl.h"
+
+namespace apps {
+
+class GameExtras : public SourceExtras {
+ public:
+  // Which cloud gaming source a result comes from. These values are persisted
+  // to logs. Entries should not be renumbered or reused.
+  enum class Source {
+    // TODO(crbug.com/1305880): Replace with real source.
+    kTemporarySource = 1,
+  };
+
+  GameExtras(const std::string& id,
+             const std::u16string& title,
+             Source source,
+             const absl::optional<std::vector<std::u16string>>& platforms,
+             const GURL& icon_url);
+  GameExtras(const GameExtras&) = delete;
+  GameExtras& operator=(const GameExtras&) = delete;
+  ~GameExtras() override;
+
+  const std::string& GetId() const;
+  const std::u16string& GetTitle() const;
+  Source GetSource() const;
+  const absl::optional<std::vector<std::u16string>>& GetPlatforms() const;
+  const GURL& GetIconUrl() const;
+
+  // Result::SourceExtras:
+  GameExtras* AsGameExtras() override;
+
+ private:
+  std::string id_;
+  std::u16string title_;
+  Source source_;
+  absl::optional<std::vector<std::u16string>> platforms_;
+  GURL icon_url_;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APP_DISCOVERY_SERVICE_GAME_EXTRAS_H_
diff --git a/chrome/browser/apps/app_discovery_service/play_extras.h b/chrome/browser/apps/app_discovery_service/play_extras.h
index 4efa24d..02fde68 100644
--- a/chrome/browser/apps/app_discovery_service/play_extras.h
+++ b/chrome/browser/apps/app_discovery_service/play_extras.h
@@ -14,16 +14,16 @@
 
 class PlayExtras : public SourceExtras {
  public:
-  explicit PlayExtras(const std::string& package_name,
-                      const GURL& icon_url,
-                      const std::u16string& category,
-                      const std::u16string& description,
-                      const std::u16string& content_rating,
-                      const GURL& content_rating_icon_url,
-                      const bool in_app_purchases,
-                      const bool previously_installed,
-                      const bool contains_ads,
-                      const bool optimized_for_chrome);
+  PlayExtras(const std::string& package_name,
+             const GURL& icon_url,
+             const std::u16string& category,
+             const std::u16string& description,
+             const std::u16string& content_rating,
+             const GURL& content_rating_icon_url,
+             const bool in_app_purchases,
+             const bool previously_installed,
+             const bool contains_ads,
+             const bool optimized_for_chrome);
   PlayExtras(const PlayExtras&) = delete;
   PlayExtras& operator=(const PlayExtras&) = delete;
   ~PlayExtras() override;
diff --git a/chrome/browser/apps/app_discovery_service/result.cc b/chrome/browser/apps/app_discovery_service/result.cc
index 8007569..906f69d 100644
--- a/chrome/browser/apps/app_discovery_service/result.cc
+++ b/chrome/browser/apps/app_discovery_service/result.cc
@@ -8,6 +8,10 @@
 
 namespace apps {
 
+GameExtras* SourceExtras::AsGameExtras() {
+  return nullptr;
+}
+
 PlayExtras* SourceExtras::AsPlayExtras() {
   return nullptr;
 }
diff --git a/chrome/browser/apps/app_discovery_service/result.h b/chrome/browser/apps/app_discovery_service/result.h
index d2d2666b..e8152c4 100644
--- a/chrome/browser/apps/app_discovery_service/result.h
+++ b/chrome/browser/apps/app_discovery_service/result.h
@@ -11,6 +11,7 @@
 namespace apps {
 
 enum class AppSource;
+class GameExtras;
 class PlayExtras;
 
 // Can be overridden by Sources that have unique fields.
@@ -23,6 +24,7 @@
   // virtual FooExtras* AsFooExtras { return nullptr; }
 
   // Safe downcasts:
+  virtual GameExtras* AsGameExtras();
   virtual PlayExtras* AsPlayExtras();
 };
 
diff --git a/chrome/browser/apps/app_service/publishers/app_publisher.h b/chrome/browser/apps/app_service/publishers/app_publisher.h
index 09b5041..c1ddcbc 100644
--- a/chrome/browser/apps/app_service/publishers/app_publisher.h
+++ b/chrome/browser/apps/app_service/publishers/app_publisher.h
@@ -79,6 +79,10 @@
   virtual void LaunchAppWithParams(AppLaunchParams&& params,
                                    LaunchCallback callback) = 0;
 
+  virtual void LaunchShortcut(const std::string& app_id,
+                              const std::string& shortcut_id,
+                              int64_t display_id) {}
+
  protected:
 #if !BUILDFLAG(IS_CHROMEOS_LACROS)
   // Publish one `app` to AppServiceProxy. Should be called whenever the app
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.cc b/chrome/browser/apps/app_service/publishers/arc_apps.cc
index 0bd2106..c0e93a2 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.cc
@@ -704,6 +704,12 @@
   std::move(callback).Run(LaunchResult());
 }
 
+void ArcApps::LaunchShortcut(const std::string& app_id,
+                             const std::string& shortcut_id,
+                             int64_t display_id) {
+  arc::ExecuteArcShortcutCommand(profile_, app_id, shortcut_id, display_id);
+}
+
 void ArcApps::Connect(
     mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
     apps::mojom::ConnectOptionsPtr opts) {
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps.h b/chrome/browser/apps/app_service/publishers/arc_apps.h
index 90537c5e..0fb9ee5 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps.h
+++ b/chrome/browser/apps/app_service/publishers/arc_apps.h
@@ -101,6 +101,9 @@
                 apps::LoadIconCallback callback) override;
   void LaunchAppWithParams(AppLaunchParams&& params,
                            LaunchCallback callback) override;
+  void LaunchShortcut(const std::string& app_id,
+                      const std::string& shortcut_id,
+                      int64_t display_id) override;
 
   // apps::mojom::Publisher overrides.
   void Connect(mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
diff --git a/chrome/browser/apps/app_service/publishers/crostini_apps.cc b/chrome/browser/apps/app_service/publishers/crostini_apps.cc
index 4847ff96..3afe733 100644
--- a/chrome/browser/apps/app_service/publishers/crostini_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/crostini_apps.cc
@@ -139,6 +139,15 @@
   std::move(callback).Run(LaunchResult());
 }
 
+void CrostiniApps::LaunchShortcut(const std::string& app_id,
+                                  const std::string& shortcut_id,
+                                  int64_t display_id) {
+  if (app_id == crostini::kCrostiniTerminalSystemAppId) {
+    crostini::ExecuteTerminalMenuShortcutCommand(profile_, shortcut_id,
+                                                 display_id);
+  }
+}
+
 void CrostiniApps::Connect(
     mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
     apps::mojom::ConnectOptionsPtr opts) {
diff --git a/chrome/browser/apps/app_service/publishers/crostini_apps.h b/chrome/browser/apps/app_service/publishers/crostini_apps.h
index 0451f6a..54a1b9a 100644
--- a/chrome/browser/apps/app_service/publishers/crostini_apps.h
+++ b/chrome/browser/apps/app_service/publishers/crostini_apps.h
@@ -64,6 +64,9 @@
                 apps::LoadIconCallback callback) override;
   void LaunchAppWithParams(AppLaunchParams&& params,
                            LaunchCallback callback) override;
+  void LaunchShortcut(const std::string& app_id,
+                      const std::string& shortcut_id,
+                      int64_t display_id) override;
 
   // apps::mojom::Publisher overrides.
   void Connect(mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
diff --git a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
index b87ef68..5dfad40 100644
--- a/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
+++ b/chrome/browser/apps/app_service/publishers/extension_apps_chromeos.cc
@@ -814,6 +814,7 @@
     app->show_in_launcher = false;
     app->show_in_search = false;
     app->show_in_shelf = false;
+    app->handles_intents = false;
   }
   if (disable_for_lacros)
     app->show_in_management = false;
@@ -862,6 +863,7 @@
     app->show_in_launcher = apps::mojom::OptionalBool::kFalse;
     app->show_in_search = apps::mojom::OptionalBool::kFalse;
     app->show_in_shelf = apps::mojom::OptionalBool::kFalse;
+    app->handles_intents = apps::mojom::OptionalBool::kFalse;
   }
   if (disable_for_lacros)
     app->show_in_management = apps::mojom::OptionalBool::kFalse;
diff --git a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
index 3823dd7..a6d8384 100644
--- a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
+#include "base/values.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
@@ -70,7 +71,9 @@
   base::DictionaryValue value;
   value.SetStringKey("name", name);
   value.SetStringKey("version", version);
-  value.SetStringPath("app.launch.web_url", url);
+  base::ListValue scripts;
+  scripts.Append("script.js");
+  value.SetPath("app.background.scripts", std::move(scripts));
   scoped_refptr<extensions::Extension> app = extensions::Extension::Create(
       base::FilePath(), extensions::mojom::ManifestLocation::kInternal, value,
       extensions::Extension::WAS_INSTALLED_BY_DEFAULT, id, &err);
@@ -593,6 +596,29 @@
             /*has_badge=*/true, /*paused=*/true, WindowMode::kBrowser);
 }
 
+// Check that when Lacros is primary, the app is disabled by policy and does
+// not handle intents.
+TEST_F(StandaloneBrowserPublisherTest, ExtensionAppsDisabledByPolicy) {
+  // Install a "web store" app.
+  scoped_refptr<extensions::Extension> store =
+      MakeExtensionApp("webstore", "0.0", "http://google.com",
+                       std::string(extensions::kWebStoreAppId));
+  service_->AddExtension(store.get());
+
+  AppServiceTest app_service_test;
+  app_service_test.SetUp(profile());
+  VerifyApp(AppType::kChromeApp, store->id(), store->name(),
+            Readiness::kDisabledByPolicy, InstallReason::kDefault,
+            InstallSource::kChromeWebStore, {}, base::Time(), base::Time(),
+            apps::Permissions(),
+            /*is_platform_app=*/true, /*recommendable=*/true,
+            /*searchable=*/true,
+            /*show_in_launcher=*/false, /*show_in_shelf=*/false,
+            /*show_in_search=*/false, /*show_in_management=*/false,
+            /*handles_intents=*/false, /*allow_uninstall=*/true,
+            /*has_badge=*/false, /*paused=*/false);
+}
+
 // This framework conveniently sets up everything but borealis.
 using NonBorealisPublisherTest = StandaloneBrowserPublisherTest;
 
@@ -632,7 +658,7 @@
   VerifyApp(AppType::kChromeApp, store->id(), store->name(), Readiness::kReady,
             InstallReason::kDefault, InstallSource::kChromeWebStore, {},
             base::Time(), base::Time(), apps::Permissions(),
-            /*is_platform_app=*/false, /*recommendable=*/true,
+            /*is_platform_app=*/true, /*recommendable=*/true,
             /*searchable=*/true,
             /*show_in_launcher=*/true, /*show_in_shelf=*/true,
             /*show_in_search=*/true, /*show_in_management=*/true,
@@ -646,7 +672,7 @@
   VerifyApp(AppType::kChromeApp, store->id(), store->name(),
             Readiness::kUninstalledByUser, InstallReason::kDefault,
             InstallSource::kChromeWebStore, {}, base::Time(), base::Time(),
-            apps::Permissions(), /*is_platform_app=*/false,
+            apps::Permissions(), /*is_platform_app=*/true,
             /*recommendable=*/true,
             /*searchable=*/true,
             /*show_in_launcher=*/true, /*show_in_shelf=*/true,
@@ -659,7 +685,7 @@
   VerifyApp(AppType::kChromeApp, store->id(), store->name(), Readiness::kReady,
             InstallReason::kDefault, InstallSource::kChromeWebStore, {},
             base::Time(), base::Time(), apps::Permissions(),
-            /*is_platform_app=*/false, /*recommendable=*/true,
+            /*is_platform_app=*/true, /*recommendable=*/true,
             /*searchable=*/true,
             /*show_in_launcher=*/true, /*show_in_shelf=*/true,
             /*show_in_search=*/true, /*show_in_management=*/true,
@@ -672,7 +698,7 @@
   VerifyApp(AppType::kChromeApp, store->id(), store->name(), Readiness::kReady,
             InstallReason::kDefault, InstallSource::kChromeWebStore, {},
             kLastLaunchTime, base::Time(), apps::Permissions(),
-            /*is_platform_app=*/false);
+            /*is_platform_app=*/true);
 }
 
 TEST_F(PublisherTest, WebAppsOnApps) {
diff --git a/chrome/browser/apps/app_service/publishers/web_apps_crosapi.cc b/chrome/browser/apps/app_service/publishers/web_apps_crosapi.cc
index f41ecda..62fa613 100644
--- a/chrome/browser/apps/app_service/publishers/web_apps_crosapi.cc
+++ b/chrome/browser/apps/app_service/publishers/web_apps_crosapi.cc
@@ -101,6 +101,17 @@
       apps::LaunchResultToMojomLaunchResultCallback(std::move(callback)));
 }
 
+void WebAppsCrosapi::LaunchShortcut(const std::string& app_id,
+                                    const std::string& shortcut_id,
+                                    int64_t display_id) {
+  if (!LogIfNotConnected(FROM_HERE)) {
+    return;
+  }
+
+  controller_->ExecuteContextMenuCommand(app_id, shortcut_id,
+                                         base::DoNothing());
+}
+
 void WebAppsCrosapi::Connect(
     mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
     apps::mojom::ConnectOptionsPtr opts) {
diff --git a/chrome/browser/apps/app_service/publishers/web_apps_crosapi.h b/chrome/browser/apps/app_service/publishers/web_apps_crosapi.h
index 866a519..b16db45 100644
--- a/chrome/browser/apps/app_service/publishers/web_apps_crosapi.h
+++ b/chrome/browser/apps/app_service/publishers/web_apps_crosapi.h
@@ -72,6 +72,9 @@
                 apps::LoadIconCallback callback) override;
   void LaunchAppWithParams(AppLaunchParams&& params,
                            LaunchCallback callback) override;
+  void LaunchShortcut(const std::string& app_id,
+                      const std::string& shortcut_id,
+                      int64_t display_id) override;
 
   // apps::PublisherBase overrides.
   void Connect(mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
diff --git a/chrome/browser/ash/borealis/borealis_features.cc b/chrome/browser/ash/borealis/borealis_features.cc
index d31bcae..c2dbcf1 100644
--- a/chrome/browser/ash/borealis/borealis_features.cc
+++ b/chrome/browser/ash/borealis/borealis_features.cc
@@ -224,7 +224,8 @@
   std::string board = GetBoardName();
   TokenAuthority auth = GetAuthorityForToken(board, hash_of_current_token);
   if (auth == TokenAuthority::kRejected) {
-    LOG(WARNING) << "Incorrect token for board=" << board;
+    LOG(WARNING) << "Incorrect token hash '" << hash_of_current_token
+                 << "' for board=" << board;
     return AllowStatus::kIncorrectToken;
   } else if (auth == TokenAuthority::kAllowedOverridesHardwareChecks) {
     return AllowStatus::kAllowed;
diff --git a/chrome/browser/ash/input_method/assistive_suggester.cc b/chrome/browser/ash/input_method/assistive_suggester.cc
index 31e5e87..05513866 100644
--- a/chrome/browser/ash/input_method/assistive_suggester.cc
+++ b/chrome/browser/ash/input_method/assistive_suggester.cc
@@ -1,7 +1,6 @@
 // Copyright 2019 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/ash/input_method/assistive_suggester.h"
 
 #include "ash/constants/ash_features.h"
@@ -430,7 +429,8 @@
 bool AssistiveSuggester::OnSurroundingTextChanged(const std::u16string& text,
                                                   int cursor_pos,
                                                   int anchor_pos) {
-  if (context_id_ == -1)
+  RecordAssistiveMatchMetrics(text, cursor_pos, anchor_pos);
+  if (!IsAssistiveFeatureEnabled() || context_id_ == -1)
     return false;
 
   if (IsMultiWordSuggestEnabled()) {
diff --git a/chrome/browser/ash/input_method/assistive_suggester.h b/chrome/browser/ash/input_method/assistive_suggester.h
index 94ea7f05..a5c65f41 100644
--- a/chrome/browser/ash/input_method/assistive_suggester.h
+++ b/chrome/browser/ash/input_method/assistive_suggester.h
@@ -58,12 +58,6 @@
   // Called when a text field loses focus, and suggester stops working.
   void OnBlur();
 
-  // Checks the text before cursor, emits metric if any assistive prefix is
-  // matched.
-  void RecordAssistiveMatchMetrics(const std::u16string& text,
-                                   int cursor_pos,
-                                   int anchor_pos);
-
   // Called when a surrounding text is changed.
   // Returns true if it changes the surrounding text, e.g. a suggestion is
   // generated or dismissed.
@@ -106,6 +100,12 @@
 
   bool IsExpandedMultiWordSuggestEnabled();
 
+  // Checks the text before cursor, emits metric if any assistive prefix is
+  // matched.
+  void RecordAssistiveMatchMetrics(const std::u16string& text,
+                                   int cursor_pos,
+                                   int anchor_pos);
+
   void RecordAssistiveMatchMetricsForAction(AssistiveType action);
 
   // Only the first applicable reason in DisabledReason enum is returned.
diff --git a/chrome/browser/ash/input_method/assistive_suggester_unittest.cc b/chrome/browser/ash/input_method/assistive_suggester_unittest.cc
index 0d32351..7befd13 100644
--- a/chrome/browser/ash/input_method/assistive_suggester_unittest.cc
+++ b/chrome/browser/ash/input_method/assistive_suggester_unittest.cc
@@ -30,7 +30,6 @@
 using ime::TextSuggestionMode;
 using ime::TextSuggestionType;
 
-const char kEmojiData[] = "happy,😀;😃;😄";
 const char kUsEnglishEngineId[] = "xkb:us::eng";
 const char kSpainSpanishEngineId[] = "xkb:es::spa";
 
@@ -454,6 +453,39 @@
   histogram_tester_.ExpectTotalCount("InputMethod.Assistive.Match", 0);
 }
 
+TEST_F(AssistiveSuggesterMultiWordTest, OnSuggestionExistShowSuggestion) {
+  std::vector<TextSuggestion> suggestions = {
+      TextSuggestion{.mode = TextSuggestionMode::kPrediction,
+                     .type = TextSuggestionType::kMultiWord,
+                     .text = "hello there"}};
+
+  assistive_suggester_->OnActivate(kUsEnglishEngineId);
+  assistive_suggester_->OnFocus(5);
+  assistive_suggester_->OnSurroundingTextChanged(u"", 0, 0);
+  assistive_suggester_->OnExternalSuggestionsUpdated(suggestions);
+
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), u"hello there");
+}
+
+TEST_F(AssistiveSuggesterMultiWordTest, OnDisabledFlagShouldNotShowSuggestion) {
+  feature_list_.Reset();
+  feature_list_.InitWithFeatures(
+      /*enabled_features=*/{},
+      /*disabled_features=*/{features::kAssistMultiWord});
+  std::vector<TextSuggestion> suggestions = {
+      TextSuggestion{.mode = TextSuggestionMode::kPrediction,
+                     .type = TextSuggestionType::kMultiWord,
+                     .text = "hello there"}};
+
+  assistive_suggester_->OnActivate(kUsEnglishEngineId);
+  assistive_suggester_->OnFocus(5);
+  assistive_suggester_->OnSurroundingTextChanged(u"", 0, 0);
+  assistive_suggester_->OnExternalSuggestionsUpdated(suggestions);
+
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
+}
+
 TEST_F(AssistiveSuggesterMultiWordTest,
        MatchMetricRecordedWhenOneOrMoreSuggestions) {
   std::vector<TextSuggestion> suggestions = {
@@ -672,6 +704,7 @@
   }
 
   void SetUp() override {
+    const char kEmojiData[] = "arrow,←;↑;→";
     suggestion_handler_ = std::make_unique<FakeSuggestionHandler>();
     assistive_suggester_ = std::make_unique<AssistiveSuggester>(
         suggestion_handler_.get(), profile_.get(),
@@ -704,11 +737,25 @@
       chrome_keyboard_controller_client_;
 };
 
+TEST_F(AssistiveSuggesterEmojiTest, ShouldNotSuggestWhenEmojiDisabled) {
+  profile_->GetPrefs()->SetBoolean(prefs::kEmojiSuggestionEnterpriseAllowed,
+                                   false);
+  profile_->GetPrefs()->SetBoolean(prefs::kEmojiSuggestionEnabled, false);
+
+  assistive_suggester_->OnActivate(kUsEnglishEngineId);
+  assistive_suggester_->OnFocus(5);
+
+  EXPECT_FALSE(assistive_suggester_->OnSurroundingTextChanged(u"arrow ", 6, 6));
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
+}
+
 TEST_F(AssistiveSuggesterEmojiTest, ShouldReturnPrefixBasedEmojiSuggestions) {
   assistive_suggester_->OnActivate(kUsEnglishEngineId);
   assistive_suggester_->OnFocus(5);
 
-  EXPECT_TRUE(assistive_suggester_->OnSurroundingTextChanged(u"happy ", 6, 6));
+  EXPECT_TRUE(assistive_suggester_->OnSurroundingTextChanged(u"arrow ", 6, 6));
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), u"←");
 }
 
 }  // namespace input_method
diff --git a/chrome/browser/ash/input_method/fake_suggestion_handler.cc b/chrome/browser/ash/input_method/fake_suggestion_handler.cc
index 6082bcb..15bb8844 100644
--- a/chrome/browser/ash/input_method/fake_suggestion_handler.cc
+++ b/chrome/browser/ash/input_method/fake_suggestion_handler.cc
@@ -67,7 +67,19 @@
     int context_id,
     const AssistiveWindowProperties& assistive_window,
     std::string* error) {
-  return false;
+  if (assistive_window.candidates.empty()) {
+    return true;
+  }
+
+  if (assistive_window.visible) {
+    showing_suggestion_ = true;
+    // TODO(b/225988020): Expand this class to allow for multiple suggestion
+    // candidates. Currently only saves the first one.
+    suggestion_text_ = assistive_window.candidates.front();
+  } else {
+    showing_suggestion_ = false;
+  }
+  return true;
 }
 
 void FakeSuggestionHandler::Announce(const std::u16string& message) {
diff --git a/chrome/browser/ash/input_method/native_input_method_engine.cc b/chrome/browser/ash/input_method/native_input_method_engine.cc
index 6342c0a..85108a9 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine.cc
@@ -190,9 +190,11 @@
   }
 }
 
-mojom::AutocorrectMode AutocorrectFlagsToMojoType(int flags) {
-  if ((flags & ui::TEXT_INPUT_FLAG_AUTOCORRECT_OFF) ||
-      (flags & ui::TEXT_INPUT_FLAG_SPELLCHECK_OFF)) {
+mojom::AutocorrectMode AutocorrectFlagsToMojoType(int flags,
+                                                  bool is_normal_screen) {
+  if (((flags & ui::TEXT_INPUT_FLAG_AUTOCORRECT_OFF) ||
+       (flags & ui::TEXT_INPUT_FLAG_SPELLCHECK_OFF)) ||
+      !is_normal_screen) {
     return mojom::AutocorrectMode::kDisabled;
   }
   return mojom::AutocorrectMode::kEnabled;
@@ -771,11 +773,15 @@
       OverrideXkbLayoutIfNeeded(InputMethodManager::Get()->GetImeKeyboard(),
                                 settings);
 
+      const bool is_normal_screen =
+          InputMethodManager::Get()->GetActiveIMEState()->GetUIStyle() ==
+          InputMethodManager::UIStyle::kNormal;
       auto input_field_info = mojom::InputFieldInfo::New(
           TextInputTypeToMojoType(context.type),
-          AutocorrectFlagsToMojoType(context.flags),
-          context.should_do_learning ? mojom::PersonalizationMode::kEnabled
-                                     : mojom::PersonalizationMode::kDisabled);
+          AutocorrectFlagsToMojoType(context.flags, is_normal_screen),
+          context.should_do_learning && is_normal_screen
+              ? mojom::PersonalizationMode::kEnabled
+              : mojom::PersonalizationMode::kDisabled);
       auto on_focus_callback = base::BindOnce(
           &NativeInputMethodEngine::ImeObserver::ActivateTextClient,
           weak_ptr_factory_.GetWeakPtr(), text_client_->context_id);
@@ -950,12 +956,7 @@
                                            .anchor_pos = anchor_pos,
                                            .offset_pos = offset_pos};
 
-  assistive_suggester_->RecordAssistiveMatchMetrics(text, cursor_pos,
-                                                    anchor_pos);
-  if (assistive_suggester_->IsAssistiveFeatureEnabled()) {
-    assistive_suggester_->OnSurroundingTextChanged(text, cursor_pos,
-                                                   anchor_pos);
-  }
+  assistive_suggester_->OnSurroundingTextChanged(text, cursor_pos, anchor_pos);
   autocorrect_manager_->OnSurroundingTextChanged(text, cursor_pos, anchor_pos);
   if (grammar_manager_->IsOnDeviceGrammarEnabled()) {
     grammar_manager_->OnSurroundingTextChanged(text, cursor_pos, anchor_pos);
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc b/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
index c2bb549..00a17487 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
@@ -501,6 +501,57 @@
   InputMethodManager::Shutdown();
 }
 
+TEST_F(NativeInputMethodEngineTest,
+       DisablesAutocorrectAndLearningAtLockScreen) {
+  TestingProfile testing_profile;
+  SetInputMethodOptions(testing_profile, /*autocorrect_enabled=*/true,
+                        /*predictive_writing_enabled=*/false);
+
+  testing::StrictMock<MockInputMethod> mock_input_method;
+  InputMethodManager::Initialize(
+      new TestInputMethodManager(&mock_input_method));
+  InputMethodManager::Get()->GetActiveIMEState()->SetUIStyle(
+      InputMethodManager::UIStyle::kLock);
+  NativeInputMethodEngine engine;
+  engine.Initialize(std::make_unique<StubInputMethodEngineObserver>(),
+                    /*extension_id=*/"", &testing_profile);
+
+  {
+    testing::InSequence seq;
+    EXPECT_CALL(mock_input_method,
+                OnFocus(MojoEq(ime::mojom::InputFieldInfo(
+                            ime::mojom::InputFieldType::kText,
+                            ime::mojom::AutocorrectMode::kDisabled,
+                            ime::mojom::PersonalizationMode::kDisabled)),
+                        _, _))
+        .WillOnce(
+            ::testing::Invoke([](ime::mojom::InputFieldInfoPtr info,
+                                 ime::mojom::InputMethodSettingsPtr settings,
+                                 base::OnceCallback<void(bool)> callback) {
+              EXPECT_EQ(*settings,
+                        *ime::mojom::InputMethodSettings::NewLatinSettings(
+                            ime::mojom::LatinSettings::New(
+                                /*autocorrect=*/true,
+                                /*predictive_writing=*/false)));
+              std::move(callback).Run(true);
+            }));
+    EXPECT_CALL(mock_input_method, OnSurroundingTextChanged(_, _, _));
+  }
+
+  ui::IMEEngineHandlerInterface::InputContext input_context(
+      ui::TEXT_INPUT_TYPE_TEXT, ui::TEXT_INPUT_MODE_DEFAULT,
+      ui::TEXT_INPUT_FLAG_NONE, ui::TextInputClient::FOCUS_REASON_MOUSE,
+      /*should_do_learning=*/true);
+  engine.Enable(kEngineIdUs);
+  engine.FlushForTesting();  // ensure input_method connected.
+  engine.FocusIn(input_context);
+  engine.FlushForTesting();
+
+  InputMethodManager::Get()->GetActiveIMEState()->SetUIStyle(
+      InputMethodManager::UIStyle::kNormal);
+  InputMethodManager::Shutdown();
+}
+
 TEST_F(NativeInputMethodEngineTest, FocusUpdatesXkbLayout) {
   TestingProfile testing_profile;
   SetPinyinLayoutPrefs(testing_profile, "Colemak");
diff --git a/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc b/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
index 53aedf6..33120a3 100644
--- a/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/marketing_opt_in_screen_browsertest.cc
@@ -194,7 +194,9 @@
   GetScreen()->set_ingore_pref_sync_for_testing(true);
 
   OobeBaseTest::SetUpOnMainThread();
-  LoginDisplayHost::default_host()->GetWizardContext()->is_branded_build = true;
+  auto* wizard_context = LoginDisplayHost::default_host()->GetWizardContext();
+  wizard_context->is_branded_build = true;
+  wizard_context->defer_oobe_flow_finished_for_tests = true;
 }
 
 MarketingOptInScreen* MarketingOptInScreenTest::GetScreen() {
diff --git a/chrome/browser/attribution_reporting/chrome_attribution_browsertest.cc b/chrome/browser/attribution_reporting/chrome_attribution_browsertest.cc
index ff32757..ff067163 100644
--- a/chrome/browser/attribution_reporting/chrome_attribution_browsertest.cc
+++ b/chrome/browser/attribution_reporting/chrome_attribution_browsertest.cc
@@ -31,6 +31,7 @@
     // Sets up the blink runtime feature for ConversionMeasurement.
     command_line->AppendSwitch(
         switches::kEnableExperimentalWebPlatformFeatures);
+    command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
   }
 
   void SetUpOnMainThread() override {
diff --git a/chrome/browser/attribution_reporting/conversions_usage_restriction_trial_browsertest.cc b/chrome/browser/attribution_reporting/conversions_usage_restriction_trial_browsertest.cc
deleted file mode 100644
index 6bd281e1..0000000
--- a/chrome/browser/attribution_reporting/conversions_usage_restriction_trial_browsertest.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/strings/strcat.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/embedder_support/origin_trials/features.h"
-#include "components/embedder_support/switches.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/content_features.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/url_loader_interceptor.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace {
-constexpr char kBaseDataDir[] = "content/test/data/attribution_reporting";
-constexpr char kOriginTrialTestPublicKey[] =
-    "dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=";
-}  // namespace
-
-// Chrome layer equivalent to
-// content/browser/conversion_origin_trial_browsertest.cc. Usage restrictions
-// are not implemented within content/shell.
-class ConversionsUsageRestrictionTrialBrowserTestBase
-    : public InProcessBrowserTest {
- public:
-  ConversionsUsageRestrictionTrialBrowserTestBase() = default;
-
-  void SetUpOnMainThread() override {
-    // We use a URLLoaderInterceptor, rather than the EmbeddedTestServer, since
-    // the origin trial token in the response is associated with a fixed
-    // origin, whereas EmbeddedTestServer serves content on a random port.
-    url_loader_interceptor_ =
-        std::make_unique<content::URLLoaderInterceptor>(base::BindRepeating(
-            [](content::URLLoaderInterceptor::RequestParams* params) -> bool {
-              if (params->url_request.url.path_piece() !=
-                      "/impression_with_third_party_trial.html" &&
-                  params->url_request.url.path_piece() !=
-                      "/third_party_token_injector.js") {
-                return false;
-              }
-
-              content::URLLoaderInterceptor::WriteResponse(
-                  base::StrCat(
-                      {kBaseDataDir, params->url_request.url.path_piece()}),
-                  params->client.get());
-              return true;
-            }));
-  }
-
-  void TearDownOnMainThread() override { url_loader_interceptor_.reset(); }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII(embedder_support::kOriginTrialPublicKey,
-                                    kOriginTrialTestPublicKey);
-  }
-
- private:
-  std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_;
-};
-
-class ConversionsOriginTrialSubsetExclusionBrowserTest
-    : public ConversionsUsageRestrictionTrialBrowserTestBase {
- public:
-  ConversionsOriginTrialSubsetExclusionBrowserTest() {
-    // Disable the alternative usage feature, this should prevent the OT token
-    // from enabling the API.
-    feature_list_.InitWithFeatures(
-        {}, {embedder_support::kConversionMeasurementAPIAlternativeUsage});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(ConversionsOriginTrialSubsetExclusionBrowserTest,
-                       InSubsetExclusion_TrialDisabled) {
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(
-      browser(),
-      GURL("https://example.test/impression_with_third_party_trial.html")));
-
-  EXPECT_EQ(false, EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                          "document.featurePolicy.features().includes('"
-                          "attribution-reporting')"));
-}
-
-class ConversionsOriginTrialNoSubsetExclusionBrowserTest
-    : public ConversionsUsageRestrictionTrialBrowserTestBase {
- public:
-  ConversionsOriginTrialNoSubsetExclusionBrowserTest() {
-    // Enable the alternative usage feature, this should allow the OT token to
-    // enable the API.
-    feature_list_.InitWithFeatures(
-        {embedder_support::kConversionMeasurementAPIAlternativeUsage}, {});
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(ConversionsOriginTrialNoSubsetExclusionBrowserTest,
-                       OutOfSubsetExclusion_TrialEnabled) {
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(
-      browser(),
-      GURL("https://example.test/impression_with_third_party_trial.html")));
-
-  EXPECT_EQ(true, EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
-                         "document.featurePolicy.features().includes('"
-                         "attribution-reporting')"));
-}
diff --git a/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc b/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
index 23a26cf..d8b2cc0 100644
--- a/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
+++ b/chrome/browser/browsing_topics/browsing_topics_service_browsertest.cc
@@ -9,6 +9,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/browsing_topics/browsing_topics_service.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/request_handler_util.h"
@@ -90,7 +91,8 @@
  public:
   BrowsingTopicsBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{blink::features::kBrowsingTopics},
+        /*enabled_features=*/{blink::features::kBrowsingTopics,
+                              features::kPrivacySandboxAdsAPIsOverride},
         /*disabled_features=*/{});
   }
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.cc b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
index 2ecd1adc..a3031bf 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
@@ -1294,11 +1294,11 @@
       }
     }
 
-    return;
+    // Send the progress report to the system notification regardless of whether
+    // Files app window exists as we may need to remove an existing
+    // notification.
   }
 
-  // If no Files app window exists we send the progress to the system
-  // notification.
   notification_manager_->HandleIOTaskProgress(status);
 }
 void EventRouter::OnRegistered(guest_os::GuestOsMountProviderRegistry::Id id,
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 241c28e..5c8ebc50 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -185,7 +185,8 @@
         /*enabled_features=*/
         {blink::features::kInterestGroupStorage,
          blink::features::kAdInterestGroupAPI, blink::features::kFledge,
-         blink::features::kFencedFrames},
+         blink::features::kFencedFrames,
+         features::kPrivacySandboxAdsAPIsOverride},
         /*disabled_features=*/
         {});
     net::test_server::RegisterDefaultHandlers(embedded_test_server());
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc b/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
index 50f36b6c..00a662a 100644
--- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
+++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
@@ -19,6 +19,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/common/switches.h"
+#include "extensions/test/test_extension_dir.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
@@ -241,4 +242,81 @@
   EXPECT_TRUE(test_flags[2].picker_deleted);
 }
 
+// Not specifying a tab defaults to the extension's background page.
+// Service worker-based extensions don't have one, so they must specify
+// a tab. This is a regression test for crbug.com/1271590.
+IN_PROC_BROWSER_TEST_F(DesktopCaptureApiTest, ServiceWorkerMustSpecifyTab) {
+  static constexpr char kManifest[] =
+      R"({
+           "name": "Desktop Capture",
+           "manifest_version": 3,
+           "version": "0.1",
+           "background": { "service_worker": "worker.js" },
+           "permissions": ["desktopCapture"]
+         })";
+
+  static constexpr char kWorker[] =
+      R"(chrome.test.runTests([
+           function noTabIdSpecified() {
+             chrome.desktopCapture.chooseDesktopMedia(
+               ["screen", "window"],
+               function(id) {
+                 chrome.test.assertLastError(
+                     'A target tab is required when called from a service ' +
+                     'worker context.');
+                 chrome.test.succeed();
+             });
+        }]))";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(FILE_PATH_LITERAL("worker.js"), kWorker);
+
+  ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(DesktopCaptureApiTest, FromServiceWorker) {
+  static constexpr char kManifest[] =
+      R"({
+           "name": "Desktop Capture",
+           "manifest_version": 3,
+           "version": "0.1",
+           "background": { "service_worker": "worker.js" },
+           "permissions": ["desktopCapture", "tabs"]
+         })";
+
+  static constexpr char kWorker[] =
+      R"(chrome.test.runTests([
+           function tabIdSpecified() {
+             chrome.tabs.query({}, function(tabs) {
+               chrome.test.assertTrue(tabs.length == 1);
+               chrome.desktopCapture.chooseDesktopMedia(
+                 ["tab"], tabs[0],
+                 function(id) {
+                   chrome.test.assertEq("string", typeof id);
+                   chrome.test.assertTrue(id != "");
+                   chrome.test.succeed();
+                 });
+             });
+        }]))";
+
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(kManifest);
+  test_dir.WriteFile(FILE_PATH_LITERAL("worker.js"), kWorker);
+
+  // Open a tab to capture.
+  embedded_test_server()->ServeFilesFromDirectory(GetTestResourcesParentDir());
+  ASSERT_TRUE(StartEmbeddedTestServer());
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), GetURLForPath("localhost", "/test_file.html")));
+
+  FakeDesktopMediaPickerFactory::TestFlags test_flags[] = {
+      {.expect_tabs = true,
+       .selected_source = MakeFakeWebContentsMediaId(true)},
+  };
+  picker_factory_.SetTestFlags(test_flags, std::size(test_flags));
+
+  ASSERT_TRUE(RunExtensionTest(test_dir.UnpackedPath(), {}, {})) << message_;
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 9d70865..78ff18d 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -589,6 +589,10 @@
   features.Append(GenerateFeatureFlag(
       "multilingualtyping",
       base::FeatureList::IsEnabled(chromeos::features::kMultilingualTyping)));
+  features.Append(
+      GenerateFeatureFlag("autocorrectparamstuning",
+                          base::FeatureList::IsEnabled(
+                              chromeos::features::kAutocorrectParamsTuning)));
 
   results->SetKey("features", std::move(features));
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index dd5dcc1f..bf174be 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1249,7 +1249,6 @@
   {
     "name": "disable-office-editing-component-app",
     "owners": [
-      "chrome-apps-platform-rationalization@google.com",
       "quickoffice-chrome-eng@google.com"
     ],
     "expiry_milestone": 110
@@ -4864,6 +4863,14 @@
     "expiry_milestone": 104
   },
   {
+    "name": "privacy-sandbox-ads-apis",
+    "owners": [
+      "johnidel",
+      "jkarlin",
+      "pauljensen"],
+    "expiry_milestone": 104
+  },
+  {
     "name": "privacy-sandbox-v3-android",
     "owners": [
       "dullweber",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index dc7d3cd..454ff3e 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2130,6 +2130,11 @@
     "Enables UI updates for Privacy Guide. This requires #privacy-guide to "
     "also be enabled";
 
+const char kPrivacySandboxAdsAPIsOverrideName[] = "Privacy Sandbox Ads APIs";
+const char kPrivacySandboxAdsAPIsOverrideDescription[] =
+    "Enables Privacy Sandbox APIs: Attribtuion Reporting, Fledge, Topics and "
+    "their associated features.";
+
 const char kPrivacySandboxV3Name[] = "Privacy Sandbox V3";
 const char kPrivacySandboxV3Description[] =
     "Enables an updated Privacy Sandbox UI. Also enables some related "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index b9288ad..6229130c 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1200,6 +1200,9 @@
 extern const char kPrivacyGuide2Name[];
 extern const char kPrivacyGuide2Description[];
 
+extern const char kPrivacySandboxAdsAPIsOverrideName[];
+extern const char kPrivacySandboxAdsAPIsOverrideDescription[];
+
 extern const char kPrivacySandboxV3Name[];
 extern const char kPrivacySandboxV3Description[];
 
diff --git a/chrome/browser/interest_group/interest_group_permissions_browsertest.cc b/chrome/browser/interest_group/interest_group_permissions_browsertest.cc
index 5dd9f55e..f71d200 100644
--- a/chrome/browser/interest_group/interest_group_permissions_browsertest.cc
+++ b/chrome/browser/interest_group/interest_group_permissions_browsertest.cc
@@ -15,6 +15,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -28,7 +29,8 @@
     scoped_feature_list_.InitWithFeatures(
         /*enabled_features=*/
         {blink::features::kInterestGroupStorage,
-         blink::features::kAdInterestGroupAPI, blink::features::kFledge},
+         blink::features::kAdInterestGroupAPI, blink::features::kFledge,
+         features::kPrivacySandboxAdsAPIsOverride},
         /*disabled_features=*/
         {blink::features::kFencedFrames});
   }
@@ -202,7 +204,7 @@
     scoped_feature_list_.InitWithFeatures(
         {blink::features::kInterestGroupStorage},
         {blink::features::kAdInterestGroupAPI, blink::features::kFledge,
-         blink::features::kParakeet});
+         blink::features::kParakeet, features::kPrivacySandboxAdsAPIsOverride});
   }
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -222,7 +224,8 @@
  public:
   InterestGroupFledgeOnBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
-        {blink::features::kInterestGroupStorage, blink::features::kFledge},
+        {blink::features::kInterestGroupStorage, blink::features::kFledge,
+         features::kPrivacySandboxAdsAPIsOverride},
         {blink::features::kAdInterestGroupAPI, blink::features::kParakeet});
   }
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -245,7 +248,8 @@
   InterestGroupParakeetOnBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
         {blink::features::kInterestGroupStorage, blink::features::kParakeet},
-        {blink::features::kAdInterestGroupAPI, blink::features::kFledge});
+        {blink::features::kAdInterestGroupAPI, blink::features::kFledge,
+         features::kPrivacySandboxAdsAPIsOverride});
   }
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -269,7 +273,8 @@
     scoped_feature_list_.InitWithFeatures(
         {blink::features::kInterestGroupStorage,
          blink::features::kAdInterestGroupAPI},
-        {blink::features::kParakeet, blink::features::kFledge});
+        {blink::features::kParakeet, blink::features::kFledge,
+         features::kPrivacySandboxAdsAPIsOverride});
   }
   base::test::ScopedFeatureList scoped_feature_list_;
 };
diff --git a/chrome/browser/lacros/holding_space_service_lacros_browsertest.cc b/chrome/browser/lacros/holding_space_service_lacros_browsertest.cc
index 8d3beb8..8ab67c1 100644
--- a/chrome/browser/lacros/holding_space_service_lacros_browsertest.cc
+++ b/chrome/browser/lacros/holding_space_service_lacros_browsertest.cc
@@ -133,7 +133,7 @@
 
 // Verifies that print-to-PDF adds an associated item to holding space.
 IN_PROC_BROWSER_TEST_P(HoldingSpaceServicePrintToPdfIntegrationBrowserTest,
-                       AddPrintedPdfItem) {
+                       DISABLED_AddPrintedPdfItem) {
   // If holding space service interface is not available on this version of
   // ash-chrome, this test suite will no-op.
   if (!IsServiceAvailable())
diff --git a/chrome/browser/net/profile_network_context_service_browsertest.cc b/chrome/browser/net/profile_network_context_service_browsertest.cc
index b58138d..f101a9f 100644
--- a/chrome/browser/net/profile_network_context_service_browsertest.cc
+++ b/chrome/browser/net/profile_network_context_service_browsertest.cc
@@ -51,6 +51,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
 #include "components/privacy_sandbox/privacy_sandbox_settings.h"
+#include "components/privacy_sandbox/privacy_sandbox_test_util.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/content_features.h"
@@ -804,6 +805,12 @@
   ProvideRequestHandlerKeyCommitmentsToNetworkService("a.test");
   auto* privacy_sandbox_settings =
       PrivacySandboxSettingsFactory::GetForProfile(browser()->profile());
+  auto privacy_sandbox_delegate = std::make_unique<
+      privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>();
+  privacy_sandbox_delegate->SetupDefaultResponse(/*restricted=*/false,
+                                                 /*confirmed=*/true);
+  privacy_sandbox_settings->SetDelegateForTesting(
+      std::move(privacy_sandbox_delegate));
   privacy_sandbox_settings->SetPrivacySandboxEnabled(true);
   browser()->profile()->GetPrefs()->SetInteger(
       prefs::kCookieControlsMode,
diff --git a/chrome/browser/policy/messaging_layer/upload/record_upload_request_builder_unittest.cc b/chrome/browser/policy/messaging_layer/upload/record_upload_request_builder_unittest.cc
index 6375f03..6f5042ac 100644
--- a/chrome/browser/policy/messaging_layer/upload/record_upload_request_builder_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/upload/record_upload_request_builder_unittest.cc
@@ -136,7 +136,8 @@
   ASSERT_FALSE(request_payload.has_value()) << request_payload.value();
 }
 
-TEST_P(RecordUploadRequestBuilderTest, DenyPoorlyFormedEncryptedRecords) {
+TEST_P(RecordUploadRequestBuilderTest,
+       DISABLED_DenyPoorlyFormedEncryptedRecords) {
   // Reject empty record.
   EncryptedRecord record;
 
@@ -202,7 +203,7 @@
 }
 
 TEST_P(RecordUploadRequestBuilderTest,
-       DontBuildCompressionRequestIfNoInformation) {
+       DISABLED_DontBuildCompressionRequestIfNoInformation) {
   EncryptedRecord compressionless_record = GenerateEncryptedRecord("TEST_INFO");
   ASSERT_FALSE(compressionless_record.has_compression_information());
 
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/AdPersonalizationFragment.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/AdPersonalizationFragment.java
index 50fca7d..379ac1f 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/AdPersonalizationFragment.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/AdPersonalizationFragment.java
@@ -81,9 +81,9 @@
         List<Topic> blockedTopics = PrivacySandboxBridge.getBlockedTopics();
 
         int description = PrivacySandboxBridge.isPrivacySandboxEnabled()
-                ? (!currentTopics.isEmpty()
-                                ? R.string.privacy_sandbox_ad_personalization_description_trials_on
-                                : R.string.privacy_sandbox_ad_personalization_description_no_items)
+                ? (currentTopics.isEmpty() && blockedTopics.isEmpty()
+                                ? R.string.privacy_sandbox_ad_personalization_description_no_items
+                                : R.string.privacy_sandbox_ad_personalization_description_trials_on)
                 : R.string.privacy_sandbox_ad_personalization_description_trials_off;
         mDescriptionPreference.setSummary(description);
 
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
index fbfb56e..eaf3d35 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
@@ -111,6 +111,16 @@
   return number_of_days;
 }
 
+// Returns whether 3P cookies are blocked by |cookie_settings|. This can be
+// either through blocking 3P cookies directly, or blocking all cookies.
+bool AreThirdPartyCookiesBlocked(
+    content_settings::CookieSettings* cookie_settings) {
+  const auto default_content_setting =
+      cookie_settings->GetDefaultCookieSetting(/*provider_id=*/nullptr);
+  return cookie_settings->ShouldBlockThirdPartyCookies() ||
+         default_content_setting == ContentSetting::CONTENT_SETTING_BLOCK;
+}
+
 }  // namespace
 
 PrivacySandboxService::PrivacySandboxService() = default;
@@ -163,6 +173,11 @@
   // further separated from cookie controls.
   MaybeReconcilePrivacySandboxPref();
 
+  // When the user enters the Privacy Sandbox 3 experiment, the default value
+  // of their V2 pref must be set. This is a one time operation that is checked
+  // here to ensure it runs on profile startup.
+  InitializePrivacySandboxV2Pref();
+
   // If the Sandbox is currently restricted, disable the V2 preference. The user
   // must manually enable the sandbox if they stop being restricted.
   if (IsPrivacySandboxRestricted())
@@ -173,15 +188,8 @@
 
 PrivacySandboxService::DialogType
 PrivacySandboxService::GetRequiredDialogType() {
-  const auto cookie_controls_mode =
-      static_cast<content_settings::CookieControlsMode>(
-          pref_service_->GetInteger(prefs::kCookieControlsMode));
-  const auto default_content_setting =
-      cookie_settings_->GetDefaultCookieSetting(/*provider_id=*/nullptr);
   const auto third_party_cookies_blocked =
-      default_content_setting == ContentSetting::CONTENT_SETTING_BLOCK ||
-      cookie_controls_mode ==
-          content_settings::CookieControlsMode::kBlockThirdParty;
+      AreThirdPartyCookiesBlocked(cookie_settings_);
   return GetRequiredDialogTypeInternal(pref_service_, profile_type_,
                                        privacy_sandbox_settings_,
                                        third_party_cookies_blocked);
@@ -356,7 +364,9 @@
 }
 
 bool PrivacySandboxService::IsPrivacySandboxEnabled() {
-  return privacy_sandbox_settings_->IsPrivacySandboxEnabled();
+  return base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings3)
+             ? pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabledV2)
+             : pref_service_->GetBoolean(prefs::kPrivacySandboxFlocEnabled);
 }
 
 bool PrivacySandboxService::IsPrivacySandboxManaged() {
@@ -374,6 +384,11 @@
 }
 
 void PrivacySandboxService::SetPrivacySandboxEnabled(bool enabled) {
+  pref_service_->SetBoolean(
+      base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings3)
+          ? prefs::kPrivacySandboxManuallyControlledV2
+          : prefs::kPrivacySandboxManuallyControlled,
+      true);
   privacy_sandbox_settings_->SetPrivacySandboxEnabled(enabled);
 }
 
@@ -574,6 +589,39 @@
   LogPrivacySandboxState();
 }
 
+void PrivacySandboxService::InitializePrivacySandboxV2Pref() {
+  if (!base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings3))
+    return;
+
+  // The initialization process may turn a preference which is otherwise default
+  // off, on. The default setting for the user is provided by Finch and may
+  // change over time (e.g. location change). This init logic is however only
+  // ever performed once per profile, and so will not attempt to enable if the
+  // user changes location.
+  if (pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabledV2Init))
+    return;
+
+  pref_service_->SetBoolean(prefs::kPrivacySandboxApisEnabledV2Init, true);
+
+  // This logic should run before the user has had an opporunity to interact
+  // with the Privacy Sandbox controls.
+  DCHECK(
+      !pref_service_->GetBoolean(prefs::kPrivacySandboxManuallyControlledV2));
+
+  // Users must have the V1 sandbox enabled, 3P cookies enabled, and the
+  // appropriate feature parameter for the V2 pref to be default enabled.
+  if (!pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabled))
+    return;
+
+  if (AreThirdPartyCookiesBlocked(cookie_settings_))
+    return;
+
+  if (!privacy_sandbox::kPrivacySandboxSettings3DefaultOn.Get())
+    return;
+
+  pref_service_->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, true);
+}
+
 void PrivacySandboxService::StopObserving() {
   // Removing a non-observing observer is a no-op.
   sync_service_observer_.Reset();
@@ -629,6 +677,16 @@
                                   PSStartupStates::kDialogOffRestricted);
     return;
   }
+  // Handle manually controlled
+  if (pref_service_->GetBoolean(
+          prefs::kPrivacySandboxNoConfirmationManuallyControlled)) {
+    base::UmaHistogramEnumeration(
+        privacy_sandbox_startup_histogram,
+        sandbox_v2_enabled
+            ? PSStartupStates::kDialogOffManuallyControlledEnabled
+            : PSStartupStates::kDialogOffManuallyControlledDisabled);
+    return;
+  }
   if (privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get()) {
     if (!pref_service_->GetBoolean(prefs::kPrivacySandboxConsentDecisionMade)) {
       base::UmaHistogramEnumeration(privacy_sandbox_startup_histogram,
@@ -639,7 +697,7 @@
                                   sandbox_v2_enabled
                                       ? PSStartupStates::kConsentShownEnabled
                                       : PSStartupStates::kConsentShownDisabled);
-  } else {  // Notice required.
+  } else if (privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get()) {
     if (!pref_service_->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed)) {
       base::UmaHistogramEnumeration(privacy_sandbox_startup_histogram,
                                     PSStartupStates::kDialogWaiting);
@@ -649,6 +707,11 @@
                                   sandbox_v2_enabled
                                       ? PSStartupStates::kNoticeShownEnabled
                                       : PSStartupStates::kNoticeShownDisabled);
+  } else {  // No dialog currently required.
+    base::UmaHistogramEnumeration(
+        privacy_sandbox_startup_histogram,
+        sandbox_v2_enabled ? PSStartupStates::kNoDialogRequiredEnabled
+                           : PSStartupStates::kNoDialogRequiredDisabled);
   }
 }
 
@@ -824,6 +887,16 @@
   if (privacy_sandbox::kPrivacySandboxSettings3ForceShowNoticeForTesting.Get())
     return DialogType::kNotice;
 
+  // If neither consent or notice is required, no dialog is required.
+  if (!privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get() &&
+      !privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get()) {
+    return DialogType::kNone;
+  }
+
+  // Only one of the consent or notice should be required by Finch parameters.
+  DCHECK(!privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get() ||
+         !privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get());
+
   // Start by checking for any previous decision about the dialog, such as
   // it already having been shown, or not having been shown for some reason.
   // These checks for previous decisions occur in advance of their corresponding
@@ -869,6 +942,13 @@
     return DialogType::kNone;
   }
 
+  // If the user wasn't shown a confirmation because they are already manually
+  // controlling the sandbox, do not attempt to show one.
+  if (pref_service->GetBoolean(
+          prefs::kPrivacySandboxNoConfirmationManuallyControlled)) {
+    return DialogType::kNone;
+  }
+
   // If the Privacy Sandbox is restricted, no dialog is shown.
   if (privacy_sandbox_settings->IsPrivacySandboxRestricted()) {
     pref_service->SetBoolean(
@@ -891,6 +971,14 @@
     return DialogType::kNone;
   }
 
+  // If the Privacy Sandbox has been manually controlled by the user, no dialog
+  // is shown.
+  if (pref_service->GetBoolean(prefs::kPrivacySandboxManuallyControlledV2)) {
+    pref_service->SetBoolean(
+        prefs::kPrivacySandboxNoConfirmationManuallyControlled, true);
+    return DialogType::kNone;
+  }
+
   // If a user now requires consent, but has previously seen a notice, whether
   // a consent is shown depends on their current Privacy Sandbox setting.
   if (privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get() &&
@@ -929,11 +1017,6 @@
   DCHECK(!pref_service->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed));
   DCHECK(!pref_service->GetBoolean(prefs::kPrivacySandboxConsentDecisionMade));
 
-  // The user should not have been able to enable the Sandbox without a
-  // previous decision having been made. The exception to this is through test
-  // only feature parameters, which will have let the user skip confirmation.
-  DCHECK(!pref_service->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
-
   // If the user had previously disabled the Privacy Sandbox, no confirmation
   // will be shown.
   if (!pref_service->GetBoolean(prefs::kPrivacySandboxApisEnabled)) {
@@ -944,9 +1027,11 @@
 
   // Check if the users requires a consent. This information is provided by
   // feature parameter to allow Finch based geo-targeting.
-  if (privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get())
+  if (privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get()) {
     return DialogType::kConsent;
+  }
 
   // Finally a notice is required.
+  DCHECK(privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get());
   return DialogType::kNotice;
 }
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service.h b/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
index a561f8a..9b353b5 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
@@ -177,13 +177,18 @@
   // Sets the FLoC preference to |enabled|.
   void SetFlocPrefEnabled(bool enabled) const;
 
-  // Disables the Privacy Sandbox completely if |enabled| is false, if |enabled|
-  // is true, more granular checks will still be performed to determine if
-  // specific APIs are available in specific contexts.
+  // Disables the Privacy Sandbox completely if |enabled| is false. If |enabled|
+  // is true, context specific as well as restriction/confirmation checks
+  // will still be performed to determine if specific APIs are available in
+  // specific contexts.
   void SetPrivacySandboxEnabled(bool enabled);
 
-  // Used by the UI to check if the API is enabled. Checks the primary
-  // pref directly.
+  // Used by the UI to check if the API is enabled. This is a UI function ONLY.
+  // Checks the primary pref directly, and _only_ the primary pref. There are
+  // many other reasons that API access may be denied that are not checked by
+  // this function. All decisions for allowing access to APIs should be routed
+  // through the PrivacySandboxSettings class.
+  // TODO(crbug.com/1310157): Rename this function to better reflect this.
   bool IsPrivacySandboxEnabled();
 
   // Returns whether the state of the API is managed.
@@ -288,6 +293,9 @@
                            NoMetricsRecorded);
   FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDialogTest, RestrictedDialog);
   FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDialogTest, ManagedNoDialog);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDialogTest,
+                           ManuallyControlledNoDialog);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDialogTest, NoParamNoDialog);
   FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDeathTest,
                            GetRequiredDialogType);
   FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
@@ -314,6 +322,15 @@
                            PrivacySandboxManagedEnabled);
   FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
                            PrivacySandboxManagedDisabled);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
+                           PrivacySandboxManuallyControlledEnabled);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
+                           PrivacySandboxManuallyControlledDisabled);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
+                           PrivacySandboxNoDialogDisabled);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
+                           PrivacySandboxNoDialogEnabled);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest, InitializeV2Pref);
   FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest, PrivacySandboxRestricted);
 
   // Should be used only for tests when mocking the service.
@@ -357,10 +374,14 @@
     kDialogOffManagedEnabled = 9,
     kDialogOffManagedDisabled = 10,
     kDialogOffRestricted = 11,
+    kDialogOffManuallyControlledEnabled = 12,
+    kDialogOffManuallyControlledDisabled = 13,
+    kNoDialogRequiredEnabled = 14,
+    kNoDialogRequiredDisabled = 15,
 
     // Add values above this line with a corresponding label in
     // tools/metrics/histograms/enums.xml
-    kMaxValue = kDialogOffRestricted,
+    kMaxValue = kNoDialogRequiredDisabled,
   };
 
   // Inspects the current sync state and settings to determine if the Privacy
@@ -375,6 +396,10 @@
   // user out of the sandbox.
   void ReconcilePrivacySandboxPref();
 
+  // Potentially enables the Privacy Sandbox V2 pref if required based on
+  // feature parameters and the profiles current state.
+  void InitializePrivacySandboxV2Pref();
+
   // Stops any observation of services being performed by this class.
   void StopObserving();
 
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc
index 060be49..a72191ba 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc
@@ -134,33 +134,33 @@
       /*new_api_pref=*/true,
       /*notice_displayed=*/false, /*consent_decision_made=*/false,
       /*confirmation_not_shown=*/false},
-     {/*dcheck_failure=*/true,
+     {/*dcheck_failure=*/false,
       /*dialog_type=*/PrivacySandboxService::DialogType::kNone,
-      /*new_api_pref=*/false}},
+      /*new_api_pref=*/true}},
 
     {{/*consent_required=*/true, /*old_api_pref=*/false,
       /*new_api_pref=*/true,
       /*notice_displayed=*/false, /*consent_decision_made=*/false,
       /*confirmation_not_shown=*/false},
-     {/*dcheck_failure=*/true,
+     {/*dcheck_failure=*/false,
       /*dialog_type=*/PrivacySandboxService::DialogType::kNone,
-      /*new_api_pref=*/false}},
+      /*new_api_pref=*/true}},
 
     {{/*consent_required=*/false, /*old_api_pref=*/true,
       /*new_api_pref=*/true,
       /*notice_displayed=*/false, /*consent_decision_made=*/false,
       /*confirmation_not_shown=*/false},
-     {/*dcheck_failure=*/true,
-      /*dialog_type=*/PrivacySandboxService::DialogType::kNone,
-      /*new_api_pref=*/false}},
+     {/*dcheck_failure=*/false,
+      /*dialog_type=*/PrivacySandboxService::DialogType::kNotice,
+      /*new_api_pref=*/true}},
 
     {{/*consent_required=*/true, /*old_api_pref=*/true,
       /*new_api_pref=*/true,
       /*notice_displayed=*/false, /*consent_decision_made=*/false,
       /*confirmation_not_shown=*/false},
-     {/*dcheck_failure=*/true,
-      /*dialog_type=*/PrivacySandboxService::DialogType::kNone,
-      /*new_api_pref=*/false}},
+     {/*dcheck_failure=*/false,
+      /*dialog_type=*/PrivacySandboxService::DialogType::kConsent,
+      /*new_api_pref=*/true}},
 
     {{/*consent_required=*/false, /*old_api_pref=*/false,
       /*new_api_pref=*/false,
@@ -618,7 +618,8 @@
   feature_list->Reset();
   feature_list->InitAndEnableFeatureWithParameters(
       privacy_sandbox::kPrivacySandboxSettings3,
-      {{"consent-required", test_state.consent_required ? "true" : "false"}});
+      {{"consent-required", test_state.consent_required ? "true" : "false"},
+       {"notice-required", !test_state.consent_required ? "true" : "false"}});
 
   pref_service->SetUserPref(
       prefs::kPrivacySandboxApisEnabled,
@@ -669,7 +670,8 @@
   }
 
   virtual void InitializeBeforeStart() {
-    mock_delegate()->SetupDefaultResponse(/*restricted=*/false);
+    mock_delegate()->SetupDefaultResponse(/*restricted=*/false,
+                                          /*confirmed=*/true);
   }
 
   virtual profile_metrics::BrowserProfileType GetProfileType() {
@@ -911,8 +913,7 @@
   base::HistogramTester histogram_tester;
   feature_list()->Reset();
   feature_list()->InitAndEnableFeatureWithParameters(
-      privacy_sandbox::kPrivacySandboxSettings3,
-      {{"consent-required", "false" /* consent required */}});
+      privacy_sandbox::kPrivacySandboxSettings3, {{"notice-required", "true"}});
   prefs()->SetUserPref(prefs::kPrivacySandboxNoConfirmationSandboxDisabled,
                        std::make_unique<base::Value>(false));
   prefs()->SetUserPref(prefs::kPrivacySandboxNoticeDisplayed,
@@ -1202,7 +1203,7 @@
   feature_list()->Reset();
   feature_list()->InitAndEnableFeatureWithParameters(
       privacy_sandbox::kPrivacySandboxSettings3,
-      {{"consent-required", "false" /* consent required */}});
+      {{"notice-required", "true" /* consent required */}});
   prefs()->SetUserPref(prefs::kPrivacySandboxNoConfirmationSandboxDisabled,
                        std::make_unique<base::Value>(false));
   prefs()->SetUserPref(prefs::kPrivacySandboxNoticeDisplayed,
@@ -1230,7 +1231,7 @@
   feature_list()->Reset();
   feature_list()->InitAndEnableFeatureWithParameters(
       privacy_sandbox::kPrivacySandboxSettings3,
-      {{"consent-required", "false" /* consent required */}});
+      {{"notice-required", "true" /* consent required */}});
   prefs()->SetUserPref(prefs::kPrivacySandboxNoConfirmationSandboxDisabled,
                        std::make_unique<base::Value>(false));
   prefs()->SetUserPref(prefs::kPrivacySandboxNoticeDisplayed,
@@ -1253,6 +1254,60 @@
       PrivacySandboxService::PSStartupStates::kNoticeShownDisabled, 1);
 }
 
+TEST_F(PrivacySandboxServiceTest, PrivacySandboxManuallyControlledEnabled) {
+  base::HistogramTester histogram_tester;
+  feature_list()->InitAndEnableFeature(
+      privacy_sandbox::kPrivacySandboxSettings3);
+  prefs()->SetUserPref(prefs::kPrivacySandboxApisEnabledV2,
+                       std::make_unique<base::Value>(true));
+  prefs()->SetUserPref(prefs::kPrivacySandboxNoConfirmationManuallyControlled,
+                       std::make_unique<base::Value>(true));
+  privacy_sandbox_service()->LogPrivacySandboxState();
+  histogram_tester.ExpectUniqueSample(kPrivacySandboxStartupHistogram,
+                                      PrivacySandboxService::PSStartupStates::
+                                          kDialogOffManuallyControlledEnabled,
+                                      1);
+}
+
+TEST_F(PrivacySandboxServiceTest, PrivacySandboxManuallyControlledDisabled) {
+  base::HistogramTester histogram_tester;
+  feature_list()->InitAndEnableFeature(
+      privacy_sandbox::kPrivacySandboxSettings3);
+  prefs()->SetUserPref(prefs::kPrivacySandboxApisEnabledV2,
+                       std::make_unique<base::Value>(false));
+  prefs()->SetUserPref(prefs::kPrivacySandboxNoConfirmationManuallyControlled,
+                       std::make_unique<base::Value>(true));
+  privacy_sandbox_service()->LogPrivacySandboxState();
+  histogram_tester.ExpectUniqueSample(kPrivacySandboxStartupHistogram,
+                                      PrivacySandboxService::PSStartupStates::
+                                          kDialogOffManuallyControlledDisabled,
+                                      1);
+}
+
+TEST_F(PrivacySandboxServiceTest, PrivacySandboxNoDialogDisabled) {
+  base::HistogramTester histogram_tester;
+  feature_list()->InitAndEnableFeature(
+      privacy_sandbox::kPrivacySandboxSettings3);
+  prefs()->SetUserPref(prefs::kPrivacySandboxApisEnabledV2,
+                       std::make_unique<base::Value>(false));
+  privacy_sandbox_service()->LogPrivacySandboxState();
+  histogram_tester.ExpectUniqueSample(
+      kPrivacySandboxStartupHistogram,
+      PrivacySandboxService::PSStartupStates::kNoDialogRequiredDisabled, 1);
+}
+
+TEST_F(PrivacySandboxServiceTest, PrivacySandboxNoDialogEnabled) {
+  base::HistogramTester histogram_tester;
+  feature_list()->InitAndEnableFeature(
+      privacy_sandbox::kPrivacySandboxSettings3);
+  prefs()->SetUserPref(prefs::kPrivacySandboxApisEnabledV2,
+                       std::make_unique<base::Value>(true));
+  privacy_sandbox_service()->LogPrivacySandboxState();
+  histogram_tester.ExpectUniqueSample(
+      kPrivacySandboxStartupHistogram,
+      PrivacySandboxService::PSStartupStates::kNoDialogRequiredEnabled, 1);
+}
+
 TEST_F(PrivacySandboxServiceTest, DialogActionsUMAActions) {
   base::UserActionTester user_action_tester;
 
@@ -1448,10 +1503,88 @@
             browsing_data_remover()->GetLastUsedOriginTypeMaskForTesting());
 }
 
+TEST_F(PrivacySandboxServiceTest, InitializeV2Pref) {
+  // Check that when the feature + parameters dictate, the V2 preference is
+  // turned on.
+  feature_list()->InitAndDisableFeature(
+      privacy_sandbox::kPrivacySandboxSettings3);
+  privacy_sandbox_service()->InitializePrivacySandboxV2Pref();
+  EXPECT_FALSE(prefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
+  prefs()->RemoveUserPref(prefs::kPrivacySandboxApisEnabledV2Init);
+
+  feature_list()->Reset();
+  feature_list()->InitAndEnableFeature(
+      privacy_sandbox::kPrivacySandboxSettings3);
+  privacy_sandbox_service()->InitializePrivacySandboxV2Pref();
+  EXPECT_FALSE(prefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
+  prefs()->RemoveUserPref(prefs::kPrivacySandboxApisEnabledV2Init);
+
+  feature_list()->Reset();
+  feature_list()->InitAndEnableFeatureWithParameters(
+      privacy_sandbox::kPrivacySandboxSettings3,
+      {{"setting-default-on", "true"}});
+  privacy_sandbox_service()->InitializePrivacySandboxV2Pref();
+  EXPECT_TRUE(prefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
+  prefs()->RemoveUserPref(prefs::kPrivacySandboxApisEnabledV2);
+  prefs()->RemoveUserPref(prefs::kPrivacySandboxApisEnabledV2Init);
+
+  // Blocking 3PC should prevent the pref from being enabled.
+  prefs()->SetUserPref(
+      prefs::kCookieControlsMode,
+      std::make_unique<base::Value>(static_cast<int>(
+          content_settings::CookieControlsMode::kBlockThirdParty)));
+  privacy_sandbox_service()->InitializePrivacySandboxV2Pref();
+  EXPECT_FALSE(prefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
+  prefs()->RemoveUserPref(prefs::kPrivacySandboxApisEnabledV2Init);
+  prefs()->RemoveUserPref(prefs::kCookieControlsMode);
+
+  // Blocking all cookies should prevent the pref from being enabled.
+  cookie_settings()->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
+  privacy_sandbox_service()->InitializePrivacySandboxV2Pref();
+  EXPECT_FALSE(prefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
+  prefs()->RemoveUserPref(prefs::kPrivacySandboxApisEnabledV2Init);
+  cookie_settings()->SetDefaultCookieSetting(CONTENT_SETTING_ALLOW);
+
+  // Having a disabled Privacy Sandbox V1 control should prevent the pref from
+  // being enabled.
+  prefs()->SetBoolean(prefs::kPrivacySandboxApisEnabled, false);
+  privacy_sandbox_service()->InitializePrivacySandboxV2Pref();
+  EXPECT_FALSE(prefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
+  prefs()->RemoveUserPref(prefs::kPrivacySandboxApisEnabledV2Init);
+  prefs()->RemoveUserPref(prefs::kPrivacySandboxApisEnabled);
+
+  // Otherwise the pref should be enabled, but only once.
+  privacy_sandbox_service()->InitializePrivacySandboxV2Pref();
+  EXPECT_TRUE(prefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
+  prefs()->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, false);
+  privacy_sandbox_service()->InitializePrivacySandboxV2Pref();
+  EXPECT_FALSE(prefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
+}
+
+class PrivacySandboxPrefInitTest : public PrivacySandboxServiceTest {
+  void InitializeBeforeStart() override {
+    feature_list()->InitAndEnableFeatureWithParameters(
+        privacy_sandbox::kPrivacySandboxSettings3,
+        {{"setting-default-on", "true"}});
+  }
+};
+
+TEST_F(PrivacySandboxPrefInitTest, InitalizeV2PrefOnStartup) {
+  // Confirm that the V2 pref has been initialized as part of the service
+  // startup. Conditions for initialization were set in the test creation.
+  EXPECT_TRUE(prefs()->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
+}
+
 class PrivacySandboxRestrictedTest : public PrivacySandboxServiceTest {
   void InitializeBeforeStart() override {
     prefs()->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, true);
-    mock_delegate()->SetupDefaultResponse(/*restricted=*/true);
+    mock_delegate()->SetupDefaultResponse(/*restricted=*/true,
+                                          /*confirmed=*/true);
+
+    // A restriction should override a default on preference.
+    feature_list()->InitAndEnableFeatureWithParameters(
+        privacy_sandbox::kPrivacySandboxSettings3,
+        {{"setting-default-on", "true"}});
   }
 };
 
@@ -1503,6 +1636,8 @@
     profile()->GetTestingPrefService()->SetUserPref(
         prefs::kPrivacySandboxPreferencesReconciled,
         std::make_unique<base::Value>(true));
+    mock_delegate()->SetupDefaultResponse(/*restricted=*/false,
+                                          /*confirmed=*/true);
   }
 
   void ResetReconciledPref() {
@@ -2276,6 +2411,14 @@
 TEST_F(PrivacySandboxServiceDialogTest, ManagedNoDialog) {
   // Confirm that when the Privacy Sandbox is managed, that no dialog is
   // shown.
+  SetupDialogTestState(feature_list(), prefs(),
+                       {/*consent_required=*/true,
+                        /*old_api_pref=*/true,
+                        /*new_api_pref=*/false,
+                        /*notice_displayed=*/false,
+                        /*consent_decision_made=*/false,
+                        /*confirmation_not_shown=*/false});
+
   prefs()->SetManagedPref(prefs::kPrivacySandboxApisEnabledV2,
                           base::Value(true));
   EXPECT_EQ(
@@ -2293,6 +2436,37 @@
           privacy_sandbox_settings(), /*third_party_cookies_blocked=*/false));
 }
 
+TEST_F(PrivacySandboxServiceDialogTest, ManuallyControlledNoDialog) {
+  // Confirm that if the Privacy Sandbox V2 is manually controlled by the user,
+  // that no dialog is shown.
+  SetupDialogTestState(feature_list(), prefs(),
+                       {/*consent_required=*/true,
+                        /*old_api_pref=*/true,
+                        /*new_api_pref=*/false,
+                        /*notice_displayed=*/false,
+                        /*consent_decision_made=*/false,
+                        /*confirmation_not_shown=*/false});
+  prefs()->SetUserPref(prefs::kPrivacySandboxManuallyControlledV2,
+                       base::Value(true));
+  EXPECT_EQ(
+      PrivacySandboxService::DialogType::kNone,
+      PrivacySandboxService::GetRequiredDialogTypeInternal(
+          prefs(), profile_metrics::BrowserProfileType::kRegular,
+          privacy_sandbox_settings(), /*third_party_cookies_blocked=*/false));
+}
+
+TEST_F(PrivacySandboxServiceDialogTest, NoParamNoDialog) {
+  // Confirm that if neither the consent or notice parameter is set, no dialog
+  // is required.
+  feature_list()->InitAndEnableFeature(
+      privacy_sandbox::kPrivacySandboxSettings3);
+  EXPECT_EQ(
+      PrivacySandboxService::DialogType::kNone,
+      PrivacySandboxService::GetRequiredDialogTypeInternal(
+          prefs(), profile_metrics::BrowserProfileType::kRegular,
+          privacy_sandbox_settings(), /*third_party_cookies_blocked=*/false));
+}
+
 class PrivacySandboxServiceDeathTest
     : public PrivacySandboxServiceDialogTestBase,
       public testing::TestWithParam<int> {};
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc
index a31b09a9..d3933826 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.cc
@@ -7,7 +7,9 @@
 #include "base/feature_list.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "components/prefs/pref_service.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/tribool.h"
 
@@ -48,3 +50,26 @@
   // No restrictions apply otherwise.
   return false;
 }
+
+bool PrivacySandboxSettingsDelegate::IsPrivacySandboxConfirmed() {
+  // Confirmation is only required for Privacy Sandbox release 3.
+  if (!base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings3))
+    return true;
+
+  // Manually enabling the override feature counts as confirmation.
+  if (base::FeatureList::IsEnabled(
+          privacy_sandbox::kOverridePrivacySandboxSettingsLocalTesting)) {
+    return true;
+  }
+
+  // Confirmation requires that either the Privacy Sandbox is manually
+  // controlled, or the user has seen the appropriate level of confirmation.
+  if (profile_->GetPrefs()->GetBoolean(
+          prefs::kPrivacySandboxManuallyControlledV2))
+    return true;
+
+  return profile_->GetPrefs()->GetBoolean(
+      privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get()
+          ? prefs::kPrivacySandboxConsentDecisionMade
+          : prefs::kPrivacySandboxNoticeDisplayed);
+}
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.h b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.h
index b9a9e460..0c458fe 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.h
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate.h
@@ -17,6 +17,7 @@
 
   // PrivacySandboxSettings::Delegate:
   bool IsPrivacySandboxRestricted() override;
+  bool IsPrivacySandboxConfirmed() override;
 
  private:
   Profile* profile_;
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate_unittest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate_unittest.cc
index 9e65a15..f92cd2f7 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate_unittest.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_settings_delegate_unittest.cc
@@ -9,6 +9,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
 #include "components/signin/public/identity_manager/account_capabilities_test_mutator.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "content/public/test/browser_task_environment.h"
@@ -51,6 +52,9 @@
     return adapter_->identity_test_env();
   }
   TestingProfile* profile() { return profile_.get(); }
+  sync_preferences::TestingPrefServiceSyncable* prefs() {
+    return profile()->GetTestingPrefService();
+  }
 
  private:
   content::BrowserTaskEnvironment browser_task_environment_;
@@ -103,3 +107,37 @@
   EXPECT_FALSE(delegate()->IsPrivacySandboxRestricted());
 }
 #endif
+
+TEST_F(PrivacySandboxSettingsDelegateTest, Confirmation_Release3Enabled) {
+  feature_list()->InitAndEnableFeature(
+      privacy_sandbox::kPrivacySandboxSettings3);
+  EXPECT_FALSE(delegate()->IsPrivacySandboxConfirmed());
+
+  // Manually controlling V1 should not count as confirmation, while V2 should.
+  prefs()->SetBoolean(prefs::kPrivacySandboxManuallyControlled, true);
+  EXPECT_FALSE(delegate()->IsPrivacySandboxConfirmed());
+  prefs()->SetBoolean(prefs::kPrivacySandboxManuallyControlledV2, true);
+  EXPECT_TRUE(delegate()->IsPrivacySandboxConfirmed());
+  prefs()->SetBoolean(prefs::kPrivacySandboxManuallyControlledV2, false);
+
+  // While a consent is not required, seeing a notice should suffice.
+  prefs()->SetBoolean(prefs::kPrivacySandboxNoticeDisplayed, true);
+  EXPECT_TRUE(delegate()->IsPrivacySandboxConfirmed());
+
+  // If a notice is required, it should not suffice.
+  feature_list()->Reset();
+  feature_list()->InitAndEnableFeatureWithParameters(
+      privacy_sandbox::kPrivacySandboxSettings3,
+      {{"consent-required", "true"}});
+  EXPECT_FALSE(delegate()->IsPrivacySandboxConfirmed());
+  prefs()->SetBoolean(prefs::kPrivacySandboxConsentDecisionMade, true);
+  EXPECT_TRUE(delegate()->IsPrivacySandboxConfirmed());
+}
+
+TEST_F(PrivacySandboxSettingsDelegateTest, Confirmation_Release3Disabled) {
+  // If the Privacy Sandbox Settings 3 feature is disabled, no confirmation is
+  // required.
+  feature_list()->InitAndDisableFeature(
+      privacy_sandbox::kPrivacySandboxSettings3);
+  EXPECT_TRUE(delegate()->IsPrivacySandboxConfirmed());
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index b1e36c1..a3f5b0c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -42,7 +42,6 @@
   "background/logging/log_types.js",
   "background/logging/output_logger.js",
   "background/logging/tree_dumper.js",
-  "background/math_handler.js",
   "background/output/output.js",
   "background/output/output_ancestry_info.js",
   "background/output/output_format_parser.js",
@@ -51,7 +50,6 @@
   "background/output/output_types.js",
   "background/panel_command.js",
   "background/phonetic_data.js",
-  "background/prefs.js",
   "background/user_action_monitor.js",
   "braille/bluetooth_braille_display_manager.js",
   "braille/bluetooth_braille_display_ui.js",
@@ -113,10 +111,12 @@
   "background/injected_script_loader.js",
   "background/keyboard_handler.js",
   "background/live_regions.js",
+  "background/math_handler.js",
   "background/media_automation_handler.js",
   "background/range_automation_handler.js",
   "background/page_load_sound_handler.js",
   "background/pointer_handler.js",
+  "background/prefs.js",
   "background/smart_sticky_mode.js",
   "background/logging/log.js",
   "braille/braille_key_event_rewriter.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index ac70485..4161194 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -16,6 +16,7 @@
 import {GestureCommandHandler} from './gesture_command_handler.js';
 import {BackgroundKeyboardHandler} from './keyboard_handler.js';
 import {LiveRegions} from './live_regions.js';
+import {MathHandler} from './math_handler.js';
 import {MediaAutomationHandler} from './media_automation_handler.js';
 import {PageLoadSoundHandler} from './page_load_sound_handler.js';
 import {RangeAutomationHandler} from './range_automation_handler.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js
index 795ddf14..c4addc6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js
@@ -12,6 +12,7 @@
 
 import {BrailleBackground} from './braille_background.js';
 import {InjectedScriptLoader} from './injected_script_loader.js';
+import {ChromeVoxPrefs} from './prefs.js';
 
 /**
  * This is the legacy ChromeVox background object.
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
index 28df8f7..122cbd37 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -16,6 +16,7 @@
 import {DesktopAutomationInterface} from './desktop_automation_interface.js';
 import {GestureGranularity} from './gesture_command_data.js';
 import {GestureInterface} from './gesture_interface.js';
+import {ChromeVoxPrefs} from './prefs.js';
 import {SmartStickyMode} from './smart_sticky_mode.js';
 
 const ActionType = chrome.automation.ActionType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
index 24a62ef..607cf40 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler.js
@@ -7,6 +7,9 @@
  */
 import {ChromeVoxKbHandler} from '../common/keyboard_handler.js';
 
+import {MathHandler} from './math_handler.js';
+import {ChromeVoxPrefs} from './prefs.js';
+
 /**
  * @enum {string}
  * Internal pass through mode state (see usage below).
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index 167efb6a..77fa89c0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -20,7 +20,6 @@
 goog.require('BrailleKeyEvent');
 goog.require('BrailleTranslatorManager');
 goog.require('ChromeVox');
-goog.require('ChromeVoxPrefs');
 goog.require('ChromeVoxState');
 goog.require('ChromeVoxStateObserver');
 goog.require('CommandHandlerInterface');
@@ -38,7 +37,6 @@
 goog.require('LibLouis.FormType');
 goog.require('LocaleOutputHelper');
 goog.require('LogStore');
-goog.require('MathHandler');
 goog.require('Msgs');
 goog.require('NavBraille');
 goog.require('Output');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/math_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/math_handler.js
index 23d905a..811ad78 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/math_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/math_handler.js
@@ -6,12 +6,10 @@
  * @fileoverview Handles math output and exploration.
  */
 
-goog.provide('MathHandler');
-
 /**
  * Initializes math for output and exploration.
  */
-MathHandler = class {
+export class MathHandler {
   /**
    * @param {!chrome.automation.AutomationNode} node
    */
@@ -93,7 +91,7 @@
     }
     return false;
   }
-};
+}
 
 
 /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
index 6acceb3..52c3eda 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
@@ -8,20 +8,12 @@
  *
  */
 
-goog.provide('ChromeVoxPrefs');
-goog.provide('RichTextSpeechStyle');
-
-goog.require('ConsoleTts');
-goog.require('EventStreamLogger');
-goog.require('ChromeVox');
-goog.require('ExtensionBridge');
-
 /**
  * This object has default values of preferences and contains the common
  * code for working with preferences shared by the Options and Background
  * pages.
  */
-ChromeVoxPrefs = class {
+export class ChromeVoxPrefs {
   constructor() {
     let lastRunVersion = localStorage['lastRunVersion'];
     if (!lastRunVersion) {
@@ -100,7 +92,7 @@
       EventStreamLogger.instance.notifyEventStreamFilterChangedAll(value);
     }
   }
-};
+}
 
 
 /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
index 643e955b..6a79707 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
@@ -6,6 +6,7 @@
  * @fileoverview ChromeVox options page.
  *
  */
+import {ChromeVoxPrefs} from '../background/prefs.js';
 
 import {TtsBackground} from '../common/tts_background.js';
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_loader.js
index 1323afd..3bc8cf77 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options_loader.js
@@ -8,11 +8,11 @@
 
 goog.require('AbstractTts');
 goog.require('BluetoothBrailleDisplayUI');
-goog.require('ConsoleTts');
-goog.require('Msgs');
-goog.require('PanelCommand');
 goog.require('BrailleTable');
 goog.require('BrailleTranslatorManager');
 goog.require('ChromeVox');
-goog.require('ChromeVoxPrefs');
+goog.require('ConsoleTts');
+goog.require('EventStreamLogger');
 goog.require('ExtensionBridge');
+goog.require('Msgs');
+goog.require('PanelCommand');
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index 20d2438..6fb97f01 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//chrome/browser/resources/tools/optimize_webui.gni")
 import("//chrome/common/features.gni")
+import("//tools/code_coverage/create_js_source_maps/create_js_source_maps.gni")
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/preprocess_if_expr.gni")
 import("//tools/polymer/html_to_wrapper.gni")
@@ -28,6 +29,7 @@
   in_folder = "."
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = ts_files
+  enable_removal_comments = true
 }
 
 preprocess_if_expr("preprocess_gen") {
@@ -36,6 +38,7 @@
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = html_wrapper_files
+  enable_removal_comments = true
 }
 
 # Copy all Mojom generated JS files used by the NTP to a common location so that
@@ -214,3 +217,10 @@
     ":preprocess_gen",
   ]
 }
+
+create_js_source_maps("sourcemaps") {
+  deps = [
+    ":preprocess",
+    ":preprocess_gen",
+  ]
+}
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.html b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.html
index 8caf22e8..e8624d0a 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.html
@@ -57,7 +57,7 @@
     <div id="resultText" aria-hidden="true"
         inner-h-t-m-l="[[getResultInnerHtml_(searchResult)]]">
     </div>
-    <iron-icon id="actionTypeIcon" icon="[[getActionTypeIcon_(searchResult)]]">
+    <iron-icon id="actionTypeIcon" icon="cr:arrow-forward">
     </iron-icon>
   </div>
 </div>
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.js b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.js
index a0d60c2..b1a2a4f 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_search_box/os_search_result_row.js
@@ -12,7 +12,6 @@
 import {assert, assertNotReached} from '//resources/js/assert.m.js';
 import {FocusRowBehavior} from '//resources/js/cr/ui/focus_row_behavior.m.js';
 import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
-import {loadTimeData} from '//resources/js/load_time_data.m.js';
 import {IronA11yAnnouncer} from '//resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js';
 import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -597,14 +596,6 @@
       assert(this.searchResult.urlPathWithParameters, 'Url path is empty.');
       this.recordSearchResultMetrics_();
 
-      // Enable launching Personalization Hub for settings related to wallpaper,
-      // ambient mode, user avatar, etc.
-      const externalUrlToOpen = this.getExternalUrlForSearchResult_();
-      if (externalUrlToOpen) {
-        OpenWindowProxyImpl.getInstance().openURL(externalUrlToOpen);
-        return;
-      }
-
       // |this.searchResult.urlPathWithParameters| separates the path and params
       // by a '?' char.
       const pathAndOptParams =
@@ -717,69 +708,4 @@
           return 'os-settings:settings-general';
       }
     },
-
-    /**
-     * @return {string} the external url to be opened in a new window. Empty if
-     *     no external url should be opened.
-     * @private
-     */
-    getExternalUrlForSearchResult_() {
-      if (!loadTimeData.getBoolean('isPersonalizationHubEnabled')) {
-        return '';
-      }
-
-      const PERSONALIZATION_ROOT_URL = 'chrome://personalization';
-      const PERSONALIZATION_AMBIENT_URL = PERSONALIZATION_ROOT_URL + '/ambient';
-      const PERSONALIZATION_USER_URL = PERSONALIZATION_ROOT_URL + '/user';
-      const PERSONALIZATION_WALLPAPER_URL =
-          PERSONALIZATION_ROOT_URL + '/wallpaper';
-
-      const SearchResultType = chromeos.settings.mojom.SearchResultType;
-      const Setting = chromeos.settings.mojom.Setting;
-      const Section = chromeos.settings.mojom.Section;
-      const Subpage = chromeos.settings.mojom.Subpage;
-      switch (this.searchResult.type) {
-        case SearchResultType.kSection: {
-          switch (this.searchResult.id.section) {
-            case Section.kPersonalization:
-              return PERSONALIZATION_ROOT_URL;
-          }
-        }
-        case SearchResultType.kSetting: {
-          switch (this.searchResult.id.setting) {
-            case Setting.kDarkModeOnOff:
-              return PERSONALIZATION_ROOT_URL;
-            case Setting.kAmbientModeOnOff:
-            case Setting.kAmbientModeSource:
-              return PERSONALIZATION_AMBIENT_URL;
-            case Setting.kChangeDeviceAccountImage:
-              return PERSONALIZATION_USER_URL;
-            case Setting.kOpenWallpaper:
-              return PERSONALIZATION_WALLPAPER_URL;
-          }
-        }
-        case SearchResultType.kSubpage: {
-          switch (this.searchResult.id.subpage) {
-            case Subpage.kDarkMode:
-              return PERSONALIZATION_ROOT_URL;
-            case Subpage.kAmbientMode:
-            case Subpage.kAmbientModeArtGalleryAlbum:
-            case Subpage.kAmbientModeGooglePhotosAlbum:
-              return PERSONALIZATION_AMBIENT_URL;
-            case Subpage.kChangePicture:
-              return PERSONALIZATION_USER_URL;
-          }
-        }
-      }
-      return '';
-    },
-
-    /**
-     * @return {string} The name of the icon to use.
-     * @private
-     */
-    getActionTypeIcon_() {
-      return this.getExternalUrlForSearchResult_() ? 'cr:open-in-new' :
-                                                     'cr:arrow-forward';
-    },
   });
diff --git a/chrome/browser/resources/settings/privacy_sandbox/app.html b/chrome/browser/resources/settings/privacy_sandbox/app.html
index 2ad6f2da..77c87c3 100644
--- a/chrome/browser/resources/settings/privacy_sandbox/app.html
+++ b/chrome/browser/resources/settings/privacy_sandbox/app.html
@@ -284,9 +284,10 @@
             </div>
             <div class="ad-personalization-section-title">
               $i18n{privacySandboxAdPersonalizationDialogTopicsTitle}
-              <iron-icon id="topicsTooltipIcon" tabindex="0"
+              <iron-icon id="topicsTooltipIcon" tabindex="0" role="button"
                   aria-describedby="topicsTooltip" icon="cr:info-outline"
-                  on-focus="onShowTooltip_" on-mouseenter="onShowTooltip_">
+                  on-focus="onShowTooltip_" on-mouseenter="onShowTooltip_"
+                  aria-label="$i18n{privacySandboxAdPersonalizationDialogTopicsTitle}">
               </iron-icon>
               <paper-tooltip id="topicsTooltip" for="topicsTooltipIcon"
                   position="bottom" manual-mode fit-to-visible-bounds>
@@ -321,9 +322,10 @@
             </div>
             <div class="ad-personalization-section-title">
               $i18n{privacySandboxAdPersonalizationDialogFledgeTitle}
-              <iron-icon id="fledgeTooltipIcon" tabindex="0"
+              <iron-icon id="fledgeTooltipIcon" tabindex="0" role="button"
                   aria-describedby="fledgeTooltip" icon="cr:info-outline"
-                  on-focus="onShowTooltip_" on-mouseenter="onShowTooltip_">
+                  on-focus="onShowTooltip_" on-mouseenter="onShowTooltip_"
+                  aria-label="$i18n{privacySandboxAdPersonalizationDialogFledgeTitle}">
               </iron-icon>
               <paper-tooltip id="fledgeTooltip" for="fledgeTooltipIcon"
                   position="bottom" manual-mode fit-to-visible-bounds>
@@ -364,7 +366,7 @@
                     privacySandboxSettingsView_)]]">
           <div class="ad-personalization-title" slot="title">
             <cr-icon-button id="adPersonalizationBackButton"
-                class="icon-arrow-back"
+                class="icon-arrow-back" aria-label="$i18n{back}"
                 on-click="onAdPersonalizationBackButtonClick_">
             </cr-icon-button>
             <span class="flex">
diff --git a/chrome/browser/resources/settings/privacy_sandbox/app.ts b/chrome/browser/resources/settings/privacy_sandbox/app.ts
index 7cea5202..80aaabf 100644
--- a/chrome/browser/resources/settings/privacy_sandbox/app.ts
+++ b/chrome/browser/resources/settings/privacy_sandbox/app.ts
@@ -246,7 +246,8 @@
     const enabled = this.getPref('privacy_sandbox.apis_enabled_v2').value;
     if (enabled) {
       return loadTimeData.getString(
-          this.topTopics_.length || this.joiningSites_.length ?
+          this.topTopics_.length || this.blockedTopics_.length ||
+                  this.joiningSites_.length || this.blockedSites_.length ?
               'privacySandboxAdPersonalizationDialogDescription' :
               'privacySandboxAdPersonalizationDialogDescriptionListsEmpty');
     }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 86996472..c449ce4 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2042,6 +2042,8 @@
       "app_list/search/mixer.h",
       "app_list/search/omnibox_answer_result.cc",
       "app_list/search/omnibox_answer_result.h",
+      "app_list/search/omnibox_lacros_provider.cc",
+      "app_list/search/omnibox_lacros_provider.h",
       "app_list/search/omnibox_provider.cc",
       "app_list/search/omnibox_provider.h",
       "app_list/search/omnibox_result.cc",
diff --git a/chrome/browser/ui/app_list/search/omnibox_lacros_provider.cc b/chrome/browser/ui/app_list/search/omnibox_lacros_provider.cc
new file mode 100644
index 0000000..fa05697
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/omnibox_lacros_provider.cc
@@ -0,0 +1,51 @@
+// Copyright 2022 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/app_list/search/omnibox_lacros_provider.h"
+
+#include "base/bind.h"
+#include "chrome/browser/ash/crosapi/crosapi_ash.h"
+#include "chrome/browser/ash/crosapi/crosapi_manager.h"
+#include "chrome/browser/ash/crosapi/search_provider_ash.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
+
+namespace app_list {
+
+OmniboxLacrosProvider::OmniboxLacrosProvider(
+    Profile* profile,
+    AppListControllerDelegate* list_controller)
+    : profile_(profile), list_controller_(list_controller) {
+  DCHECK(profile_);
+  DCHECK(list_controller_);
+
+  if (crosapi::CrosapiManager::IsInitialized()) {
+    search_provider_ =
+        crosapi::CrosapiManager::Get()->crosapi_ash()->search_provider_ash();
+    DCHECK(search_provider_);
+  }
+}
+
+OmniboxLacrosProvider::~OmniboxLacrosProvider() = default;
+
+void OmniboxLacrosProvider::Start(const std::u16string& query) {
+  if (!search_provider_)
+    return;
+
+  search_provider_->Search(
+      query, base::BindRepeating(&OmniboxLacrosProvider::OnResultsReceived,
+                                 weak_factory_.GetWeakPtr()));
+}
+
+ash::AppListSearchResultType OmniboxLacrosProvider::ResultType() const {
+  return ash::AppListSearchResultType::kOmnibox;
+}
+
+void OmniboxLacrosProvider::OnResultsReceived(
+    std::vector<crosapi::mojom::SearchResultPtr> results) {
+  // TODO(crbug.com/1228587): Implement.
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/omnibox_lacros_provider.h b/chrome/browser/ui/app_list/search/omnibox_lacros_provider.h
new file mode 100644
index 0000000..e9f8bd8
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/omnibox_lacros_provider.h
@@ -0,0 +1,44 @@
+// Copyright 2022 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_APP_LIST_SEARCH_OMNIBOX_LACROS_PROVIDER_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_OMNIBOX_LACROS_PROVIDER_H_
+
+#include "ash/public/cpp/app_list/app_list_types.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/app_list/search/search_provider.h"
+#include "chromeos/crosapi/mojom/launcher_search.mojom.h"
+
+class AppListControllerDelegate;
+class Profile;
+
+namespace crosapi {
+class SearchProviderAsh;
+}  // namespace crosapi
+
+namespace app_list {
+
+class OmniboxLacrosProvider : public SearchProvider {
+ public:
+  OmniboxLacrosProvider(Profile* profile,
+                        AppListControllerDelegate* list_controller);
+  ~OmniboxLacrosProvider() override;
+
+  // SearchProvider:
+  void Start(const std::u16string& query) override;
+  ash::AppListSearchResultType ResultType() const override;
+
+ private:
+  void OnResultsReceived(std::vector<crosapi::mojom::SearchResultPtr> results);
+
+  crosapi::SearchProviderAsh* search_provider_;
+  Profile* profile_;
+  AppListControllerDelegate* list_controller_;
+
+  base::WeakPtrFactory<OmniboxLacrosProvider> weak_factory_{this};
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_OMNIBOX_LACROS_PROVIDER_H_
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index 16d87ff..b3e14db 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -29,6 +29,7 @@
 #include "chrome/browser/ui/app_list/search/help_app_provider.h"
 #include "chrome/browser/ui/app_list/search/keyboard_shortcut_provider.h"
 #include "chrome/browser/ui/app_list/search/mixer.h"
+#include "chrome/browser/ui/app_list/search/omnibox_lacros_provider.h"
 #include "chrome/browser/ui/app_list/search/omnibox_provider.h"
 #include "chrome/browser/ui/app_list/search/os_settings_provider.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
@@ -106,8 +107,14 @@
                          profile, list_controller,
                          base::DefaultClock::GetInstance(), model_updater));
 
-  controller->AddProvider(omnibox_group_id, std::make_unique<OmniboxProvider>(
-                                                profile, list_controller));
+  if (app_list_features::IsLauncherLacrosIntegrationEnabled()) {
+    controller->AddProvider(
+        omnibox_group_id,
+        std::make_unique<OmniboxLacrosProvider>(profile, list_controller));
+  } else {
+    controller->AddProvider(omnibox_group_id, std::make_unique<OmniboxProvider>(
+                                                  profile, list_controller));
+  }
 
   size_t assistant_group_id = controller->AddGroup(kMaxAssistantTextResults);
   controller->AddProvider(assistant_group_id,
diff --git a/chrome/browser/ui/ash/ambient/ambient_client_impl.cc b/chrome/browser/ui/ash/ambient/ambient_client_impl.cc
index 82172c3..8f58000 100644
--- a/chrome/browser/ui/ash/ambient/ambient_client_impl.cc
+++ b/chrome/browser/ui/ash/ambient/ambient_client_impl.cc
@@ -111,6 +111,10 @@
 bool AmbientClientImpl::IsAmbientModeAllowed() {
   DCHECK(chromeos::features::IsAmbientModeEnabled());
 
+  if (is_allowed_for_testing_.has_value()) {
+    return is_allowed_for_testing_.value();
+  }
+
   if (ash::DemoSession::IsDeviceInDemoMode())
     return false;
 
@@ -141,6 +145,10 @@
   return true;
 }
 
+void AmbientClientImpl::SetAmbientModeAllowedForTesting(bool allowed) {
+  is_allowed_for_testing_ = allowed;
+}
+
 void AmbientClientImpl::RequestAccessToken(GetAccessTokenCallback callback) {
   auto* profile = GetProfileForActiveUser();
   DCHECK(profile);
diff --git a/chrome/browser/ui/ash/ambient/ambient_client_impl.h b/chrome/browser/ui/ash/ambient/ambient_client_impl.h
index c9c5b580..a83bf47 100644
--- a/chrome/browser/ui/ash/ambient/ambient_client_impl.h
+++ b/chrome/browser/ui/ash/ambient/ambient_client_impl.h
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/ambient/ambient_client.h"
 #include "ash/public/cpp/image_downloader.h"
 #include "base/memory/weak_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GoogleServiceAuthError;
 
@@ -27,6 +28,7 @@
 
   // ash::AmbientClient:
   bool IsAmbientModeAllowed() override;
+  void SetAmbientModeAllowedForTesting(bool allowed) override;
   void RequestAccessToken(GetAccessTokenCallback callback) override;
   void DownloadImage(const std::string& url,
                      ash::ImageDownloader::DownloadCallback callback) override;
@@ -50,6 +52,7 @@
 
   std::map<base::UnguessableToken, std::unique_ptr<signin::AccessTokenFetcher>>
       token_fetchers_;
+  absl::optional<bool> is_allowed_for_testing_;
   base::WeakPtrFactory<AmbientClientImpl> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
index 8fa941b..cc872427 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_ui_browsertest.cc
@@ -420,13 +420,6 @@
     return ui::DragDropTypes::DRAG_COPY;
   }
 
-  ui::mojom::DragOperation OnPerformDrop(
-      const ui::DropTargetEvent& event) override {
-    ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
-    PerformDrop(event, output_drag_op);
-    return output_drag_op;
-  }
-
   DropCallback GetDropCallback(const ui::DropTargetEvent& event) override {
     return base::BindOnce(&DropTargetView::PerformDrop, base::Unretained(this));
   }
diff --git a/chrome/browser/ui/quick_answers/quick_answers_state_controller_unittest.cc b/chrome/browser/ui/quick_answers/quick_answers_state_controller_unittest.cc
index f733b1482..87cde4bf 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_state_controller_unittest.cc
+++ b/chrome/browser/ui/quick_answers/quick_answers_state_controller_unittest.cc
@@ -24,11 +24,17 @@
   void OnSettingsEnabled(bool settings_enabled) override {
     settings_enabled_ = settings_enabled;
   }
+  void OnApplicationLocaleReady(
+      const std::string& application_locale) override {
+    application_locale_ = application_locale;
+  }
 
   bool settings_enabled() const { return settings_enabled_; }
+  const std::string& application_locale() const { return application_locale_; }
 
  private:
   bool settings_enabled_ = false;
+  std::string application_locale_;
 };
 
 class QuickAnswersStateControllerTest : public ChromeQuickAnswersTestBase {
@@ -91,6 +97,22 @@
   QuickAnswersState::Get()->RemoveObserver(observer());
 }
 
+TEST_F(QuickAnswersStateControllerTest, NotifyApplicationLocaleReady) {
+  QuickAnswersState::Get()->AddObserver(observer());
+
+  EXPECT_TRUE(QuickAnswersState::Get()->application_locale().empty());
+  EXPECT_TRUE(observer()->application_locale().empty());
+
+  const std::string application_locale = "en-US";
+
+  // The observer class should get an notification when the pref value changes.
+  prefs()->SetString(language::prefs::kApplicationLocale, application_locale);
+  EXPECT_EQ(QuickAnswersState::Get()->application_locale(), application_locale);
+  EXPECT_EQ(observer()->application_locale(), application_locale);
+
+  QuickAnswersState::Get()->RemoveObserver(observer());
+}
+
 TEST_F(QuickAnswersStateControllerTest, LocaleEligible) {
   UErrorCode error_code = U_ZERO_ERROR;
   icu::Locale::setDefault(icu::Locale(ULOC_US), error_code);
diff --git a/chrome/browser/ui/tabs/existing_base_sub_menu_model.cc b/chrome/browser/ui/tabs/existing_base_sub_menu_model.cc
index 9dec4757..4796dd1 100644
--- a/chrome/browser/ui/tabs/existing_base_sub_menu_model.cc
+++ b/chrome/browser/ui/tabs/existing_base_sub_menu_model.cc
@@ -20,12 +20,6 @@
       min_command_id_(min_command_id),
       parent_new_command_id_(parent_new_command_id) {}
 
-bool ExistingBaseSubMenuModel::GetAcceleratorForCommandId(
-    int command_id,
-    ui::Accelerator* accelerator) const {
-  return false;
-}
-
 const gfx::FontList* ExistingBaseSubMenuModel::GetLabelFontListAt(
     int index) const {
   if (GetTypeAt(index) == ui::MenuModel::TYPE_TITLE) {
@@ -40,14 +34,6 @@
          parent_delegate()->IsCommandIdAlerted(parent_new_command_id_);
 }
 
-bool ExistingBaseSubMenuModel::IsCommandIdChecked(int command_id) const {
-  return false;
-}
-
-bool ExistingBaseSubMenuModel::IsCommandIdEnabled(int command_id) const {
-  return true;
-}
-
 constexpr int ExistingBaseSubMenuModel::kMinExistingWindowCommandId;
 constexpr int ExistingBaseSubMenuModel::kMinExistingTabGroupCommandId;
 
diff --git a/chrome/browser/ui/tabs/existing_base_sub_menu_model.h b/chrome/browser/ui/tabs/existing_base_sub_menu_model.h
index 4aa0e15..c2eba01d 100644
--- a/chrome/browser/ui/tabs/existing_base_sub_menu_model.h
+++ b/chrome/browser/ui/tabs/existing_base_sub_menu_model.h
@@ -36,14 +36,10 @@
                            int parent_new_command_id_);
 
   // ui::SimpleMenuModel
-  bool GetAcceleratorForCommandId(int command_id,
-                                  ui::Accelerator* accelerator) const override;
   const gfx::FontList* GetLabelFontListAt(int index) const override;
 
   // ui::SimpleMenuModel::Delegate
   bool IsCommandIdAlerted(int command_id) const override;
-  bool IsCommandIdChecked(int command_id) const override;
-  bool IsCommandIdEnabled(int command_id) const override;
   void ExecuteCommand(int command_id, int event_flags) final;
 
   // Command IDs for various submenus.
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
index 8a96045..5a87a59b 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -34,6 +34,7 @@
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/style/typography.h"
+#include "ui/views/view.h"
 #include "ui/views/view_class_properties.h"
 
 namespace {
@@ -223,10 +224,10 @@
   tab_strip_->OnDragExited();
 }
 
-ui::mojom::DragOperation TabStripRegionView::OnPerformDrop(
+views::View::DropCallback TabStripRegionView::GetDropCallback(
     const ui::DropTargetEvent& event) {
   DCHECK(tab_strip_->WantsToReceiveAllDragEvents());
-  return tab_strip_->OnPerformDrop(event);
+  return tab_strip_->GetDropCallback(event);
 }
 
 void TabStripRegionView::ChildPreferredSizeChanged(views::View* child) {
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.h b/chrome/browser/ui/views/frame/tab_strip_region_view.h
index 9740ae4..a788e6e 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.h
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.h
@@ -60,8 +60,7 @@
   void OnDragEntered(const ui::DropTargetEvent& event) override;
   int OnDragUpdated(const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
-  ui::mojom::DragOperation OnPerformDrop(
-      const ui::DropTargetEvent& event) override;
+  DropCallback GetDropCallback(const ui::DropTargetEvent& event) override;
 
   // views::AccessiblePaneView:
   void ChildPreferredSizeChanged(views::View* child) override;
diff --git a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
index 33b85e0..f139ba7 100644
--- a/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
+++ b/chrome/browser/ui/views/menu_view_drag_and_drop_test.cc
@@ -83,7 +83,6 @@
   bool CanDrop(const OSExchangeData& data) override;
   void OnDragEntered(const ui::DropTargetEvent& event) override;
   int OnDragUpdated(const ui::DropTargetEvent& event) override;
-  DragOperation OnPerformDrop(const ui::DropTargetEvent& event) override;
   DropCallback GetDropCallback(const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
 
@@ -136,13 +135,6 @@
   return ui::DragDropTypes::DRAG_MOVE;
 }
 
-DragOperation TestTargetView::OnPerformDrop(const ui::DropTargetEvent& event) {
-  auto drop_cb = GetDropCallback(event);
-  ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
-  std::move(drop_cb).Run(event, output_drag_op);
-  return output_drag_op;
-}
-
 views::View::DropCallback TestTargetView::GetDropCallback(
     const ui::DropTargetEvent& event) {
   dragging_ = false;
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.cc b/chrome/browser/ui/views/passwords/password_save_update_view.cc
index 3c11fbe..745c326 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_view.cc
@@ -666,11 +666,6 @@
   if (!promo_controller)
     return;
 
-  // Make sure the Save/Update bubble doesn't get closed when the IPH bubble is
-  // opened.
-  const bool old_close_on_deactivate = close_on_deactivate();
-  set_close_on_deactivate(false);
-
   switch (type) {
     case IPHType::kRegular:
       if (promo_controller->MaybeShowPromo(
@@ -701,8 +696,6 @@
       break;
     }
   }
-
-  set_close_on_deactivate(old_close_on_deactivate);
 }
 
 void PasswordSaveUpdateView::CloseIPHBubbleIfOpen() {
diff --git a/chrome/browser/ui/views/user_education/help_bubble_factory_views.cc b/chrome/browser/ui/views/user_education/help_bubble_factory_views.cc
index a29dbd6..063e4ad 100644
--- a/chrome/browser/ui/views/user_education/help_bubble_factory_views.cc
+++ b/chrome/browser/ui/views/user_education/help_bubble_factory_views.cc
@@ -22,24 +22,6 @@
 #include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/view_utils.h"
 
-namespace {
-
-// Returns whether losing focus would cause a widget to be destroyed.
-// This prevents us from accidentally closing a widget a bubble is anchored to
-// at the cost of not being able to directly access the help bubble.
-bool BlurWouldCloseWidget(const views::Widget* widget) {
-  // Right now, we can only ask the question if we know the bubble is
-  // controlled by a BubbleDialogDelegateView, since runtime type information
-  // isn't present for any of the other objects involved.
-  auto* const contents = widget->widget_delegate()->GetContentsView();
-  return contents &&
-         views::IsViewClass<views::BubbleDialogDelegateView>(contents) &&
-         static_cast<const views::BubbleDialogDelegateView*>(contents)
-             ->close_on_deactivate();
-}
-
-}  // namespace
-
 DEFINE_FRAMEWORK_SPECIFIC_METADATA(HelpBubbleViews)
 DEFINE_FRAMEWORK_SPECIFIC_METADATA(HelpBubbleFactoryViews)
 
@@ -75,8 +57,7 @@
   // If the focus isn't in the help bubble, focus the help bubble.
   // Note that if is_focus_in_ancestor_widget is true, then anchor both exists
   // and has a widget, so anchor->GetWidget() will always be valid.
-  if (is_focus_in_ancestor_widget &&
-      !BlurWouldCloseWidget(anchor->GetWidget())) {
+  if (is_focus_in_ancestor_widget) {
     help_bubble_view_->GetWidget()->Activate();
     help_bubble_view_->RequestFocus();
     return true;
diff --git a/chrome/browser/ui/views/user_education/help_bubble_view.cc b/chrome/browser/ui/views/user_education/help_bubble_view.cc
index dfedc65..d92b82d 100644
--- a/chrome/browser/ui/views/user_education/help_bubble_view.cc
+++ b/chrome/browser/ui/views/user_education/help_bubble_view.cc
@@ -22,6 +22,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/vector_icons/vector_icons.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -281,6 +282,9 @@
 
 }  // namespace
 
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(HelpBubbleView,
+                                      kHelpBubbleElementIdForTesting);
+
 // Explicitly don't use the default DIALOG_SHADOW as it will show a black
 // outline in dark mode on Mac. Use our own shadow instead. The shadow type is
 // the same for all other platforms.
@@ -558,6 +562,7 @@
     SetInitiallyFocusedView(close_button);
   }
 
+  SetProperty(views::kElementIdentifierKey, kHelpBubbleElementIdForTesting);
   set_margins(gfx::Insets());
   set_title_margins(gfx::Insets());
   SetButtons(ui::DIALOG_BUTTON_NONE);
@@ -578,6 +583,10 @@
   SizeToContents();
 
   widget->ShowInactive();
+  auto* const anchor_bubble =
+      anchor_view->GetWidget()->widget_delegate()->AsBubbleDialogDelegate();
+  if (anchor_bubble)
+    anchor_pin_ = anchor_bubble->PreventCloseOnDeactivate();
   MaybeStartAutoCloseTimer();
 }
 
diff --git a/chrome/browser/ui/views/user_education/help_bubble_view.h b/chrome/browser/ui/views/user_education/help_bubble_view.h
index afbd2ef..0d355c0c 100644
--- a/chrome/browser/ui/views/user_education/help_bubble_view.h
+++ b/chrome/browser/ui/views/user_education/help_bubble_view.h
@@ -13,6 +13,7 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/ui/user_education/help_bubble_params.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/bubble/bubble_border.h"
@@ -35,6 +36,8 @@
 class HelpBubbleView : public views::BubbleDialogDelegateView {
  public:
   METADATA_HEADER(HelpBubbleView);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kHelpBubbleElementIdForTesting);
+
   HelpBubbleView(views::View* anchor_view,
                  HelpBubbleParams params,
                  absl::optional<gfx::Rect> anchor_rect = absl::nullopt);
@@ -72,7 +75,7 @@
   // localized, and a visible arrow is not shown.
   absl::optional<gfx::Rect> force_anchor_rect_;
 
-  views::ImageView* icon_view_ = nullptr;
+  base::raw_ptr<views::ImageView> icon_view_ = nullptr;
   std::vector<views::Label*> labels_;
 
   // If the bubble has buttons, it must be focusable.
@@ -89,6 +92,10 @@
   // than 1 we won't re-read the screenreader hint again.
   int activate_count_ = 0;
 
+  // Prevents the widget we're anchored to from disappearing when it loses
+  // focus, even if it's marked as close_on_deactivate.
+  std::unique_ptr<CloseOnDeactivatePin> anchor_pin_;
+
   // Auto close timeout. If the value is 0 (default), the bubble never times
   // out.
   base::TimeDelta timeout_;
diff --git a/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc
index 246b28e5..50b31f68 100644
--- a/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc
@@ -2,19 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/ui/user_education/help_bubble_params.h"
 #include "chrome/browser/ui/views/user_education/help_bubble_view.h"
 
+#include "base/test/bind.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/tabs/tab_group_header.h"
 #include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "content/public/test/browser_test.h"
+#include "ui/base/interaction/expect_call_in_scope.h"
+#include "ui/base/interaction/interaction_sequence.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/test/widget_test.h"
+#include "ui/views/view_utils.h"
 
 class HelpBubbleViewInteractiveTest : public InProcessBrowserTest {
  public:
@@ -57,3 +65,142 @@
   EXPECT_FALSE(bubble->GetWidget()->IsActive());
   bubble->Close();
 }
+
+// This is a regression test to ensure that help bubbles prevent other bubbles
+// they are anchored to from closing on loss of focus. Failing to do this
+// results in situations where a user can abort a user education journey by
+// entering accessible keyboard navigation commands to try to read the help
+// bubble, or by trying to interact with the help bubble with the mouse to e.g.
+// close it.
+IN_PROC_BROWSER_TEST_F(HelpBubbleViewInteractiveTest,
+                       BubblePreventsCloseOnLossOfFocus) {
+  UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
+
+  HelpBubbleView* help_bubble_view = nullptr;
+
+  browser()->tab_strip_model()->AddToNewGroup({0});
+  auto sequence =
+      ui::InteractionSequence::Builder()
+          .SetContext(browser()->window()->GetElementContext())
+          .SetCompletedCallback(completed.Get())
+          .AddStep(
+              ui::InteractionSequence::StepBuilder()
+                  .SetElementID(kTabGroupHeaderElementId)
+                  .SetType(ui::InteractionSequence::StepType::kShown)
+                  .SetStartCallback(base::BindLambdaForTesting(
+                      [](ui::InteractionSequence*,
+                         ui::TrackedElement* element) {
+                        // Show the tab group editor bubble.
+                        auto* const view =
+                            element->AsA<views::TrackedElementViews>()->view();
+                        view->ShowContextMenu(
+                            view->GetLocalBounds().CenterPoint(),
+                            ui::MenuSourceType::MENU_SOURCE_KEYBOARD);
+                      }))
+                  .Build())
+          .AddStep(
+              ui::InteractionSequence::StepBuilder()
+                  .SetElementID(kTabGroupEditorBubbleId)
+                  .SetType(ui::InteractionSequence::StepType::kShown)
+                  .SetMustRemainVisible(true)
+                  .SetStartCallback(base::BindLambdaForTesting(
+                      [&](ui::InteractionSequence*,
+                          ui::TrackedElement* element) {
+                        // Show a help bubble attached to the tab group editor
+                        // bubble.
+                        auto* const anchor_view =
+                            element->AsA<views::TrackedElementViews>()->view();
+                        HelpBubbleParams params;
+                        params.body_text = u"foo";
+                        help_bubble_view =
+                            new HelpBubbleView(anchor_view, std::move(params));
+                      }))
+                  .Build())
+          .AddStep(
+              ui::InteractionSequence::StepBuilder()
+                  .SetElementID(HelpBubbleView::kHelpBubbleElementIdForTesting)
+                  .SetType(ui::InteractionSequence::StepType::kShown)
+                  .SetMustRemainVisible(true)
+                  .SetStartCallback(base::BindLambdaForTesting(
+                      [&](ui::InteractionSequence*,
+                          ui::TrackedElement* element) {
+                        // Activate the help bubble. This should not cause the
+                        // editor to close.
+                        auto* const widget =
+                            element->AsA<views::TrackedElementViews>()
+                                ->view()
+                                ->GetWidget();
+                        widget->Activate();
+                        views::test::WidgetActivationWaiter(widget, true)
+                            .Wait();
+                      }))
+                  .Build())
+          .AddStep(ui::InteractionSequence::StepBuilder()
+                       .SetElementID(kTabGroupEditorBubbleId)
+                       .SetType(ui::InteractionSequence::StepType::kShown)
+                       .SetMustBeVisibleAtStart(true)
+                       .SetMustRemainVisible(true)
+                       .SetStartCallback(base::BindLambdaForTesting(
+                           [&](ui::InteractionSequence*,
+                               ui::TrackedElement* element) {
+                             // Activate the editor then close the help bubble.
+                             auto* const widget =
+                                 element->AsA<views::TrackedElementViews>()
+                                     ->view()
+                                     ->GetWidget();
+                             widget->Activate();
+                             views::test::WidgetActivationWaiter(widget, true)
+                                 .Wait();
+                             ASSERT_TRUE(widget->IsActive());
+                             help_bubble_view->GetWidget()->Close();
+                           }))
+                       .Build())
+          .AddStep(
+              ui::InteractionSequence::StepBuilder()
+                  // Wait for the help bubble to close.
+                  .SetElementID(HelpBubbleView::kHelpBubbleElementIdForTesting)
+                  .SetType(ui::InteractionSequence::StepType::kHidden)
+                  .Build())
+          .AddStep(
+              ui::InteractionSequence::StepBuilder()
+                  .SetElementID(kTabGroupEditorBubbleId)
+                  .SetType(ui::InteractionSequence::StepType::kShown)
+                  .SetMustBeVisibleAtStart(true)
+                  .SetStartCallback(base::BindLambdaForTesting(
+                      [&](ui::InteractionSequence*,
+                          ui::TrackedElement* element) {
+                        // Now that the help bubble is gone, locate the editor
+                        // again and transfer activation to its primary window
+                        // widget (the browser window)
+                        // - this should close the editor as it is no longer
+                        // pinned by the help bubble.
+                        auto* const widget =
+                            element->AsA<views::TrackedElementViews>()
+                                ->view()
+                                ->GetWidget();
+                        // Delay this in case we're chaining off of the previous
+                        // hidden step; we need the help bubble to fully clean
+                        // up (this wouldn't be an issue in an actual live
+                        // browser because the activation would be due to user
+                        // input and therefore have to be processed via the
+                        // message pump instead of being allowed to execute
+                        // inside the Widget's close logic).
+                        base::ThreadTaskRunnerHandle::Get()->PostTask(
+                            FROM_HERE,
+                            base::BindOnce(
+                                [](views::Widget* widget) {
+                                  widget->GetPrimaryWindowWidget()->Activate();
+                                },
+                                base::Unretained(widget)));
+                      }))
+                  .Build())
+          .AddStep(ui::InteractionSequence::StepBuilder()
+                       // Verify that the editor bubble closes now that it has
+                       // lost focus.
+                       .SetElementID(kTabGroupEditorBubbleId)
+                       .SetType(ui::InteractionSequence::StepType::kHidden)
+                       .Build())
+          .Build();
+
+  EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
+}
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 1e0d786..75c176d 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -973,8 +973,12 @@
                                 print_preview_ui()->initiator_title());
   initial_settings.SetBoolKey(kSettingPreviewModifiable,
                               print_preview_ui()->source_is_modifiable());
-  initial_settings.SetBoolKey(kSettingPreviewIsFromArc,
-                              print_preview_ui()->source_is_arc());
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  bool source_is_arc = print_preview_ui()->source_is_arc();
+#else
+  bool source_is_arc = false;
+#endif
+  initial_settings.SetBoolKey(kSettingPreviewIsFromArc, source_is_arc);
   initial_settings.SetStringKey(kSettingPrinterName, default_printer);
   initial_settings.SetBoolKey(kDocumentHasSelection,
                               print_preview_ui()->source_has_selection());
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index eba14d7..a09bcee 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -737,7 +737,9 @@
     return;
   PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
       print_preview_dialog->GetWebUI()->GetController());
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   print_preview_ui->source_is_arc_ = params.is_from_arc;
+#endif
   print_preview_ui->source_is_modifiable_ = params.is_modifiable;
   print_preview_ui->source_has_selection_ = params.has_selection;
   print_preview_ui->print_selection_only_ = params.selection_only;
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.h b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
index 43295b6..3d86a760 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
@@ -84,7 +84,9 @@
 
   const std::u16string& initiator_title() const { return initiator_title_; }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   bool source_is_arc() const { return source_is_arc_; }
+#endif
 
   bool source_is_modifiable() const { return source_is_modifiable_; }
 
@@ -261,8 +263,10 @@
   // Weak pointer to the WebUI handler.
   const raw_ptr<PrintPreviewHandler> handler_;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Indicates whether the source document is from ARC.
   bool source_is_arc_ = false;
+#endif
 
   // Indicates whether the source document can be modified.
   bool source_is_modifiable_ = true;
diff --git a/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc b/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
index ed47d5f..d875c9f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
@@ -212,6 +212,12 @@
   if (features::IsGuestModeActive())
     return;
 
+  if (ash::features::IsPersonalizationHubEnabled()) {
+    // Personalization search is handled by Personalization Hub when feature is
+    // on.
+    return;
+  }
+
   SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
   updater.AddSearchTags(GetPersonalizationSearchConcepts());
 
diff --git a/chrome/browser/web_applications/app_service/web_apps.cc b/chrome/browser/web_applications/app_service/web_apps.cc
index a3b86725..cfe76b1 100644
--- a/chrome/browser/web_applications/app_service/web_apps.cc
+++ b/chrome/browser/web_applications/app_service/web_apps.cc
@@ -155,6 +155,12 @@
   std::move(callback).Run(apps::LaunchResult());
 }
 
+void WebApps::LaunchShortcut(const std::string& app_id,
+                             const std::string& shortcut_id,
+                             int64_t display_id) {
+  publisher_helper().ExecuteContextMenuCommand(app_id, shortcut_id, display_id);
+}
+
 void WebApps::Connect(
     mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
     apps::mojom::ConnectOptionsPtr opts) {
diff --git a/chrome/browser/web_applications/app_service/web_apps.h b/chrome/browser/web_applications/app_service/web_apps.h
index 84f5314b..88a0cec 100644
--- a/chrome/browser/web_applications/app_service/web_apps.h
+++ b/chrome/browser/web_applications/app_service/web_apps.h
@@ -94,6 +94,9 @@
                 apps::LoadIconCallback callback) override;
   void LaunchAppWithParams(apps::AppLaunchParams&& params,
                            apps::LaunchCallback callback) override;
+  void LaunchShortcut(const std::string& app_id,
+                      const std::string& shortcut_id,
+                      int64_t display_id) override;
 
   // apps::mojom::Publisher overrides.
   void Connect(mojo::PendingRemote<apps::mojom::Subscriber> subscriber_remote,
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager.cc b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
index c42d88d..b2287c4 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
@@ -562,6 +562,11 @@
     return;
   }
 
+  if (PreinstalledWebAppsDisabled()) {
+    std::move(callback).Run({});
+    return;
+  }
+
   base::FilePath config_dir = GetConfigDir();
   if (config_dir.empty()) {
     std::move(callback).Run({});
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc b/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
index 634a55d..1f10e70 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/containers/contains.h"
 #include "base/feature_list.h"
 #include "base/path_service.h"
@@ -28,6 +29,7 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/account_id/account_id.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
@@ -536,4 +538,19 @@
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_CHROMEOS)
+class DisabledPreinstalledWebAppManagerTest
+    : public PreinstalledWebAppManagerTest {
+ public:
+  DisabledPreinstalledWebAppManagerTest() {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(
+        switches::kDisablePreinstalledApps);
+  }
+};
+
+TEST_F(DisabledPreinstalledWebAppManagerTest, LoadConfigsWhileDisabled) {
+  EXPECT_EQ(LoadApps(kGoodJsonTestDir).size(), 0u);
+}
+#endif  // #if BUILDFLAG(IS_CHROMEOS)
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc b/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc
index d1b885208..6a95a912 100644
--- a/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc
+++ b/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc
@@ -42,14 +42,17 @@
 
 }  // namespace
 
+bool PreinstalledWebAppsDisabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      ::switches::kDisablePreinstalledApps);
+}
+
 std::vector<ExternalInstallOptions> GetPreinstalledWebApps() {
   if (g_preinstalled_app_data_for_testing)
     return *g_preinstalled_app_data_for_testing;
 
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ::switches::kDisablePreinstalledApps)) {
+  if (PreinstalledWebAppsDisabled())
     return {};
-  }
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   // TODO(crbug.com/1104692): Replace these C++ configs with JSON configs like
@@ -121,49 +124,49 @@
     migrations.push_back(std::move(migration));
   }
 
-  if (g_preinstalled_app_data_for_testing)
-    return migrations;
-
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING) && BUILDFLAG(IS_CHROMEOS)
-  // Manually hard coded entries from
-  // https://chrome-internal.googlesource.com/chromeos/overlays/chromeos-overlay/+/main/chromeos-base/chromeos-default-apps/files/web_apps
-  // for any json configs that include a uninstall_and_replace field.
-  // This is a temporary measure while the default web app duplication
-  // issue is cleaned up.
-  // TODO(crbug.com/1290716): Clean up once no longer needed.
-  if (IsPreinstalledAppInstallFeatureEnabled(
-          kMigrateDefaultChromeAppToWebAppsGSuite.name, profile)) {
-    PreinstalledWebAppMigration keep_migration;
-    keep_migration.install_url =
-        GURL("https://keep.google.com/installwebapp?usp=chrome_default");
-    keep_migration.expected_web_app_id = kGoogleKeepAppId;
-    keep_migration.old_chrome_app_id = extension_misc::kGoogleKeepAppId;
-    migrations.push_back(std::move(keep_migration));
-  }
+  if (!g_preinstalled_app_data_for_testing && !PreinstalledWebAppsDisabled()) {
+    // Manually hard coded entries from
+    // https://chrome-internal.googlesource.com/chromeos/overlays/chromeos-overlay/+/main/chromeos-base/chromeos-default-apps/files/web_apps
+    // for any json configs that include a uninstall_and_replace field.
+    // This is a temporary measure while the default web app duplication
+    // issue is cleaned up.
+    // TODO(crbug.com/1290716): Clean up once no longer needed.
+    if (IsPreinstalledAppInstallFeatureEnabled(
+            kMigrateDefaultChromeAppToWebAppsGSuite.name, profile)) {
+      PreinstalledWebAppMigration keep_migration;
+      keep_migration.install_url =
+          GURL("https://keep.google.com/installwebapp?usp=chrome_default");
+      keep_migration.expected_web_app_id = kGoogleKeepAppId;
+      keep_migration.old_chrome_app_id = extension_misc::kGoogleKeepAppId;
+      migrations.push_back(std::move(keep_migration));
+    }
 
-  if (IsPreinstalledAppInstallFeatureEnabled(
-          kMigrateDefaultChromeAppToWebAppsNonGSuite.name, profile)) {
-    PreinstalledWebAppMigration books_migration;
-    books_migration.install_url =
-        GURL("https://play.google.com/books/installwebapp?usp=chromedefault");
-    books_migration.expected_web_app_id = kPlayBooksAppId;
-    books_migration.old_chrome_app_id = extension_misc::kGooglePlayBooksAppId;
-    migrations.push_back(std::move(books_migration));
+    if (IsPreinstalledAppInstallFeatureEnabled(
+            kMigrateDefaultChromeAppToWebAppsNonGSuite.name, profile)) {
+      PreinstalledWebAppMigration books_migration;
+      books_migration.install_url =
+          GURL("https://play.google.com/books/installwebapp?usp=chromedefault");
+      books_migration.expected_web_app_id = kPlayBooksAppId;
+      books_migration.old_chrome_app_id = extension_misc::kGooglePlayBooksAppId;
+      migrations.push_back(std::move(books_migration));
 
-    PreinstalledWebAppMigration maps_migration;
-    maps_migration.install_url =
-        GURL("https://www.google.com/maps/preview/pwa/ttinstall.html");
-    maps_migration.expected_web_app_id = kGoogleMapsAppId;
-    maps_migration.old_chrome_app_id = extension_misc::kGoogleMapsAppId;
-    migrations.push_back(std::move(maps_migration));
+      PreinstalledWebAppMigration maps_migration;
+      maps_migration.install_url =
+          GURL("https://www.google.com/maps/preview/pwa/ttinstall.html");
+      maps_migration.expected_web_app_id = kGoogleMapsAppId;
+      maps_migration.old_chrome_app_id = extension_misc::kGoogleMapsAppId;
+      migrations.push_back(std::move(maps_migration));
 
-    PreinstalledWebAppMigration movies_migration;
-    movies_migration.install_url = GURL(
-        "https://play.google.com/store/movies/"
-        "installwebapp?usp=chrome_default");
-    movies_migration.expected_web_app_id = kGoogleMoviesAppId;
-    movies_migration.old_chrome_app_id = extension_misc::kGooglePlayMoviesAppId;
-    migrations.push_back(std::move(movies_migration));
+      PreinstalledWebAppMigration movies_migration;
+      movies_migration.install_url = GURL(
+          "https://play.google.com/store/movies/"
+          "installwebapp?usp=chrome_default");
+      movies_migration.expected_web_app_id = kGoogleMoviesAppId;
+      movies_migration.old_chrome_app_id =
+          extension_misc::kGooglePlayMoviesAppId;
+      migrations.push_back(std::move(movies_migration));
+    }
   }
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING) && BUILDFLAG(IS_CHROMEOS)
   return migrations;
diff --git a/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.h b/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.h
index 6c2d84b..c0a9283 100644
--- a/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.h
+++ b/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.h
@@ -15,6 +15,8 @@
 
 namespace web_app {
 
+bool PreinstalledWebAppsDisabled();
+
 // Returns the list of web apps that should be pre-installed on new profiles.
 std::vector<ExternalInstallOptions> GetPreinstalledWebApps();
 
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java
index dfde561..0942c283 100644
--- a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java
@@ -6,6 +6,10 @@
 
 import android.view.View;
 
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Map;
 
 /**
@@ -137,4 +141,20 @@
      * @param key Key to identify the type of the notice.
      */
     default void reportNoticeDismissed(String key) {}
+
+    /** Types of feeds that can be invalidated. */
+    @IntDef({FeedIdentifier.ALL_FEEDS, FeedIdentifier.MAIN_FEED, FeedIdentifier.FOLLOWING_FEED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeedIdentifier {
+        int ALL_FEEDS = 0;
+        int MAIN_FEED = 1;
+        int FOLLOWING_FEED = 2;
+    }
+
+    /**
+     * Requests that the cache of one or all feeds should be invalidated so that that their contents
+     * are re-fetched the next time the feed is shown.
+     * @param toInvalidate Identifies which feed or feeds should have their caches invalidated.
+     */
+    default void invalidateContentCacheFor(@FeedIdentifier int toInvalidate) {}
 }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index c2632e3..938a1983 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1648144749-ccfe60ff8537a513b27416548f37272ec8626ab2.profdata
+chrome-linux-main-1648187939-32be12a69eb8bfe1cd98347ae985c46aab73d016.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index f20ea03..f154f00 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1648144749-e50da85dc22932c4d917580e7bd267cfe889b31b.profdata
+chrome-mac-arm-main-1648187939-42b0fe3811b3081ce1958f642076e2e290dc2502.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index b72f566..7566652 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1648144749-c9baea862655d2ef23474abda8fce3ac8875e181.profdata
+chrome-mac-main-1648187939-bca456cc755ac42c2a52ac2f92bcf1243fed3472.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 88648ef..49ed275 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1648155422-141ba16e22bbbd8aa760dfc86e2f4a05a810e7f1.profdata
+chrome-win32-main-1648187939-84b924621540e05a2715db8e2ebe859d79059acd.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index f066b11..e20d5d5 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1648155422-19d2500873166865703fbc30aa2ee02d166c26d0.profdata
+chrome-win64-main-1648187939-4e8a9495d3761f4dc4660c426f71b772b1f625a1.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 6ba85101..2eeb0b3 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1590,7 +1590,6 @@
       "../browser/apps/platform_apps/platform_app_navigation_redirector_browsertest.cc",
       "../browser/apps/platform_apps/service_worker_browsertest.cc",
       "../browser/attribution_reporting/chrome_attribution_browsertest.cc",
-      "../browser/attribution_reporting/conversions_usage_restriction_trial_browsertest.cc",
       "../browser/autocomplete/autocomplete_browsertest.cc",
       "../browser/autofill/autofill_autocomplete_browsertest.cc",
       "../browser/autofill/autofill_browsertest.cc",
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test.js
index 3ddbf19..762ff31 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test.js
@@ -214,6 +214,39 @@
         assert(!recentlyUsed);
       });
 
+  test('recently used should be empty after clearing', async () => {
+    EmojiPickerApiProxyImpl.getInstance().isIncognitoTextField = () =>
+        // first - insert an emoji to populate recently used
+        new Promise((resolve) => resolve({incognito: false}));
+    // yield to allow emoji-group and emoji buttons to render.
+    const emojiButton = (await waitForCondition(
+                             () => findInEmojiPicker(
+                                 '[data-group="0"] > emoji-group',
+                                 'emoji-button:nth-child(2)')))
+                            .shadowRoot.querySelector('button');
+    emojiButton.click();
+
+    // wait until emoji exists in recently used section.
+    const recentlyUsed =
+        (await waitForCondition(
+             () => findInEmojiPicker(
+                 '[data-group=history] > emoji-group', 'emoji-button')))
+            .shadowRoot.querySelector('button');
+
+    // click show clear button
+    findInEmojiPicker('.group', '#show-clear').click();
+    await waitForCondition(() => findInEmojiPicker('.group', '#clear-recents'));
+
+    // click clear button
+    findInEmojiPicker('.group', '#clear-recents').click();
+
+    // Expect no more history.
+    await waitForCondition(
+        () => findInEmojiPicker('[data-group=history] > emoji-group')
+                  .style.display === 'none',
+        'history failed to disappear');
+  });
+
 
   suite('<emoji-variants>', () => {
     /** @type {!EmojiButton} */
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.ts
index 84433e56..c585e8f 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_main_element_test.ts
@@ -4,7 +4,9 @@
 
 import {PersonalizationMain} from 'chrome://personalization/trusted/personalization_main_element.js';
 import {Paths, PersonalizationRouter} from 'chrome://personalization/trusted/personalization_router_element.js';
-import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {initElement, teardownElement} from './personalization_app_test_utils.js';
 
@@ -41,6 +43,7 @@
   });
 
   test('links to ambient subpage', async () => {
+    loadTimeData.overrideValues({'isAmbientModeAllowed': true});
     personalizationMainElement = initElement(PersonalizationMain);
     const original = PersonalizationRouter.instance;
     const goToRoutePromise = new Promise<[Paths, Object]>(resolve => {
@@ -61,4 +64,35 @@
     assertEquals(Paths.Ambient, path);
     assertDeepEquals({}, queryParams);
   });
+
+  test('no links to ambient subpage', async () => {
+    loadTimeData.overrideValues({'isAmbientModeAllowed': false});
+    personalizationMainElement = initElement(PersonalizationMain);
+
+    const ambientSubpageLink =
+        personalizationMainElement!.shadowRoot!.getElementById(
+            'ambientSubpageLink')!;
+    assertFalse(!!ambientSubpageLink);
+  });
+
+  test('has ambient preview', async () => {
+    loadTimeData.overrideValues({'isAmbientModeAllowed': true});
+    personalizationMainElement = initElement(PersonalizationMain);
+    await waitAfterNextRender(personalizationMainElement);
+
+    const preview = personalizationMainElement!.shadowRoot!.querySelector(
+        'ambient-preview')!;
+    assertTrue(!!preview);
+  });
+
+  test('has no ambient preview', async () => {
+    loadTimeData.overrideValues({'isAmbientModeAllowed': false});
+    personalizationMainElement = initElement(PersonalizationMain);
+    await waitAfterNextRender(personalizationMainElement);
+
+
+    const preview = personalizationMainElement!.shadowRoot!.querySelector(
+        'ambient-preview')!;
+    assertFalse(!!preview);
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.ts
index 96ac0f4..b6dd0a4f 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_router_element_test.ts
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PersonalizationRouter} from 'chrome://personalization/trusted/personalization_router_element.js';
+import {Paths, PersonalizationRouter} from 'chrome://personalization/trusted/personalization_router_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {initElement} from './personalization_app_test_utils.js';
 
@@ -16,4 +18,36 @@
     initElement(PersonalizationRouter);
     await reloadCalledPromise;
   });
+
+  test('will show ambient subpage if allowed', async () => {
+    loadTimeData.overrideValues({'isPersonalizationHubEnabled': true});
+    loadTimeData.overrideValues({'isAmbientModeAllowed': true});
+    const routerElement = initElement(PersonalizationRouter);
+    PersonalizationRouter.instance().goToRoute(Paths.Ambient);
+    await waitAfterNextRender(routerElement);
+
+    const mainElement =
+        routerElement.shadowRoot!.querySelector('personalization-main');
+    assertFalse(!!mainElement);
+
+    const ambientSubpage =
+        routerElement.shadowRoot!.querySelector('ambient-subpage');
+    assertTrue(!!ambientSubpage);
+  });
+
+  test('will not show ambient subpage if disallowed', async () => {
+    loadTimeData.overrideValues({'isPersonalizationHubEnabled': true});
+    loadTimeData.overrideValues({'isAmbientModeAllowed': false});
+    const routerElement = initElement(PersonalizationRouter);
+    PersonalizationRouter.instance().goToRoute(Paths.Ambient);
+    await waitAfterNextRender(routerElement);
+
+    const mainElement =
+        routerElement.shadowRoot!.querySelector('personalization-main');
+    assertTrue(!!mainElement);
+
+    const ambientSubpage =
+        routerElement.shadowRoot!.querySelector('ambient-subpage');
+    assertFalse(!!ambientSubpage);
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_update_page_test.js b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_update_page_test.js
index 899f3df..4849a952 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_update_page_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_update_page_test.js
@@ -115,17 +115,19 @@
     const version = '90.1.2.3';
     await initializeUpdatePage(version);
 
-    const progressComponent =
-        component.shadowRoot.querySelector('#progressMessage');
-    assertEquals('', progressComponent.textContent.trim());
+    const updateInstructionsDiv =
+        component.shadowRoot.querySelector('#updateInstructionsDiv');
+    assertFalse(updateInstructionsDiv.hidden);
+    const updateStatusDiv =
+        component.shadowRoot.querySelector('#updateStatusDiv');
+    assertTrue(updateStatusDiv.hidden);
     await clickPerformUpdateButton();
 
     service.triggerOsUpdateObserver(OsUpdateOperation.kDownloading, 0.5, 0);
     await flushTasks();
 
-    // TODO(gavindodd): update with i18n string
-    assertTrue(progressComponent.textContent.trim().startsWith(
-        'OS update progress received '));
+    assertTrue(updateInstructionsDiv.hidden);
+    assertFalse(updateStatusDiv.hidden);
   });
 
   test('UpdatePageShowHideUnqualifiedComponentsLink', () => {
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js
index 11d02b7..f27cd3e 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_search_box_test.js
@@ -368,57 +368,6 @@
   });
 
   test(
-      'Keypress Enter on personalization hub result can open a new window',
-      async () => {
-        // Enable personalization hub feature.
-        loadTimeData.overrideValues({isPersonalizationHubEnabled: true});
-        assertTrue(loadTimeData.getBoolean('isPersonalizationHubEnabled'));
-
-        const result = fakeResult('Wallpaper', 'personalization?settingId=500');
-        result.id.setting = chromeos.settings.mojom.Setting.kOpenWallpaper;
-
-        settingsSearchHandler.setFakeResults([result]);
-        await simulateSearch('fake query 1');
-        await waitForListUpdate();
-
-        const selectedOsRow = searchBox.getSelectedOsSearchResultRow_();
-        assertTrue(!!selectedOsRow);
-        assertEquals('cr:open-in-new', selectedOsRow.getActionTypeIcon_());
-
-        // Keypress with Enter key on any row specifically causes navigation to
-        // selected row's route. This can't happen unless the row is focused.
-        const enterEvent = new KeyboardEvent(
-            'keypress', {cancelable: true, key: 'Enter', keyCode: 13});
-        selectedOsRow.$.searchResultContainer.dispatchEvent(enterEvent);
-
-        assertEquals(1, openWindowProxy.getCallCount('openURL'));
-      });
-
-  test(
-      'Clicking on personalization hub result can open a new window',
-      async () => {
-        // Enable personalization hub feature.
-        loadTimeData.overrideValues({isPersonalizationHubEnabled: true});
-        assertTrue(loadTimeData.getBoolean('isPersonalizationHubEnabled'));
-
-        const result = fakeResult('Wallpaper', 'personalization?settingId=500');
-        result.id.setting = chromeos.settings.mojom.Setting.kOpenWallpaper;
-
-        settingsSearchHandler.setFakeResults([result]);
-        await simulateSearch('fake query 1');
-        await waitForListUpdate();
-
-        const selectedOsRow = searchBox.getSelectedOsSearchResultRow_();
-        assertTrue(!!selectedOsRow);
-        assertEquals('cr:open-in-new', selectedOsRow.getActionTypeIcon_());
-
-        // Clicking on the searchResultContainer of the row opens a new window.
-        selectedOsRow.$.searchResultContainer.click();
-
-        assertEquals(1, openWindowProxy.getCallCount('openURL'));
-      });
-
-  test(
       'Clicking on personalization hub result causes route change' +
           ' if personalization hub feature is disabled',
       async () => {
@@ -435,7 +384,6 @@
 
         const selectedOsRow = searchBox.getSelectedOsSearchResultRow_();
         assertTrue(!!selectedOsRow);
-        assertEquals('cr:arrow-forward', selectedOsRow.getActionTypeIcon_());
 
         // Clicking on the searchResultContainer of the row correctly changes
         // the route and dropdown to close.
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index bbda328..3b4f23a 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -2752,8 +2752,8 @@
       <message name="IDS_SHIMLESS_RMA_UPDATE_OS_VERY_OUT_OF_DATE" translateable="false" desc="Description when the installed OS is too many versions behind the update.">
         Chrome OS needs an additional update to get fully up to date
       </message>
-      <message name="IDS_SHIMLESS_RMA_UPDATE_OS_OUT_OF_DATE" translateable="false" desc="Description when the installed OS is not the most recent available.">
-        Chrome OS needs an additional update to get fully up to date.
+      <message name="IDS_SHIMLESS_RMA_UPDATE_OS_OUT_OF_DATE" translateable="false" desc="The instructions shown when there is a Chrome OS update available.">
+        Update to the latest version of Chrome OS for an optimized repair process
       </message>
       <message name="IDS_SHIMLESS_RMA_UPDATE_OS_NETWORK_UNAVAILABLE" translateable="false" desc="Notice that no network is available when attempting to update Chrome OS.">
         To check if OS is up to date, connect to the internet in previous screen
@@ -2773,6 +2773,9 @@
       <message name="IDS_SHIMLESS_RMA_UPDATE_VERSION_AND_RESTART" translateable="false" desc="Label for the button that updates the device to the latest version of Chrome OS then restarts the device.">
         Update to <ph name="VERSION_NUMBER">$1</ph> &#38; restart
       </message>
+      <message name="IDS_SHIMLESS_RMA_UPDATING_OS_VERSION" translateable="false" desc="The message shown while updating the device's OS version.">
+        Updating OS version
+      </message>
       <!-- Manually disable wp page -->
       <message name="IDS_SHIMLESS_RMA_MANUALLY_DISABLE_WP_INSTRUCTIONS" translateable="false" desc="The text instructions for how to manually disable write-protect on the device.">
         Disable write-protect to continue to the next screen. Learn how to disable write-protect for this device by viewing the instructions at the device manufacturer's support page. Once you disable write-protect, you will need to leave it disabled until the repair process is finished. Please expect a reboot after disabling write protection.
diff --git a/chromeos/components/quick_answers/BUILD.gn b/chromeos/components/quick_answers/BUILD.gn
index 3259b9b..9d987c34 100644
--- a/chromeos/components/quick_answers/BUILD.gn
+++ b/chromeos/components/quick_answers/BUILD.gn
@@ -38,6 +38,8 @@
     "utils/quick_answers_metrics.h",
     "utils/quick_answers_utils.cc",
     "utils/quick_answers_utils.h",
+    "utils/spell_checker.cc",
+    "utils/spell_checker.h",
     "utils/unit_conversion_constants.cc",
     "utils/unit_conversion_constants.h",
     "utils/unit_converter.cc",
@@ -45,8 +47,10 @@
   ]
   deps = [
     "//base",
+    "//chrome/common:constants",
     "//chromeos/components/quick_answers/public/cpp:cpp",
     "//chromeos/components/quick_answers/public/cpp:prefs",
+    "//chromeos/components/quick_answers/public/mojom",
     "//chromeos/constants",
     "//chromeos/services/assistant/public/shared",
     "//chromeos/services/machine_learning/public/cpp",
@@ -56,6 +60,8 @@
     "//components/prefs:prefs",
     "//components/signin/public/base",
     "//components/signin/public/identity_manager",
+    "//components/spellcheck/common",
+    "//content/public/browser",
     "//net:net",
     "//services/data_decoder/public/cpp",
     "//services/network/public/cpp:cpp",
diff --git a/chromeos/components/quick_answers/DEPS b/chromeos/components/quick_answers/DEPS
index 34c2af07..4e5168e 100644
--- a/chromeos/components/quick_answers/DEPS
+++ b/chromeos/components/quick_answers/DEPS
@@ -1,6 +1,9 @@
 include_rules = [
   "+ash/public",
+  "+chrome/common",
   "+components/language/core/browser",
+  "+components/spellcheck/common",
+  "+content/public/browser",
   "+services/data_decoder/public",
   "+third_party/hunspell",
   "+ui/base/l10n",
diff --git a/chromeos/components/quick_answers/public/cpp/quick_answers_state.cc b/chromeos/components/quick_answers/public/cpp/quick_answers_state.cc
index fc3f5ee..e3eef81 100644
--- a/chromeos/components/quick_answers/public/cpp/quick_answers_state.cc
+++ b/chromeos/components/quick_answers/public/cpp/quick_answers_state.cc
@@ -134,12 +134,17 @@
       kQuickAnswersUnitConversionEnabled,
       base::BindRepeating(&QuickAnswersState::UpdateUnitConversionEnabled,
                           base::Unretained(this)));
+  pref_change_registrar_->Add(
+      language::prefs::kApplicationLocale,
+      base::BindRepeating(&QuickAnswersState::OnApplicationLocaleReady,
+                          base::Unretained(this)));
 
   UpdateSettingsEnabled();
   UpdateConsentStatus();
   UpdateDefinitionEnabled();
   UpdateTranslationEnabled();
   UpdateUnitConversionEnabled();
+  OnApplicationLocaleReady();
 
   prefs_initialized_ = true;
 
@@ -267,14 +272,30 @@
   unit_conversion_enabled_ = unit_conversion_enabled;
 }
 
+void QuickAnswersState::OnApplicationLocaleReady() {
+  auto locale = pref_change_registrar_->prefs()->GetString(
+      language::prefs::kApplicationLocale);
+  if (application_locale_ == locale) {
+    return;
+  }
+  application_locale_ = locale;
+
+  for (auto& observer : observers_) {
+    observer.OnApplicationLocaleReady(locale);
+  }
+
+  UpdateEligibility();
+}
+
 void QuickAnswersState::UpdateEligibility() {
   if (!pref_change_registrar_)
     return;
 
-  std::string locale = pref_change_registrar_->prefs()->GetString(
-      language::prefs::kApplicationLocale);
+  if (application_locale_.empty())
+    return;
+
   std::string resolved_locale;
-  l10n_util::CheckAndResolveLocale(locale, &resolved_locale,
+  l10n_util::CheckAndResolveLocale(application_locale_, &resolved_locale,
                                    /*perform_io=*/false);
   is_eligible_ = IsQuickAnswersAllowedForLocale(
       resolved_locale, icu::Locale::getDefault().getName());
diff --git a/chromeos/components/quick_answers/public/cpp/quick_answers_state.h b/chromeos/components/quick_answers/public/cpp/quick_answers_state.h
index 5700e1e4..1b74634 100644
--- a/chromeos/components/quick_answers/public/cpp/quick_answers_state.h
+++ b/chromeos/components/quick_answers/public/cpp/quick_answers_state.h
@@ -35,6 +35,7 @@
 class QuickAnswersStateObserver : public base::CheckedObserver {
  public:
   virtual void OnSettingsEnabled(bool enabled) {}
+  virtual void OnApplicationLocaleReady(const std::string& locale) {}
 };
 
 // A class that holds Quick Answers related prefs and states.
@@ -66,6 +67,7 @@
   bool definition_enabled() const { return definition_enabled_; }
   bool translation_enabled() const { return translation_enabled_; }
   bool unit_conversion_enabled() const { return unit_conversion_enabled_; }
+  const std::string& application_locale() const { return application_locale_; }
   bool is_eligible() const { return is_eligible_; }
 
   void set_eligibility_for_testing(bool is_eligible) {
@@ -84,6 +86,7 @@
   void UpdateDefinitionEnabled();
   void UpdateTranslationEnabled();
   void UpdateUnitConversionEnabled();
+  void OnApplicationLocaleReady();
 
   // Called when the feature eligibility might change.
   void UpdateEligibility();
@@ -104,6 +107,9 @@
   // Whether the Quick Answers unit conversion is enabled.
   bool unit_conversion_enabled_ = true;
 
+  // The application locale.
+  std::string application_locale_;
+
   // Whether the Quick Answers feature is eligible. The value is derived from a
   // number of other states.
   bool is_eligible_ = false;
diff --git a/chromeos/components/quick_answers/utils/spell_checker.cc b/chromeos/components/quick_answers/utils/spell_checker.cc
new file mode 100644
index 0000000..b2ae686d
--- /dev/null
+++ b/chromeos/components/quick_answers/utils/spell_checker.cc
@@ -0,0 +1,229 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/quick_answers/utils/spell_checker.h"
+
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "chrome/common/chrome_paths.h"
+#include "components/spellcheck/common/spellcheck_common.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/service_process_host.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/url_response_head.mojom.h"
+
+namespace quick_answers {
+namespace {
+
+constexpr char kDownloadServerUrl[] =
+    "https://redirector.gvt1.com/edgedl/chrome/dict/";
+
+constexpr net::NetworkTrafficAnnotationTag kNetworkTrafficAnnotationTag =
+    net::DefineNetworkTrafficAnnotation("quick_answers_spellchecker", R"(
+          semantics {
+            sender: "Quick answers Spellchecker"
+            description:
+              "Download dictionary for Quick answers feature if necessary."
+            trigger: "Quick answers feature enabled."
+            data:
+              "The spell checking language identifier. No user identifier is "
+              "sent."
+            destination: GOOGLE_OWNED_SERVICE
+          }
+          policy {
+            cookies_allowed: NO
+            setting:
+              "Quick Answers can be enabled/disabled in ChromeOS Settings and"
+              "is subject to eligibility requirements."
+          })");
+
+constexpr int kMaxRetries = 3;
+
+base::FilePath GetDictionaryFilePath(const std::string& language) {
+  base::FilePath dict_dir;
+  base::PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir);
+  base::FilePath dict_path =
+      spellcheck::GetVersionedFileName(language, dict_dir);
+  return dict_path;
+}
+
+GURL GetDictionaryURL(const std::string& file_name) {
+  return GURL(std::string(kDownloadServerUrl) + base::ToLowerASCII(file_name));
+}
+
+bool SaveDictionaryData(std::unique_ptr<std::string> data,
+                        const base::FilePath& file_path) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+
+  // Create a temporary file.
+  base::FilePath tmp_path;
+  if (!base::CreateTemporaryFileInDir(file_path.DirName(), &tmp_path)) {
+    LOG(ERROR) << "Failed to create a temporary file.";
+    return false;
+  }
+
+  // Write to the temporary file.
+  size_t bytes_written =
+      base::WriteFile(tmp_path, data->data(), data->length());
+  if (bytes_written != data->length()) {
+    base::DeleteFile(tmp_path);
+    LOG(ERROR) << "Failed to write dictionary data to the temporary file";
+    return false;
+  }
+
+  // Atomically rename the temporary file to become the real one.
+  return base::ReplaceFile(tmp_path, file_path, nullptr);
+}
+
+void RemoveDictionaryFle(const base::FilePath& file_path) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::MAY_BLOCK);
+
+  base::DeleteFile(file_path);
+}
+
+}  // namespace
+
+SpellChecker::SpellChecker(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : url_loader_factory_(url_loader_factory) {
+  quick_answers_state_observation_.Observe(QuickAnswersState::Get());
+}
+
+SpellChecker::~SpellChecker() = default;
+
+void SpellChecker::CheckSpelling(const std::string& word,
+                                 CheckSpellingCallback callback) {
+  if (!dictionary_initialized_) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  dictionary_->CheckSpelling(word, std::move(callback));
+}
+
+void SpellChecker::OnSettingsEnabled(bool enabled) {
+  feature_enabled_ = enabled;
+
+  // Reset spell check service if the feature is disabled.
+  if (!enabled) {
+    dictionary_.reset();
+    service_.reset();
+    return;
+  }
+
+  if (!dictionary_file_path_.empty())
+    InitializeDictionary();
+}
+
+void SpellChecker::OnApplicationLocaleReady(const std::string& locale) {
+  dictionary_file_path_ = GetDictionaryFilePath(locale);
+
+  if (feature_enabled_)
+    InitializeDictionary();
+}
+
+void SpellChecker::InitializeDictionary() {
+  DCHECK(!dictionary_file_path_.empty());
+
+  // If the dictionary is not available, try to download it from the server.
+  if (!base::PathExists(dictionary_file_path_)) {
+    auto url =
+        GetDictionaryURL(dictionary_file_path_.BaseName().MaybeAsASCII());
+
+    auto resource_request = std::make_unique<network::ResourceRequest>();
+    resource_request->url = url;
+    resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+    loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                               kNetworkTrafficAnnotationTag);
+    // TODO(b/226221138): Probably use |DownloadToTempFile| instead.
+    loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+        url_loader_factory_.get(),
+        base::BindOnce(&SpellChecker::OnSimpleURLLoaderComplete,
+                       base::Unretained(this)));
+    return;
+  }
+
+  InitializeSpellCheckService();
+}
+
+void SpellChecker::InitializeSpellCheckService() {
+  DCHECK(base::PathExists(dictionary_file_path_));
+
+  if (!service_) {
+    service_ = content::ServiceProcessHost::Launch<mojom::SpellCheckService>(
+        content::ServiceProcessHost::Options()
+            .WithDisplayName("Quick answers spell check service")
+            .Pass());
+  }
+
+  base::File file(dictionary_file_path_,
+                  base::File::FLAG_READ | base::File::FLAG_OPEN);
+
+  service_->CreateDictionary(file.Duplicate(),
+                             base::BindOnce(&SpellChecker::OnDictionaryCreated,
+                                            base::Unretained(this)));
+}
+
+void SpellChecker::OnSimpleURLLoaderComplete(
+    std::unique_ptr<std::string> data) {
+  int response_code = -1;
+  if (loader_->ResponseInfo() && loader_->ResponseInfo()->headers)
+    response_code = loader_->ResponseInfo()->headers->response_code();
+
+  if (loader_->NetError() != net::OK || ((response_code / 100) != 2)) {
+    LOG(ERROR) << "Failed to download the dictionary.";
+    MaybeRetryInitialize();
+    return;
+  }
+
+  // Basic sanity check on the dictionary data.
+  if (!data || data->size() < 4 || data->compare(0, 4, "BDic") != 0) {
+    LOG(ERROR) << "Downloaded dictionary data is empty or broken.";
+    MaybeRetryInitialize();
+    return;
+  }
+
+  if (!SaveDictionaryData(std::move(data), dictionary_file_path_)) {
+    MaybeRetryInitialize();
+    return;
+  }
+
+  InitializeSpellCheckService();
+}
+
+void SpellChecker::OnDictionaryCreated(
+    mojo::PendingRemote<mojom::SpellCheckDictionary> dictionary) {
+  dictionary_.reset();
+
+  if (dictionary.is_valid()) {
+    dictionary_.Bind(std::move(dictionary));
+    dictionary_initialized_ = true;
+    return;
+  }
+
+  MaybeRetryInitialize();
+}
+
+void SpellChecker::MaybeRetryInitialize() {
+  RemoveDictionaryFle(dictionary_file_path_);
+
+  if (num_retries_ >= kMaxRetries) {
+    LOG(ERROR) << "Service initialize failed after max retries";
+    service_.reset();
+    return;
+  }
+
+  ++num_retries_;
+  InitializeDictionary();
+}
+
+}  // namespace quick_answers
diff --git a/chromeos/components/quick_answers/utils/spell_checker.h b/chromeos/components/quick_answers/utils/spell_checker.h
new file mode 100644
index 0000000..5ee1d5a
--- /dev/null
+++ b/chromeos/components/quick_answers/utils/spell_checker.h
@@ -0,0 +1,80 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_SPELL_CHECKER_H_
+#define CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_SPELL_CHECKER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h"
+#include "chromeos/components/quick_answers/public/mojom/spell_check.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+}  // namespace network
+
+namespace quick_answers {
+
+// Utility class for spell check.
+class SpellChecker : public QuickAnswersStateObserver {
+ public:
+  using CheckSpellingCallback = base::OnceCallback<void(bool)>;
+
+  explicit SpellChecker(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+
+  SpellChecker(const SpellChecker&) = delete;
+  SpellChecker& operator=(const SpellChecker&) = delete;
+
+  ~SpellChecker() override;
+
+  // Check spelling of the given word, run |callback| with true if the word is
+  // spelled correctly.
+  void CheckSpelling(const std::string& word, CheckSpellingCallback callback);
+
+  // QuickAnswersStateObserver:
+  void OnSettingsEnabled(bool enabled) override;
+  void OnApplicationLocaleReady(const std::string& locale) override;
+
+ private:
+  void InitializeDictionary();
+  void InitializeSpellCheckService();
+
+  void OnSimpleURLLoaderComplete(std::unique_ptr<std::string> response_body);
+
+  void OnDictionaryCreated(
+      mojo::PendingRemote<mojom::SpellCheckDictionary> dictionary);
+
+  void MaybeRetryInitialize();
+
+  // Whether the Quick answers feature is enabled in settings.
+  bool feature_enabled_ = false;
+
+  // Whether the spell check dictionary has been successfully initialized.
+  bool dictionary_initialized_ = false;
+
+  // Number of retries used for initializing the spell check service.
+  int num_retries_ = 0;
+
+  base::FilePath dictionary_file_path_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  std::unique_ptr<network::SimpleURLLoader> loader_;
+  mojo::Remote<mojom::SpellCheckService> service_;
+  mojo::Remote<mojom::SpellCheckDictionary> dictionary_;
+
+  base::ScopedObservation<QuickAnswersState, QuickAnswersStateObserver>
+      quick_answers_state_observation_{this};
+
+  base::WeakPtrFactory<SpellChecker> weak_factory_{this};
+};
+
+}  // namespace quick_answers
+
+#endif  // CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_SPELL_CHECKER_H_
diff --git a/chromeos/dbus/concierge/concierge_client.cc b/chromeos/dbus/concierge/concierge_client.cc
index 60f528299..531beef 100644
--- a/chromeos/dbus/concierge/concierge_client.cc
+++ b/chromeos/dbus/concierge/concierge_client.cc
@@ -270,6 +270,12 @@
     CallMethod(concierge::kReclaimVmMemoryMethod, request, std::move(callback));
   }
 
+  void ListVms(
+      const concierge::ListVmsRequest& request,
+      DBusMethodCallback<concierge::ListVmsResponse> callback) override {
+    CallMethod(concierge::kListVmsMethod, request, std::move(callback));
+  }
+
   void Init(dbus::Bus* bus) override {
     concierge_proxy_ = bus->GetObjectProxy(
         concierge::kVmConciergeServiceName,
diff --git a/chromeos/dbus/concierge/concierge_client.h b/chromeos/dbus/concierge/concierge_client.h
index 0d21e41dc..1a7f3bf 100644
--- a/chromeos/dbus/concierge/concierge_client.h
+++ b/chromeos/dbus/concierge/concierge_client.h
@@ -283,6 +283,12 @@
       DBusMethodCallback<vm_tools::concierge::ReclaimVmMemoryResponse>
           callback) = 0;
 
+  // Lists running VMs.
+  // |callback| is called after the method call finishes.
+  virtual void ListVms(
+      const vm_tools::concierge::ListVmsRequest& request,
+      DBusMethodCallback<vm_tools::concierge::ListVmsResponse> callback) = 0;
+
   // Creates and initializes the global instance. |bus| must not be null.
   static void Initialize(dbus::Bus* bus);
 
diff --git a/chromeos/dbus/concierge/fake_concierge_client.cc b/chromeos/dbus/concierge/fake_concierge_client.cc
index 4984b02..5330677 100644
--- a/chromeos/dbus/concierge/fake_concierge_client.cc
+++ b/chromeos/dbus/concierge/fake_concierge_client.cc
@@ -380,6 +380,14 @@
       base::BindOnce(std::move(callback), reclaim_vm_memory_response_));
 }
 
+void FakeConciergeClient::ListVms(
+    const vm_tools::concierge::ListVmsRequest& request,
+    DBusMethodCallback<vm_tools::concierge::ListVmsResponse> callback) {
+  list_vms_call_count_++;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), list_vms_response_));
+}
+
 void FakeConciergeClient::NotifyVmStarted(
     const vm_tools::concierge::VmStartedSignal& signal) {
   // Now GetVmInfo can return success.
diff --git a/chromeos/dbus/concierge/fake_concierge_client.h b/chromeos/dbus/concierge/fake_concierge_client.h
index 7d3ef9a..fd71f55 100644
--- a/chromeos/dbus/concierge/fake_concierge_client.h
+++ b/chromeos/dbus/concierge/fake_concierge_client.h
@@ -140,6 +140,10 @@
       DBusMethodCallback<vm_tools::concierge::ReclaimVmMemoryResponse> callback)
       override;
 
+  void ListVms(const vm_tools::concierge::ListVmsRequest& request,
+               DBusMethodCallback<vm_tools::concierge::ListVmsResponse>
+                   callback) override;
+
   const base::ObserverList<Observer>& observer_list() const {
     return observer_list_;
   }
@@ -195,6 +199,7 @@
   int reclaim_vm_memory_call_count() const {
     return reclaim_vm_memory_call_count_;
   }
+  int list_vms_call_count() const { return list_vms_call_count_; }
 
   void set_vm_started_signal_connected(bool connected) {
     is_vm_started_signal_connected_ = connected;
@@ -306,6 +311,10 @@
           reclaim_vm_memory_response) {
     reclaim_vm_memory_response_ = reclaim_vm_memory_response;
   }
+  void set_list_vms_response(
+      absl::optional<vm_tools::concierge::ListVmsResponse> list_vms_response) {
+    list_vms_response_ = list_vms_response;
+  }
 
   void set_send_create_disk_image_response_delay(base::TimeDelta delay) {
     send_create_disk_image_response_delay_ = delay;
@@ -369,6 +378,7 @@
   int resize_disk_image_call_count_ = 0;
   int set_vm_id_call_count_ = 0;
   int reclaim_vm_memory_call_count_ = 0;
+  int list_vms_call_count_ = 0;
 
   bool is_vm_started_signal_connected_ = true;
   bool is_vm_stopped_signal_connected_ = true;
@@ -410,6 +420,7 @@
   absl::optional<vm_tools::concierge::SetVmIdResponse> set_vm_id_response_;
   absl::optional<vm_tools::concierge::ReclaimVmMemoryResponse>
       reclaim_vm_memory_response_;
+  absl::optional<vm_tools::concierge::ListVmsResponse> list_vms_response_;
 
   base::TimeDelta send_create_disk_image_response_delay_;
   base::TimeDelta send_start_vm_response_delay_;
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index e9231a3..165f718a 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -172,6 +172,15 @@
   "arc.SettingsBridge.vm",
   "arc.StartStop.vm",
   "arc.TextToSpeech.vm",
+  "arc.VMPstoreDump",
+
+  # Flaky on chromeos-eve-chrome. b/226284050.
+  "crostini.UninstallInvalidApp.bullseye_stable",
+  "crostini.Xattrs.bullseye_stable",
+  "crostini.SSHFSMount.bullseye_stable",
+  "crostini.GPUEnabled.gpu_bullseye_stable",
+  "crostini.Basic.bullseye_stable",
+  "crostini.CrashReporter.bullseye_stable",
 ]
 
 # To disable a specific test in lacros_all_tast_tests, add it the following
diff --git a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc
index 490cebfe..e64fd6c8 100644
--- a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc
+++ b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.cc
@@ -100,6 +100,15 @@
       true);
 }
 
+void LogVirtualCardEnrollmentStrikeDatabaseEvent(
+    VirtualCardEnrollmentSource source,
+    VirtualCardEnrollmentStrikeDatabaseEvent strike_event) {
+  base::UmaHistogramEnumeration(
+      "Autofill.VirtualCardEnrollmentStrikeDatabase." +
+          VirtualCardEnrollmentSourceToMetricSuffix(source),
+      strike_event);
+}
+
 void LogVirtualCardEnrollBubbleLatencySinceUpstream(
     const base::TimeDelta& latency) {
   base::UmaHistogramTimes(
@@ -124,6 +133,19 @@
   }
 }
 
+const std::string VirtualCardEnrollmentLinkTypeToMetricSuffix(
+    VirtualCardEnrollmentLinkType link_type) {
+  switch (link_type) {
+    case VirtualCardEnrollmentLinkType::
+        VIRTUAL_CARD_ENROLLMENT_GOOGLE_PAYMENTS_TOS_LINK:
+      return "GoogleLegalMessageLink";
+    case VirtualCardEnrollmentLinkType::VIRTUAL_CARD_ENROLLMENT_ISSUER_TOS_LINK:
+      return "IssuerLegalMessageLink";
+    case VirtualCardEnrollmentLinkType::VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK:
+      return "LearnMoreLink";
+  }
+}
+
 const std::string VirtualCardEnrollmentSourceToMetricSuffix(
     VirtualCardEnrollmentSource source) {
   switch (source) {
@@ -138,17 +160,4 @@
   }
 }
 
-const std::string VirtualCardEnrollmentLinkTypeToMetricSuffix(
-    VirtualCardEnrollmentLinkType link_type) {
-  switch (link_type) {
-    case VirtualCardEnrollmentLinkType::
-        VIRTUAL_CARD_ENROLLMENT_GOOGLE_PAYMENTS_TOS_LINK:
-      return "GoogleLegalMessageLink";
-    case VirtualCardEnrollmentLinkType::VIRTUAL_CARD_ENROLLMENT_ISSUER_TOS_LINK:
-      return "IssuerLegalMessageLink";
-    case VirtualCardEnrollmentLinkType::VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK:
-      return "LearnMoreLink";
-  }
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
index bfe9c3e1..fb084b8 100644
--- a/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
+++ b/components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h
@@ -81,6 +81,18 @@
     VirtualCardEnrollmentBubbleSource source,
     bool is_reshow);
 
+// Metrics to measure strikes logged or cleared in strike database.
+enum class VirtualCardEnrollmentStrikeDatabaseEvent {
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+
+  // Strike logged as enrollment bubble was not accepted.
+  VIRTUAL_CARD_ENROLLMENT_STRIKE_DATABASE_STRIKE_LOGGED = 0,
+  // All strikes cleared as user accepted virtual card enrollment.
+  VIRTUAL_CARD_ENROLLMENT_STRIKE_DATABASE_STRIKES_CLEARED = 1,
+  kMaxValue = VIRTUAL_CARD_ENROLLMENT_STRIKE_DATABASE_STRIKES_CLEARED,
+};
+
 // GetDetailsForEnrollmentRequest related metrics. Attempts and results should
 // be 1:1 mapping.
 void LogGetDetailsForEnrollmentRequestAttempt(
@@ -103,6 +115,11 @@
     VirtualCardEnrollmentLinkType link_type,
     VirtualCardEnrollmentBubbleSource source);
 
+// Virtual card enrollment strike database event metrics.
+void LogVirtualCardEnrollmentStrikeDatabaseEvent(
+    VirtualCardEnrollmentSource source,
+    VirtualCardEnrollmentStrikeDatabaseEvent strike_event);
+
 // Latency Since Upstream metrics. Used to determine the time that it takes for
 // the server calls that need to be made between Save Card Bubble accept and
 // when the Virtual Card Enroll Bubble is shown.
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
index 50bab1d..0988036 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -42,10 +42,14 @@
     : autofill_client_(autofill_client),
       personal_data_manager_(personal_data_manager),
       payments_client_(payments_client) {
-  if (autofill_client_) {
-    StrikeDatabaseBase* strike_database = autofill_client->GetStrikeDatabase();
+  // |autofill_client_| does not exist on Clank settings page where this flow
+  // can also be triggered.
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillEnableUpdateVirtualCardEnrollment) &&
+      autofill_client_ && autofill_client_->GetStrikeDatabase()) {
     virtual_card_enrollment_strike_database_ =
-        std::make_unique<VirtualCardEnrollmentStrikeDatabase>(strike_database);
+        std::make_unique<VirtualCardEnrollmentStrikeDatabase>(
+            autofill_client_->GetStrikeDatabase());
   }
 }
 
@@ -58,6 +62,14 @@
     RiskAssessmentFunction risk_assessment_function,
     VirtualCardEnrollmentFieldsLoadedCallback
         virtual_card_enrollment_fields_loaded_callback) {
+  // If at strike limit, exit enrollment flow.
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillEnableUpdateVirtualCardEnrollment) &&
+      IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+          credit_card.guid(), virtual_card_enrollment_source)) {
+    return;
+  }
+
   Reset();
   DCHECK_NE(virtual_card_enrollment_source, VirtualCardEnrollmentSource::kNone);
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
@@ -66,16 +78,17 @@
   DCHECK(autofill_client_);
   autofill_client_->HideVirtualCardEnrollBubbleAndIconIfVisible();
 #endif
+
   state_.virtual_card_enrollment_fields.credit_card = credit_card;
   risk_assessment_function_ = std::move(risk_assessment_function);
   virtual_card_enrollment_fields_loaded_callback_ =
       std::move(virtual_card_enrollment_fields_loaded_callback);
   // The |card_art_image| might not be synced yet from the sync server which
-  // will result in a nullptr. This situation can occur in the upstream flow. If
-  // it is not synced, GetCreditCardArtImageForUrl() will send a fetch request
-  // to sync the |card_art_image|, and before showing the
-  // VirtualCardEnrollmentBubble we will try to fetch the |card_art_image| from
-  // the local cache.
+  // will result in a nullptr. This situation can occur in the upstream flow.
+  // If it is not synced, GetCreditCardArtImageForUrl() will send a fetch
+  // request to sync the |card_art_image|, and before showing the
+  // VirtualCardEnrollmentBubble we will try to fetch the |card_art_image|
+  // from the local cache.
   state_.virtual_card_enrollment_fields.card_art_image =
       personal_data_manager_->GetCreditCardArtImageForUrl(
           credit_card.card_art_url());
@@ -120,6 +133,11 @@
                          OnDidGetUpdateVirtualCardEnrollmentResponse,
                      weak_ptr_factory_.GetWeakPtr(),
                      VirtualCardEnrollmentRequestType::kEnroll));
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillEnableUpdateVirtualCardEnrollment)) {
+    RemoveAllStrikesToBlockOfferingVirtualCardEnrollment(
+        state_.virtual_card_enrollment_fields.credit_card.guid());
+  }
 }
 
 void VirtualCardEnrollmentManager::Unenroll(int64_t instrument_id) {
@@ -150,11 +168,19 @@
                      VirtualCardEnrollmentRequestType::kUnenroll));
 }
 
-bool VirtualCardEnrollmentManager::IsVirtualCardEnrollmentBlocked(
-    const std::string& guid) const {
-  return GetVirtualCardEnrollmentStrikeDatabase() &&
-         GetVirtualCardEnrollmentStrikeDatabase()->IsMaxStrikesLimitReached(
-             guid);
+bool VirtualCardEnrollmentManager::
+    IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+        const std::string& guid,
+        VirtualCardEnrollmentSource virtual_card_enrollment_source) const {
+  if (virtual_card_enrollment_source ==
+      VirtualCardEnrollmentSource::kSettingsPage)
+    return false;
+
+  if (!GetVirtualCardEnrollmentStrikeDatabase())
+    return false;
+
+  return GetVirtualCardEnrollmentStrikeDatabase()->IsMaxStrikesLimitReached(
+      guid);
 }
 
 void VirtualCardEnrollmentManager::
@@ -163,6 +189,12 @@
     return;
 
   GetVirtualCardEnrollmentStrikeDatabase()->AddStrike(guid);
+
+  // Log that a strike has been recorded.
+  LogVirtualCardEnrollmentStrikeDatabaseEvent(
+      state_.virtual_card_enrollment_fields.virtual_card_enrollment_source,
+      VirtualCardEnrollmentStrikeDatabaseEvent::
+          VIRTUAL_CARD_ENROLLMENT_STRIKE_DATABASE_STRIKE_LOGGED);
 }
 
 void VirtualCardEnrollmentManager::
@@ -172,6 +204,12 @@
     return;
 
   GetVirtualCardEnrollmentStrikeDatabase()->ClearStrikes(guid);
+
+  // Log that strikes are being cleared.
+  LogVirtualCardEnrollmentStrikeDatabaseEvent(
+      state_.virtual_card_enrollment_fields.virtual_card_enrollment_source,
+      VirtualCardEnrollmentStrikeDatabaseEvent::
+          VIRTUAL_CARD_ENROLLMENT_STRIKE_DATABASE_STRIKES_CLEARED);
 }
 
 void VirtualCardEnrollmentManager::SetSaveCardBubbleAcceptedTimestamp(
@@ -333,6 +371,11 @@
 }
 
 void VirtualCardEnrollmentManager::OnVirtualCardEnrollmentBubbleCancelled() {
+  if (base::FeatureList::IsEnabled(
+          features::kAutofillEnableUpdateVirtualCardEnrollment)) {
+    AddStrikeToBlockOfferingVirtualCardEnrollment(
+        state_.virtual_card_enrollment_fields.credit_card.guid());
+  }
   Reset();
 }
 
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
index b0f1397..67c6879a 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
@@ -140,10 +140,12 @@
   // Unenrolls the card mapped to the given |instrument_id|.
   void Unenroll(int64_t instrument_id);
 
-  // Returns true if a credit card identified by its |guid| is blocked for
-  // virtual card enrollment. Does nothing if the strike database is not
-  // available.
-  bool IsVirtualCardEnrollmentBlocked(const std::string& guid) const;
+  // Returns true if a credit card identified by its |guid| is
+  // blocked for virtual card enrollment and is not attempting to enroll from
+  // the settings page. Does nothing if the strike database is not available.
+  bool IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+      const std::string& guid,
+      VirtualCardEnrollmentSource virtual_card_enrollment_source) const;
 
   // Adds a strike to block enrollment for credit card identified by its |guid|.
   // Does nothing if the strike database is not available.
@@ -216,6 +218,24 @@
       virtual_card_enrollment_fields_loaded_callback_;
 
  private:
+  friend class VirtualCardEnrollmentManagerTest;
+  FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+                           OnDidGetDetailsForEnrollResponse);
+  FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+                           OnDidGetDetailsForEnrollResponse_Reset);
+  FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+                           OnRiskDataLoadedForVirtualCard);
+  FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+                           OnVirtualCardEnrollmentBubbleAccepted);
+  FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+                           StrikeDatabase_BubbleAccepted);
+  FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+                           StrikeDatabase_BubbleBlocked);
+  FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+                           StrikeDatabase_BubbleCanceled);
+  FRIEND_TEST_ALL_PREFIXES(VirtualCardEnrollmentManagerTest,
+                           StrikeDatabase_SettingsPageNotBlocked);
+
   // Called once the risk data is loaded. The |risk_data| will be used with
   // |state_|'s |virtual_card_enrollment_fields|'s |credit_card|'s
   // |instrument_id_| field to make a GetDetailsForEnroll request, and
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
index a95d0f4a..67507a85 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager_unittest.cc
@@ -12,10 +12,12 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/data_model/credit_card_art_image.h"
+#include "components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h"
 #include "components/autofill/core/browser/payments/payments_requests/update_virtual_card_enrollment_request.h"
 #include "components/autofill/core/browser/payments/payments_util.h"
 #include "components/autofill/core/browser/payments/test_legal_message_line.h"
 #include "components/autofill/core/browser/payments/test_payments_client.h"
+#include "components/autofill/core/browser/payments/test_strike_database.h"
 #include "components/autofill/core/browser/payments/test_virtual_card_enrollment_manager.h"
 #include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
@@ -40,6 +42,8 @@
 class VirtualCardEnrollmentManagerTest : public testing::Test {
  public:
   void SetUp() override {
+    feature_list_.InitAndEnableFeature(
+        features::kAutofillEnableUpdateVirtualCardEnrollment);
     autofill_client_ = std::make_unique<TestAutofillClient>();
     autofill_client_->SetPrefs(test::PrefServiceForTesting());
     user_prefs_ = autofill_client_->GetPrefs();
@@ -60,6 +64,8 @@
             autofill_driver_->GetURLLoaderFactory(),
             autofill_client_->GetIdentityManager(),
             personal_data_manager_.get()));
+    auto test_strike_database = std::make_unique<TestStrikeDatabase>();
+    autofill_client_->set_test_strike_database(std::move(test_strike_database));
     payments_client_ = static_cast<payments::TestPaymentsClient*>(
         autofill_client_->GetPaymentsClient());
     virtual_card_enrollment_manager_ =
@@ -78,6 +84,7 @@
     card_ = std::make_unique<CreditCard>(test::GetMaskedServerCard());
     card_->set_card_art_url(autofill_client_->form_origin());
     card_->set_instrument_id(112233445566);
+    card_->set_guid("00000000-0000-0000-0000-000000000001");
     personal_data_manager_->AddFullServerCreditCard(*card_.get());
   }
 
@@ -110,6 +117,7 @@
   }
 
  protected:
+  base::test::ScopedFeatureList feature_list_;
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   std::unique_ptr<TestAutofillClient> autofill_client_;
@@ -312,6 +320,8 @@
   raw_ptr<VirtualCardEnrollmentProcessState> state =
       virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
   state->vcn_context_token = kTestVcnContextToken;
+  SetUpCard();
+  SetValidCardArtImageForCard(*card_);
   personal_data_manager_->SetPaymentsCustomerData(
       std::make_unique<PaymentsCustomerData>(/*customer_id=*/"123456"));
 
@@ -506,6 +516,143 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
+#if !BUILDFLAG(IS_IOS)
+TEST_F(VirtualCardEnrollmentManagerTest, StrikeDatabase_BubbleAccepted) {
+  base::HistogramTester histogram_tester;
+  raw_ptr<VirtualCardEnrollmentProcessState> state =
+      virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
+  state->vcn_context_token = kTestVcnContextToken;
+  SetUpCard();
+  state->virtual_card_enrollment_fields.credit_card = *card_;
+  personal_data_manager_->SetPaymentsCustomerData(
+      std::make_unique<PaymentsCustomerData>("123456"));
+  EXPECT_FALSE(virtual_card_enrollment_manager_
+                   ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+                       state->virtual_card_enrollment_fields.credit_card.guid(),
+                       VirtualCardEnrollmentSource::kUpstream));
+
+  // Reject the bubble and log strike.
+  virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+  EXPECT_EQ(
+      virtual_card_enrollment_manager_->GetVirtualCardEnrollmentStrikeDatabase()
+          ->GetStrikes(
+              state->virtual_card_enrollment_fields.credit_card.guid()),
+      1);
+
+  // Ensure a strike has been removed after enrollment accepted.
+  virtual_card_enrollment_manager_->Enroll();
+  EXPECT_EQ(
+      virtual_card_enrollment_manager_->GetVirtualCardEnrollmentStrikeDatabase()
+          ->GetStrikes(
+              state->virtual_card_enrollment_fields.credit_card.guid()),
+      0);
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.VirtualCardEnrollmentStrikeDatabase." +
+          VirtualCardEnrollmentSourceToMetricSuffix(
+              state->virtual_card_enrollment_fields
+                  .virtual_card_enrollment_source),
+      VirtualCardEnrollmentStrikeDatabaseEvent::
+          VIRTUAL_CARD_ENROLLMENT_STRIKE_DATABASE_STRIKES_CLEARED,
+      1);
+}
+
+TEST_F(VirtualCardEnrollmentManagerTest, StrikeDatabase_BubbleCanceled) {
+  base::HistogramTester histogram_tester;
+  raw_ptr<VirtualCardEnrollmentProcessState> state =
+      virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
+  state->vcn_context_token = kTestVcnContextToken;
+  SetUpCard();
+  state->virtual_card_enrollment_fields.credit_card = *card_;
+  personal_data_manager_->SetPaymentsCustomerData(
+      std::make_unique<PaymentsCustomerData>("123456"));
+  EXPECT_FALSE(virtual_card_enrollment_manager_
+                   ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+                       state->virtual_card_enrollment_fields.credit_card.guid(),
+                       VirtualCardEnrollmentSource::kUpstream));
+
+  // Reject the bubble and log strike.
+  virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+
+  // Ensure a strike has been logged.
+  EXPECT_EQ(
+      virtual_card_enrollment_manager_->GetVirtualCardEnrollmentStrikeDatabase()
+          ->GetStrikes(
+              state->virtual_card_enrollment_fields.credit_card.guid()),
+      1);
+
+  histogram_tester.ExpectBucketCount(
+      "Autofill.VirtualCardEnrollmentStrikeDatabase." +
+          VirtualCardEnrollmentSourceToMetricSuffix(
+              state->virtual_card_enrollment_fields
+                  .virtual_card_enrollment_source),
+      VirtualCardEnrollmentStrikeDatabaseEvent::
+          VIRTUAL_CARD_ENROLLMENT_STRIKE_DATABASE_STRIKE_LOGGED,
+      1);
+}
+
+TEST_F(VirtualCardEnrollmentManagerTest, StrikeDatabase_BubbleBlocked) {
+  raw_ptr<VirtualCardEnrollmentProcessState> state =
+      virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
+  state->vcn_context_token = kTestVcnContextToken;
+  SetUpCard();
+  state->virtual_card_enrollment_fields.credit_card = *card_;
+  personal_data_manager_->SetPaymentsCustomerData(
+      std::make_unique<PaymentsCustomerData>("123456"));
+  EXPECT_FALSE(virtual_card_enrollment_manager_
+                   ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+                       state->virtual_card_enrollment_fields.credit_card.guid(),
+                       VirtualCardEnrollmentSource::kUpstream));
+  EXPECT_FALSE(virtual_card_enrollment_manager_
+                   ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+                       state->virtual_card_enrollment_fields.credit_card.guid(),
+                       VirtualCardEnrollmentSource::kDownstream));
+
+  for (int i = 0; i < virtual_card_enrollment_manager_
+                          ->GetVirtualCardEnrollmentStrikeDatabase()
+                          ->GetMaxStrikesLimit();
+       i++) {
+    // Reject the bubble and log strike.
+    virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+  }
+
+  EXPECT_TRUE(virtual_card_enrollment_manager_
+                  ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+                      state->virtual_card_enrollment_fields.credit_card.guid(),
+                      VirtualCardEnrollmentSource::kUpstream));
+  EXPECT_TRUE(virtual_card_enrollment_manager_
+                  ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+                      state->virtual_card_enrollment_fields.credit_card.guid(),
+                      VirtualCardEnrollmentSource::kDownstream));
+}
+
+TEST_F(VirtualCardEnrollmentManagerTest,
+       StrikeDatabase_SettingsPageNotBlocked) {
+  raw_ptr<VirtualCardEnrollmentProcessState> state =
+      virtual_card_enrollment_manager_->GetVirtualCardEnrollmentProcessState();
+  state->vcn_context_token = kTestVcnContextToken;
+  SetUpCard();
+  state->virtual_card_enrollment_fields.credit_card = *card_;
+
+  personal_data_manager_->SetPaymentsCustomerData(
+      std::make_unique<PaymentsCustomerData>("123456"));
+
+  for (int i = 0; i < virtual_card_enrollment_manager_
+                          ->GetVirtualCardEnrollmentStrikeDatabase()
+                          ->GetMaxStrikesLimit();
+       i++) {
+    // Reject the bubble and log strike.
+    virtual_card_enrollment_manager_->OnVirtualCardEnrollmentBubbleCancelled();
+  }
+
+  // Make sure enrollment is not blocked through settings page.
+  EXPECT_FALSE(virtual_card_enrollment_manager_
+                   ->IsVirtualCardEnrollmentBlockedDueToMaxStrikes(
+                       state->virtual_card_enrollment_fields.credit_card.guid(),
+                       VirtualCardEnrollmentSource::kSettingsPage));
+}
+#endif  // !BUILDFLAG(IS_IOS)
+
 TEST_F(VirtualCardEnrollmentManagerTest, Metrics_LatencySinceUpstream) {
   base::HistogramTester histogram_tester;
   TestAutofillClock test_autofill_clock;
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.cc
index 5c3781d..a315de25 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database.cc
@@ -20,18 +20,20 @@
 int kCardMaximumStrikes = 3;
 
 // The number of days until strikes expire for virtual card enrollment.
-constexpr size_t kDaysUntilCardStrikeExpiry = 180;
+int kDaysUntilCardStrikeExpiry = 180;
 
 VirtualCardEnrollmentStrikeDatabase::VirtualCardEnrollmentStrikeDatabase(
     StrikeDatabaseBase* strike_database)
-    : StrikeDatabaseIntegratorBase(strike_database) {}
+    : StrikeDatabaseIntegratorBase(strike_database) {
+  RemoveExpiredStrikes();
+}
 
 VirtualCardEnrollmentStrikeDatabase::~VirtualCardEnrollmentStrikeDatabase() =
     default;
 
 absl::optional<size_t> VirtualCardEnrollmentStrikeDatabase::GetMaximumEntries()
     const {
-  return kMaxStrikeEntities;
+  return absl::optional<size_t>(kMaxStrikeEntities);
 }
 
 absl::optional<size_t>
@@ -51,7 +53,8 @@
 absl::optional<base::TimeDelta>
 VirtualCardEnrollmentStrikeDatabase::GetExpiryTimeDelta() const {
   // Expiry time is 180 days by default.
-  return base::Days(kDaysUntilCardStrikeExpiry);
+  return absl::optional<base::TimeDelta>(
+      base::Days(kDaysUntilCardStrikeExpiry));
 }
 
 bool VirtualCardEnrollmentStrikeDatabase::UniqueIdsRequired() const {
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database_unittest.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database_unittest.cc
index 463b53c0..4427583 100644
--- a/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database_unittest.cc
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_strike_database_unittest.cc
@@ -55,6 +55,7 @@
   int max_strikes = strike_database_->GetMaxStrikesLimit();
   std::string test_guid = "00000000-0000-0000-0000-000000000001";
 
+  EXPECT_EQ(strike_database_->GetStrikes(test_guid), 0);
   strike_database_->AddStrike(test_guid);
   EXPECT_EQ(strike_database_->GetStrikes(test_guid), 1);
   EXPECT_FALSE(strike_database_->IsMaxStrikesLimitReached(test_guid));
diff --git a/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc b/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
index 1bdda0d..aa0ec92 100644
--- a/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
+++ b/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
@@ -216,7 +216,7 @@
   auto element = std::make_unique<ElementFinder::Result>();
   content::WebContentsTester::For(web_contents_.get())
       ->NavigateAndCommit(GURL("https://www.example.com"));
-  element->container_frame_host = web_contents_->GetMainFrame();
+  element->SetRenderFrameHost(web_contents_->GetMainFrame());
 
   user_data_.selected_login_ = absl::make_optional<WebsiteLoginManager::Login>(
       GURL("https://www.example.com"), "username");
@@ -239,8 +239,7 @@
 
 TEST_F(ActionDelegateUtilTest, PerformWithFailingPasswordManagerValue) {
   auto element = std::make_unique<ElementFinder::Result>();
-
-  element->container_frame_host = web_contents_->GetMainFrame();
+  element->SetRenderFrameHost(web_contents_->GetMainFrame());
 
   user_data_.selected_login_ = absl::make_optional<WebsiteLoginManager::Login>(
       GURL("https://www.example.com"), "username");
diff --git a/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc b/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
index 4a46d59..0eaed9fc 100644
--- a/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
@@ -702,7 +702,7 @@
       .WillOnce(WithArgs<1>([this](auto&& callback) {
         std::unique_ptr<ElementFinder::Result> element =
             std::make_unique<ElementFinder::Result>();
-        element->container_frame_host = web_contents_->GetMainFrame();
+        element->SetRenderFrameHost(web_contents_->GetMainFrame());
         std::move(callback).Run(OkClientStatus(), std::move(element));
       }));
   EXPECT_CALL(mock_website_login_manager_, GetPasswordForLogin(_, _))
diff --git a/components/autofill_assistant/browser/actions/js_flow_action.cc b/components/autofill_assistant/browser/actions/js_flow_action.cc
index 8b069eb4..6303274 100644
--- a/components/autofill_assistant/browser/actions/js_flow_action.cc
+++ b/components/autofill_assistant/browser/actions/js_flow_action.cc
@@ -5,6 +5,7 @@
 #include "components/autofill_assistant/browser/actions/js_flow_action.h"
 #include "base/base64.h"
 #include "base/json/json_writer.h"
+#include "base/metrics/field_trial.h"
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
 #include "components/autofill_assistant/browser/js_flow_executor_impl.h"
 #include "components/autofill_assistant/browser/js_flow_util.h"
@@ -12,6 +13,17 @@
 
 namespace autofill_assistant {
 
+namespace {
+
+// When starting a JS flow action, a synthetic field trial is recorded. This is
+// used to allow tracking stability metrics as we start using this new action.
+// Note there is no control group - this is purely for stability tracking.
+const char kJsFlowActionSyntheticFieldTrialName[] =
+    "AutofillAssistantJsFlowAction";
+const char kJsFlowActionEnabledGroup[] = "Enabled";
+
+}  // namespace
+
 JsFlowAction::JsFlowAction(ActionDelegate* delegate, const ActionProto& proto)
     : Action(delegate, proto),
       js_flow_executor_(
@@ -85,6 +97,9 @@
 }
 
 void JsFlowAction::InternalProcessAction(ProcessActionCallback callback) {
+  base::FieldTrialList::CreateFieldTrial(kJsFlowActionSyntheticFieldTrialName,
+                                         kJsFlowActionEnabledGroup);
+
   js_flow_executor_->Start(
       proto_.js_flow().js_flow(),
       base::BindOnce(&JsFlowAction::OnFlowFinished,
diff --git a/components/autofill_assistant/browser/actions/send_keystroke_events_action.cc b/components/autofill_assistant/browser/actions/send_keystroke_events_action.cc
index 840ed39..113aa1e 100644
--- a/components/autofill_assistant/browser/actions/send_keystroke_events_action.cc
+++ b/components/autofill_assistant/browser/actions/send_keystroke_events_action.cc
@@ -41,14 +41,14 @@
   // When dealing with a password field we also want to know the last time that
   // a user has used it.
   auto selected_login_opt = delegate_->GetUserData()->selected_login_;
+  auto* target_render_frame_host = element_.render_frame_host();
   if (proto_.send_keystroke_events().value().value_case() ==
           TextValue::kPasswordManagerValue &&
-      selected_login_opt) {
+      selected_login_opt && target_render_frame_host) {
     // Origin check is done in PWM based on the
-    // |target_element.container_frame_host->GetLastCommittedURL()|
-    selected_login_opt->origin =
-        element_.container_frame_host->GetLastCommittedURL()
-            .DeprecatedGetOriginAsURL();
+    // |target_render_frame_host->GetLastCommittedURL()|
+    selected_login_opt->origin = target_render_frame_host->GetLastCommittedURL()
+                                     .DeprecatedGetOriginAsURL();
 
     delegate_->GetWebsiteLoginManager()->GetGetLastTimePasswordUsed(
         *selected_login_opt,
diff --git a/components/autofill_assistant/browser/actions/send_keystroke_events_action_unittest.cc b/components/autofill_assistant/browser/actions/send_keystroke_events_action_unittest.cc
index 0470cd0..0779f1cf 100644
--- a/components/autofill_assistant/browser/actions/send_keystroke_events_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/send_keystroke_events_action_unittest.cc
@@ -119,7 +119,7 @@
   content::WebContentsTester::For(web_contents_.get())
       ->NavigateAndCommit(GURL(kUrl));
   element.dom_object.object_data.object_id = "id";
-  element.container_frame_host = web_contents_->GetMainFrame();
+  element.SetRenderFrameHost(web_contents_->GetMainFrame());
   mock_action_delegate_.GetElementStore()->AddElement("e", element.dom_object);
 
   EXPECT_CALL(mock_web_controller_,
@@ -159,7 +159,7 @@
   content::WebContentsTester::For(web_contents_.get())
       ->NavigateAndCommit(GURL(kUrl));
   element.dom_object.object_data.object_id = "id";
-  element.container_frame_host = web_contents_->GetMainFrame();
+  element.SetRenderFrameHost(web_contents_->GetMainFrame());
   mock_action_delegate_.GetElementStore()->AddElement("e", element.dom_object);
 
   EXPECT_CALL(mock_web_controller_,
diff --git a/components/autofill_assistant/browser/user_data_util.cc b/components/autofill_assistant/browser/user_data_util.cc
index ffe2306..0a175dab 100644
--- a/components/autofill_assistant/browser/user_data_util.cc
+++ b/components/autofill_assistant/browser/user_data_util.cc
@@ -606,7 +606,8 @@
     std::move(callback).Run(ClientStatus(PRECONDITION_FAILED), std::string());
     return;
   }
-  if (!target_element.container_frame_host) {
+  auto* target_render_frame_host = target_element.render_frame_host();
+  if (!target_render_frame_host) {
     std::move(callback).Run(ClientStatus(PASSWORD_ORIGIN_MISMATCH),
                             std::string());
     return;
@@ -616,8 +617,8 @@
     case PasswordManagerValue::PASSWORD: {
       auto login = *user_data->selected_login_;
       // Origin check is done in PWM based on the
-      // |target_element.container_frame_host->GetLastCommittedURL()|
-      login.origin = target_element.container_frame_host->GetLastCommittedURL()
+      // |target_render_frame_host->GetLastCommittedURL()|
+      login.origin = target_render_frame_host->GetLastCommittedURL()
                          .DeprecatedGetOriginAsURL();
       website_login_manager->GetPasswordForLogin(
           login, base::BindOnce(&OnGetStoredPassword, std::move(callback)));
diff --git a/components/autofill_assistant/browser/user_data_util_unittest.cc b/components/autofill_assistant/browser/user_data_util_unittest.cc
index 7703e8c..80bae5f4 100644
--- a/components/autofill_assistant/browser/user_data_util_unittest.cc
+++ b/components/autofill_assistant/browser/user_data_util_unittest.cc
@@ -1261,7 +1261,7 @@
       GURL("https://www.example.com"), "username");
 
   ElementFinder::Result element;
-  element.container_frame_host = web_contents_->GetMainFrame();
+  element.SetRenderFrameHost(web_contents_->GetMainFrame());
 
   PasswordManagerValue password_manager_value;
   password_manager_value.set_credential_type(PasswordManagerValue::USERNAME);
@@ -1279,7 +1279,7 @@
       GURL("https://www.example.com"), "username");
 
   ElementFinder::Result element;
-  element.container_frame_host = web_contents_->GetMainFrame();
+  element.SetRenderFrameHost(web_contents_->GetMainFrame());
 
   PasswordManagerValue password_manager_value;
   password_manager_value.set_credential_type(PasswordManagerValue::PASSWORD);
@@ -1301,7 +1301,7 @@
   ElementFinder::Result element;
   content::WebContentsTester::For(web_contents_.get())
       ->NavigateAndCommit(GURL("https://www.example.com"));
-  element.container_frame_host = web_contents_->GetMainFrame();
+  element.SetRenderFrameHost(web_contents_->GetMainFrame());
 
   PasswordManagerValue password_manager_value;
   password_manager_value.set_credential_type(PasswordManagerValue::PASSWORD);
@@ -1382,7 +1382,7 @@
   ElementFinder::Result element;
   content::WebContentsTester::For(web_contents_.get())
       ->NavigateAndCommit(GURL("https://www.example.com"));
-  element.container_frame_host = web_contents_->GetMainFrame();
+  element.SetRenderFrameHost(web_contents_->GetMainFrame());
 
   TextValue text_value;
   text_value.mutable_password_manager_value()->set_credential_type(
diff --git a/components/autofill_assistant/browser/web/click_or_tap_worker.cc b/components/autofill_assistant/browser/web/click_or_tap_worker.cc
index f50afda..fa5b4cf 100644
--- a/components/autofill_assistant/browser/web/click_or_tap_worker.cc
+++ b/components/autofill_assistant/browser/web/click_or_tap_worker.cc
@@ -31,7 +31,7 @@
       /* check_interval= */ base::Milliseconds(0), node_frame_id_);
 
   element_position_getter_->Start(
-      element.container_frame_host, element.object_id(),
+      element.render_frame_host(), element.object_id(),
       base::BindOnce(&ClickOrTapWorker::OnGetCoordinates,
                      weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/components/autofill_assistant/browser/web/element_finder.cc b/components/autofill_assistant/browser/web/element_finder.cc
index a8bd92c..d9791797 100644
--- a/components/autofill_assistant/browser/web/element_finder.cc
+++ b/components/autofill_assistant/browser/web/element_finder.cc
@@ -159,10 +159,9 @@
     return;
   }
 
-  if (start_element.container_frame_host == nullptr) {
+  current_frame_ = start_element.render_frame_host();
+  if (current_frame_ == nullptr) {
     current_frame_ = web_contents_->GetMainFrame();
-  } else {
-    current_frame_ = start_element.container_frame_host;
   }
   current_frame_id_ = start_element.node_frame_id();
   frame_stack_ = start_element.frame_stack();
@@ -244,7 +243,7 @@
 
 ElementFinder::Result ElementFinder::BuildResult(const std::string& object_id) {
   Result result;
-  result.container_frame_host = current_frame_;
+  result.SetRenderFrameHost(current_frame_);
   result.dom_object.object_data.object_id = object_id;
   result.dom_object.object_data.node_frame_id = current_frame_id_;
   return result;
diff --git a/components/autofill_assistant/browser/web/element_finder.h b/components/autofill_assistant/browser/web/element_finder.h
index c644b79..2bf03ce5c 100644
--- a/components/autofill_assistant/browser/web/element_finder.h
+++ b/components/autofill_assistant/browser/web/element_finder.h
@@ -60,8 +60,9 @@
   };
 
   // Result is the fully resolved element that can be used without limitations.
-  // This means that |container_frame_host| has been found and is not nullptr.
-  struct Result {
+  // This means that |render_frame_host()| has been found and is not nullptr.
+  class Result {
+   public:
     Result();
     ~Result();
     Result(const Result&);
@@ -72,8 +73,12 @@
 
     DomObjectFrameStack dom_object;
 
-    // The render frame host contains the element.
-    raw_ptr<content::RenderFrameHost> container_frame_host = nullptr;
+    content::RenderFrameHost* render_frame_host() const {
+      if (!render_frame_id_) {
+        return nullptr;
+      }
+      return content::RenderFrameHost::FromID(*render_frame_id_);
+    }
 
     const std::string& object_id() const {
       return dom_object.object_data.object_id;
@@ -90,6 +95,17 @@
     bool IsEmpty() const {
       return object_id().empty() && node_frame_id().empty();
     }
+
+    void SetRenderFrameHost(content::RenderFrameHost* render_frame_host) {
+      if (!render_frame_host) {
+        return;
+      }
+      render_frame_id_ = render_frame_host->GetGlobalId();
+    }
+
+   private:
+    // The id of the render frame host that contains the element.
+    absl::optional<content::GlobalRenderFrameHostId> render_frame_id_;
   };
 
   // |web_contents|, |devtools_client| and |user_data| must be valid for the
diff --git a/components/autofill_assistant/browser/web/element_store.cc b/components/autofill_assistant/browser/web/element_store.cc
index 12658bf..edbfebd 100644
--- a/components/autofill_assistant/browser/web/element_store.cc
+++ b/components/autofill_assistant/browser/web/element_store.cc
@@ -44,7 +44,7 @@
     VLOG(1) << __func__ << " failed to resolve frame.";
     return ClientStatus(CLIENT_ID_RESOLUTION_FAILED);
   }
-  out_element->container_frame_host = frame;
+  out_element->SetRenderFrameHost(frame);
   return OkClientStatus();
 }
 
diff --git a/components/autofill_assistant/browser/web/element_store_unittest.cc b/components/autofill_assistant/browser/web/element_store_unittest.cc
index f638f592..9888072 100644
--- a/components/autofill_assistant/browser/web/element_store_unittest.cc
+++ b/components/autofill_assistant/browser/web/element_store_unittest.cc
@@ -87,7 +87,7 @@
   ElementFinder::Result result;
   EXPECT_EQ(ACTION_APPLIED,
             element_store_->GetElement("1", &result).proto_status());
-  EXPECT_EQ(web_contents_->GetMainFrame(), result.container_frame_host);
+  EXPECT_EQ(web_contents_->GetMainFrame(), result.render_frame_host());
 }
 
 TEST_F(ElementStoreTest, AddElementToStoreOverwrites) {
diff --git a/components/autofill_assistant/browser/web/fake_element_store.cc b/components/autofill_assistant/browser/web/fake_element_store.cc
index 25d4c3cb..2718171 100644
--- a/components/autofill_assistant/browser/web/fake_element_store.cc
+++ b/components/autofill_assistant/browser/web/fake_element_store.cc
@@ -25,7 +25,7 @@
 
   out_element->dom_object = it->second;
   if (web_contents_ != nullptr) {
-    out_element->container_frame_host = web_contents_->GetMainFrame();
+    out_element->SetRenderFrameHost(web_contents_->GetMainFrame());
   }
   return OkClientStatus();
 }
diff --git a/components/autofill_assistant/browser/web/web_controller.cc b/components/autofill_assistant/browser/web/web_controller.cc
index b8a2b9a..a2632e6 100644
--- a/components/autofill_assistant/browser/web/web_controller.cc
+++ b/components/autofill_assistant/browser/web/web_controller.cc
@@ -732,7 +732,7 @@
                                               element.node_frame_id());
   auto* ptr = getter.get();
   pending_workers_.emplace_back(std::move(getter));
-  ptr->Start(element.container_frame_host, element.object_id(),
+  ptr->Start(element.render_frame_host(), element.object_id(),
              base::BindOnce(&WebController::OnWaitUntilElementIsStable,
                             weak_ptr_factory_.GetWeakPtr(), ptr,
                             base::TimeTicks::Now(), std::move(callback)));
@@ -1048,8 +1048,8 @@
     return;
   }
 
-  ContentAutofillDriver* driver = ContentAutofillDriver::GetForRenderFrameHost(
-      element.container_frame_host);
+  ContentAutofillDriver* driver =
+      ContentAutofillDriver::GetForRenderFrameHost(element.render_frame_host());
   if (driver == nullptr) {
     DVLOG(1) << __func__ << " Failed to get the autofill driver.";
     std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__), nullptr,
diff --git a/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index 5773d4cd..88e7612a4 100644
--- a/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -602,11 +602,11 @@
                               bool is_main_frame) {
     if (is_main_frame) {
       EXPECT_EQ(shell()->web_contents()->GetMainFrame(),
-                result.container_frame_host);
+                result.render_frame_host());
       EXPECT_EQ(result.frame_stack().size(), 0u);
     } else {
       EXPECT_NE(shell()->web_contents()->GetMainFrame(),
-                result.container_frame_host);
+                result.render_frame_host());
       EXPECT_GE(result.frame_stack().size(), 1u);
     }
     EXPECT_FALSE(result.object_id().empty());
@@ -2758,7 +2758,7 @@
   // This makes the devtools action fail.
   ElementFinder::Result element;
   element.dom_object.object_data.node_frame_id = "doesnotexist";
-  element.container_frame_host = web_contents()->GetMainFrame();
+  element.SetRenderFrameHost(web_contents()->GetMainFrame());
 
   EXPECT_EQ(ELEMENT_POSITION_NOT_FOUND,
             WaitUntilElementIsStable(element, 10, base::Milliseconds(100))
@@ -3210,9 +3210,9 @@
 
   // Create fake element without object id and frame information only.
   ElementFinder::Result fake_frame_element;
-  fake_frame_element.container_frame_host = frame_element.container_frame_host;
+  fake_frame_element.SetRenderFrameHost(frame_element.render_frame_host());
   fake_frame_element.dom_object.object_data.node_frame_id =
-      frame_element.container_frame_host->GetDevToolsFrameToken().ToString();
+      frame_element.render_frame_host()->GetDevToolsFrameToken().ToString();
 
   ClientStatus button_status;
   ElementFinder::Result button_element;
diff --git a/components/browsing_topics/browsing_topics_calculator_unittest.cc b/components/browsing_topics/browsing_topics_calculator_unittest.cc
index 79fcfb0..3f0a05c 100644
--- a/components/browsing_topics/browsing_topics_calculator_unittest.cc
+++ b/components/browsing_topics/browsing_topics_calculator_unittest.cc
@@ -67,12 +67,15 @@
         /*restore_session=*/false);
     cookie_settings_ = new content_settings::CookieSettings(
         host_content_settings_map_.get(), &prefs_, false, "chrome-extension");
-    privacy_sandbox_settings_ = std::make_unique<
-        privacy_sandbox::PrivacySandboxSettings>(
-        std::make_unique<
-            privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>(),
-        host_content_settings_map_.get(), cookie_settings_, &prefs_,
-        /*incognito_profile=*/false);
+    auto privacy_sandbox_delegate = std::make_unique<
+        privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>();
+    privacy_sandbox_delegate->SetupDefaultResponse(/*restricted=*/false,
+                                                   /*confirmed=*/true);
+    privacy_sandbox_settings_ =
+        std::make_unique<privacy_sandbox::PrivacySandboxSettings>(
+            std::move(privacy_sandbox_delegate),
+            host_content_settings_map_.get(), cookie_settings_, &prefs_,
+            /*incognito_profile=*/false);
 
     topics_site_data_manager_ =
         std::make_unique<content::TesterBrowsingTopicsSiteDataManager>(
diff --git a/components/feed/core/proto/BUILD.gn b/components/feed/core/proto/BUILD.gn
index 4e5922c..2b1d9bc 100644
--- a/components/feed/core/proto/BUILD.gn
+++ b/components/feed/core/proto/BUILD.gn
@@ -24,6 +24,7 @@
     "v2/wire/chrome_feed_response_metadata.proto",
     "v2/wire/chrome_fulfillment_info.proto",
     "v2/wire/client_info.proto",
+    "v2/wire/client_user_profiles.proto",
     "v2/wire/consistency_token.proto",
     "v2/wire/content_id.proto",
     "v2/wire/content_lifetime.proto",
diff --git a/components/feed/core/proto/v2/wire/action_surface.proto b/components/feed/core/proto/v2/wire/action_surface.proto
index 7a280b12..d563581 100644
--- a/components/feed/core/proto/v2/wire/action_surface.proto
+++ b/components/feed/core/proto/v2/wire/action_surface.proto
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-syntax = "proto2";
+syntax = "proto3";
 
 package feedwire;
 
diff --git a/components/feed/core/proto/v2/wire/capability.proto b/components/feed/core/proto/v2/wire/capability.proto
index 427be86..b94df7421 100644
--- a/components/feed/core/proto/v2/wire/capability.proto
+++ b/components/feed/core/proto/v2/wire/capability.proto
@@ -36,4 +36,5 @@
   CONTENT_LIFETIME = 64;
   OPEN_IN_INCOGNITO = 67;
   ON_DEVICE_USER_PROFILE = 70;
+  INVALIDATE_CACHE_COMMAND = 73;
 }
diff --git a/components/feed/core/proto/v2/wire/client_info.proto b/components/feed/core/proto/v2/wire/client_info.proto
index 4b67fa5b..a203639 100644
--- a/components/feed/core/proto/v2/wire/client_info.proto
+++ b/components/feed/core/proto/v2/wire/client_info.proto
@@ -28,7 +28,6 @@
   repeated DisplayInfo display_info = 6;
   optional string client_instance_id = 7;
   optional string advertising_id = 8;
-  optional string device_country = 9;
   optional Device device = 10;
   optional ChromeClientInfo chrome_client_info = 338478298;
 }
diff --git a/components/feed/core/proto/v2/wire/client_user_profiles.proto b/components/feed/core/proto/v2/wire/client_user_profiles.proto
new file mode 100644
index 0000000..674dc6c
--- /dev/null
+++ b/components/feed/core/proto/v2/wire/client_user_profiles.proto
@@ -0,0 +1,39 @@
+// Copyright 2022 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.
+
+syntax = "proto2";
+
+package feedwire;
+
+option optimize_for = LITE_RUNTIME;
+
+message ClientUserProfiles {
+  optional DiscoverUserActionsProfile discover_user_actions_profile = 1;
+}
+message ActionCounts {
+  message Counts {
+    optional uint32 count_1d = 1;
+    optional uint32 count_7d = 2;
+    optional uint32 count_30d = 3;
+  }
+  enum ActionType {
+    UNKNOWN = 0;
+    CLICK = 1;
+  }
+  optional ActionType type = 1;
+  optional Counts counts = 2;
+}
+message DiscoverUserActionsProfile {
+  message ContentMediaXEntityActionCounts {
+    optional uint64 mid = 2;
+    repeated ActionCounts counts = 3;
+  }
+  message CardCategoryXEntityActionCounts {
+    optional uint64 card_category = 1;
+    optional uint64 mid = 2;
+    repeated ActionCounts counts = 3;
+  }
+  repeated ContentMediaXEntityActionCounts content_media_x_entity = 2;
+  repeated CardCategoryXEntityActionCounts card_category_x_entity = 3;
+}
diff --git a/components/feed/core/proto/v2/wire/feed_request.proto b/components/feed/core/proto/v2/wire/feed_request.proto
index d3a26bc..c7f3973 100644
--- a/components/feed/core/proto/v2/wire/feed_request.proto
+++ b/components/feed/core/proto/v2/wire/feed_request.proto
@@ -8,6 +8,7 @@
 
 import "components/feed/core/proto/v2/wire/capability.proto";
 import "components/feed/core/proto/v2/wire/client_info.proto";
+import "components/feed/core/proto/v2/wire/client_user_profiles.proto";
 import "components/feed/core/proto/v2/wire/consistency_token.proto";
 import "components/feed/core/proto/v2/wire/diagnostic_info.proto";
 import "components/feed/core/proto/v2/wire/feed_query.proto";
@@ -20,4 +21,5 @@
   repeated Capability client_capability = 4;
   optional ConsistencyToken consistency_token = 5;
   optional DiagnosticInfo diagnostic_info = 14;
+  optional ClientUserProfiles client_user_profiles = 16;
 }
diff --git a/components/feed/core/v2/test/proto_printer.cc b/components/feed/core/v2/test/proto_printer.cc
index a0283d8..c6d33de 100644
--- a/components/feed/core/v2/test/proto_printer.cc
+++ b/components/feed/core/v2/test/proto_printer.cc
@@ -146,7 +146,6 @@
     PRINT_FIELD(display_info);
     PRINT_FIELD(client_instance_id);
     PRINT_FIELD(advertising_id);
-    PRINT_FIELD(device_country);
     EndMessage();
     return *this;
   }
diff --git a/components/printing/browser/print_manager_utils.cc b/components/printing/browser/print_manager_utils.cc
index 737ac37..dacdecf 100644
--- a/components/printing/browser/print_manager_utils.cc
+++ b/components/printing/browser/print_manager_utils.cc
@@ -30,12 +30,8 @@
 
 void CreateCompositeClientIfNeeded(content::WebContents* web_contents,
                                    const std::string& user_agent) {
-  // TODO(weili): We only create pdf compositor client and use pdf compositor
-  // service when site-per-process or isolate-origins flag/feature is enabled,
-  // or top-document-isolation feature is enabled. This may not cover all cases
-  // where OOPIF is used such as isolate-extensions, but should be good for
-  // feature testing purpose. Eventually, we will remove this check and use pdf
-  // compositor service by default for printing.
+  // TODO(crbug.com/1022917): Once ShouldPdfCompositorBeEnabledForOopifs()
+  // always returns true, just remove the check altogether.
   if (site_isolation::SiteIsolationPolicy::
           ShouldPdfCompositorBeEnabledForOopifs()) {
     PrintCompositeClient::CreateForWebContents(web_contents);
diff --git a/components/printing/common/print.mojom b/components/printing/common/print.mojom
index 5afad24..7a79f3b0 100644
--- a/components/printing/common/print.mojom
+++ b/components/printing/common/print.mojom
@@ -25,7 +25,9 @@
 // Parameters required to do print preview.
 [EnableIf=enable_print_preview]
 struct RequestPrintPreviewParams {
+  [EnableIf=is_chromeos_ash]
   bool is_from_arc = false;
+
   bool is_modifiable = false;
   bool webnode_only = false;
   bool has_selection = false;
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 06652157..eb2ccdf 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -1362,10 +1362,12 @@
   if (ipc_nesting_level_ > kAllowedIpcDepthForPrint)
     return;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   if (print_renderer) {
     print_renderer_.Bind(std::move(print_renderer));
     print_preview_context_.SetIsForArc(true);
   }
+#endif
 
   blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
 
@@ -1389,10 +1391,12 @@
 
   print_preview_context_.OnPrintPreview();
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   if (print_preview_context_.IsForArc()) {
     base::UmaHistogramEnumeration("Arc.PrintPreview.PreviewEvent",
                                   PREVIEW_EVENT_REQUESTED, PREVIEW_EVENT_MAX);
   }
+#endif
 
   if (!print_preview_context_.source_frame()) {
     DidFinishPrinting(FAIL_PREVIEW);
@@ -1410,10 +1414,12 @@
     return;
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Save the job settings if a PrintRenderer will be used to create the preview
   // document.
   if (print_renderer_)
     print_renderer_job_settings_ = std::move(settings);
+#endif
 
   // Set the options from document if we are previewing a pdf and send a
   // message to browser.
@@ -1599,8 +1605,12 @@
   }
 
   CreatePreviewDocumentResult result = CreatePreviewDocument();
-  if (result != CREATE_IN_PROGRESS)
-    DidFinishPrinting(result == CREATE_SUCCESS ? OK : FAIL_PREVIEW);
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (result == CREATE_IN_PROGRESS)
+    return;
+#endif
+
+  DidFinishPrinting(result == CREATE_SUCCESS ? OK : FAIL_PREVIEW);
 }
 
 PrintRenderFrameHelper::CreatePreviewDocumentResult
@@ -1608,18 +1618,23 @@
   if (!print_pages_params_ || CheckForCancel() || !preview_ui_)
     return CREATE_FAIL;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   if (print_preview_context_.IsForArc()) {
     base::UmaHistogramEnumeration("Arc.PrintPreview.PreviewEvent",
                                   PREVIEW_EVENT_CREATE_DOCUMENT,
                                   PREVIEW_EVENT_MAX);
   }
+#endif
 
   const mojom::PrintParams& print_params = *print_pages_params_->params;
   const std::vector<uint32_t>& pages = print_pages_params_->pages;
 
   bool require_document_metafile =
-      print_renderer_ ||
       print_params.printed_doc_type != mojom::SkiaDocumentType::kMSKP;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  require_document_metafile = require_document_metafile || print_renderer_;
+#endif
+
   if (!print_preview_context_.CreatePreviewDocument(
           std::move(prep_frame_view_), pages, print_params.printed_doc_type,
           print_params.document_cookie, require_document_metafile)) {
@@ -1660,6 +1675,7 @@
   if (CheckForCancel())
     return CREATE_FAIL;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // If a PrintRenderer has been provided, use it to create the preview
   // document.
   if (print_renderer_) {
@@ -1671,6 +1687,7 @@
                        print_params.document_cookie, begin_time));
     return CREATE_IN_PROGRESS;
   }
+#endif
 
   if (print_pages_params_->params->printed_doc_type ==
       mojom::SkiaDocumentType::kMSKP) {
@@ -2535,7 +2552,9 @@
     is_loading_ = print_preview_context_.source_frame()->WillPrintSoon();
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   const bool is_from_arc = print_preview_context_.IsForArc();
+#endif
   const bool is_modifiable = print_preview_context_.IsModifiable();
   const bool has_selection = print_preview_context_.HasSelection();
 
@@ -2547,7 +2566,9 @@
     snapshotter_ = render_frame()->CreateAXTreeSnapshotter(ui::AXMode::kPDF);
 
   auto params = mojom::RequestPrintPreviewParams::New();
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   params->is_from_arc = is_from_arc;
+#endif
   params->is_modifiable = is_modifiable;
   params->has_selection = has_selection;
   switch (type) {
@@ -2626,10 +2647,12 @@
     }
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   if (print_preview_context_.IsForArc()) {
     base::UmaHistogramEnumeration("Arc.PrintPreview.PreviewEvent",
                                   PREVIEW_EVENT_INITIATED, PREVIEW_EVENT_MAX);
   }
+#endif
   GetPrintManagerHost()->RequestPrintPreview(std::move(params));
 }
 
@@ -2853,9 +2876,12 @@
   state_ = INITIALIZED;
   if (report_error) {
     DCHECK_NE(PREVIEW_ERROR_NONE, error_);
-    base::UmaHistogramEnumeration(is_for_arc_ ? "Arc.PrintPreview.RendererError"
-                                              : "PrintPreview.RendererError",
-                                  error_, PREVIEW_ERROR_LAST_ENUM);
+    const char* name = "PrintPreview.RendererError";
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    if (is_for_arc_)
+      name = "Arc.PrintPreview.RendererError";
+#endif
+    base::UmaHistogramEnumeration(name, error_, PREVIEW_ERROR_LAST_ENUM);
   }
   ClearContext();
 }
@@ -2871,10 +2897,12 @@
   return state_ == RENDERING || state_ == DONE;
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 bool PrintRenderFrameHelper::PrintPreviewContext::IsForArc() const {
   DCHECK_NE(state_, UNINITIALIZED);
   return is_for_arc_;
 }
+#endif
 
 bool PrintRenderFrameHelper::PrintPreviewContext::IsPlugin() const {
   DCHECK(state_ != UNINITIALIZED);
@@ -2901,9 +2929,11 @@
   return static_cast<size_t>(current_page_index_) == pages_to_render_.size();
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 void PrintRenderFrameHelper::PrintPreviewContext::SetIsForArc(bool is_for_arc) {
   is_for_arc_ = is_for_arc;
 }
+#endif
 
 void PrintRenderFrameHelper::PrintPreviewContext::set_error(
     enum PrintPreviewErrorBuckets error) {
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
index 2b70311..0235941 100644
--- a/components/printing/renderer/print_render_frame_helper.h
+++ b/components/printing/renderer/print_render_frame_helper.h
@@ -181,9 +181,11 @@
   // CREATE_IN_PROGRESS signifies that the preview document is being rendered
   // asynchronously by a PrintRenderer.
   enum CreatePreviewDocumentResult {
-    CREATE_SUCCESS,
-    CREATE_IN_PROGRESS,
-    CREATE_FAIL,
+    CREATE_SUCCESS = 0,
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    CREATE_IN_PROGRESS = 1,
+#endif
+    CREATE_FAIL = 2,
   };
 
   enum PrintingResult {
@@ -485,12 +487,14 @@
   // Used to check the prerendering status.
   const std::unique_ptr<Delegate> delegate_;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   // Settings used by a PrintRenderer to create a preview document.
   base::Value print_renderer_job_settings_;
 
   // Used to render print documents from an external source (ARC, Crostini,
   // etc.).
   mojo::AssociatedRemote<mojom::PrintRenderer> print_renderer_;
+#endif
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
   // Used to notify the browser of preview UI actions.
@@ -554,7 +558,9 @@
     // Helper functions
     uint32_t GetNextPageNumber();
     bool IsRendering() const;
+#if BUILDFLAG(IS_CHROMEOS_ASH)
     bool IsForArc() const;
+#endif
     bool IsPlugin() const;
     bool IsModifiable() const;
     bool HasSelection();
@@ -562,7 +568,9 @@
     bool IsFinalPageRendered() const;
 
     // Setters
+#if BUILDFLAG(IS_CHROMEOS_ASH)
     void SetIsForArc(bool is_for_arc);
+#endif
     void set_error(enum PrintPreviewErrorBuckets error);
 
     // Getters
@@ -625,8 +633,10 @@
     // True, if the document source is modifiable. e.g. HTML and not PDF.
     bool is_modifiable_ = true;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
     // True, if the document source is from ARC.
     bool is_for_arc_ = false;
+#endif
 
     // Specifies the total number of pages in the print ready metafile.
     int print_ready_metafile_page_count_ = 0;
diff --git a/components/privacy_sandbox/privacy_sandbox_features.cc b/components/privacy_sandbox/privacy_sandbox_features.cc
index 68f0b09..b247aec 100644
--- a/components/privacy_sandbox/privacy_sandbox_features.cc
+++ b/components/privacy_sandbox/privacy_sandbox_features.cc
@@ -8,8 +8,12 @@
 
 const base::Feature kPrivacySandboxSettings3{"PrivacySandboxSettings3",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
+const base::FeatureParam<bool> kPrivacySandboxSettings3DefaultOn{
+    &kPrivacySandboxSettings3, "setting-default-on", false};
 const base::FeatureParam<bool> kPrivacySandboxSettings3ConsentRequired{
     &kPrivacySandboxSettings3, "consent-required", false};
+const base::FeatureParam<bool> kPrivacySandboxSettings3NoticeRequired{
+    &kPrivacySandboxSettings3, "notice-required", false};
 const base::FeatureParam<bool> kPrivacySandboxSettings3DisableDialogForTesting{
     &kPrivacySandboxSettings3, "disable-dialog-for-testing", false};
 const base::FeatureParam<bool>
@@ -21,4 +25,8 @@
 const base::FeatureParam<bool> kPrivacySandboxSettings3ShowSampleDataForTesting{
     &kPrivacySandboxSettings3, "show-sample-data", false};
 
+const base::Feature kOverridePrivacySandboxSettingsLocalTesting{
+    "OverridePrivacySandboxSettingsLocalTesting",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace privacy_sandbox
diff --git a/components/privacy_sandbox/privacy_sandbox_features.h b/components/privacy_sandbox/privacy_sandbox_features.h
index a724071..62e75e0 100644
--- a/components/privacy_sandbox/privacy_sandbox_features.h
+++ b/components/privacy_sandbox/privacy_sandbox_features.h
@@ -12,7 +12,17 @@
 
 // Enables the third release of the Privacy Sandbox settings.
 extern const base::Feature kPrivacySandboxSettings3;
+// Determines whether the user facing controls for Privacy Sandbox Settings 3
+// should be default on.
+extern const base::FeatureParam<bool> kPrivacySandboxSettings3DefaultOn;
+// When true, the user will be shown a consent to enable the Privacy Sandbox
+// release 3, if they accept the APIs will become active. Only one of this and
+// the below notice feature should be enabled at any one time.
 extern const base::FeatureParam<bool> kPrivacySandboxSettings3ConsentRequired;
+// When true, the user will be shown a notice, after which the Privacy Sandbox
+// 3 APIs will become active. Only one of this and the above consent feature
+// should be enabled at any one time.
+extern const base::FeatureParam<bool> kPrivacySandboxSettings3NoticeRequired;
 
 // Feature parameters which should exclusively be used for testing purposes.
 // Enabling any of these parameters may result in the Privacy Sandbox prefs
@@ -27,6 +37,8 @@
 extern const base::FeatureParam<bool>
     kPrivacySandboxSettings3ShowSampleDataForTesting;
 
+extern const base::Feature kOverridePrivacySandboxSettingsLocalTesting;
+
 }  // namespace privacy_sandbox
 
 #endif  // COMPONENTS_PRIVACY_SANDBOX_PRIVACY_SANDBOX_FEATURES_H_
diff --git a/components/privacy_sandbox/privacy_sandbox_prefs.cc b/components/privacy_sandbox/privacy_sandbox_prefs.cc
index 14f3d904..393bb02a 100644
--- a/components/privacy_sandbox/privacy_sandbox_prefs.cc
+++ b/components/privacy_sandbox/privacy_sandbox_prefs.cc
@@ -14,9 +14,15 @@
 
 const char kPrivacySandboxApisEnabledV2[] = "privacy_sandbox.apis_enabled_v2";
 
+const char kPrivacySandboxApisEnabledV2Init[] =
+    "privacy_sandbox.apis_enabled_v2_init";
+
 const char kPrivacySandboxManuallyControlled[] =
     "privacy_sandbox.manually_controlled";
 
+const char kPrivacySandboxManuallyControlledV2[] =
+    "privacy_sandbox.manually_controlled_v2";
+
 const char kPrivacySandboxPreferencesReconciled[] =
     "privacy_sandbox.preferences_reconciled";
 
@@ -53,6 +59,9 @@
 extern const char kPrivacySandboxNoConfirmationThirdPartyCookiesBlocked[] =
     "privacy_sandbox.no_confirmation_3PC_blocked";
 
+extern const char kPrivacySandboxNoConfirmationManuallyControlled[] =
+    "privacy_sandbox.no_confirmation_manually_controlled";
+
 extern const char kPrivacySandboxDisabledInsufficientConfirmation[] =
     "privacy_sandbox.disabled_insufficient_confirmation";
 
@@ -65,9 +74,12 @@
       prefs::kPrivacySandboxApisEnabled, true,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterBooleanPref(prefs::kPrivacySandboxApisEnabledV2, false);
+  registry->RegisterBooleanPref(prefs::kPrivacySandboxApisEnabledV2Init, false);
   registry->RegisterBooleanPref(
       prefs::kPrivacySandboxManuallyControlled, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+  registry->RegisterBooleanPref(prefs::kPrivacySandboxManuallyControlledV2,
+                                false);
   registry->RegisterBooleanPref(prefs::kPrivacySandboxPreferencesReconciled,
                                 false);
   registry->RegisterBooleanPref(prefs::kPrivacySandboxPageViewed, false);
@@ -92,6 +104,8 @@
   registry->RegisterBooleanPref(
       prefs::kPrivacySandboxNoConfirmationThirdPartyCookiesBlocked, false);
   registry->RegisterBooleanPref(
+      prefs::kPrivacySandboxNoConfirmationManuallyControlled, false);
+  registry->RegisterBooleanPref(
       prefs::kPrivacySandboxDisabledInsufficientConfirmation, false);
 }
 
diff --git a/components/privacy_sandbox/privacy_sandbox_prefs.h b/components/privacy_sandbox/privacy_sandbox_prefs.h
index 702fc54..568681d 100644
--- a/components/privacy_sandbox/privacy_sandbox_prefs.h
+++ b/components/privacy_sandbox/privacy_sandbox_prefs.h
@@ -22,11 +22,23 @@
 // enabled.
 extern const char kPrivacySandboxApisEnabledV2[];
 
+// Un-synced boolean pref. This is set to true when the one-time initialization
+// of the users kPrivacySandboxApisEnabledV2 has run. Some users may have the V2
+// pref enabled by default by the PrivacySandboxService.
+extern const char kPrivacySandboxApisEnabledV2Init[];
+
 // Synced boolean that indicates if a user has manually toggled the settings
 // associated with the PrivacySandboxSettings feature.
+// TODO(crbug.com/1292898): Deprecate this preference once all users have been
+// migrated to the V2 pref.
 extern const char kPrivacySandboxManuallyControlled[];
 
-// Boolean to indicate whether or not the preferecnes have been reconciled for
+// Un-synced boolean pref. This is a replacement for the synced preference
+// above. It it set to true when the user manually toggles the setting on the
+// updated settings page.
+extern const char kPrivacySandboxManuallyControlledV2[];
+
+// Boolean to indicate whether or not the preferences have been reconciled for
 // this device. This occurs for each device once when privacy sandbox is first
 // enabled.
 extern const char kPrivacySandboxPreferencesReconciled[];
@@ -84,6 +96,10 @@
 // profile because the third party cookies were being blocked.
 extern const char kPrivacySandboxNoConfirmationThirdPartyCookiesBlocked[];
 
+// Boolean that indicates a Privacy Sandbox confirmation was not shown to the
+// profile because the Privacy Sandbox is being manually controlled.
+extern const char kPrivacySandboxNoConfirmationManuallyControlled[];
+
 // Boolean that indicates the user's Privacy Sandbox setting was disabled
 // automatically because they do not have the correct level of confirmation.
 extern const char kPrivacySandboxDisabledInsufficientConfirmation[];
diff --git a/components/privacy_sandbox/privacy_sandbox_settings.cc b/components/privacy_sandbox/privacy_sandbox_settings.cc
index 5dcb1c92..3f43dc20 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings.cc
+++ b/components/privacy_sandbox/privacy_sandbox_settings.cc
@@ -339,9 +339,18 @@
 }
 
 bool PrivacySandboxSettings::IsPrivacySandboxEnabled() const {
-  // If the delegate is restricting access, the Privacy Sandbox is disabled.
-  if (delegate_->IsPrivacySandboxRestricted())
+  // If the delegate is restricting access, or indicates confirmation has not
+  // occurred, the Privacy Sandbox is disabled.
+  if (delegate_->IsPrivacySandboxRestricted() ||
+      !delegate_->IsPrivacySandboxConfirmed()) {
     return false;
+  }
+
+  // For Measurement and Relevance APIs, we explicitly do not require the
+  // underlying pref to be enabled if there is a local flag enabling the APIs to
+  // allow for local testing.
+  bool should_override_setting_for_local_testing = base::FeatureList::IsEnabled(
+      privacy_sandbox::kOverridePrivacySandboxSettingsLocalTesting);
 
   // Which preference is consulted is dependent on whether release 3 of the
   // settings is available.
@@ -350,20 +359,23 @@
     if (incognito_profile_)
       return false;
 
-    // For Privacy Sadnbox Settings 3, APIs may be restricted via the delegate.
+    if (should_override_setting_for_local_testing) {
+      return true;
+    }
+
+    // For Privacy Sandbox Settings 3, APIs may be restricted via the delegate.
     // The V2 pref was introduced with the 3rd Privacy Sandbox release.
     return pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabledV2);
   }
 
+  if (should_override_setting_for_local_testing)
+    return true;
+
   return pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabled);
 }
 
 void PrivacySandboxSettings::SetPrivacySandboxEnabled(bool enabled) {
-  pref_service_->SetBoolean(prefs::kPrivacySandboxManuallyControlled, true);
-
-  // Only apply the decision to the appropriate preference. Confirmation logic
-  // DCHECKS that the user has not been able to enable the V2 preference
-  // without seeing a dialog.
+  // Only apply the decision to the appropriate preference.
   if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings3)) {
     pref_service_->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, enabled);
   } else {
@@ -406,6 +418,11 @@
   observers_.RemoveObserver(observer);
 }
 
+void PrivacySandboxSettings::SetDelegateForTesting(
+    std::unique_ptr<Delegate> delegate) {
+  delegate_ = std::move(delegate);
+}
+
 PrivacySandboxSettings::PrivacySandboxSettings() = default;
 
 bool PrivacySandboxSettings::IsPrivacySandboxEnabledForContext(
diff --git a/components/privacy_sandbox/privacy_sandbox_settings.h b/components/privacy_sandbox/privacy_sandbox_settings.h
index d76c179..b1a4b3f 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings.h
+++ b/components/privacy_sandbox/privacy_sandbox_settings.h
@@ -59,6 +59,12 @@
     // consulted on every access check, and it is acceptable for this to change
     // return value over the life of the service.
     virtual bool IsPrivacySandboxRestricted() = 0;
+
+    // Allows the delegate to express the concept of confirmation, e.g. a notice
+    // or consent, required before Privacy Sandbox operation can occur. This is
+    // checked on every access check, and must return true for Privacy Sandbox
+    // APIs to run.
+    virtual bool IsPrivacySandboxConfirmed() = 0;
   };
 
   PrivacySandboxSettings(
@@ -149,16 +155,18 @@
       const url::Origin& top_frame_origin,
       const std::vector<GURL>& auction_parties);
 
-  // Returns whether the profile has the Privacy Sandbox enabled. This directly
-  // reflects the state of the main privacy sandbox control, and does not
-  // consider any cookie settings. A return value of false means that no
-  // Privacy Sandbox operations can occur. A return value of true must be
-  // followed up with the appropriate IsXAllowed() call.
+  // Returns whether the profile has the Privacy Sandbox enabled. This consults
+  // the main preference, as well as the delegate to check whether the sandbox
+  // is restricted, or has not been confirmed.  It does not consider any cookie
+  // settings. A return value of false means that no Privacy Sandbox operations
+  // can occur. A return value of true must be followed up with the appropriate
+  // IsXAllowed() call.
   bool IsPrivacySandboxEnabled() const;
 
   // Disables the Privacy Sandbox completely if |enabled| is false, if |enabled|
-  // is true, more granular checks will still be performed to determine if
-  // specific APIs are available in specific contexts.
+  // is true, more granular checks will still be performed, and the delegate
+  // consulted, to determine if specific APIs are available in specific
+  // contexts.
   void SetPrivacySandboxEnabled(bool enabled);
 
   // Returns whether Trust Tokens are "generally" available. A return value of
@@ -182,6 +190,9 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  // Overrides the internal delegate for test purposes.
+  void SetDelegateForTesting(std::unique_ptr<Delegate> delegate);
+
  protected:
   // Protected default constructor to allow mocking in tests.
   PrivacySandboxSettings();
diff --git a/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc b/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc
index a64acbe..b9246e3 100644
--- a/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc
+++ b/components/privacy_sandbox/privacy_sandbox_settings_unittest.cc
@@ -70,7 +70,8 @@
   }
 
   virtual void InitializeDelegateBeforeStart() {
-    mock_delegate()->SetupDefaultResponse(/*restricted=*/false);
+    mock_delegate()->SetupDefaultResponse(/*restricted=*/false,
+                                          /*confirmed=*/true);
   }
 
   virtual bool IsIncognitoProfile() { return false; }
@@ -93,9 +94,11 @@
     return &browser_task_environment_;
   }
 
+ protected:
+  base::test::ScopedFeatureList feature_list_;
+
  private:
   content::BrowserTaskEnvironment browser_task_environment_;
-  base::test::ScopedFeatureList feature_list_;
   raw_ptr<privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>
       mock_delegate_;
   sync_preferences::TestingPrefServiceSyncable prefs_;
@@ -863,6 +866,9 @@
 TEST_P(PrivacySandboxSettingsMockDelegateTest, IsPrivacySandboxRestricted) {
   // When the sandbox is otherwise enabled, the delegate returning true for
   // IsPrivacySandboxRestricted() should disable the sandbox.
+  ON_CALL(*mock_delegate(), IsPrivacySandboxConfirmed).WillByDefault([=]() {
+    return true;
+  });
   privacy_sandbox_settings()->SetPrivacySandboxEnabled(true);
   EXPECT_CALL(*mock_delegate(), IsPrivacySandboxRestricted())
       .Times(1)
@@ -882,8 +888,64 @@
   EXPECT_FALSE(privacy_sandbox_settings()->IsPrivacySandboxEnabled());
 }
 
+TEST_P(PrivacySandboxSettingsMockDelegateTest, IsPrivacySandboxConfirmed) {
+  // When the sandbox is otherwise enabled, the delegate returning false for
+  // IsPrivacySandboxConfirmed() should disable the sandbox.
+  ON_CALL(*mock_delegate(), IsPrivacySandboxRestricted).WillByDefault([=]() {
+    return false;
+  });
+  privacy_sandbox_settings()->SetPrivacySandboxEnabled(true);
+  EXPECT_CALL(*mock_delegate(), IsPrivacySandboxConfirmed())
+      .Times(1)
+      .WillOnce(testing::Return(false));
+  EXPECT_FALSE(privacy_sandbox_settings()->IsPrivacySandboxEnabled());
+
+  privacy_sandbox_settings()->SetPrivacySandboxEnabled(true);
+  EXPECT_CALL(*mock_delegate(), IsPrivacySandboxConfirmed())
+      .Times(1)
+      .WillOnce(testing::Return(true));
+  EXPECT_TRUE(privacy_sandbox_settings()->IsPrivacySandboxEnabled());
+
+  // The delegate should not override a disabled sandbox.
+  privacy_sandbox_settings()->SetPrivacySandboxEnabled(false);
+  EXPECT_CALL(*mock_delegate(), IsPrivacySandboxConfirmed())
+      .Times(1)
+      .WillOnce(testing::Return(true));
+  EXPECT_FALSE(privacy_sandbox_settings()->IsPrivacySandboxEnabled());
+}
+
 INSTANTIATE_TEST_SUITE_P(PrivacySandboxSettingsMockDelegateTestInstance,
                          PrivacySandboxSettingsMockDelegateTest,
                          testing::Bool());
 
+class PrivacySandboxSettingLocalOverrideTest
+    : public PrivacySandboxSettingsTest {
+  void InitializeFeaturesBeforeStart() override {
+    if (GetParam()) {
+      feature_list_.InitWithFeatures(
+          {privacy_sandbox::kPrivacySandboxSettings3,
+           privacy_sandbox::kOverridePrivacySandboxSettingsLocalTesting},
+          /*disabled_features=*/{});
+    } else {
+      feature_list_.InitWithFeatures(
+          {privacy_sandbox::kPrivacySandboxSettings3},
+          {privacy_sandbox::kOverridePrivacySandboxSettingsLocalTesting});
+    }
+  }
+};
+
+TEST_P(PrivacySandboxSettingLocalOverrideTest, FollowsOverrideBehavior) {
+  // When the Release 3 flag is enabled, APIs should always be disabled in
+  // incognito. The Release 3 flag is set based on the test param.
+  privacy_sandbox_settings()->SetPrivacySandboxEnabled(false);
+  if (GetParam())
+    EXPECT_TRUE(privacy_sandbox_settings()->IsPrivacySandboxEnabled());
+  else
+    EXPECT_FALSE(privacy_sandbox_settings()->IsPrivacySandboxEnabled());
+}
+
+INSTANTIATE_TEST_SUITE_P(PrivacySandboxSettingLocalOverrideTestInstance,
+                         PrivacySandboxSettingLocalOverrideTest,
+                         testing::Bool());
+
 }  // namespace privacy_sandbox
diff --git a/components/privacy_sandbox/privacy_sandbox_test_util.h b/components/privacy_sandbox/privacy_sandbox_test_util.h
index f36bae7..a4fec3a 100644
--- a/components/privacy_sandbox/privacy_sandbox_test_util.h
+++ b/components/privacy_sandbox/privacy_sandbox_test_util.h
@@ -33,12 +33,16 @@
  public:
   MockPrivacySandboxSettingsDelegate();
   ~MockPrivacySandboxSettingsDelegate() override;
-  void SetupDefaultResponse(bool restricted) {
+  void SetupDefaultResponse(bool restricted, bool confirmed) {
     ON_CALL(*this, IsPrivacySandboxRestricted).WillByDefault([=]() {
       return restricted;
     });
+    ON_CALL(*this, IsPrivacySandboxConfirmed).WillByDefault([=]() {
+      return confirmed;
+    });
   }
   MOCK_METHOD(bool, IsPrivacySandboxRestricted, (), (override));
+  MOCK_METHOD(bool, IsPrivacySandboxConfirmed, (), (override));
 };
 
 // Define an additional content setting value to simulate an unmanaged default
diff --git a/components/services/app_service/public/cpp/intent_filter_util.cc b/components/services/app_service/public/cpp/intent_filter_util.cc
index bed5aabe..b433edfb 100644
--- a/components/services/app_service/public/cpp/intent_filter_util.cc
+++ b/components/services/app_service/public/cpp/intent_filter_util.cc
@@ -405,6 +405,38 @@
   return false;
 }
 
+size_t IntentFilterUrlMatchLength(const apps::IntentFilterPtr& intent_filter,
+                                  const GURL& url) {
+  apps::Intent intent(url);
+  if (!intent.MatchFilter(intent_filter)) {
+    return 0;
+  }
+  // If the filter matches, all URL components match, so a kPattern condition
+  // matches and we add up the length of the filter's URL components (scheme,
+  // host, path).
+  size_t path_length = 0;
+  for (const apps::ConditionPtr& condition : intent_filter->conditions) {
+    if (condition->condition_type == apps::ConditionType::kPattern) {
+      for (const apps::ConditionValuePtr& value : condition->condition_values) {
+        switch (value->match_type) {
+          case apps::PatternMatchType::kLiteral:
+          case apps::PatternMatchType::kPrefix:
+            path_length = std::max(path_length, value->value.size());
+            break;
+          default:
+            // the rest are ignored.
+            break;
+        }
+      }
+    }
+  }
+  if (path_length == 0) {
+    return 0;
+  }
+  return url.scheme_piece().size() + /*length("://")*/ 3 +
+         url.host_piece().size() + path_length;
+}
+
 }  // namespace apps_util
 
 namespace apps {
diff --git a/components/services/app_service/public/cpp/intent_filter_util.h b/components/services/app_service/public/cpp/intent_filter_util.h
index e69396a..c7c3dd6 100644
--- a/components/services/app_service/public/cpp/intent_filter_util.h
+++ b/components/services/app_service/public/cpp/intent_filter_util.h
@@ -87,6 +87,14 @@
 bool IsSupportedLinkForApp(const std::string& app_id,
                            const apps::mojom::IntentFilterPtr& intent_filter);
 
+// If |intent_filter| matches a URL by prefix, return the length of the longest
+// match. For example, if the filter matches "https://example.org/app/*", the
+// longest match for the URL "https://example.org/app/foo/bar" is the length of
+// "https://example.org/app/" (24). If |intent_filter| does not match |url|, or
+// if the filter does not match a prefix (e.g. glob), 0 is returned.
+size_t IntentFilterUrlMatchLength(const apps::IntentFilterPtr& intent_filter,
+                                  const GURL& url);
+
 }  // namespace apps_util
 
 namespace apps {
diff --git a/components/services/app_service/public/cpp/intent_filter_util_unittest.cc b/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
index b48c3b9..a535726 100644
--- a/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
+++ b/components/services/app_service/public/cpp/intent_filter_util_unittest.cc
@@ -600,3 +600,51 @@
     EXPECT_EQ(condition->condition_values[0]->value, "7");
   }
 }
+
+TEST_F(IntentFilterUtilTest, TestIntentFilterUrlMatchLength) {
+  const auto kPrefix = apps::PatternMatchType::kPrefix;
+  const auto kLiteral = apps::PatternMatchType::kLiteral;
+  const auto kGlob = apps::PatternMatchType::kGlob;
+  struct Test {
+    std::string filter_url;
+    std::string matched_url;
+    apps::PatternMatchType pattern_match_type;
+    size_t expected;
+  };
+  std::vector<Test> tests{
+      {"https://prefix.a.com/a", "https://prefix.a.com/a", kPrefix, 22},
+      {"https://prefix.a.com/a", "https://prefix.a.com/a?x=y", kPrefix, 22},
+      {"https://prefix.a.com/a", "https://prefix.a.com/a/b", kPrefix, 22},
+      {"https://prefix.a.com/", "https://prefix.a.com/", kPrefix, 21},
+      {"https://prefix.a.com", "https://prefix.a.com", kPrefix, 21},
+      {"https://prefix.a.com/a", "", kPrefix, 0},
+      {"https://prefix.a.com/a/b", "https://prefix.a.com/a", kPrefix, 0},
+      {"https://prefix.a.com/a", "https://prefix.a.com/", kPrefix, 0},
+      {"https://prefix.a.com/a", "https://prefix.a.org/a", kPrefix, 0},
+      {"https://prefix.a.com/a", "http://prefix.a.com/a", kPrefix, 0},
+
+      {"https://exact.a.com/a", "https://exact.a.com/a", kLiteral, 21},
+      {"https://exact.a.com/", "https://exact.a.com/", kLiteral, 20},
+      {"https://exact.a.com", "https://exact.a.com", kLiteral, 20},
+      {"https://exact.a.com/a", "https://exact.a.com/a/b", kLiteral, 0},
+      {"https://exact.a.com/a/b", "https://exact.a.com/a", kLiteral, 0},
+      {"https://exact.a.com/a", "https://exact.a.org/a", kLiteral, 0},
+      {"https://exact.a.com/a", "http://exact.a.com/a", kLiteral, 0},
+
+      // Glob is not supported.
+      {"https://glob.a.com/a/.*", "https://glob.a.com/a", kGlob, 0},
+      {"https://glob.a.com/a/.*", "https://glob.a.com/a/b", kGlob, 0},
+      {"https://glob.a.com/a/.*/b", "https://glob.a.com/a/b", kGlob, 0},
+  };
+  for (size_t i = 0; i < tests.size(); ++i) {
+    const auto& test = tests[i];
+    GURL filter_url(test.filter_url);
+    GURL matched_url(test.matched_url);
+    auto filter = MakeFilter(filter_url.scheme(), filter_url.host(),
+                             filter_url.path(), test.pattern_match_type);
+    EXPECT_EQ(apps_util::IntentFilterUrlMatchLength(filter, matched_url),
+              test.expected)
+        << "Test #" << i << " url=" << test.matched_url
+        << " filter=" << test.filter_url;
+  }
+}
diff --git a/components/services/heap_profiling/public/cpp/profiling_client.cc b/components/services/heap_profiling/public/cpp/profiling_client.cc
index 964890d3..70de019 100644
--- a/components/services/heap_profiling/public/cpp/profiling_client.cc
+++ b/components/services/heap_profiling/public/cpp/profiling_client.cc
@@ -49,7 +49,6 @@
   if (started_profiling_)
     return;
   started_profiling_ = true;
-  base::trace_event::MallocDumpProvider::GetInstance()->DisableMetrics();
 
 #if BUILDFLAG(IS_APPLE)
   // On macOS, this call is necessary to shim malloc zones that were created
diff --git a/components/site_isolation/site_isolation_policy.cc b/components/site_isolation/site_isolation_policy.cc
index d7caa5a3..4ed12c9 100644
--- a/components/site_isolation/site_isolation_policy.cc
+++ b/components/site_isolation/site_isolation_policy.cc
@@ -337,17 +337,23 @@
 
 // static
 bool SiteIsolationPolicy::ShouldPdfCompositorBeEnabledForOopifs() {
-  // We only create pdf compositor client and use pdf compositor service when
-  // one of the site isolation modes that forces OOPIFs is on. This includes
-  // full site isolation on desktop, password-triggered site isolation on
-  // Android for high-memory devices, and/or isolated origins specified via
-  // command line, enterprise policy, or field trials.
+#if BUILDFLAG(IS_ANDROID)
+  // TODO(crbug.com/1022917): Always enable on Android, at which point, this
+  // method should go away.
   //
-  // TODO(weili, thestig): Eventually, we should remove this check and use pdf
-  // compositor service by default for printing.
+  // Only use the PDF compositor when one of the site isolation modes that
+  // forces OOPIFs is on. This includes:
+  // - Full site isolation, which may be forced on.
+  // - Password-triggered site isolation for high-memory devices
+  // - Isolated origins specified via command line, enterprise policy, or field
+  //   trials.
   return content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites() ||
          IsIsolationForPasswordSitesEnabled() ||
          content::SiteIsolationPolicy::AreIsolatedOriginsEnabled();
+#else
+  // Always use the PDF compositor on non-mobile platforms.
+  return true;
+#endif
 }
 
 }  // namespace site_isolation
diff --git a/components/sync/PRESUBMIT.py b/components/sync/PRESUBMIT.py
index 29fc4ab..0b80a04 100644
--- a/components/sync/PRESUBMIT.py
+++ b/components/sync/PRESUBMIT.py
@@ -220,8 +220,7 @@
 
 
 def IsTitleCased(string):
-  return reduce(lambda bool1, bool2: bool1 and bool2,
-    [s[0].isupper() for s in string.split(' ')])
+  return all([s[0].isupper() for s in string.split(' ')])
 
 
 def FormatPresubmitError(output_api, message, affected_lines):
@@ -334,7 +333,7 @@
     StripTrailingS(map_entry.root_tag)):
     return [
       FormatPresubmitError(
-        output_api,'root tag "%s" does not match model type. It should'
+        output_api,'root tag "%s" does not match model type. It should '
         'be "%s"' % (map_entry.root_tag, expected_root_tag),
         map_entry.affected_lines)]
   return []
diff --git a/components/sync/PRESUBMIT_test.py b/components/sync/PRESUBMIT_test.py
index feb8ffce..2403e83 100755
--- a/components/sync/PRESUBMIT_test.py
+++ b/components/sync/PRESUBMIT_test.py
@@ -162,7 +162,7 @@
   def _testChange(self, modeltype_literal):
     mock_input_api = MockInputApi()
     mock_input_api.files = [
-      MockFile(os.path.abspath('./protocol/sync.proto'),
+      MockFile(os.path.abspath('./protocol/entity_specifics.proto'),
         MOCK_PROTOFILE_CONTENTS),
       MockFile(os.path.abspath('./base/model_type.cc'),
         MOCK_MODELTYPE_CONTENTS % (modeltype_literal))
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index 553c3be..51a2be13 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -1188,7 +1188,7 @@
     surface->getCanvas()->save();
     gfx::RRectF clip_rect(backdrop_filter_bounds.value());
     surface->getCanvas()->setMatrix(
-        SkMatrix(backdrop_filter_bounds_transform.matrix()));
+        backdrop_filter_bounds_transform.matrix().asM33());
     surface->getCanvas()->clipRRect(SkRRect(clip_rect), SkClipOp::kIntersect,
                                     true /* antialias */);
     surface->getCanvas()->resetMatrix();
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc
index 730b7f03..945245b 100644
--- a/components/viz/service/display/software_renderer.cc
+++ b/components/viz/service/display/software_renderer.cc
@@ -189,7 +189,7 @@
   gfx::Transform screen_transform =
       current_frame()->window_matrix * current_frame()->projection_matrix;
   SkRRect result;
-  if (SkRRect(rrect).transform(SkMatrix(screen_transform.matrix()), &result)) {
+  if (SkRRect(rrect).transform(screen_transform.matrix().asM33(), &result)) {
     // Skia applies the current matrix to clip rects so we reset it temporarily.
     SkMatrix current_matrix = current_canvas_->getTotalMatrix();
     current_canvas_->resetMatrix();
@@ -825,7 +825,7 @@
     return nullptr;
 
   SkMatrix filter_backdrop_transform =
-      SkMatrix(contents_device_transform_inverse.matrix());
+      contents_device_transform_inverse.matrix().asM33();
   filter_backdrop_transform.preTranslate(backdrop_rect.x(), backdrop_rect.y());
 
   SkBitmap backdrop_bitmap = GetBackdropBitmap(backdrop_rect);
@@ -877,7 +877,7 @@
 
   // Clip the filtered image to the (rounded) bounding box of the element.
   if (backdrop_filter_bounds) {
-    canvas.setMatrix(SkMatrix(backdrop_filter_bounds_transform.matrix()));
+    canvas.setMatrix(backdrop_filter_bounds_transform.matrix().asM33());
     canvas.clipRRect(SkRRect(*backdrop_filter_bounds), SkClipOp::kIntersect,
                      true /* antialias */);
     canvas.resetMatrix();
diff --git a/content/browser/android/synchronous_compositor_host.cc b/content/browser/android/synchronous_compositor_host.cc
index 7bf9860..3fe0dec 100644
--- a/content/browser/android/synchronous_compositor_host.cc
+++ b/content/browser/android/synchronous_compositor_host.cc
@@ -384,7 +384,7 @@
                            canvas->getBaseLayerSize().height());
   SkIRect canvas_clip = canvas->getDeviceClipBounds();
   params->clip = gfx::SkIRectToRect(canvas_clip);
-  params->transform.matrix() = canvas->getTotalMatrix();
+  params->transform = gfx::Transform(canvas->getTotalMatrix());
   if (params->size.IsEmpty())
     return true;
 
diff --git a/content/browser/attribution_reporting/attributions_browsertest.cc b/content/browser/attribution_reporting/attributions_browsertest.cc
index 83827f5..54b2b645 100644
--- a/content/browser/attribution_reporting/attributions_browsertest.cc
+++ b/content/browser/attribution_reporting/attributions_browsertest.cc
@@ -154,6 +154,7 @@
     // Sets up the blink runtime feature for ConversionMeasurement.
     command_line->AppendSwitch(
         switches::kEnableExperimentalWebPlatformFeatures);
+    command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
   }
 
   void SetUpOnMainThread() override {
diff --git a/content/browser/attribution_reporting/attributions_origin_trial_browsertest.cc b/content/browser/attribution_reporting/attributions_origin_trial_browsertest.cc
deleted file mode 100644
index 98decd9..0000000
--- a/content/browser/attribution_reporting/attributions_origin_trial_browsertest.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2019 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 <vector>
-
-#include "base/strings/strcat.h"
-#include "base/test/bind.h"
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "content/browser/attribution_reporting/attribution_manager_impl.h"
-#include "content/browser/attribution_reporting/stored_source.h"
-#include "content/browser/storage_partition_impl.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "content/public/test/test_navigation_observer.h"
-#include "content/public/test/url_loader_interceptor.h"
-#include "content/shell/browser/shell.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-#include "url/gurl.h"
-
-namespace content {
-
-namespace {
-constexpr char kBaseDataDir[] = "content/test/data/attribution_reporting/";
-}
-
-class AttributionsOriginTrialBrowserTest : public ContentBrowserTest {
- public:
-  AttributionsOriginTrialBrowserTest() = default;
-
-  void SetUpOnMainThread() override {
-    ContentBrowserTest::SetUpOnMainThread();
-
-    // We use a URLLoaderInterceptor, rather than the EmbeddedTestServer, since
-    // the origin trial token in the response is associated with a fixed
-    // origin, whereas EmbeddedTestServer serves content on a random port.
-    url_loader_interceptor_ =
-        std::make_unique<URLLoaderInterceptor>(base::BindLambdaForTesting(
-            [&](URLLoaderInterceptor::RequestParams* params) -> bool {
-              URLLoaderInterceptor::WriteResponse(
-                  base::StrCat(
-                      {kBaseDataDir, params->url_request.url.path_piece()}),
-                  params->client.get());
-              return true;
-            }));
-  }
-
-  void TearDownOnMainThread() override { url_loader_interceptor_.reset(); }
-
-  WebContents* web_contents() { return shell()->web_contents(); }
-
- private:
-  std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_;
-};
-
-IN_PROC_BROWSER_TEST_F(AttributionsOriginTrialBrowserTest,
-                       OriginTrialEnabled_FeatureDetected) {
-  EXPECT_TRUE(NavigateToURL(
-      shell(), GURL("https://example.test/impression_with_origin_trial.html")));
-
-  EXPECT_EQ(true, EvalJs(shell(),
-                         "document.featurePolicy.features().includes('"
-                         "attribution-reporting')"));
-  EXPECT_EQ(true, EvalJs(shell(), "window.attributionReporting !== undefined"));
-}
-
-IN_PROC_BROWSER_TEST_F(AttributionsOriginTrialBrowserTest,
-                       OriginTrialDisabled_FeatureNotDetected) {
-  // Navigate to a page without an OT token.
-  EXPECT_TRUE(NavigateToURL(
-      shell(), GURL("https://example.test/page_with_impression_creator.html")));
-
-  EXPECT_EQ(false, EvalJs(shell(),
-                          "document.featurePolicy.features().includes('"
-                          "conversion-measurement')"));
-  EXPECT_EQ(true, EvalJs(shell(), "window.attributionReporting === undefined"));
-}
-
-#if BUILDFLAG(IS_LINUX)
-// TODO(https://crbug.com/1121464): Flaky on linux.
-#define MAYBE_OriginTrialEnabled_ImpressionRegistered \
-  DISABLED_OriginTrialEnabled_ImpressionRegistered
-#else
-#define MAYBE_OriginTrialEnabled_ImpressionRegistered \
-  OriginTrialEnabled_ImpressionRegistered
-#endif
-
-IN_PROC_BROWSER_TEST_F(AttributionsOriginTrialBrowserTest,
-                       MAYBE_OriginTrialEnabled_ImpressionRegistered) {
-  EXPECT_TRUE(NavigateToURL(
-      shell(), GURL("https://example.test/impression_with_origin_trial.html")));
-
-  EXPECT_TRUE(ExecJs(shell(), R"(
-    createImpressionTag({id: 'link',
-                        url: 'https://example.test/page_with_conversion_redirect.html',
-                        data: '1',
-                        destination: 'https://example.test/'});)"));
-
-  TestNavigationObserver observer(web_contents());
-  EXPECT_TRUE(ExecJs(shell(), "simulateClick('link');"));
-  observer.Wait();
-
-  AttributionManagerImpl* attribution_manager =
-      static_cast<StoragePartitionImpl*>(
-          web_contents()->GetBrowserContext()->GetDefaultStoragePartition())
-          ->GetAttributionManager();
-
-  base::RunLoop run_loop;
-
-  // Verify we have received and logged an impression for the origin trial.
-  attribution_manager->GetActiveSourcesForWebUI(base::BindLambdaForTesting(
-      [&](std::vector<StoredSource> impressions) -> void {
-        EXPECT_EQ(1u, impressions.size());
-        run_loop.Quit();
-      }));
-  run_loop.Run();
-}
-
-// TODO(johnidel): Add tests that exercise the conversion side logic as well.
-// This requires also using an embedded test server because the
-// UrlLoadInterceptor cannot properly redirect the conversion pings.
-
-class AttributionsOriginTrialNoBrowserFeatureBrowserTest
-    : public AttributionsOriginTrialBrowserTest {
- public:
-  AttributionsOriginTrialNoBrowserFeatureBrowserTest() {
-    feature_list_.InitAndDisableFeature(
-        blink::features::kConversionMeasurement);
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(AttributionsOriginTrialNoBrowserFeatureBrowserTest,
-                       BrowserSideLogicNotEnabled_FeatureNotDetected) {
-  EXPECT_TRUE(NavigateToURL(
-      shell(), GURL("https://example.test/impression_with_origin_trial.html")));
-
-  EXPECT_EQ(false, EvalJs(shell(),
-                          "document.featurePolicy.features().includes('"
-                          "attribution-reporting')"));
-  EXPECT_EQ(true, EvalJs(shell(), "window.attributionReporting === undefined"));
-}
-
-}  // namespace content
diff --git a/content/browser/attribution_reporting/privacy_sandbox_ads_apis_browsertest.cc b/content/browser/attribution_reporting/privacy_sandbox_ads_apis_browsertest.cc
new file mode 100644
index 0000000..82745dd0
--- /dev/null
+++ b/content/browser/attribution_reporting/privacy_sandbox_ads_apis_browsertest.cc
@@ -0,0 +1,216 @@
+// Copyright 2019 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 <vector>
+
+#include "base/strings/strcat.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "content/browser/storage_partition_impl.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/storage_partition.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/public/test/url_loader_interceptor.h"
+#include "content/shell/browser/shell.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+constexpr char kBaseDataDir[] = "content/test/data/attribution_reporting/";
+}
+
+class PrivacySandboxAdsAPIsBrowserTestBase : public ContentBrowserTest {
+ public:
+  PrivacySandboxAdsAPIsBrowserTestBase() = default;
+
+  void SetUpOnMainThread() override {
+    ContentBrowserTest::SetUpOnMainThread();
+
+    // We use a URLLoaderInterceptor, rather than the EmbeddedTestServer, since
+    // the origin trial token in the response is associated with a fixed
+    // origin, whereas EmbeddedTestServer serves content on a random port.
+    url_loader_interceptor_ =
+        std::make_unique<URLLoaderInterceptor>(base::BindLambdaForTesting(
+            [&](URLLoaderInterceptor::RequestParams* params) -> bool {
+              URLLoaderInterceptor::WriteResponse(
+                  base::StrCat(
+                      {kBaseDataDir, params->url_request.url.path_piece()}),
+                  params->client.get());
+              return true;
+            }));
+  }
+
+  void TearDownOnMainThread() override { url_loader_interceptor_.reset(); }
+
+  WebContents* web_contents() { return shell()->web_contents(); }
+
+ private:
+  std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_;
+};
+
+class PrivacySandboxAdsAPIsAllEnabledBrowserTest
+    : public PrivacySandboxAdsAPIsBrowserTestBase {
+ public:
+  PrivacySandboxAdsAPIsAllEnabledBrowserTest() {
+    feature_list_.InitWithFeatures(
+        {blink::features::kPrivacySandboxAdsAPIs,
+         blink::features::kBrowsingTopics, blink::features::kFledge},
+        /*disabled_features=*/{});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PrivacySandboxAdsAPIsAllEnabledBrowserTest,
+                       OriginTrialEnabled_FeatureDetected) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), GURL("https://example.test/page_with_ads_apis_ot.html")));
+
+  EXPECT_EQ(true, EvalJs(shell(),
+                         "document.featurePolicy.features().includes('"
+                         "attribution-reporting')"));
+  EXPECT_EQ(true, EvalJs(shell(),
+                         "document.featurePolicy.features().includes('"
+                         "browsing-topics')"));
+  EXPECT_EQ(true, EvalJs(shell(),
+                         "document.featurePolicy.features().includes('"
+                         "join-ad-interest-group')"));
+
+  EXPECT_EQ(true, EvalJs(shell(), "window.attributionReporting !== undefined"));
+  EXPECT_EQ(true, EvalJs(shell(), "document.browsingTopics !== undefined"));
+  EXPECT_EQ(true, EvalJs(shell(), "navigator.runAdAuction !== undefined"));
+  EXPECT_EQ(true,
+            EvalJs(shell(), "navigator.joinAdInterestGroup !== undefined"));
+}
+
+IN_PROC_BROWSER_TEST_F(PrivacySandboxAdsAPIsAllEnabledBrowserTest,
+                       OriginTrialDisabled_FeatureNotDetected) {
+  // Navigate to a page without an OT token.
+  EXPECT_TRUE(NavigateToURL(
+      shell(), GURL("https://example.test/page_without_ads_apis_ot.html")));
+
+  EXPECT_EQ(false, EvalJs(shell(),
+                          "document.featurePolicy.features().includes('"
+                          "attribution-reporting')"));
+  EXPECT_EQ(false, EvalJs(shell(),
+                          "document.featurePolicy.features().includes('"
+                          "browsing-topics')"));
+  EXPECT_EQ(false, EvalJs(shell(),
+                          "document.featurePolicy.features().includes('"
+                          "join-ad-interest-group')"));
+
+  EXPECT_EQ(true, EvalJs(shell(), "window.attributionReporting === undefined"));
+  EXPECT_EQ(true, EvalJs(shell(), "document.browsingTopics === undefined"));
+  EXPECT_EQ(true, EvalJs(shell(), "navigator.runAdAuction === undefined"));
+  EXPECT_EQ(true,
+            EvalJs(shell(), "navigator.joinAdInterestGroup === undefined"));
+}
+
+class PrivacySandboxAdsAPIsTopicsDisabledBrowserTest
+    : public PrivacySandboxAdsAPIsBrowserTestBase {
+ public:
+  PrivacySandboxAdsAPIsTopicsDisabledBrowserTest() {
+    feature_list_.InitWithFeatures({blink::features::kPrivacySandboxAdsAPIs},
+                                   {blink::features::kBrowsingTopics});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PrivacySandboxAdsAPIsTopicsDisabledBrowserTest,
+                       OriginTrialEnabled_CorrectFeaturesDetected) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), GURL("https://example.test/page_with_ads_apis_ot.html")));
+
+  EXPECT_EQ(true, EvalJs(shell(),
+                         "document.featurePolicy.features().includes('"
+                         "attribution-reporting')"));
+  EXPECT_EQ(false, EvalJs(shell(),
+                          "document.featurePolicy.features().includes('"
+                          "browsing-topics')"));
+
+  EXPECT_EQ(true, EvalJs(shell(), "window.attributionReporting !== undefined"));
+  EXPECT_EQ(false, EvalJs(shell(), "document.browsingTopics !== undefined"));
+}
+
+class PrivacySandboxAdsAPIsFledgeDisabledBrowserTest
+    : public PrivacySandboxAdsAPIsBrowserTestBase {
+ public:
+  PrivacySandboxAdsAPIsFledgeDisabledBrowserTest() {
+    feature_list_.InitWithFeatures({blink::features::kPrivacySandboxAdsAPIs},
+                                   {blink::features::kFledge});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PrivacySandboxAdsAPIsFledgeDisabledBrowserTest,
+                       OriginTrialEnabled_CorrectFeaturesDetected) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), GURL("https://example.test/page_with_ads_apis_ot.html")));
+
+  EXPECT_EQ(true, EvalJs(shell(),
+                         "document.featurePolicy.features().includes('"
+                         "attribution-reporting')"));
+  EXPECT_EQ(false, EvalJs(shell(),
+                          "document.featurePolicy.features().includes('"
+                          "join-ad-interest-group')"));
+
+  EXPECT_EQ(true, EvalJs(shell(), "window.attributionReporting !== undefined"));
+  EXPECT_EQ(false, EvalJs(shell(), "navigator.runAdAuction !== undefined"));
+  EXPECT_EQ(false,
+            EvalJs(shell(), "navigator.joinAdInterestGroup !== undefined"));
+}
+
+class PrivacySandboxAdsAPIsDisabledBrowserTest
+    : public PrivacySandboxAdsAPIsBrowserTestBase {
+ public:
+  PrivacySandboxAdsAPIsDisabledBrowserTest() {
+    feature_list_.InitAndDisableFeature(
+        blink::features::kPrivacySandboxAdsAPIs);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PrivacySandboxAdsAPIsDisabledBrowserTest,
+                       BaseFeatureDisabled_FeatureNotDetected) {
+  EXPECT_TRUE(NavigateToURL(
+      shell(), GURL("https://example.test/page_with_ads_apis_ot.html")));
+
+  EXPECT_EQ(false, EvalJs(shell(),
+                          "document.featurePolicy.features().includes('"
+                          "attribution-reporting')"));
+  EXPECT_EQ(false, EvalJs(shell(),
+                          "document.featurePolicy.features().includes('"
+                          "browsing-topics')"));
+  EXPECT_EQ(false, EvalJs(shell(),
+                          "document.featurePolicy.features().includes('"
+                          "join-ad-interest-group')"));
+
+  EXPECT_EQ(true, EvalJs(shell(), "window.attributionReporting === undefined"));
+  EXPECT_EQ(true, EvalJs(shell(), "document.browsingTopics === undefined"));
+  EXPECT_EQ(true, EvalJs(shell(), "navigator.runAdAuction === undefined"));
+  EXPECT_EQ(true,
+            EvalJs(shell(), "navigator.joinAdInterestGroup === undefined"));
+}
+
+}  // namespace content
diff --git a/content/browser/attribution_reporting/source_declaration_browsertest.cc b/content/browser/attribution_reporting/source_declaration_browsertest.cc
index 4e9ac3d..330b83f 100644
--- a/content/browser/attribution_reporting/source_declaration_browsertest.cc
+++ b/content/browser/attribution_reporting/source_declaration_browsertest.cc
@@ -98,6 +98,7 @@
     // Sets up the blink runtime feature for ConversionMeasurement.
     command_line->AppendSwitch(
         switches::kEnableExperimentalWebPlatformFeatures);
+    command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
   }
 };
 
diff --git a/content/browser/attribution_reporting/trigger_registration_browsertest.cc b/content/browser/attribution_reporting/trigger_registration_browsertest.cc
index 49a6ca47..90b2b4c 100644
--- a/content/browser/attribution_reporting/trigger_registration_browsertest.cc
+++ b/content/browser/attribution_reporting/trigger_registration_browsertest.cc
@@ -90,6 +90,7 @@
     // Sets up the blink runtime feature for ConversionMeasurement.
     command_line->AppendSwitch(
         switches::kEnableExperimentalWebPlatformFeatures);
+    command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
   }
 };
 
diff --git a/content/browser/fenced_frame/fenced_frame_browsertest.cc b/content/browser/fenced_frame/fenced_frame_browsertest.cc
index 0423f83..244c05b 100644
--- a/content/browser/fenced_frame/fenced_frame_browsertest.cc
+++ b/content/browser/fenced_frame/fenced_frame_browsertest.cc
@@ -880,6 +880,35 @@
   }
 }
 
+// Tests that an input event targeted to a fenced frame correctly
+// triggers a user interaction notification for WebContentsObservers.
+IN_PROC_BROWSER_TEST_F(FencedFrameBrowserTest, UserInteractionForFencedFrame) {
+  ASSERT_TRUE(https_server()->Start());
+  const GURL main_url = https_server()->GetURL("c.test", "/title1.html");
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  RenderFrameHostImplWrapper primary_rfh(primary_main_frame_host());
+
+  const GURL fenced_frame_url =
+      https_server()->GetURL("c.test", "/fenced_frames/title1.html");
+  RenderFrameHostImplWrapper fenced_frame_rfh(
+      fenced_frame_test_helper().CreateFencedFrame(primary_rfh.get(),
+                                                   fenced_frame_url));
+
+  ::testing::NiceMock<MockWebContentsObserver> web_contents_observer(
+      web_contents());
+  EXPECT_CALL(web_contents_observer, DidGetUserInteraction(testing::_))
+      .Times(1);
+
+  // Target an event to the fenced frame's RenderWidgetHostView.
+  blink::WebMouseEvent mouse_event(
+      blink::WebInputEvent::Type::kMouseDown,
+      blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::GetStaticTimeStampForTests());
+  mouse_event.button = blink::WebPointerProperties::Button::kLeft;
+  mouse_event.SetPositionInWidget(5, 5);
+  fenced_frame_rfh->GetRenderWidgetHost()->ForwardMouseEvent(mouse_event);
+}
+
 namespace {
 
 enum class FrameTypeWithOrigin {
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 4a1c5261..b8613fb 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -245,7 +245,8 @@
         {blink::features::kInterestGroupStorage,
          blink::features::kAdInterestGroupAPI, blink::features::kParakeet,
          blink::features::kFledge, blink::features::kAllowURNsInIframes,
-         blink::features::kBiddingAndScoringDebugReportingAPI},
+         blink::features::kBiddingAndScoringDebugReportingAPI,
+         features::kPrivacySandboxAdsAPIsOverride},
         /*disabled_features=*/
         {blink::features::kFencedFrames});
   }
diff --git a/content/browser/loader/navigation_early_hints_browsertest.cc b/content/browser/loader/navigation_early_hints_browsertest.cc
index 4213c80..8f8d730d 100644
--- a/content/browser/loader/navigation_early_hints_browsertest.cc
+++ b/content/browser/loader/navigation_early_hints_browsertest.cc
@@ -501,7 +501,14 @@
   EXPECT_TRUE(NavigateToURL(shell(), net::QuicSimpleTestServer::GetFileURL(
                                          kPageWithHintedScriptPath)));
   PreloadedResources preloads = WaitForPreloadedResources();
-  EXPECT_TRUE(preloads.empty());
+  EXPECT_EQ(preloads.size(), 1UL);
+
+  GURL preloaded_url = net::QuicSimpleTestServer::GetFileURL(kHintedScriptPath);
+  auto it = preloads.find(preloaded_url);
+  ASSERT_NE(it, preloads.end());
+  ASSERT_FALSE(it->second.was_canceled);
+  ASSERT_TRUE(it->second.error_code.has_value());
+  EXPECT_EQ(it->second.error_code.value(), net::OK);
 }
 
 IN_PROC_BROWSER_TEST_F(NavigationEarlyHintsTest, RedirectSameOrigin) {
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 3f06e6b8..ce15866 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -81,7 +81,6 @@
 #include "ppapi/buildflags/buildflags.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/cpp/constants.h"
-#include "services/network/public/cpp/cors/cors.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/request_destination.h"
 #include "services/network/public/cpp/url_util.h"
@@ -1477,10 +1476,9 @@
     early_hints.was_resource_hints_received =
         early_hints_manager_->WasResourceHintsReceived();
 
-    // Make Early Hints manager outlive this loader only when the final response
-    // succeeds. Dropping the manager cancels inflight preloads.
-    if (response_head && response_head->headers &&
-        network::cors::IsOkStatus(response_head->headers->response_code())) {
+    // Make Early Hints manager outlive this loader only when the response
+    // headers are available. Dropping the manager cancels inflight preloads.
+    if (response_head && response_head->headers) {
       early_hints.manager = std::move(early_hints_manager_);
     }
   }
diff --git a/content/browser/media/media_license_manager.cc b/content/browser/media/media_license_manager.cc
index 50e9039..31b42b6 100644
--- a/content/browser/media/media_license_manager.cc
+++ b/content/browser/media/media_license_manager.cc
@@ -25,6 +25,7 @@
 #include "base/task/thread_pool.h"
 #include "base/threading/sequence_bound.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "components/services/storage/public/cpp/buckets/constants.h"
 #include "components/services/storage/public/cpp/constants.h"
 #include "content/browser/media/media_license_database.h"
@@ -525,13 +526,6 @@
     storage::QuotaErrorOr<storage::BucketInfo> result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // TODO(crbug.com/1231162): Handle failure case.
-  DCHECK(result.ok());
-
-  // All receivers associated with `storage_key` will be bound to the same host.
-  auto storage_host = std::make_unique<MediaLicenseStorageHost>(
-      this, result->ToBucketLocator());
-
   auto it = pending_receivers_.find(storage_key);
   if (it == pending_receivers_.end()) {
     // No receivers to bind.
@@ -547,6 +541,23 @@
   pending_receivers_.erase(it);
   DCHECK_GT(receivers_list.size(), 0u);
 
+  storage::BucketLocator bucket_locator;
+  if (result.ok()) {
+    bucket_locator = result->ToBucketLocator();
+  } else {
+    // Use the null locator, but update the `storage_key` field so
+    // `storage_host` can be identified when it is to be removed from `hosts_`.
+    // We could consider falling back to using an in-memory database in this
+    // case, but failing here seems easier to reason about from a website
+    // author's point of view.
+    DCHECK(bucket_locator.id.is_null());
+    bucket_locator.storage_key = storage_key;
+  }
+
+  // All receivers associated with `storage_key` will be bound to the same host.
+  auto storage_host =
+      std::make_unique<MediaLicenseStorageHost>(this, bucket_locator);
+
   for (auto& context_and_receiver : receivers_list) {
     storage_host->BindReceiver(context_and_receiver.first,
                                std::move(context_and_receiver.second));
diff --git a/content/browser/media/media_license_manager_unittest.cc b/content/browser/media/media_license_manager_unittest.cc
index a4593219..7e210a77 100644
--- a/content/browser/media/media_license_manager_unittest.cc
+++ b/content/browser/media/media_license_manager_unittest.cc
@@ -128,7 +128,7 @@
   }
 
  protected:
-  scoped_refptr<storage::QuotaManager> quota_manager_;
+  scoped_refptr<storage::MockQuotaManager> quota_manager_;
   scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
   scoped_refptr<storage::FileSystemContext> file_system_context_;
 
@@ -276,6 +276,31 @@
   EXPECT_TRUE(base::PathExists(database_file.DirName()));
 }
 
+TEST_F(MediaLicenseManagerTest, BucketCreationFailed) {
+  const std::string kTestData("Test Data");
+  mojo::Remote<media::mojom::CdmStorage> remote;
+  blink::StorageKey storage_key =
+      blink::StorageKey::CreateFromStringForTesting(kExampleOrigin);
+  storage::BucketLocator bucket = GetOrCreateBucket(storage_key);
+  MediaLicenseManager::BindingContext binding_context(storage_key, kCdmType);
+
+  // Disable the quota database, causing GetOrCreateBucket() to fail.
+  quota_manager_->SetDisableDatabase(/*disable=*/true);
+
+  // Open CDM storage for a storage key.
+  manager_->OpenCdmStorage(binding_context,
+                           remote.BindNewPipeAndPassReceiver());
+  // Opening a CDM file should fail.
+  base::test::TestFuture<media::mojom::CdmStorage::Status,
+                         mojo::PendingAssociatedRemote<media::mojom::CdmFile>>
+      open_future;
+  remote->Open("test_file", open_future.GetCallback());
+
+  auto result = open_future.Take();
+  EXPECT_EQ(std::get<0>(result), media::mojom::CdmStorage::Status::kFailure);
+  EXPECT_FALSE(std::get<1>(result).is_valid());
+}
+
 class MediaLicenseManagerIncognitoTest : public MediaLicenseManagerTest {
   void SetUp() override {
     // Still create this dir so the teardown will confirm it remains empty (on
diff --git a/content/browser/media/media_license_storage_host.cc b/content/browser/media/media_license_storage_host.cc
index 0c634f0..0c028ec 100644
--- a/content/browser/media/media_license_storage_host.cc
+++ b/content/browser/media/media_license_storage_host.cc
@@ -52,6 +52,12 @@
                                    OpenCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (bucket_locator_.id.is_null()) {
+    DVLOG(1) << "Could not retrieve valid bucket.";
+    std::move(callback).Run(Status::kFailure, mojo::NullAssociatedRemote());
+    return;
+  }
+
   if (file_name.empty()) {
     DVLOG(1) << "No file specified.";
     std::move(callback).Run(Status::kFailure, mojo::NullAssociatedRemote());
@@ -63,8 +69,6 @@
     return;
   }
 
-  // TODO(crbug.com/1231162): Notify the quota system of a write.
-
   const BindingContext& binding_context = receivers_.current_context();
   db_.AsyncCall(&MediaLicenseDatabase::OpenFile)
       .WithArgs(binding_context.cdm_type, file_name)
@@ -138,8 +142,6 @@
                                         WriteFileCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // TODO(crbug.com/1231162): Notify the quota system of a write.
-
   db_.AsyncCall(&MediaLicenseDatabase::WriteFile)
       .WithArgs(cdm_type, file_name, data)
       .Then(base::BindOnce(&MediaLicenseStorageHost::DidWriteFile,
@@ -169,11 +171,10 @@
                                          DeleteFileCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // TODO(crbug.com/1231162): Notify the quota system of a write.
-
   db_.AsyncCall(&MediaLicenseDatabase::DeleteFile)
       .WithArgs(cdm_type, file_name)
-      .Then(std::move(callback));
+      .Then(base::BindOnce(&MediaLicenseStorageHost::DidWriteFile,
+                           weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void MediaLicenseStorageHost::DeleteBucketData(
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 2afbf96..c21b0c44 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -212,16 +212,22 @@
     WebContentsImpl::FriendWrapper::CreatedCallback>>::DestructorAtExit
     g_created_callbacks = LAZY_INSTANCE_INITIALIZER;
 
-bool HasMatchingWidgetHost(FrameTree* tree, RenderWidgetHost* host) {
+bool HasMatchingWidgetHost(FrameTree* tree, RenderWidgetHostImpl* host) {
   // This method scans the frame tree rather than checking whether
   // host->delegate() == this, which allows it to return false when the host
   // for a frame that is pending or pending deletion.
   if (!host)
     return false;
 
-  for (FrameTreeNode* node : tree->Nodes()) {
-    if (node->current_frame_host()->GetRenderWidgetHost() == host)
+  for (FrameTreeNode* node : tree->NodesIncludingInnerTreeNodes()) {
+    // We might cross a WebContents boundary here, but it's fine as we are only
+    // comparing the RWHI with the given `host`, which is always guaranteed to
+    // belong to the same WebContents as `tree`.
+    if (node->current_frame_host()->GetRenderWidgetHost() == host) {
+      DCHECK_EQ(WebContentsImpl::FromFrameTreeNode(node),
+                WebContentsImpl::FromRenderWidgetHostImpl(host));
       return true;
+    }
   }
   return false;
 }
diff --git a/content/browser/web_contents/web_drag_dest_mac.mm b/content/browser/web_contents/web_drag_dest_mac.mm
index 6455404..dab3703 100644
--- a/content/browser/web_contents/web_drag_dest_mac.mm
+++ b/content/browser/web_contents/web_drag_dest_mac.mm
@@ -204,8 +204,11 @@
   NSDragOperation mask = info->operation_mask;
 
   // Give the delegate an opportunity to cancel the drag.
-  _canceled = !_webContents->GetDelegate()->CanDragEnter(
-      _webContents, *dropData, static_cast<DragOperationsMask>(mask));
+  if (auto* delegate = _webContents->GetDelegate()) {
+    _canceled = !delegate->CanDragEnter(_webContents, *dropData,
+                                        static_cast<DragOperationsMask>(mask));
+  }
+
   if (_canceled)
     return NSDragOperationNone;
 
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index e9db09d..9ee1655 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -326,6 +326,8 @@
            blink::features::kAdInterestGroupAPIRestrictedPolicyByDefault},
           {"AllowContentInitiatedDataUrlNavigations",
            features::kAllowContentInitiatedDataUrlNavigations},
+          {"AttributionReporting", features::kPrivacySandboxAdsAPIsOverride,
+           kSetOnlyIfOverridden},
           {"AutofillShadowDOM", blink::features::kAutofillShadowDOM},
           {"AndroidDownloadableFontsMatching",
            features::kAndroidDownloadableFontsMatching},
@@ -346,7 +348,8 @@
           {"EditingNG", blink::features::kEditingNG},
           {"ElementSuperRareData", blink::features::kElementSuperRareData},
           {"FileHandling", blink::features::kFileHandlingAPI},
-          {"Fledge", blink::features::kFledge},
+          {"Fledge", features::kPrivacySandboxAdsAPIsOverride,
+           kSetOnlyIfOverridden},
           {"FontAccess", blink::features::kFontAccess},
           {"FontSrcLocalMatching", features::kFontSrcLocalMatching},
           {"ForceSynchronousHTMLParsing",
@@ -376,7 +379,8 @@
           {"ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes",
            blink::features::
                kThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes},
-          {"TopicsAPI", blink::features::kBrowsingTopics},
+          {"TopicsAPI", features::kPrivacySandboxAdsAPIsOverride,
+           kSetOnlyIfOverridden},
           {"TrustedDOMTypes", features::kTrustedDOMTypes},
           {"UserAgentClientHint", blink::features::kUserAgentClientHint},
           {"ViewportHeightClientHintHeader",
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 8ce64f9..fdcce6e 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -666,6 +666,11 @@
 const base::Feature kHighPriorityBeforeUnload{
     "HighPriorityBeforeUnload", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables exposure of ads APIs in the renderer: Attribution Reporting,
+// FLEDGE, Topics.
+const base::Feature kPrivacySandboxAdsAPIsOverride{
+    "PrivacySandboxAdsAPIsOverride", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables Private Network Access checks for all types of web workers.
 //
 // This affects initial worker script fetches, fetches initiated by workers
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index d8a30f7..4114347 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -165,6 +165,7 @@
 CONTENT_EXPORT extern const base::Feature kPepperCrossOriginRedirectRestriction;
 CONTENT_EXPORT extern const base::Feature kPictureInPictureV2;
 CONTENT_EXPORT extern const base::Feature kHighPriorityBeforeUnload;
+CONTENT_EXPORT extern const base::Feature kPrivacySandboxAdsAPIsOverride;
 CONTENT_EXPORT extern const base::Feature kPrivateNetworkAccessForWorkers;
 CONTENT_EXPORT extern const base::Feature
     kPrivateNetworkAccessRespectPreflightResults;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1a4e2941..6f1b651 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1107,7 +1107,7 @@
     "../browser/attribution_reporting/attribution_internals_browsertest.cc",
     "../browser/attribution_reporting/attribution_src_browsertest.cc",
     "../browser/attribution_reporting/attributions_browsertest.cc",
-    "../browser/attribution_reporting/attributions_origin_trial_browsertest.cc",
+    "../browser/attribution_reporting/privacy_sandbox_ads_apis_browsertest.cc",
     "../browser/attribution_reporting/source_declaration_browsertest.cc",
     "../browser/attribution_reporting/trigger_registration_browsertest.cc",
     "../browser/back_forward_cache_basics_browsertest.cc",
diff --git a/content/test/data/attribution_reporting/impression_with_origin_trial.html b/content/test/data/attribution_reporting/impression_with_origin_trial.html
index d2b5b326..07d4852 100644
--- a/content/test/data/attribution_reporting/impression_with_origin_trial.html
+++ b/content/test/data/attribution_reporting/impression_with_origin_trial.html
@@ -2,8 +2,8 @@
   <head>
     <!-- TODO(johnidel): Find a better way to provide this toke, as this has an expiration in 2033.
    Generate this token with the command:
-   generate_token.py https://example.test ConversionMeasurement --expire-timestamp=2000000000 -->
-    <meta http-equiv="origin-trial" content="AyiEpqRSCsaH3iE8GrZXh/DPVQPQr21JkMfKwjBFQKXHORXnLsT2AVEbhAECynhD0Um/tHnHHgN/mKJ8JWd1lAUAAABgeyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzIiwgImZlYXR1cmUiOiAiQ29udmVyc2lvbk1lYXN1cmVtZW50IiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9">
+   generate_token.py https://example.test AttributionReporting --expire-timestamp=2000000000 -->
+    <meta http-equiv="origin-trial" content="A5RM267BZhqCOmeoEBNC6xxxFokBf5MLgeHTUD8VhToVaNidt/g6UEjsplcBlcI6jyzWdP0MGMzj4ab3S7CVkgUAAABfeyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzIiwgImZlYXR1cmUiOiAiQXR0cmlidXRpb25SZXBvcnRpbmciLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=">
     <script src="register_impression.js"></script>
   </head>
 </html>
diff --git a/content/test/data/attribution_reporting/page_with_ads_apis_ot.html b/content/test/data/attribution_reporting/page_with_ads_apis_ot.html
new file mode 100644
index 0000000..5a253b2e
--- /dev/null
+++ b/content/test/data/attribution_reporting/page_with_ads_apis_ot.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <!-- TODO(johnidel): Find a better way to provide this token, as this has an expiration in 2033.
+   Generate this token with the command:
+   generate_token.py https://example.test PrivacySandboxAdsAPIs --expire-timestamp=2000000000 -->
+    <meta http-equiv="origin-trial" content="A5tnx3M8YgPvr7n0sKItbVfImY3JFbP+KhW5eL3wxOHDTKpJ/AcBbmFpEx8Feve0uzgxVIfCPSjw++Gv1GCPkQUAAABgeyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzIiwgImZlYXR1cmUiOiAiUHJpdmFjeVNhbmRib3hBZHNBUElzIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9">
+  </head>
+</html>
diff --git a/content/test/data/attribution_reporting/page_without_ads_apis_ot.html b/content/test/data/attribution_reporting/page_without_ads_apis_ot.html
new file mode 100644
index 0000000..d0903c6e
--- /dev/null
+++ b/content/test/data/attribution_reporting/page_without_ads_apis_ot.html
@@ -0,0 +1,3 @@
+<html>
+  This page does not enable the Ads APIs OT.
+</html>
diff --git a/content/test/data/attribution_reporting/third_party_token_injector.js b/content/test/data/attribution_reporting/third_party_token_injector.js
index 22c6c7f..33a52febd 100644
--- a/content/test/data/attribution_reporting/third_party_token_injector.js
+++ b/content/test/data/attribution_reporting/third_party_token_injector.js
@@ -7,7 +7,7 @@
 
 // Third party OT token with subset usage restriction. Expires in 2033.
 // Generated using:
-// python generate_token.py https://example.test ConversionMeasurement \
+// python generate_token.py https://example.test AttributionReporting \
 // --usage-restriction=subset --is-third-party --expire-timestamp=2000000000
-meta.content = 'A8xSVOSM2Fo25Ot5f+WXIRxAVTNK+R4JLQZvX0gbwUWq6PWGggKsIi/6HvkDNmDK64/dGOo2fJwUW4Fi7NRRhQ8AAACJeyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzIiwgImlzVGhpcmRQYXJ0eSI6IHRydWUsICJ1c2FnZSI6ICJzdWJzZXQiLCAiZmVhdHVyZSI6ICJDb252ZXJzaW9uTWVhc3VyZW1lbnQiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=';
+meta.content = 'AwcYHeC1CwkNTksEvdIHHIDSuz+xzNsrkeDgg+6zlRWO8sljNr19o/rzFp2cLekz2arxAdag8AYK7e8yMynF4woAAACIeyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzIiwgImZlYXR1cmUiOiAiQXR0cmlidXRpb25SZXBvcnRpbmciLCAiZXhwaXJ5IjogMjAwMDAwMDAwMCwgImlzVGhpcmRQYXJ0eSI6IHRydWUsICJ1c2FnZSI6ICJzdWJzZXQifQ==';
 document.head.appendChild(meta);
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index f498732..3977126 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -458,7 +458,7 @@
 as required builders.
 
 * [android-12-x64-rel](https://ci.chromium.org/p/chromium/builders/try/android-12-x64-rel) ([definition](https://cs.chromium.org/search?q=+file:/try.star$+""android-12-x64-rel"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""android-12-x64-rel""))
-  * Experiment percentage: 20.0
+  * Experiment percentage: 50.0
 
 * [android-pie-arm64-coverage-experimental-rel](https://ci.chromium.org/p/chromium/builders/try/android-pie-arm64-coverage-experimental-rel) ([definition](https://cs.chromium.org/search?q=+file:/try.star$+""android-pie-arm64-coverage-experimental-rel"")) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+""android-pie-arm64-coverage-experimental-rel""))
   * Experiment percentage: 3.0
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index efda8bf..602480e 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -279,7 +279,7 @@
       }
       builders {
         name: "chromium/try/android-12-x64-rel"
-        experiment_percentage: 20
+        experiment_percentage: 50
         location_regexp: ".*"
         location_regexp_exclude: ".+/[+]/docs/.+"
         location_regexp_exclude: ".+/[+]/infra/config/.+"
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 4bc6501..dcf9fd0 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -47,7 +47,7 @@
     # branch_selector = branches.STANDARD_MILESTONE,
     main_list_view = "try",
     tryjob = try_.job(
-        experiment_percentage = 20,
+        experiment_percentage = 50,
     ),
 )
 
diff --git a/ios/web/js_messaging/web_frame_impl_inttest.mm b/ios/web/js_messaging/web_frame_impl_inttest.mm
index a7d16a01..8171b36 100644
--- a/ios/web/js_messaging/web_frame_impl_inttest.mm
+++ b/ios/web/js_messaging/web_frame_impl_inttest.mm
@@ -7,7 +7,6 @@
 #import <WebKit/WebKit.h>
 
 #include "base/bind.h"
-#include "base/ios/ios_util.h"
 #import "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #import "ios/web/js_messaging/java_script_content_world.h"
@@ -266,10 +265,6 @@
 // function via |CallJavaScriptFunction| on the main frame in the page content
 // world.
 TEST_F(WebFrameImplIntTest, CallJavaScriptFunctionMainFramePageContentWorld) {
-  if (!base::ios::IsRunningOnIOS14OrLater()) {
-    return;
-  }
-
   ASSERT_TRUE(LoadHtml("<p>"));
   ExecuteJavaScript(@"__gCrWeb = {};"
                     @"__gCrWeb['fakeFunction'] = function() {"
@@ -283,20 +278,18 @@
   NSTimeInterval js_timeout = kWaitForJSCompletionTimeout;
   __block bool called = false;
 
-  if (@available(ios 14, *)) {
-    JavaScriptContentWorld world(GetBrowserState(), WKContentWorld.pageWorld);
+  JavaScriptContentWorld world(GetBrowserState(), WKContentWorld.pageWorld);
 
-    std::vector<base::Value> function_params;
-    EXPECT_TRUE(main_frame_impl->CallJavaScriptFunctionInContentWorld(
-        "fakeFunction", function_params, &world,
-        base::BindOnce(^(const base::Value* value) {
-          ASSERT_TRUE(value->is_string());
-          EXPECT_EQ(value->GetString(), "10");
-          called = true;
-        }),
-        // Increase feature timeout in order to fail on test specific timeout.
-        base::Seconds(2 * js_timeout)));
-  }
+  std::vector<base::Value> function_params;
+  EXPECT_TRUE(main_frame_impl->CallJavaScriptFunctionInContentWorld(
+      "fakeFunction", function_params, &world,
+      base::BindOnce(^(const base::Value* value) {
+        ASSERT_TRUE(value->is_string());
+        EXPECT_EQ(value->GetString(), "10");
+        called = true;
+      }),
+      // Increase feature timeout in order to fail on test specific timeout.
+      base::Seconds(2 * js_timeout)));
 
   EXPECT_TRUE(WaitUntilConditionOrTimeout(js_timeout, ^bool {
     return called;
@@ -307,21 +300,15 @@
 // function via |CallJavaScriptFunction| on the main frame in an isolated
 // world.
 TEST_F(WebFrameImplIntTest, CallJavaScriptFunctionMainFrameIsolatedWorld) {
-  if (!base::ios::IsRunningOnIOS14OrLater()) {
-    return;
-  }
-
   ASSERT_TRUE(LoadHtml("<p>"));
 
-  if (@available(ios 14, *)) {
-    WKWebView* web_view =
-        [web::test::GetWebController(web_state()) ensureWebViewCreated];
-    test::ExecuteJavaScript(web_view, WKContentWorld.defaultClientWorld,
-                            @"__gCrWeb = {};"
-                            @"__gCrWeb['fakeFunction'] = function() {"
-                            @"  return '10';"
-                            @"}");
-  }
+  WKWebView* web_view =
+      [web::test::GetWebController(web_state()) ensureWebViewCreated];
+  test::ExecuteJavaScript(web_view, WKContentWorld.defaultClientWorld,
+                          @"__gCrWeb = {};"
+                          @"__gCrWeb['fakeFunction'] = function() {"
+                          @"  return '10';"
+                          @"}");
 
   web::WebFrameImpl* main_frame_impl = static_cast<web::WebFrameImpl*>(
       web_state()->GetWebFramesManager()->GetMainWebFrame());
@@ -330,21 +317,19 @@
   NSTimeInterval js_timeout = kWaitForJSCompletionTimeout;
   __block bool called = false;
 
-  if (@available(ios 14, *)) {
-    JavaScriptContentWorld world(GetBrowserState(),
-                                 WKContentWorld.defaultClientWorld);
+  JavaScriptContentWorld world(GetBrowserState(),
+                               WKContentWorld.defaultClientWorld);
 
-    std::vector<base::Value> function_params;
-    EXPECT_TRUE(main_frame_impl->CallJavaScriptFunctionInContentWorld(
-        "fakeFunction", function_params, &world,
-        base::BindOnce(^(const base::Value* value) {
-          ASSERT_TRUE(value->is_string());
-          EXPECT_EQ(value->GetString(), "10");
-          called = true;
-        }),
-        // Increase feature timeout in order to fail on test specific timeout.
-        base::Seconds(2 * js_timeout)));
-  }
+  std::vector<base::Value> function_params;
+  EXPECT_TRUE(main_frame_impl->CallJavaScriptFunctionInContentWorld(
+      "fakeFunction", function_params, &world,
+      base::BindOnce(^(const base::Value* value) {
+        ASSERT_TRUE(value->is_string());
+        EXPECT_EQ(value->GetString(), "10");
+        called = true;
+      }),
+      // Increase feature timeout in order to fail on test specific timeout.
+      base::Seconds(2 * js_timeout)));
 
   EXPECT_TRUE(WaitUntilConditionOrTimeout(js_timeout, ^bool {
     return called;
diff --git a/media/base/video_util.cc b/media/base/video_util.cc
index 8d7037e..e5affe4 100644
--- a/media/base/video_util.cc
+++ b/media/base/video_util.cc
@@ -879,7 +879,7 @@
 
   if (dst_frame.format() == PIXEL_FORMAT_I420 &&
       src_frame.format() == PIXEL_FORMAT_NV12) {
-    if (src_frame.visible_rect() == dst_frame.visible_rect()) {
+    if (src_frame.visible_rect().size() == dst_frame.visible_rect().size()) {
       // Both frames have the same size, only NV12-to-I420 conversion is
       // required.
       int error = libyuv::NV12ToI420(
@@ -940,7 +940,7 @@
 
   if (dst_frame.format() == PIXEL_FORMAT_NV12 &&
       src_frame.format() == PIXEL_FORMAT_I420) {
-    if (src_frame.visible_rect() == dst_frame.visible_rect()) {
+    if (src_frame.visible_rect().size() == dst_frame.visible_rect().size()) {
       // Both frames have the same size, only I420-to-NV12 conversion is
       // required.
       int error = libyuv::I420ToNV12(
diff --git a/media/video/software_video_encoder_test.cc b/media/video/software_video_encoder_test.cc
index 21d7ccc..4c8e816 100644
--- a/media/video/software_video_encoder_test.cc
+++ b/media/video/software_video_encoder_test.cc
@@ -79,10 +79,9 @@
       gfx::Size size,
       VideoDecoder::OutputCB output_cb,
       std::vector<uint8_t> extra_data = std::vector<uint8_t>()) {
-    gfx::Rect visible_rect(size.width(), size.height());
     VideoDecoderConfig config(
         codec_, profile_, VideoDecoderConfig::AlphaMode::kIsOpaque,
-        VideoColorSpace::JPEG(), VideoTransformation(), size, visible_rect,
+        VideoColorSpace::JPEG(), VideoTransformation(), size, gfx::Rect(size),
         size, extra_data, EncryptionScheme::kUnencrypted);
 
     if (codec_ == VideoCodec::kH264 || codec_ == VideoCodec::kVP8) {
@@ -652,7 +651,8 @@
     auto original_frame = frames_to_encode[i];
     auto decoded_frame = decoded_frames[i];
     EXPECT_EQ(decoded_frame->timestamp(), original_frame->timestamp());
-    EXPECT_EQ(decoded_frame->visible_rect(), original_frame->visible_rect());
+    EXPECT_EQ(decoded_frame->visible_rect().size(),
+              original_frame->visible_rect().size());
     if (decoded_frame->format() != original_frame->format()) {
       // The frame was converted from RGB to YUV, we can't easily compare to
       // the original frame, so we're going to compare with a new white frame.
diff --git a/net/socket/websocket_transport_connect_sub_job.cc b/net/socket/websocket_transport_connect_sub_job.cc
index 1668fdb..807d69c8 100644
--- a/net/socket/websocket_transport_connect_sub_job.cc
+++ b/net/socket/websocket_transport_connect_sub_job.cc
@@ -134,16 +134,7 @@
       type_(type),
       websocket_endpoint_lock_manager_(websocket_endpoint_lock_manager) {}
 
-WebSocketTransportConnectSubJob::~WebSocketTransportConnectSubJob() {
-  // We don't worry about cancelling the TCP connect, since ~StreamSocket will
-  // take care of it.
-  if (next()) {
-    DCHECK_EQ(STATE_OBTAIN_LOCK_COMPLETE, next_state_);
-    // The ~Waiter destructor will remove this object from the waiting list.
-  } else if (next_state_ == STATE_TRANSPORT_CONNECT_COMPLETE) {
-    websocket_endpoint_lock_manager_->UnlockEndpoint(CurrentAddress());
-  }
-}
+WebSocketTransportConnectSubJob::~WebSocketTransportConnectSubJob() = default;
 
 // Start connecting.
 int WebSocketTransportConnectSubJob::Start() {
@@ -164,7 +155,6 @@
     case STATE_OBTAIN_LOCK_COMPLETE:
       // TODO(ricea): Add a WebSocket-specific LOAD_STATE ?
       return LOAD_STATE_WAITING_FOR_AVAILABLE_SOCKET;
-    case STATE_TRANSPORT_CONNECT:
     case STATE_TRANSPORT_CONNECT_COMPLETE:
     case STATE_DONE:
       return LOAD_STATE_CONNECTING;
@@ -211,10 +201,6 @@
         DCHECK_EQ(OK, rv);
         rv = DoEndpointLockComplete();
         break;
-      case STATE_TRANSPORT_CONNECT:
-        DCHECK_EQ(OK, rv);
-        rv = DoTransportConnect();
-        break;
       case STATE_TRANSPORT_CONNECT_COMPLETE:
         rv = DoTransportConnectComplete(rv);
         break;
@@ -237,11 +223,6 @@
 }
 
 int WebSocketTransportConnectSubJob::DoEndpointLockComplete() {
-  next_state_ = STATE_TRANSPORT_CONNECT;
-  return OK;
-}
-
-int WebSocketTransportConnectSubJob::DoTransportConnect() {
   // TODO(ricea): Update global g_last_connect_time and report
   // ConnectInterval.
   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
@@ -249,9 +230,16 @@
   // TODO(https://crbug.com/1123197): Pass a non-null NetworkQualityEstimator.
   NetworkQualityEstimator* network_quality_estimator = nullptr;
 
-  transport_socket_ = client_socket_factory()->CreateTransportClientSocket(
-      one_address, nullptr, network_quality_estimator, net_log().net_log(),
-      net_log().source());
+  // This class now owns an endpoint lock. Wrap `socket` in a
+  // `WebSocketStreamSocket` to take ownership of the lock and release it when
+  // the socket goes out of scope.
+  std::unique_ptr<StreamSocket> socket =
+      client_socket_factory()->CreateTransportClientSocket(
+          one_address, nullptr, network_quality_estimator, net_log().net_log(),
+          net_log().source());
+  transport_socket_ = std::make_unique<WebSocketStreamSocket>(
+      std::move(socket), websocket_endpoint_lock_manager_, CurrentAddress());
+
   // This use of base::Unretained() is safe because transport_socket_ is
   // destroyed in the destructor.
   return transport_socket_->Connect(base::BindOnce(
@@ -261,7 +249,8 @@
 int WebSocketTransportConnectSubJob::DoTransportConnectComplete(int result) {
   next_state_ = STATE_DONE;
   if (result != OK) {
-    websocket_endpoint_lock_manager_->UnlockEndpoint(CurrentAddress());
+    // Drop the socket to release the endpoint lock.
+    transport_socket_.reset();
 
     if (current_address_index_ + 1 < addresses_.size()) {
       // Try falling back to the next address in the list.
@@ -273,12 +262,6 @@
     return result;
   }
 
-  // On success, need to register the socket with the
-  // WebSocketEndpointLockManager.
-  transport_socket_ = std::make_unique<WebSocketStreamSocket>(
-      std::move(transport_socket_), websocket_endpoint_lock_manager_,
-      CurrentAddress());
-
   return result;
 }
 
diff --git a/net/socket/websocket_transport_connect_sub_job.h b/net/socket/websocket_transport_connect_sub_job.h
index cc5d759..71ad315 100644
--- a/net/socket/websocket_transport_connect_sub_job.h
+++ b/net/socket/websocket_transport_connect_sub_job.h
@@ -68,7 +68,6 @@
     STATE_NONE,
     STATE_OBTAIN_LOCK,
     STATE_OBTAIN_LOCK_COMPLETE,
-    STATE_TRANSPORT_CONNECT,
     STATE_TRANSPORT_CONNECT_COMPLETE,
     STATE_DONE,
   };
@@ -83,7 +82,6 @@
   int DoLoop(int result);
   int DoEndpointLock();
   int DoEndpointLockComplete();
-  int DoTransportConnect();
   int DoTransportConnectComplete(int result);
 
   const raw_ptr<WebSocketTransportConnectJob> parent_job_;
diff --git a/services/device/generic_sensor/platform_sensor_fusion.h b/services/device/generic_sensor/platform_sensor_fusion.h
index 70d172bb..70515f0 100644
--- a/services/device/generic_sensor/platform_sensor_fusion.h
+++ b/services/device/generic_sensor/platform_sensor_fusion.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/containers/flat_map.h"
+#include "base/gtest_prod_util.h"
 #include "services/device/generic_sensor/platform_sensor.h"
 #include "services/device/generic_sensor/platform_sensor_provider_base.h"
 
@@ -72,6 +73,12 @@
   bool StartSensor(const PlatformSensorConfiguration& configuration) override;
   void StopSensor() override;
 
+  PlatformSensorFusionAlgorithm* fusion_algorithm() const {
+    return fusion_algorithm_.get();
+  }
+
+  FRIEND_TEST_ALL_PREFIXES(PlatformSensorFusionTest, OnSensorReadingChanged);
+
  private:
   SensorReading reading_;
   std::unique_ptr<PlatformSensorFusionAlgorithm> fusion_algorithm_;
diff --git a/services/device/generic_sensor/platform_sensor_fusion_algorithm.h b/services/device/generic_sensor/platform_sensor_fusion_algorithm.h
index d3f8214d..af03c49 100644
--- a/services/device/generic_sensor/platform_sensor_fusion_algorithm.h
+++ b/services/device/generic_sensor/platform_sensor_fusion_algorithm.h
@@ -25,6 +25,7 @@
   virtual ~PlatformSensorFusionAlgorithm();
 
   void set_threshold(double threshold) { threshold_ = threshold; }
+  double threshold() const { return threshold_; }
 
   void set_fusion_sensor(PlatformSensorFusion* fusion_sensor) {
     fusion_sensor_ = fusion_sensor;
diff --git a/services/device/generic_sensor/platform_sensor_fusion_unittest.cc b/services/device/generic_sensor/platform_sensor_fusion_unittest.cc
index 29206b1..41bd1c0c 100644
--- a/services/device/generic_sensor/platform_sensor_fusion_unittest.cc
+++ b/services/device/generic_sensor/platform_sensor_fusion_unittest.cc
@@ -7,13 +7,16 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "services/device/generic_sensor/absolute_orientation_euler_angles_fusion_algorithm_using_accelerometer_and_magnetometer.h"
 #include "services/device/generic_sensor/fake_platform_sensor_and_provider.h"
+#include "services/device/generic_sensor/generic_sensor_consts.h"
 #include "services/device/generic_sensor/linear_acceleration_fusion_algorithm_using_accelerometer.h"
 #include "services/device/generic_sensor/platform_sensor.h"
 #include "services/device/generic_sensor/platform_sensor_fusion.h"
+#include "services/device/generic_sensor/platform_sensor_fusion_algorithm.h"
 #include "services/device/generic_sensor/platform_sensor_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -25,6 +28,100 @@
 
 using mojom::SensorType;
 
+namespace {
+
+void ExpectNoReadingChangedEvent(MockPlatformSensorClient* sensor_client,
+                                 mojom::SensorType sensor_type) {
+  base::RunLoop run_loop;
+  EXPECT_CALL(*sensor_client, OnSensorReadingChanged(sensor_type)).Times(0);
+  run_loop.RunUntilIdle();
+}
+
+void ExpectReadingChangedEvent(MockPlatformSensorClient* sensor_client,
+                               mojom::SensorType sensor_type) {
+  base::RunLoop run_loop;
+  EXPECT_CALL(*sensor_client, OnSensorReadingChanged(sensor_type))
+      .WillOnce(Invoke([&](SensorType) { run_loop.Quit(); }));
+  run_loop.Run();
+}
+
+// Attempts to add a new reading to the sensor owned by |sensor_client|, and
+// asserts that it does not lead to OnSensorReadingChanged() being called (i.e.
+// PlatformSensor's significance check has failed).
+void AddNewReadingAndExpectNoReadingChangedEvent(
+    MockPlatformSensorClient* sensor_client,
+    const SensorReading& new_reading,
+    mojom::SensorType sensor_type) {
+  scoped_refptr<FakePlatformSensor> fake_sensor =
+      static_cast<FakePlatformSensor*>(sensor_client->sensor().get());
+  fake_sensor->AddNewReading(new_reading);
+  ExpectNoReadingChangedEvent(sensor_client, sensor_type);
+}
+
+// Add a new reading to the sensor owned by |sensor_client|, and expect reading
+// change event.
+void AddNewReadingAndExpectReadingChangedEvent(
+    MockPlatformSensorClient* sensor_client,
+    const SensorReading& new_reading,
+    mojom::SensorType sensor_type) {
+  scoped_refptr<FakePlatformSensor> fake_sensor =
+      static_cast<FakePlatformSensor*>(sensor_client->sensor().get());
+  fake_sensor->AddNewReading(new_reading);
+  ExpectReadingChangedEvent(sensor_client, sensor_type);
+}
+
+void FusionAlgorithmCopyLowLevelValues(const SensorReading& low_level_reading,
+                                       SensorReading* fused_reading) {
+  fused_reading->raw.values[0] = low_level_reading.raw.values[0];
+  fused_reading->raw.values[1] = low_level_reading.raw.values[1];
+  fused_reading->raw.values[2] = low_level_reading.raw.values[2];
+}
+
+void FusionAlgorithmSubtractEpsilonFromX(const SensorReading& low_level_reading,
+                                         SensorReading* fused_reading) {
+  fused_reading->raw.values[0] = low_level_reading.raw.values[0] - kEpsilon;
+  fused_reading->raw.values[1] = low_level_reading.raw.values[1];
+  fused_reading->raw.values[2] = low_level_reading.raw.values[2];
+}
+
+// A PlatformSensorFusionAlgorithm whose fusion algorithm can be customized
+// at runtime via set_fusion_function().
+class CustomizableFusionAlgorithm : public PlatformSensorFusionAlgorithm {
+ public:
+  using FusionFunction =
+      base::RepeatingCallback<void(const SensorReading& low_level_reading,
+                                   SensorReading* fused_reading)>;
+  static constexpr mojom::SensorType kLowLevelSensorType =
+      SensorType::ACCELEROMETER;
+  static constexpr mojom::SensorType kFusionSensorType = SensorType::GRAVITY;
+
+  CustomizableFusionAlgorithm()
+      : PlatformSensorFusionAlgorithm(kFusionSensorType,
+                                      {kLowLevelSensorType}) {}
+  ~CustomizableFusionAlgorithm() override = default;
+
+  bool GetFusedDataInternal(mojom::SensorType which_sensor_changed,
+                            SensorReading* fused_reading) override {
+    EXPECT_EQ(which_sensor_changed, kLowLevelSensorType);
+
+    SensorReading low_level_reading;
+    EXPECT_TRUE(fusion_sensor_->GetSourceReading(kLowLevelSensorType,
+                                                 &low_level_reading));
+
+    fusion_function_.Run(low_level_reading, fused_reading);
+    return true;
+  }
+
+  void set_fusion_function(FusionFunction fusion_function) {
+    fusion_function_ = std::move(fusion_function);
+  }
+
+ private:
+  FusionFunction fusion_function_;
+};
+
+}  // namespace
+
 class PlatformSensorFusionTest : public testing::Test {
  public:
   PlatformSensorFusionTest() {
@@ -334,4 +431,194 @@
       client.get(), PlatformSensorConfiguration(30.0)));
 }
 
+TEST_F(PlatformSensorFusionTest, FusionIsSignificantlyDifferent) {
+  // Due to inaccuracy of calculations between doubles, difference between two
+  // input values has to be bigger than the threshold value used in
+  // significantly different check.
+  CustomizableFusionAlgorithm fusion_algorithm;
+
+  const double kValueToFlipThreshold = fusion_algorithm.threshold() + kEpsilon;
+  const double kValueNotToFlipThreshold =
+      fusion_algorithm.threshold() - kEpsilon;
+  SensorReading reading1;
+  SensorReading reading2;
+  // Made up test values.
+  reading1.raw.values[0] = reading2.raw.values[0] = 0.1;
+  reading1.raw.values[1] = reading2.raw.values[1] = 0.5;
+  reading1.raw.values[2] = reading2.raw.values[2] = 5.0;
+  reading1.raw.values[3] = reading2.raw.values[3] = 10.0;
+
+  // Compared values are same.
+  // reading1: 0.1, 0.5, 5.0, 10.0
+  // reading2: 0.1, 0.5, 5.0, 10.0
+  EXPECT_FALSE(
+      fusion_algorithm.IsReadingSignificantlyDifferent(reading1, reading2));
+
+  // Compared values do not significantly differ from each other.
+  // reading1: 0.1, 0.5, 5.0, 10.0
+  // reading2: 0.1, 0.5, 5.0, 10.00001
+  reading2.raw.values[3] = reading2.raw.values[3] + kValueNotToFlipThreshold;
+  EXPECT_FALSE(
+      fusion_algorithm.IsReadingSignificantlyDifferent(reading1, reading2));
+
+  // Compared values significantly differ from each other.
+  // reading1: 0.1, 0.5, 5.0, 10.0
+  // reading2: 0.1, 0.5, 5.0, 10.11001
+  reading2.raw.values[3] = reading2.raw.values[3] + kValueToFlipThreshold;
+  EXPECT_TRUE(
+      fusion_algorithm.IsReadingSignificantlyDifferent(reading1, reading2));
+}
+
+TEST_F(PlatformSensorFusionTest, OnSensorReadingChanged) {
+  // Accelerometer is selected as low-level sensor.
+  CreateAccelerometer();
+  EXPECT_TRUE(accelerometer_);
+  auto client_low_level_ =
+      std::make_unique<testing::NiceMock<MockPlatformSensorClient>>(
+          accelerometer_);
+
+  CreateFusionSensor(std::make_unique<CustomizableFusionAlgorithm>());
+  ASSERT_TRUE(fusion_sensor_);
+  auto* fusion_algorithm = static_cast<CustomizableFusionAlgorithm*>(
+      fusion_sensor_->fusion_algorithm());
+  EXPECT_EQ(CustomizableFusionAlgorithm::kFusionSensorType,
+            fusion_sensor_->GetType());
+
+  auto client_fusion =
+      std::make_unique<testing::NiceMock<MockPlatformSensorClient>>(
+          fusion_sensor_);
+  fusion_sensor_->StartListening(client_fusion.get(),
+                                 PlatformSensorConfiguration(10));
+
+  // Made up test values.
+  const double kTestValueX = 0.6;
+  const double kTestValueY = 0.9;
+  const double kTestValueZ = 1.1;
+  const double kValueToFlipThreshold = fusion_algorithm->threshold() * 2;
+
+  struct TestSensorReading {
+    const struct {
+      double x;
+      double y;
+      double z;
+    } input, expected;
+    const bool expect_reading_changed_event;
+  };
+
+  const struct {
+    TestSensorReading low_level;
+    TestSensorReading fusion;
+    CustomizableFusionAlgorithm::FusionFunction fusion_function;
+  } kTestSteps[] = {
+      // Test set 1
+      // Triggers low-level and fusion reading as initial sensor
+      // values are zero.
+      {// Low-level sensor
+       {{kTestValueX, kTestValueY, kTestValueZ},
+        {kTestValueX, kTestValueY, kTestValueZ},
+        true},
+       // Fusion sensor
+       {{kTestValueX, kTestValueY, kTestValueZ},
+        {kTestValueX, kTestValueY, kTestValueZ},
+        true},
+       base::BindRepeating(&FusionAlgorithmCopyLowLevelValues)},
+
+      // Test set 2
+      // Triggers low-level reading event, but not fusion sensor reading event
+      // as fusion sensor values are not changed compared to test set 1.
+      {// Low-level sensor
+       {{kTestValueX + kEpsilon, kTestValueY, kTestValueZ},
+        {kTestValueX, kTestValueY, kTestValueZ},
+        true},
+       // Fusion sensor
+       {{kTestValueX, kTestValueY, kTestValueZ},
+        {kTestValueX, kTestValueY, kTestValueZ},
+        false},
+       base::BindRepeating(&FusionAlgorithmSubtractEpsilonFromX)},
+
+      // Test set 3
+      // Test set has same values as previous values so low-level and fusion
+      // reading event is not expected.
+      {// Low-level sensor
+       {{kTestValueX + kEpsilon, kTestValueY, kTestValueZ},
+        {kTestValueX, kTestValueY, kTestValueZ},
+        false},
+       // Fusion sensor
+       {{kTestValueX, kTestValueY, kTestValueZ},
+        {kTestValueX, kTestValueY, kTestValueZ},
+        false},
+       base::BindRepeating(&FusionAlgorithmSubtractEpsilonFromX)},
+
+      // Test set 4
+      // In current code as fusion sensor values are rounded before
+      // PlatformSensorFusionAlgorithm::IsReadingSignificantlyDifferent() call
+      // the difference between values must much bigger than threshold value.
+      {// Low-level sensor
+       {{kTestValueX + kValueToFlipThreshold, kTestValueY, kTestValueZ},
+        {kTestValueX + kValueToFlipThreshold, kTestValueY, kTestValueZ},
+        true},
+       // Fusion sensor
+       {{kTestValueX + kValueToFlipThreshold, kTestValueY, kTestValueZ},
+        {kTestValueX + kValueToFlipThreshold, kTestValueY, kTestValueZ},
+        true},
+       base::BindRepeating(&FusionAlgorithmCopyLowLevelValues)},
+  };
+
+  for (const auto& test_step : kTestSteps) {
+    fusion_algorithm->set_fusion_function(test_step.fusion_function);
+
+    // First add low-level sensor readings.
+    SensorReading reading;
+    reading.accel.x = test_step.low_level.input.x;
+    reading.accel.y = test_step.low_level.input.y;
+    reading.accel.z = test_step.low_level.input.z;
+
+    // Code checks if PlatformSensor::OnSensorReadingChanged() is called
+    // or not called as expected.
+    if (test_step.low_level.expect_reading_changed_event) {
+      AddNewReadingAndExpectReadingChangedEvent(
+          client_low_level_.get(), reading,
+          CustomizableFusionAlgorithm::kLowLevelSensorType);
+    } else {
+      AddNewReadingAndExpectNoReadingChangedEvent(
+          client_low_level_.get(), reading,
+          CustomizableFusionAlgorithm::kLowLevelSensorType);
+    }
+
+    if (test_step.fusion.expect_reading_changed_event) {
+      ExpectReadingChangedEvent(client_fusion.get(),
+                                CustomizableFusionAlgorithm::kFusionSensorType);
+    } else {
+      ExpectNoReadingChangedEvent(
+          client_fusion.get(), CustomizableFusionAlgorithm::kFusionSensorType);
+    }
+
+    // Once new values are added, we can check that low-level sensors and
+    // fusion sensor have correct values.
+    // Check rounded low-level sensor values.
+    EXPECT_TRUE(accelerometer_->GetLatestReading(&reading));
+    EXPECT_DOUBLE_EQ(test_step.low_level.expected.x, reading.accel.x);
+    EXPECT_DOUBLE_EQ(test_step.low_level.expected.y, reading.accel.y);
+    EXPECT_DOUBLE_EQ(test_step.low_level.expected.z, reading.accel.z);
+
+    // Check raw low-level sensor values.
+    EXPECT_TRUE(accelerometer_->GetLatestRawReading(&reading));
+    EXPECT_DOUBLE_EQ(test_step.low_level.input.x, reading.accel.x);
+    EXPECT_DOUBLE_EQ(test_step.low_level.input.y, reading.accel.y);
+    EXPECT_DOUBLE_EQ(test_step.low_level.input.z, reading.accel.z);
+
+    // Check rounded fusion sensor values.
+    EXPECT_TRUE(fusion_sensor_->GetLatestReading(&reading));
+    EXPECT_DOUBLE_EQ(test_step.fusion.expected.x, reading.accel.x);
+    EXPECT_DOUBLE_EQ(test_step.fusion.expected.y, reading.accel.y);
+    EXPECT_DOUBLE_EQ(test_step.fusion.expected.z, reading.accel.z);
+
+    // Check raw fusion sensor values.
+    EXPECT_TRUE(fusion_sensor_->GetLatestRawReading(&reading));
+    EXPECT_DOUBLE_EQ(test_step.fusion.input.x, reading.accel.x);
+    EXPECT_DOUBLE_EQ(test_step.fusion.input.y, reading.accel.y);
+    EXPECT_DOUBLE_EQ(test_step.fusion.input.z, reading.accel.z);
+  }
+}
+
 }  //  namespace device
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index b138a6b..148c72e9 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5795,7 +5795,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -5803,14 +5803,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "dimension_sets": [
@@ -5937,7 +5937,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -5945,14 +5945,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 86cec13..006d68a 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -85114,7 +85114,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -85122,14 +85122,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -85231,7 +85231,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -85239,14 +85239,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -86613,21 +86613,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "dimension_sets": [
@@ -86755,21 +86755,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "dimension_sets": [
@@ -88310,21 +88310,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "dimension_sets": [
@@ -88452,21 +88452,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "dimension_sets": [
@@ -89203,21 +89203,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -89299,21 +89299,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4962.0",
+        "name": "lacros_chrome_browsertests_run_in_series_Lacros version skew testing ash 102.0.4963.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4962.0",
-              "revision": "version:102.0.4962.0"
+              "location": "lacros_version_skew_tests_v102.0.4963.0",
+              "revision": "version:102.0.4963.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index d7d35f0e..3f6590a 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -28,16 +28,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4962.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4963.0/test_ash_chrome',
       '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter',
     ],
-    'identifier': 'Lacros version skew testing ash 102.0.4962.0',
+    'identifier': 'Lacros version skew testing ash 102.0.4963.0',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v102.0.4962.0',
-          'revision': 'version:102.0.4962.0',
+          'location': 'lacros_version_skew_tests_v102.0.4963.0',
+          'revision': 'version:102.0.4963.0',
         },
       ],
     },
diff --git a/third_party/blink/common/client_hints/enabled_client_hints_unittest.cc b/third_party/blink/common/client_hints/enabled_client_hints_unittest.cc
index 08eb889f..26101764 100644
--- a/third_party/blink/common/client_hints/enabled_client_hints_unittest.cc
+++ b/third_party/blink/common/client_hints/enabled_client_hints_unittest.cc
@@ -382,10 +382,10 @@
   // The Origin Trial token expires in 2033.  Generate a new token by then, or
   // find a better way to re-generate a test trial token.
   static constexpr char kValidOriginTrialToken[] =
-      "A1ZeT+cUjeN+YRlvnoNP67cBtEZqx9Z4Gx/AmfsCIHvyULWM6t12q1Kd6YWHdMtF/"
-      "eC6MYGv0GMwIZo2J380WQ0AAABceyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0N"
-      "DQiLCAiZmVhdHVyZSI6ICJQYXJ0aXRpb25lZENvb2tpZXMiLCAiZXhwaXJ5IjogMTY0ODE2M"
-      "jU4Mn0=";
+      "A4s/"
+      "iPKfhEfgqQIIuz4zLuCpONpXOuYyJFBhBx1MfgS1aNhFujyhsg4lkfTRfjzQCI3aUbMwtNm2"
+      "5elLTR4UIgAAAABceyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NDQiLCAiZmVh"
+      "dHVyZSI6ICJQYXJ0aXRpb25lZENvb2tpZXMiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=";
 
   VerifyClientHintEnabledWithOriginTrialToken(
       kValidOriginTrialToken,
@@ -397,10 +397,10 @@
        EnabledPartitionedCookiesClientHintWithInvalidOriginTrialToken) {
   // Changed the first character of the token in the last test.
   static constexpr char kValidOriginTrialToken[] =
-      "B1ZeT+cUjeN+YRlvnoNP67cBtEZqx9Z4Gx/AmfsCIHvyULWM6t12q1Kd6YWHdMtF/"
-      "eC6MYGv0GMwIZo2J380WQ0AAABceyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0N"
-      "DQiLCAiZmVhdHVyZSI6ICJQYXJ0aXRpb25lZENvb2tpZXMiLCAiZXhwaXJ5IjogMTY0ODE2M"
-      "jU4Mn0=";
+      "B4s/"
+      "iPKfhEfgqQIIuz4zLuCpONpXOuYyJFBhBx1MfgS1aNhFujyhsg4lkfTRfjzQCI3aUbMwtNm2"
+      "5elLTR4UIgAAAABceyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6NDQ0NDQiLCAiZmVh"
+      "dHVyZSI6ICJQYXJ0aXRpb25lZENvb2tpZXMiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=";
 
   VerifyClientHintEnabledWithOriginTrialToken(
       kValidOriginTrialToken,
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index b4ddaa8..fb2c40ed 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -152,6 +152,9 @@
 const base::Feature kLayoutNGBlockInInline{"LayoutNGBlockInInline",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kPrivacySandboxAdsAPIs{"PrivacySandboxAdsAPIs",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kMixedContentAutoupgrade{"AutoupgradeMixedContent",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index b7af993d..8093cad 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -66,6 +66,7 @@
 BLINK_COMMON_EXPORT extern const base::Feature kViewportHeightClientHintHeader;
 BLINK_COMMON_EXPORT extern const base::Feature kFullUserAgent;
 BLINK_COMMON_EXPORT extern const base::Feature kPath2DPaintCache;
+BLINK_COMMON_EXPORT extern const base::Feature kPrivacySandboxAdsAPIs;
 
 enum class FencedFramesImplementationType {
   kShadowDOM,
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index bad6236..91bac78 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3505,6 +3505,8 @@
   kComputePressureObserver_Unobserve = 4184,
   kComputePressureObserver_Disconnect = 4185,
   kComputePressureObserver_TakeRecords = 4186,
+  kPrivacySandboxAdsAPIs = 4187,
+  kFledge = 4188,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index bd3c987..a3c1f019 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4791,28 +4791,40 @@
     return false;
 
   HeapVector<std::pair<Member<Element>, Member<Element>>> activatable_targets;
-  for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(*this)) {
-    auto* ancestor_element = DynamicTo<Element>(ancestor);
-    if (!ancestor_element)
+  for (Node* previous = this; previous;
+       previous = FlatTreeTraversal::Previous(*previous)) {
+    Element* prior_element = DynamicTo<Element>(previous);
+    if (!prior_element)
       continue;
-    if (auto* context = ancestor_element->GetDisplayLockContext()) {
+    if (auto* context = prior_element->GetDisplayLockContext()) {
       // If any of the ancestors is not activatable for the given reason, we
       // can't activate.
       if (context->IsLocked() && !context->IsActivatable(reason))
         return false;
-      activatable_targets.push_back(std::make_pair(
-          ancestor_element, &ancestor.GetTreeScope().Retarget(*this)));
+      // Collect display-locked ancestors and shaping-deferred prior elements.
+      if (FlatTreeTraversal::Contains(*prior_element, *this) ||
+          (prior_element->GetLayoutObject() &&
+           prior_element->GetLayoutObject()->IsShapingDeferred())) {
+        activatable_targets.push_back(std::make_pair(
+            prior_element, &prior_element->GetTreeScope().Retarget(*this)));
+      }
     }
   }
 
   bool activated = false;
   for (const auto& target : activatable_targets) {
-    // Dispatch event on activatable ancestor (target.first), with
-    // the retargeted element (target.second) as the |activatedElement|.
     if (auto* context = target.first->GetDisplayLockContext()) {
       if (context->ShouldCommitForActivation(reason)) {
         activated = true;
-        context->CommitForActivationWithSignal(target.second, reason);
+        if (target.first->GetLayoutObject() &&
+            target.first->GetLayoutObject()->IsShapingDeferred()) {
+          // Unlock shaping-deferred IFCs permanently.
+          context->SetRequestedState(EContentVisibility::kVisible);
+        } else {
+          // Dispatch event on activatable ancestor (target.first), with
+          // the retargeted element (target.second) as the |activated_element|.
+          context->CommitForActivationWithSignal(target.second, reason);
+        }
       }
     }
   }
diff --git a/third_party/blink/renderer/core/frame/attribution_reporting.idl b/third_party/blink/renderer/core/frame/attribution_reporting.idl
index 301f307..44843ac 100644
--- a/third_party/blink/renderer/core/frame/attribution_reporting.idl
+++ b/third_party/blink/renderer/core/frame/attribution_reporting.idl
@@ -7,7 +7,7 @@
 // https://github.com/WICG/conversion-measurement-api/
 [
     Exposed=Window,
-    RuntimeEnabled=ConversionMeasurement,
+    RuntimeEnabled=AttributionReporting,
     SecureContext
 ] interface AttributionReporting {
     [CallWith=ScriptState, RaisesException] Promise<void> registerSource(DOMString url);
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
index 63810da..abb000a 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -287,7 +287,7 @@
   LocalDOMWindow* window = local_frame_->DomWindow();
   DCHECK(window);
 
-  if (!RuntimeEnabledFeatures::ConversionMeasurementEnabled(window))
+  if (!RuntimeEnabledFeatures::AttributionReportingEnabled(window))
     return RegisterResult::kNotAllowed;
 
   const bool feature_policy_enabled = window->IsFeatureEnabled(
@@ -323,6 +323,13 @@
     return RegisterResult::kUntrustworthyOrigin;
   }
 
+  UseCounter::Count(window, mojom::blink::WebFeature::kConversionAPIAll);
+
+  // Only record the ads APIs counter if enabled in that manner.
+  if (RuntimeEnabledFeatures::PrivacySandboxAdsAPIsEnabled(window)) {
+    UseCounter::Count(window, mojom::blink::WebFeature::kPrivacySandboxAdsAPIs);
+  }
+
   return RegisterResult::kSuccess;
 }
 
diff --git a/third_party/blink/renderer/core/frame/window_attribution_reporting.idl b/third_party/blink/renderer/core/frame/window_attribution_reporting.idl
index 69cf0be..ad3edaa 100644
--- a/third_party/blink/renderer/core/frame/window_attribution_reporting.idl
+++ b/third_party/blink/renderer/core/frame/window_attribution_reporting.idl
@@ -5,7 +5,7 @@
 // Attribution Reporting API: https://github.com/wicg/conversion-measurement-api
 [
     ImplementedAs=AttributionReporting,
-    RuntimeEnabled=ConversionMeasurement,
+    RuntimeEnabled=AttributionReporting,
     SecureContext
 ] partial interface Window {
     readonly attribute AttributionReporting attributionReporting;
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.idl b/third_party/blink/renderer/core/html/html_anchor_element.idl
index 05545606..1ae36239 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.idl
+++ b/third_party/blink/renderer/core/html/html_anchor_element.idl
@@ -41,7 +41,7 @@
     [RuntimeEnabled=ConversionMeasurement, CEReactions,Reflect] attribute USVString attributionReportTo;
     [RuntimeEnabled=ConversionMeasurement, CEReactions,Reflect] attribute DOMString attributionExpiry;
     [RuntimeEnabled=ConversionMeasurement, CEReactions,Reflect] attribute DOMString attributionSourcePriority;
-    [RuntimeEnabled=ConversionMeasurement, CEReactions,Reflect] attribute USVString attributionSrc;
+    [RuntimeEnabled=AttributionReporting, CEReactions,Reflect] attribute USVString attributionSrc;
 
     // obsolete members
     // https://html.spec.whatwg.org/C/#HTMLAnchorElement-partial
diff --git a/third_party/blink/renderer/core/html/html_image_element.idl b/third_party/blink/renderer/core/html/html_image_element.idl
index aed92e1c..dfec08c 100644
--- a/third_party/blink/renderer/core/html/html_image_element.idl
+++ b/third_party/blink/renderer/core/html/html_image_element.idl
@@ -46,7 +46,7 @@
     [RuntimeEnabled=LazyImageLoading, CEReactions, Reflect, ReflectOnly=("lazy", "eager", "auto"), ReflectMissing="auto", ReflectInvalid="auto"] attribute DOMString loading;
 
     // Attribution Reporting
-    [RuntimeEnabled=ConversionMeasurement, CEReactions,Reflect] attribute USVString attributionSrc;
+    [RuntimeEnabled=AttributionReporting, CEReactions,Reflect] attribute USVString attributionSrc;
 
     // obsolete members
     // https://html.spec.whatwg.org/C/#HTMLImageElement-partial
diff --git a/third_party/blink/renderer/core/layout/deferred_shaping_test.cc b/third_party/blink/renderer/core/layout/deferred_shaping_test.cc
index bc43914..29ad409 100644
--- a/third_party/blink/renderer/core/layout/deferred_shaping_test.cc
+++ b/third_party/blink/renderer/core/layout/deferred_shaping_test.cc
@@ -254,6 +254,25 @@
             GetElementById("target2")->clientWidth());
 }
 
+TEST_F(DeferredShapingTest, ScrollIntoView) {
+  SetBodyInnerHTML(R"HTML(<div style="height:1800px"></div>
+<div><p id="prior">IFC</p></div>
+<div style="height:3600px"></div>
+<p id="ancestor">IFC<span style="display:inline-block" id="target"></sapn></p>
+)HTML");
+  UpdateAllLifecyclePhasesForTest();
+  EXPECT_TRUE(IsDefer("prior"));
+  EXPECT_TRUE(IsLocked("prior"));
+  EXPECT_TRUE(IsDefer("ancestor"));
+  EXPECT_TRUE(IsLocked("ancestor"));
+
+  GetElementById("target")->scrollIntoView();
+  EXPECT_FALSE(IsDefer("prior"));
+  EXPECT_FALSE(IsLocked("prior"));
+  EXPECT_FALSE(IsDefer("ancestor"));
+  EXPECT_FALSE(IsLocked("ancestor"));
+}
+
 TEST_F(DeferredShapingTest, NonLayoutNGBlockFlow) {
   SetBodyInnerHTML(R"HTML(
 <div style="height:1800px"></div>
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
index d1a80dff..de9559fd 100644
--- a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
+++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
@@ -491,6 +491,9 @@
   if (trial_name == "Portals")
     return base::FeatureList::IsEnabled(features::kPortals);
 
+  if (trial_name == "PrivacySandboxAdsAPIs")
+    return base::FeatureList::IsEnabled(features::kPrivacySandboxAdsAPIs);
+
   if (trial_name == "FencedFrames")
     return base::FeatureList::IsEnabled(features::kFencedFrames);
 
@@ -507,10 +510,6 @@
     return base::FeatureList::IsEnabled(
         features::kSpeculationRulesPrefetchProxy);
   }
-  if (trial_name == "ConversionMeasurement" &&
-      !base::FeatureList::IsEnabled(features::kConversionMeasurement)) {
-    return false;
-  }
 
   if (trial_name == "Prerender2")
     return base::FeatureList::IsEnabled(features::kPrerender2);
@@ -518,6 +517,22 @@
   return true;
 }
 
+Vector<OriginTrialFeature> OriginTrialContext::RestrictedFeaturesForTrial(
+    const String& trial_name) {
+  if (trial_name == "PrivacySandboxAdsAPIs") {
+    Vector<OriginTrialFeature> restricted;
+    if (!base::FeatureList::IsEnabled(features::kFledge))
+      restricted.push_back(OriginTrialFeature::kFledge);
+    if (!base::FeatureList::IsEnabled(features::kBrowsingTopics))
+      restricted.push_back(OriginTrialFeature::kTopicsAPI);
+    if (!base::FeatureList::IsEnabled(features::kConversionMeasurement))
+      restricted.push_back(OriginTrialFeature::kAttributionReporting);
+    return restricted;
+  }
+
+  return {};
+}
+
 OriginTrialStatus OriginTrialContext::EnableTrialFromName(
     const String& trial_name,
     base::Time expiry_time) {
@@ -526,6 +541,9 @@
     return OriginTrialStatus::kTrialNotAllowed;
   }
 
+  Vector<OriginTrialFeature> restricted =
+      RestrictedFeaturesForTrial(trial_name);
+
   bool did_enable_feature = false;
   for (OriginTrialFeature feature :
        origin_trials::FeaturesForTrial(trial_name.Utf8())) {
@@ -535,6 +553,13 @@
       continue;
     }
 
+    if (restricted.Contains(feature)) {
+      DVLOG(1) << "EnableTrialFromName: feature " << static_cast<int>(feature)
+               << " is restricted from being enabled via the trial: "
+               << trial_name << ".";
+      continue;
+    }
+
     did_enable_feature = true;
     enabled_features_.insert(feature);
 
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context.h b/third_party/blink/renderer/core/origin_trials/origin_trial_context.h
index 97d38d4..89824099 100644
--- a/third_party/blink/renderer/core/origin_trials/origin_trial_context.h
+++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context.h
@@ -192,6 +192,13 @@
   // invalid in the browser's present configuration).
   bool CanEnableTrialFromName(const StringView& trial_name);
 
+  // Returns features which are currently restricted for a given trial name,
+  // these features *will not* be enabled by the origin trial infrastructure if
+  // the given trial is enabled. The corresponding runtime features may still be
+  // enabled via command line flags, etc.
+  Vector<OriginTrialFeature> RestrictedFeaturesForTrial(
+      const String& trial_name);
+
   // Enable features by trial name. Returns true or false to indicate whether
   // some features are enabled as the result.
   OriginTrialStatus EnableTrialFromName(const String& trial_name,
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc
index 9c9bc240..30af2646 100644
--- a/third_party/blink/renderer/core/page/create_window.cc
+++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -70,9 +70,9 @@
                                               LocalDOMWindow* dom_window) {
   WebWindowFeatures window_features;
 
-  bool conversion_measurement_enabled =
+  bool attribution_reporting_enabled =
       dom_window &&
-      RuntimeEnabledFeatures::ConversionMeasurementEnabled(dom_window);
+      RuntimeEnabledFeatures::AttributionReportingEnabled(dom_window);
 
   // This code follows the HTML spec, specifically
   // https://html.spec.whatwg.org/C/#concept-window-open-features-tokenize
@@ -160,7 +160,7 @@
 
     if (!ui_features_were_disabled && key_string != "noopener" &&
         key_string != "noreferrer" &&
-        (!conversion_measurement_enabled || key_string != "attributionsrc")) {
+        (!attribution_reporting_enabled || key_string != "attributionsrc")) {
       ui_features_were_disabled = true;
       window_features.menu_bar_visible = false;
       window_features.status_bar_visible = false;
@@ -201,7 +201,7 @@
       window_features.background = true;
     } else if (key_string == "persistent") {
       window_features.persistent = true;
-    } else if (conversion_measurement_enabled &&
+    } else if (attribution_reporting_enabled &&
                key_string == "attributionsrc") {
       window_features.impression =
           dom_window->GetFrame()->GetAttributionSrcLoader()->RegisterNavigation(
diff --git a/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5 b/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
index 24f4a25..369c7be 100644
--- a/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
+++ b/third_party/blink/renderer/core/permissions_policy/permissions_policy_features.json5
@@ -191,7 +191,7 @@
     {
       name: "AttributionReporting",
       permissions_policy_name: "attribution-reporting",
-      depends_on: ["ConversionMeasurement"],
+      depends_on: ["AttributionReporting"],
     },
     {
       name: "CrossOriginIsolated",
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
index 6b6c26f..98f1718c6 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
@@ -801,6 +801,18 @@
           /*discard_duplicates=*/true);
 }
 
+void RecordCommonFledgeUseCounters(Document* document) {
+  if (!document)
+    return;
+  UseCounter::Count(document, mojom::blink::WebFeature::kFledge);
+  // Only record the ads APIs counter if enabled in that manner.
+  if (RuntimeEnabledFeatures::PrivacySandboxAdsAPIsEnabled(
+          document->GetExecutionContext())) {
+    UseCounter::Count(document,
+                      mojom::blink::WebFeature::kPrivacySandboxAdsAPIs);
+  }
+}
+
 }  // namespace
 
 NavigatorAuction::NavigatorAuction(Navigator& navigator)
@@ -887,6 +899,7 @@
                                            const AuctionAdInterestGroup* group,
                                            double duration_seconds,
                                            ExceptionState& exception_state) {
+  RecordCommonFledgeUseCounters(navigator.DomWindow()->document());
   const ExecutionContext* context = ExecutionContext::From(script_state);
   if (!context->IsFeatureEnabled(
           blink::mojom::PermissionsPolicyFeature::kJoinAdInterestGroup)) {
@@ -926,6 +939,7 @@
                                             Navigator& navigator,
                                             const AuctionAdInterestGroup* group,
                                             ExceptionState& exception_state) {
+  RecordCommonFledgeUseCounters(navigator.DomWindow()->document());
   ExecutionContext* context = ExecutionContext::From(script_state);
   if (!context->IsFeatureEnabled(
           blink::mojom::PermissionsPolicyFeature::kJoinAdInterestGroup)) {
@@ -953,6 +967,7 @@
 void NavigatorAuction::updateAdInterestGroups(ScriptState* script_state,
                                               Navigator& navigator,
                                               ExceptionState& exception_state) {
+  RecordCommonFledgeUseCounters(navigator.DomWindow()->document());
   ExecutionContext* context = ExecutionContext::From(script_state);
   if (!context->IsFeatureEnabled(
           blink::mojom::PermissionsPolicyFeature::kJoinAdInterestGroup)) {
@@ -994,6 +1009,7 @@
                                              Navigator& navigator,
                                              const AuctionAdConfig* config,
                                              ExceptionState& exception_state) {
+  RecordCommonFledgeUseCounters(navigator.DomWindow()->document());
   const ExecutionContext* context = ExecutionContext::From(script_state);
   if (!context->IsFeatureEnabled(
           blink::mojom::PermissionsPolicyFeature::kRunAdAuction)) {
@@ -1018,6 +1034,7 @@
     Navigator& navigator,
     uint16_t num_ad_components,
     ExceptionState& exception_state) {
+  RecordCommonFledgeUseCounters(navigator.DomWindow()->document());
   const auto& ad_auction_components =
       navigator.DomWindow()->document()->Loader()->AdAuctionComponents();
   Vector<String> out;
diff --git a/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc
index 4514913..9d938d2 100644
--- a/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc
+++ b/third_party/blink/renderer/modules/browsing_topics/browsing_topics_document_supplement.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom-blink.h"
 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
@@ -57,6 +58,12 @@
     return ScriptPromise();
   }
 
+  if (RuntimeEnabledFeatures::PrivacySandboxAdsAPIsEnabled(
+          document.GetExecutionContext())) {
+    UseCounter::Count(document,
+                      mojom::blink::WebFeature::kPrivacySandboxAdsAPIs);
+  }
+
   ScriptPromiseResolver* resolver =
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise promise = resolver->Promise();
diff --git a/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc b/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc
index 4128cb20..369856e 100644
--- a/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc
+++ b/third_party/blink/renderer/platform/media/watch_time_reporter_unittest.cc
@@ -662,7 +662,7 @@
   wtr_->OnPlaying();
   EXPECT_EQ(!has_video_, IsMonitoring());
 
-  Initialize(true, true, gfx::Size(100, 100));
+  Initialize(true, true, kSizeTooSmall);
   wtr_->OnPlaying();
   EXPECT_EQ(!has_video_, IsMonitoring());
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index c1dbd8f..aaa43004 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -200,6 +200,12 @@
       status: "experimental",
     },
     {
+      name: "AttributionReporting",
+      origin_trial_feature_name: "PrivacySandboxAdsAPIs",
+      origin_trial_allows_third_party: true,
+      status: "experimental",
+    },
+    {
       name: "AudioOutputDevices",
       // Android does not yet support switching of audio output devices
       status: {"Android": "", "default": "stable"},
@@ -481,9 +487,7 @@
     },
     {
       name: "ConversionMeasurement",
-      origin_trial_feature_name: "ConversionMeasurement",
-      origin_trial_allows_third_party: true,
-      status: "experimental",
+      status: "test",
     },
     {
       name: "CooperativeScheduling"
@@ -1135,7 +1139,8 @@
     },
     {
       name: "Fledge",
-      origin_trial_feature_name: "Fledge",
+      origin_trial_feature_name: "PrivacySandboxAdsAPIs",
+      origin_trial_allows_third_party: true,
     },
     {
       name: "Focusgroup",
@@ -1946,6 +1951,13 @@
       name: "PriorityHints",
       status: "stable",
     },
+    // The RTE feature encompasses multiple APIs, including: Attribution
+    // Reporting, FLEDGE, and Topics.
+    {
+      name: "PrivacySandboxAdsAPIs",
+      origin_trial_feature_name: "PrivacySandboxAdsAPIs",
+      origin_trial_allows_third_party: true,
+    },
     {
       name: "PrivateNetworkAccessNonSecureContextsAllowed",
       origin_trial_feature_name: "PrivateNetworkAccessNonSecureContextsAllowed",
@@ -2313,6 +2325,8 @@
     },
     {
       name: "TopicsAPI",
+      origin_trial_feature_name: "PrivacySandboxAdsAPIs",
+      origin_trial_allows_third_party: true,
     },
     // This feature allows touch dragging and a context menu to occur
     // simultaneously, with the assumption that the menu is non-modal.  Without
diff --git a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
index 1b26c221..ffc431d4 100644
--- a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
+++ b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
@@ -481,8 +481,8 @@
   SkIRect canvas_clip = canvas->getDeviceClipBounds();
   gfx::Rect viewport = gfx::SkIRectToRect(canvas_clip);
 
-  gfx::Transform transform(gfx::Transform::kSkipInitialization);
-  transform.matrix() = canvas->getTotalMatrix();  // Converts 3x3 matrix to 4x4.
+  // Converts 3x3 matrix to 4x4.
+  gfx::Transform transform(canvas->getTotalMatrix());
 
   // We will resize the Display to ensure it covers the entire |viewport|, so
   // save it for later.
diff --git a/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc b/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc
index ccc5a611..559b06f 100644
--- a/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc
+++ b/third_party/blink/renderer/platform/widget/input/synchronous_compositor_proxy.cc
@@ -215,7 +215,7 @@
   }
   SkCanvas canvas(bitmap);
   canvas.clipRect(gfx::RectToSkRect(params->clip));
-  canvas.concat(SkMatrix(params->transform.matrix()));
+  canvas.concat(params->transform.matrix().asM33());
 
   layer_tree_frame_sink_->DemandDrawSw(&canvas);
 }
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index f29cee1..bccd58c 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6335,7 +6335,7 @@
 crbug.com/1201365 virtual/portals/http/tests/inspector-protocol/portals/device-emulation-portals.js [ Pass Timeout ]
 
 # Sheriff 2021-04-22
-crbug.com/1191990 [ Linux ] http/tests/serviceworker/clients-openwindow.html [ Failure Pass ]
+crbug.com/1191990 http/tests/serviceworker/clients-openwindow.html [ Failure Pass ]
 
 # Sheriff 2021-04-23
 crbug.com/1198832 [ Linux ] external/wpt/css/css-sizing/aspect-ratio/replaced-element-003.html [ Failure Pass ]
@@ -7605,7 +7605,6 @@
 # Sheriff 2022-03-21: More flaky tests.
 crbug.com/1277696 [ Mac ] fast/loader/reload-zero-byte-plugin.html [ Failure Pass ]
 crbug.com/1272376 [ Mac ] plugins/plugin-document-back-forward.html [ Failure Pass ]
-crbug.com/1191990 [ Mac ] http/tests/serviceworker/clients-openwindow.html [ Pass Timeout ]
 
 # Sheriff 2022-03-23: More flaky tests.
 crbug.com/1309483 [ Win7 ] virtual/gpu-rasterization/images/directly-composited-image-orientation.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 943d157..0ebca07f 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1052,7 +1052,7 @@
       "http/tests/inspector-protocol/storage/interest-groups.js"
     ],
     "args": [
-      "--enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge,FencedFrames:implementation_type/shadow_dom"
+      "--enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge,PrivacySandboxAdsAPIsOverride,FencedFrames:implementation_type/shadow_dom"
     ]
   },
   {
diff --git a/third_party/blink/web_tests/external/wpt/loading/early-hints/404-with-early-hints.h2.window.js b/third_party/blink/web_tests/external/wpt/loading/early-hints/404-with-early-hints.h2.window.js
new file mode 100644
index 0000000..f28208a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/loading/early-hints/404-with-early-hints.h2.window.js
@@ -0,0 +1,10 @@
+// META: script=/common/utils.js
+// META: script=resources/early-hints-helpers.sub.js
+
+test(() => {
+    const params = new URLSearchParams();
+    params.set("resource-url",
+        SAME_ORIGIN_RESOURCES_URL + "/square.png?" + token());
+    const test_url = "resources/404-with-early-hints.h2.py?" + params.toString();
+    window.location.replace(new URL(test_url, window.location));
+});
diff --git a/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/404-with-early-hints.h2.py b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/404-with-early-hints.h2.py
new file mode 100644
index 0000000..bb75bc0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/404-with-early-hints.h2.py
@@ -0,0 +1,24 @@
+import os
+
+
+def handle_headers(frame, request, response):
+    resource_url = request.GET.first(b"resource-url").decode()
+    link_header_value = "<{}>; rel=preload; as=image".format(resource_url)
+    early_hints = [
+        (b":status", b"103"),
+        (b"link", link_header_value),
+    ]
+    response.writer.write_raw_header_frame(headers=early_hints,
+                                           end_headers=True)
+
+    response.status = 404
+    response.headers[b"content-type"] = "text/html"
+    response.write_status_headers()
+
+
+def main(request, response):
+    current_dir = os.path.dirname(os.path.realpath(__file__))
+    file_path = os.path.join(current_dir, "404-with-early-hints.html")
+    with open(file_path, "r") as f:
+        test_content = f.read()
+    response.writer.write_data(item=test_content, last=True)
diff --git a/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/404-with-early-hints.html b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/404-with-early-hints.html
new file mode 100644
index 0000000..2d351b0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/404-with-early-hints.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="early-hints-helpers.sub.js"></script>
+<body>
+<script>
+promise_test(async (t) => {
+    const params = new URLSearchParams(window.location.search);
+    const resource_url = params.get("resource-url");
+    await fetchImage(resource_url);
+    assert_true(isPreloadedByEarlyHints(resource_url));
+}, "404 with an early hints preload.");
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/early-hints-helpers.sub.js b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/early-hints-helpers.sub.js
index 9eb1db3..a3e0fd58 100644
--- a/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/early-hints-helpers.sub.js
+++ b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/early-hints-helpers.sub.js
@@ -45,18 +45,37 @@
 }
 
 /**
+ * Fetches a script or an image.
+ *
+ * @param {string} element - "script" or "img".
+ * @param {string} url - URL of the resource.
+ */
+async function fetchResource(element, url) {
+    return new Promise((resolve, reject) => {
+        const el = document.createElement(element);
+        el.src = url;
+        el.onload = resolve;
+        el.onerror = _ => reject(new Error("Failed to fetch resource: " + url));
+        document.body.appendChild(el);
+    });
+}
+
+/**
  * Fetches a script.
  *
  * @param {string} url
  */
 async function fetchScript(url) {
-    return new Promise((resolve, reject) => {
-        const el = document.createElement("script");
-        el.src = url;
-        el.onload = resolve;
-        el.onerror = _ => reject(new Error("Failed to fetch script"));
-        document.body.appendChild(el);
-    });
+    return fetchResource("script", url);
+}
+
+/**
+ * Fetches an image.
+ *
+ * @param {string} url
+ */
+ async function fetchImage(url) {
+    return fetchResource("img", url);
 }
 
 /**
diff --git a/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/square.png b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/square.png
new file mode 100644
index 0000000..01c9666a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/square.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/square.png.headers b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/square.png.headers
new file mode 100644
index 0000000..175cdf8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/loading/early-hints/resources/square.png.headers
@@ -0,0 +1 @@
+cache-control: max-age=600
diff --git a/third_party/webrtc_overrides/field_trial.cc b/third_party/webrtc_overrides/field_trial.cc
index 21883da..51f1c058 100644
--- a/third_party/webrtc_overrides/field_trial.cc
+++ b/third_party/webrtc_overrides/field_trial.cc
@@ -16,9 +16,5 @@
       base::StringPiece(trial_name.data(), trial_name.length()));
 }
 
-std::string FindFullName(const std::string& trial_name) {
-  return base::FieldTrialList::FindFullName(trial_name);
-}
-
 }  // namespace field_trial
 }  // namespace webrtc
diff --git a/tools/code_coverage/create_js_source_maps/create_js_source_maps.gni b/tools/code_coverage/create_js_source_maps/create_js_source_maps.gni
index 8beaac3..2812c42 100644
--- a/tools/code_coverage/create_js_source_maps/create_js_source_maps.gni
+++ b/tools/code_coverage/create_js_source_maps/create_js_source_maps.gni
@@ -24,7 +24,8 @@
         }
       }
     }
-    outputs = [ "$target_gen_dir/{{source}}.map" ]
+    rebased = rebase_path("{{source}}.map", root_out_dir, "/")
+    outputs = [ "$root_out_dir/$rebased" ]
     args = [ "{{source}}" ]
   }
 }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d959489..258bfea 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7630,6 +7630,11 @@
   <int value="5" label="User cancelled the bubble"/>
 </enum>
 
+<enum name="AutofillVirtualCardEnrollmentStrikeDatabaseEvent">
+  <int value="0" label="One strike logged"/>
+  <int value="1" label="All strikes cleared"/>
+</enum>
+
 <enum name="AutofillVirtualCardManualFallbackBubbleFieldClicked">
   <int value="0" label="Card number"/>
   <int value="1" label="Expiration month"/>
@@ -37587,6 +37592,8 @@
   <int value="4184" label="ComputePressureObserver_Unobserve"/>
   <int value="4185" label="ComputePressureObserver_Disconnect"/>
   <int value="4186" label="ComputePressureObserver_TakeRecords"/>
+  <int value="4187" label="PrivacySandboxAdsAPIs"/>
+  <int value="4188" label="Fledge"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -44783,6 +44790,8 @@
   <int value="9" label="Fault bit set during boot"/>
   <int value="10" label="MCU flash update failed"/>
   <int value="11" label="SPI flash update failed"/>
+  <int value="12" label="MCU flash update sent but requested again on reboot"/>
+  <int value="13" label="SPI flash update sent but requested again on reboot"/>
 </enum>
 
 <enum name="HrefTranslatePrefsFilterStatus">
@@ -51848,6 +51857,7 @@
   <int value="-2020024440" label="scroll-end-effect"/>
   <int value="-2018579114" label="ImprovedKeyboardShortcuts:enabled"/>
   <int value="-2017953534" label="enable-hosted-app-shim-creation"/>
+  <int value="-2017821808" label="PrivacySandboxAdsAPIsOverride:disabled"/>
   <int value="-2017778637" label="PrintSaveToDrive:disabled"/>
   <int value="-2015293660" label="AccessibilityExposeDisplayNone:disabled"/>
   <int value="-2014948560" label="TabGroupsAutoCreate:enabled"/>
@@ -55388,6 +55398,7 @@
   <int value="399177140" label="FillingPasswordsFromAnyOrigin:enabled"/>
   <int value="399398207"
       label="OmniboxUIExperimentVerticalMarginLimitToNonTouchOnly:enabled"/>
+  <int value="400239639" label="PrivacySandboxAdsAPIsOverride:enabled"/>
   <int value="400272381" label="LazyFrameLoading:disabled"/>
   <int value="400322063" label="ash-disable-screen-orientation-lock"/>
   <int value="401606992" label="AnimatedImageResume:enabled"/>
@@ -81931,6 +81942,10 @@
   <int value="9" label="Dialog Off, Managed, Enabled"/>
   <int value="10" label="Dialog Off, Managed, Disabled"/>
   <int value="11" label="Dialog Off, User is restricted"/>
+  <int value="12" label="Dialog Off, Manually Controlled, Enabled"/>
+  <int value="13" label="Dialog Off, Manually Controlled, Disabled"/>
+  <int value="14" label="Dialog not required, Enabled"/>
+  <int value="15" label="Dialog not required, Disabled"/>
 </enum>
 
 <enum name="SettingsResetPromptConfigError">
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 53ac9c9..59837ed 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -3610,6 +3610,21 @@
       variants="Autofill.VirtualCardEnrollmentSource"/>
 </histogram>
 
+<histogram
+    name="Autofill.VirtualCardEnrollmentStrikeDatabase.{EnrollmentSource}"
+    enum="AutofillVirtualCardEnrollmentStrikeDatabaseEvent"
+    expires_after="2023-04-01">
+  <owner>alexandertekle@google.com</owner>
+  <owner>payments-autofill-team@google.com</owner>
+  <summary>
+    Logged when a strike is registered in the virtual card enrollment strike
+    database because a user rejects the bubble, or when strikes are cleared
+    because the user accepted enrollment.
+  </summary>
+  <token key="EnrollmentSource"
+      variants="Autofill.VirtualCardEnrollmentSource"/>
+</histogram>
+
 <histogram name="Autofill.VirtualCardManualFallbackBubble.FieldClicked"
     enum="AutofillVirtualCardManualFallbackBubbleFieldClicked"
     expires_after="2023-04-01">
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 24e10b80..335bdce 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -13,8 +13,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/linux_arm/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell"
         },
         "mac": {
-            "hash": "815295f9d6329edb5a5a0b64c04637417b2d8dd5",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/8c8fafb6ff4b4a715b868915840fc95040290ef7/trace_processor_shell"
+            "hash": "647302801554811912ab7fedb81e5455625a9303",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/d89b25832b668dcf2d55bccbfb57f9a24723b869/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "c0397e87456ad6c6a7aa0133e5b81c97adbab4ab",
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
index 6a31e56..8b525dcd 100644
--- a/ui/gfx/canvas.cc
+++ b/ui/gfx/canvas.cc
@@ -472,7 +472,7 @@
 }
 
 void Canvas::Transform(const gfx::Transform& transform) {
-  canvas_->concat(SkMatrix(transform.matrix()));
+  canvas_->concat(transform.matrix().asM33());
 }
 
 SkBitmap Canvas::GetBitmap() const {
diff --git a/ui/gfx/geometry/matrix44.cc b/ui/gfx/geometry/matrix44.cc
index 4afa537..ced4c6d 100644
--- a/ui/gfx/geometry/matrix44.cc
+++ b/ui/gfx/geometry/matrix44.cc
@@ -1054,41 +1054,32 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static void initFromMatrix(SkScalar dst[4][4], const SkMatrix& src) {
-  dst[0][0] = src[SkMatrix::kMScaleX];
-  dst[1][0] = src[SkMatrix::kMSkewX];
-  dst[2][0] = 0;
-  dst[3][0] = src[SkMatrix::kMTransX];
-  dst[0][1] = src[SkMatrix::kMSkewY];
-  dst[1][1] = src[SkMatrix::kMScaleY];
-  dst[2][1] = 0;
-  dst[3][1] = src[SkMatrix::kMTransY];
-  dst[0][2] = 0;
-  dst[1][2] = 0;
-  dst[2][2] = 1;
-  dst[3][2] = 0;
-  dst[0][3] = src[SkMatrix::kMPersp0];
-  dst[1][3] = src[SkMatrix::kMPersp1];
-  dst[2][3] = 0;
-  dst[3][3] = src[SkMatrix::kMPersp2];
-}
-
 Matrix44::Matrix44(const SkMatrix& src) {
-  this->operator=(src);
-}
-
-Matrix44& Matrix44::operator=(const SkMatrix& src) {
-  initFromMatrix(fMat, src);
+  fMat[0][0] = src[SkMatrix::kMScaleX];
+  fMat[1][0] = src[SkMatrix::kMSkewX];
+  fMat[2][0] = 0;
+  fMat[3][0] = src[SkMatrix::kMTransX];
+  fMat[0][1] = src[SkMatrix::kMSkewY];
+  fMat[1][1] = src[SkMatrix::kMScaleY];
+  fMat[2][1] = 0;
+  fMat[3][1] = src[SkMatrix::kMTransY];
+  fMat[0][2] = 0;
+  fMat[1][2] = 0;
+  fMat[2][2] = 1;
+  fMat[3][2] = 0;
+  fMat[0][3] = src[SkMatrix::kMPersp0];
+  fMat[1][3] = src[SkMatrix::kMPersp1];
+  fMat[2][3] = 0;
+  fMat[3][3] = src[SkMatrix::kMPersp2];
 
   if (src.isIdentity()) {
     this->setTypeMask(kIdentity_Mask);
   } else {
     this->recomputeTypeMask();
   }
-  return *this;
 }
 
-Matrix44::operator SkMatrix() const {
+SkMatrix Matrix44::asM33() const {
   SkMatrix dst;
 
   dst[SkMatrix::kMScaleX] = fMat[0][0];
diff --git a/ui/gfx/geometry/matrix44.h b/ui/gfx/geometry/matrix44.h
index 7cf337d..df8864be 100644
--- a/ui/gfx/geometry/matrix44.h
+++ b/ui/gfx/geometry/matrix44.h
@@ -49,6 +49,13 @@
 
 // This is the underlying data structure of Transform. Don't use this type
 // directly. The public methods can be called through Transform::matrix().
+//
+// This class was originally SkMatrix44, then moved into Chromium as
+// skia::Matrix44, then moved here. For now this class mostly follows the
+// Skia coding style, especially the naming convention. This is to make the
+// API of this class similar to SkM44 to ease experiment with different
+// underlying matrix data structure of Transform.
+//
 class GEOMETRY_SKIA_EXPORT Matrix44 {
  public:
   enum Uninitialized_Constructor { kUninitialized_Constructor };
@@ -111,15 +118,10 @@
    * [ g h i ]      [ 0 0 1 0 ]
    *                [ g h 0 i ]
    */
-  explicit Matrix44(const SkMatrix&);
-  Matrix44& operator=(const SkMatrix& src);
+  explicit Matrix44(const SkMatrix& sk_matrix);
 
-  // TODO: make this explicit (will need to guard that change to update chrome,
-  // etc.
-#ifndef SK_SUPPORT_LEGACY_IMPLICIT_CONVERSION_MATRIX44
-  explicit
-#endif
-  operator SkMatrix() const;
+  // Inverse conversion of the above.
+  SkMatrix asM33() const;
 
   /**
    *  Return a reference to a const identity matrix
diff --git a/ui/gfx/geometry/transform.cc b/ui/gfx/geometry/transform.cc
index 52f76fd..e132843 100644
--- a/ui/gfx/geometry/transform.cc
+++ b/ui/gfx/geometry/transform.cc
@@ -477,7 +477,7 @@
     return;
 
   SkRect src = RectFToSkRect(*rect);
-  SkMatrix(matrix_).mapRect(&src);
+  matrix_.asM33().mapRect(&src);
   *rect = SkRectToRectF(src);
 }
 
@@ -490,7 +490,7 @@
     return false;
 
   SkRect src = RectFToSkRect(*rect);
-  SkMatrix(inverse).mapRect(&src);
+  inverse.asM33().mapRect(&src);
   *rect = SkRectToRectF(src);
   return true;
 }
@@ -501,7 +501,7 @@
   // SkMatrix::preservesAxisAlignment is stricter (it lacks the kEpsilon
   // test).  So after converting our Matrix44 to SkMatrix, round
   // relevant values less than kEpsilon to zero.
-  SkMatrix rounded_matrix(matrix_);
+  SkMatrix rounded_matrix = matrix_.asM33();
   if (std::abs(rounded_matrix.get(SkMatrix::kMScaleX)) < kEpsilon)
     rounded_matrix.set(SkMatrix::kMScaleX, 0.0f);
   if (std::abs(rounded_matrix.get(SkMatrix::kMSkewX)) < kEpsilon)
diff --git a/ui/ozone/platform/wayland/common/wayland_util.cc b/ui/ozone/platform/wayland/common/wayland_util.cc
index 8823a54..d9ca7d33 100644
--- a/ui/ozone/platform/wayland/common/wayland_util.cc
+++ b/ui/ozone/platform/wayland/common/wayland_util.cc
@@ -301,7 +301,7 @@
   gfx::Transform transform;
   transform.Scale(sk_scale, sk_scale);
   SkPath path_in_dips;
-  path_in_pixels.transform(SkMatrix(transform.matrix()), &path_in_dips);
+  path_in_pixels.transform(transform.matrix().asM33(), &path_in_dips);
   return path_in_dips;
 }
 
diff --git a/ui/ozone/platform/x11/x11_window.cc b/ui/ozone/platform/x11/x11_window.cc
index 430fe26..8bc7bd90d5 100644
--- a/ui/ozone/platform/x11/x11_window.cc
+++ b/ui/ozone/platform/x11/x11_window.cc
@@ -924,7 +924,7 @@
       SkPath path_in_dip;
       if (native_region.getBoundaryPath(&path_in_dip)) {
         SkPath path_in_pixels;
-        path_in_dip.transform(SkMatrix(transform.matrix()), &path_in_pixels);
+        path_in_dip.transform(transform.matrix().asM33(), &path_in_pixels);
         xregion = x11::CreateRegionFromSkPath(path_in_pixels);
       } else {
         xregion = std::make_unique<std::vector<x11::Rectangle>>();
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index eaa5dfe..67cd135 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -5,11 +5,14 @@
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 
 #include <algorithm>
+#include <set>
 #include <utility>
 #include <vector>
 
 #include "base/bind.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -383,10 +386,49 @@
   base::ScopedObservation<View, ViewObserver> observation_{this};
 };
 
+class BubbleDialogDelegateView::CloseOnDeactivatePin::Pins {
+ public:
+  Pins() = default;
+  ~Pins() = default;
+
+  bool is_pinned() const { return !pins_.empty(); }
+
+  base::WeakPtr<Pins> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
+
+  void AddPin(CloseOnDeactivatePin* pin) {
+    const auto result = pins_.insert(pin);
+    DCHECK(result.second);
+  }
+
+  void RemovePin(CloseOnDeactivatePin* pin) {
+    const auto result = pins_.erase(pin);
+    DCHECK(result);
+  }
+
+ protected:
+  std::set<CloseOnDeactivatePin*> pins_;
+  base::WeakPtrFactory<Pins> weak_ptr_factory_{this};
+};
+
+BubbleDialogDelegate::CloseOnDeactivatePin::CloseOnDeactivatePin(
+    base::WeakPtr<Pins> pins)
+    : pins_(pins) {
+  pins_->AddPin(this);
+}
+
+BubbleDialogDelegate::CloseOnDeactivatePin::~CloseOnDeactivatePin() {
+  Pins* const pins = pins_.get();
+  if (pins)
+    pins->RemovePin(this);
+}
+
 BubbleDialogDelegate::BubbleDialogDelegate(View* anchor_view,
                                            BubbleBorder::Arrow arrow,
                                            BubbleBorder::Shadow shadow)
-    : arrow_(arrow), shadow_(shadow) {
+    : arrow_(arrow),
+      shadow_(shadow),
+      close_on_deactivate_pins_(
+          std::make_unique<CloseOnDeactivatePin::Pins>()) {
   SetOwnedByWidget(true);
   SetAnchorView(anchor_view);
   SetArrow(arrow);
@@ -578,6 +620,16 @@
   return anchor_view_observer_->anchor_view();
 }
 
+bool BubbleDialogDelegate::ShouldCloseOnDeactivate() const {
+  return close_on_deactivate_ && !close_on_deactivate_pins_->is_pinned();
+}
+
+std::unique_ptr<BubbleDialogDelegate::CloseOnDeactivatePin>
+BubbleDialogDelegate::PreventCloseOnDeactivate() {
+  return base::WrapUnique(
+      new CloseOnDeactivatePin(close_on_deactivate_pins_->GetWeakPtr()));
+}
+
 void BubbleDialogDelegate::SetHighlightedButton(Button* highlighted_button) {
   bool visible = GetWidget() && GetWidget()->IsVisible();
   // If the Widget is visible, ensure the old highlight (if any) is removed
@@ -857,7 +909,7 @@
 }
 
 void BubbleDialogDelegate::OnDeactivate() {
-  if (close_on_deactivate_ && GetWidget())
+  if (ShouldCloseOnDeactivate() && GetWidget())
     GetWidget()->CloseWithReason(views::Widget::ClosedReason::kLostFocus);
 }
 
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index 954927f..393c58d 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -10,6 +10,7 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "build/build_config.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
 #include "ui/base/class_property.h"
@@ -144,10 +145,35 @@
   // Miscellaneous bubble behaviors:
   //
 
+  // Represents a pin that prevents a widget from closing on deactivation, even
+  // if `close_on_deactivate` is set to true. Prevents closing on deactivation
+  // until its destruction; if it outlives the widget it does nothing.
+  class VIEWS_EXPORT CloseOnDeactivatePin {
+   public:
+    virtual ~CloseOnDeactivatePin();
+
+    CloseOnDeactivatePin(const CloseOnDeactivatePin&) = delete;
+    void operator=(const CloseOnDeactivatePin&) = delete;
+
+   private:
+    class Pins;
+    friend class BubbleDialogDelegate;
+    explicit CloseOnDeactivatePin(base::WeakPtr<Pins> pins);
+
+    const base::WeakPtr<Pins> pins_;
+  };
+
   // Whether the bubble closes when it ceases to be the active window.
-  bool close_on_deactivate() const { return close_on_deactivate_; }
   void set_close_on_deactivate(bool close) { close_on_deactivate_ = close; }
 
+  // Returns whether the bubble should close on deactivation. May not match
+  // `close_on_deactivate` if PreventCloseOnDeactivate() has been called.
+  bool ShouldCloseOnDeactivate() const;
+
+  // Prevents close-on-deactivate for the duration of the lifetime of the pin
+  // that is returned. The pin does nothing after the widget is closed.
+  std::unique_ptr<CloseOnDeactivatePin> PreventCloseOnDeactivate();
+
   // Explicitly set the button to automatically highlight when the bubble is
   // shown. By default the anchor is highlighted, if it is a button.
   //
@@ -360,6 +386,7 @@
 
   // A flag controlling bubble closure on deactivation.
   bool close_on_deactivate_ = true;
+  std::unique_ptr<CloseOnDeactivatePin::Pins> close_on_deactivate_pins_;
 
   // Whether the |anchor_widget_| (or the |highlighted_button_tracker_|, when
   // provided) should be highlighted when this bubble is shown.
diff --git a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
index 8ce56ef..d8e01cd 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -521,6 +521,79 @@
   }
 }
 
+TEST_F(BubbleDialogDelegateViewTest, PinBlocksCloseOnDeactivate) {
+  std::unique_ptr<Widget> anchor_widget =
+      CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
+  BubbleDialogDelegateView* bubble_delegate =
+      new TestBubbleDialogDelegateView(anchor_widget->GetContentsView());
+  bubble_delegate->set_close_on_deactivate(true);
+  Widget* bubble_widget =
+      BubbleDialogDelegateView::CreateBubble(bubble_delegate);
+
+  // Pin the bubble so it does not go away on loss of focus.
+  auto pin = bubble_delegate->PreventCloseOnDeactivate();
+  bubble_widget->Show();
+  anchor_widget->Activate();
+  EXPECT_FALSE(bubble_widget->IsClosed());
+
+  // Unpin the window. The next time the bubble loses activation, it should
+  // close as expected.
+  pin.reset();
+  bubble_widget->Activate();
+  EXPECT_FALSE(bubble_widget->IsClosed());
+  anchor_widget->Activate();
+  EXPECT_TRUE(bubble_widget->IsClosed());
+}
+
+TEST_F(BubbleDialogDelegateViewTest, CloseOnDeactivatePinCanOutliveBubble) {
+  std::unique_ptr<Widget> anchor_widget =
+      CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
+  BubbleDialogDelegateView* bubble_delegate =
+      new TestBubbleDialogDelegateView(anchor_widget->GetContentsView());
+  bubble_delegate->set_close_on_deactivate(true);
+  Widget* bubble_widget =
+      BubbleDialogDelegateView::CreateBubble(bubble_delegate);
+
+  // Pin the bubble so it does not go away on loss of focus.
+  auto pin = bubble_delegate->PreventCloseOnDeactivate();
+  bubble_widget->Show();
+  anchor_widget->Activate();
+  EXPECT_FALSE(bubble_widget->IsClosed());
+  bubble_widget->CloseNow();
+  pin.reset();
+}
+
+TEST_F(BubbleDialogDelegateViewTest, MultipleCloseOnDeactivatePins) {
+  std::unique_ptr<Widget> anchor_widget =
+      CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
+  BubbleDialogDelegateView* bubble_delegate =
+      new TestBubbleDialogDelegateView(anchor_widget->GetContentsView());
+  bubble_delegate->set_close_on_deactivate(true);
+  Widget* bubble_widget =
+      BubbleDialogDelegateView::CreateBubble(bubble_delegate);
+
+  // Pin the bubble so it does not go away on loss of focus.
+  auto pin = bubble_delegate->PreventCloseOnDeactivate();
+  auto pin2 = bubble_delegate->PreventCloseOnDeactivate();
+  bubble_widget->Show();
+  anchor_widget->Activate();
+
+  // Unpinning one pin does not reset the state; both must be unpinned.
+  pin.reset();
+  bubble_widget->Activate();
+  EXPECT_FALSE(bubble_widget->IsClosed());
+  anchor_widget->Activate();
+  EXPECT_FALSE(bubble_widget->IsClosed());
+
+  // Fully unpin the window. The next time the bubble loses activation,
+  // it should close as expected.
+  pin2.reset();
+  bubble_widget->Activate();
+  EXPECT_FALSE(bubble_widget->IsClosed());
+  anchor_widget->Activate();
+  EXPECT_TRUE(bubble_widget->IsClosed());
+}
+
 TEST_F(BubbleDialogDelegateViewTest, CustomTitle) {
   std::unique_ptr<Widget> anchor_widget =
       CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
diff --git a/ui/views/cocoa/drag_drop_client_mac_unittest.mm b/ui/views/cocoa/drag_drop_client_mac_unittest.mm
index 01b1c29..c2007da6 100644
--- a/ui/views/cocoa/drag_drop_client_mac_unittest.mm
+++ b/ui/views/cocoa/drag_drop_client_mac_unittest.mm
@@ -145,10 +145,6 @@
     return ui::DragDropTypes::DRAG_COPY;
   }
 
-  DragOperation OnPerformDrop(const ui::DropTargetEvent& event) override {
-    return DragOperation::kMove;
-  }
-
   views::View::DropCallback GetDropCallback(
       const ui::DropTargetEvent& event) override {
     return base::BindOnce([](const ui::DropTargetEvent& event,
@@ -323,12 +319,6 @@
   DragDropCloseView(const DragDropCloseView&) = delete;
   DragDropCloseView& operator=(const DragDropCloseView&) = delete;
 
-  // View:
-  DragOperation OnPerformDrop(const ui::DropTargetEvent& event) override {
-    ui::mojom::DragOperation output_drag_op = ui::mojom::DragOperation::kNone;
-    PerformDrop(event, output_drag_op);
-    return output_drag_op;
-  }
   views::View::DropCallback GetDropCallback(
       const ui::DropTargetEvent& event) override {
     // base::Unretained is safe here because in the tests the view isn't deleted
@@ -345,7 +335,7 @@
   }
 };
 
-// Tests that closing Widget on OnPerformDrop does not crash.
+// Tests that closing Widget on drop does not crash.
 TEST_F(DragDropClientMacTest, CloseWidgetOnDrop) {
   OSExchangeData data;
   const std::u16string& text = u"text";
@@ -360,7 +350,7 @@
   EXPECT_EQ(DragUpdate(nil), NSDragOperationCopy);
   EXPECT_EQ(Drop(), NSDragOperationMove);
 
-  // OnPerformDrop() will have deleted the widget.
+  // Drop callback will have deleted the widget.
   widget_ = nullptr;
 }
 
diff --git a/ui/views/focus/focus_manager.cc b/ui/views/focus/focus_manager.cc
index 7b1508a..ed6ed43b 100644
--- a/ui/views/focus/focus_manager.cc
+++ b/ui/views/focus/focus_manager.cc
@@ -642,7 +642,7 @@
   // variable.
   base::WeakPtr<Widget> widget_weak_ptr = widget_->GetWeakPtr();
   const bool close_widget_on_deactivate =
-      widget_delegate->close_on_deactivate();
+      widget_delegate->ShouldCloseOnDeactivate();
 #endif
 
   // The parent view must be focused for it to process events.
diff --git a/ui/views/view.cc b/ui/views/view.cc
index a77ad92b..c9e0779 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -1167,8 +1167,7 @@
           SkFloatToScalar(paint_info.paint_recording_scale_x()),
           SkFloatToScalar(paint_info.paint_recording_scale_y()));
 
-      clip_path_in_parent.transform(
-          SkMatrix(to_parent_recording_space.matrix()));
+      clip_path_in_parent.transform(to_parent_recording_space.matrix().asM33());
       clip_recorder.ClipPathWithAntiAliasing(clip_path_in_parent);
     }
   }
@@ -1826,10 +1825,6 @@
 
 void View::OnDragExited() {}
 
-ui::mojom::DragOperation View::OnPerformDrop(const ui::DropTargetEvent& event) {
-  return ui::mojom::DragOperation::kNone;
-}
-
 void View::OnDragDone() {}
 
 View::DropCallback View::GetDropCallback(const ui::DropTargetEvent& event) {
diff --git a/ui/views/view.h b/ui/views/view.h
index f7ad6d1e..fe318cb9 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -1328,7 +1328,7 @@
   // OnDragEntered is sent to the view when the mouse first enters the view,
   // as the mouse moves around within the view OnDragUpdated is invoked.
   // If the user releases the mouse over the view and OnDragUpdated returns a
-  // valid drop, then OnPerformDrop is invoked. If the mouse moves outside the
+  // valid drop, then GetDropCallback is invoked. If the mouse moves outside the
   // view or over another view that wants the drag, OnDragExited is invoked.
   //
   // Similar to mouse events, the deepest view under the mouse is first checked
@@ -1354,7 +1354,7 @@
   // OnDragEntered is invoked when the mouse enters this view during a drag and
   // drop session and CanDrop returns true. This is immediately
   // followed by an invocation of OnDragUpdated, and eventually one of
-  // OnDragExited or OnPerformDrop.
+  // OnDragExited or GetDropCallback.
   virtual void OnDragEntered(const ui::DropTargetEvent& event);
 
   // Invoked during a drag and drop session while the mouse is over the view.
@@ -1367,13 +1367,6 @@
   // when the drag session was canceled and the mouse was over the view.
   virtual void OnDragExited();
 
-  // Invoked during a drag and drop session when OnDragUpdated returns a valid
-  // operation and the user release the mouse.
-  // TODO(crbug.com/1175682): Remove OnPerformDrop and switch to GetDropCallback
-  // instead.
-  virtual ui::mojom::DragOperation OnPerformDrop(
-      const ui::DropTargetEvent& event);
-
   // Invoked from DoDrag after the drag completes. This implementation does
   // nothing, and is intended for subclasses to do cleanup.
   virtual void OnDragDone();
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 9c2f483..f643d3f 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -2932,7 +2932,7 @@
     transform.Translate(1.0, 1.0);
 
     // convert to a 3x3 matrix.
-    const SkMatrix& matrix = SkMatrix(transform.matrix());
+    SkMatrix matrix = transform.matrix().asM33();
 
     EXPECT_EQ(210, matrix.getTranslateX());
     EXPECT_EQ(-55, matrix.getTranslateY());
@@ -2953,7 +2953,7 @@
     transform.ConcatTransform(t3);
 
     // convert to a 3x3 matrix
-    const SkMatrix& matrix = SkMatrix(transform.matrix());
+    SkMatrix matrix = transform.matrix().asM33();
 
     EXPECT_EQ(210, matrix.getTranslateX());
     EXPECT_EQ(-55, matrix.getTranslateY());
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index 1c9525d..4b0282059 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -820,7 +820,7 @@
   SkPath window_mask = GetWindowMask(GetWidget());
   // Convert SkPath in DIPs to pixels.
   if (!window_mask.isEmpty())
-    window_mask.transform(SkMatrix(GetRootTransform().matrix()));
+    window_mask.transform(GetRootTransform().matrix().asM33());
   return window_mask;
 }