diff --git a/DEPS b/DEPS
index 68dfcd1..73abc78 100644
--- a/DEPS
+++ b/DEPS
@@ -199,11 +199,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'd12c91ba318b6d6bc62411a754fdc33baca2e92e',
+  'skia_revision': 'caca7bfff996b38ee6bb6413eb5ca62b0c9a380f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '711cf5ad4a12abe0f6b50058cb2248d7fb80374e',
+  'v8_revision': 'c1afed6d5ce09f38a9eb7f39b34b4c087cd80503',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -211,7 +211,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': 'cde0e3ab3d710ca9838d226873462c5f093f60bd',
+  'angle_revision': '5e606e5bfce2b23c139c5289cbf1299844fa4b43',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -274,7 +274,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': 'b4a82aa3575f22267816d59a3af93f22ddb80bea',
+  'devtools_frontend_revision': '8793dbe78e44bc6eeceaed0cc8cf0e542d6efd4a',
   # 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.
@@ -314,7 +314,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '700809a7f75bab9df6d1195153ea1cbfb078046c',
+  'dawn_revision': 'c8d5277e86ee8c51b1dd7892b41e83eb26ac2ede',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -553,7 +553,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '4b7c446e7e14b43202421311894e388b18df6650',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '8bd8b295e227310a8c835ec9d3f58ca3b651319a',
       'condition': 'checkout_ios',
   },
 
@@ -873,7 +873,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a281380c8e264cad58769385a9fc5fcff99be110',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7a1af10b707bf9939fbe63abf0f2b305ae55dc4c',
       'condition': 'checkout_chromeos',
   },
 
@@ -893,7 +893,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '67e12286df04ef0eeec3aeaf6f224d2fe11751b7',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '46ef281c525f704bb9445142e9e4d70dacae7d4d',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1514,7 +1514,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '3c2fe3888658d82b47ca831d59a2e07579619c2d',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'fec83cc8602580ce1c078f7cd4e7f4d2be4ac850',
+    Var('webrtc_git') + '/src.git' + '@' + 'a45df0b3491ea72cfcf40e21106eb046c8594223',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1586,7 +1586,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a437db8d0e0716d8b3c34bb11c9e50eea530a7b6',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3a0cc86fe501ebb245b7d6191903579a2a7c40fe',
     'condition': 'checkout_src_internal',
   },
 
@@ -1594,7 +1594,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'MIvUdF0qcP07nO7qcpskiwhrIY76sEXm3cja55KTAs4C',
+        'version': 'qJhtBw7ouc--Fdht1yXy9-ZDPYguiqAZbixcf8MasiwC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1605,7 +1605,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'lSR2dtMxiibk0G8cpsbKHgxUhPJId7kspY1yn5iOeZEC',
+        'version': 'RYs0dVeLJjwsPik_WXMr4pMuzN55DAmi4gfQCMX-evUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/browser/tracing/aw_tracing_delegate.cc b/android_webview/browser/tracing/aw_tracing_delegate.cc
index 652c4960..3084412 100644
--- a/android_webview/browser/tracing/aw_tracing_delegate.cc
+++ b/android_webview/browser/tracing/aw_tracing_delegate.cc
@@ -33,7 +33,8 @@
 
 bool AwTracingDelegate::IsAllowedToEndBackgroundScenario(
     const content::BackgroundTracingConfig& config,
-    bool requires_anonymized_data) {
+    bool requires_anonymized_data,
+    bool is_crash_scenario) {
   // Background tracing is allowed in general and can be restricted when
   // configuring BackgroundTracingManager.
   return true;
diff --git a/android_webview/browser/tracing/aw_tracing_delegate.h b/android_webview/browser/tracing/aw_tracing_delegate.h
index 74d118b..fd76f6b4 100644
--- a/android_webview/browser/tracing/aw_tracing_delegate.h
+++ b/android_webview/browser/tracing/aw_tracing_delegate.h
@@ -28,7 +28,8 @@
       bool requires_anonymized_data) override;
   bool IsAllowedToEndBackgroundScenario(
       const content::BackgroundTracingConfig& config,
-      bool requires_anonymized_data) override;
+      bool requires_anonymized_data,
+      bool is_crash_scenario) override;
   std::unique_ptr<base::DictionaryValue> GenerateMetadataDict() override;
 };
 
diff --git a/ash/system/holding_space/holding_space_item_views_section.cc b/ash/system/holding_space/holding_space_item_views_section.cc
index e5fc23d3..6e7cbcf 100644
--- a/ash/system/holding_space/holding_space_item_views_section.cc
+++ b/ash/system/holding_space/holding_space_item_views_section.cc
@@ -26,10 +26,6 @@
     base::TimeDelta::FromMilliseconds(167);
 constexpr SkScalar kAnimationTranslationY = 20;
 
-// Value returned during notification of animation completion events in order to
-// delete the observer which provided notification.
-constexpr bool kDeleteObserver = true;
-
 // Helpers ---------------------------------------------------------------------
 
 // Initializes the layer for the specified `view` for animations.
@@ -105,6 +101,24 @@
               observer);
 }
 
+// Returns a callback which deletes the associated animation observer after
+// running another `callback`.
+using AnimationCompletedCallback =
+    base::OnceCallback<void(const ui::CallbackLayerAnimationObserver&)>;
+base::RepeatingCallback<bool(const ui::CallbackLayerAnimationObserver&)>
+DeleteObserverAfterRunning(AnimationCompletedCallback callback) {
+  return base::BindRepeating(
+      [](AnimationCompletedCallback callback,
+         const ui::CallbackLayerAnimationObserver& observer) {
+        // NOTE: It's safe to move `callback` since this code will only run
+        // once due to deletion of the associated `observer`. The `observer` is
+        // deleted by returning `true`.
+        std::move(callback).Run(observer);
+        return true;
+      },
+      base::Passed(std::move(callback)));
+}
+
 // HoldingSpaceScrollView ------------------------------------------------------
 
 class HoldingSpaceScrollView : public views::ScrollView,
@@ -340,9 +354,9 @@
 
   // NOTE: `animate_in_observer` is deleted after `OnAnimateInCompleted()`.
   ui::CallbackLayerAnimationObserver* animate_in_observer =
-      new ui::CallbackLayerAnimationObserver(base::BindRepeating(
-          &HoldingSpaceItemViewsSection::OnAnimateInCompleted,
-          base::Unretained(this)));
+      new ui::CallbackLayerAnimationObserver(DeleteObserverAfterRunning(
+          base::BindOnce(&HoldingSpaceItemViewsSection::OnAnimateInCompleted,
+                         weak_factory_.GetWeakPtr())));
 
   AnimateIn(animate_in_observer);
   animate_in_observer->SetActive();
@@ -361,9 +375,9 @@
 
   // NOTE: `animate_out_observer` is deleted after `OnAnimateOutCompleted()`.
   ui::CallbackLayerAnimationObserver* animate_out_observer =
-      new ui::CallbackLayerAnimationObserver(base::BindRepeating(
-          &HoldingSpaceItemViewsSection::OnAnimateOutCompleted,
-          base::Unretained(this)));
+      new ui::CallbackLayerAnimationObserver(DeleteObserverAfterRunning(
+          base::BindOnce(&HoldingSpaceItemViewsSection::OnAnimateOutCompleted,
+                         weak_factory_.GetWeakPtr())));
 
   AnimateOut(animate_out_observer);
   animate_out_observer->SetActive();
@@ -394,13 +408,13 @@
   DoAnimateOut(container_, animation_duration, observer);
 }
 
-bool HoldingSpaceItemViewsSection::OnAnimateInCompleted(
+void HoldingSpaceItemViewsSection::OnAnimateInCompleted(
     const ui::CallbackLayerAnimationObserver& observer) {
   DCHECK(animation_state_ & AnimationState::kAnimatingIn);
   animation_state_ &= ~AnimationState::kAnimatingIn;
 
   if (observer.aborted_count())
-    return kDeleteObserver;
+    return;
 
   DCHECK_EQ(animation_state_, AnimationState::kNotAnimating);
 
@@ -408,17 +422,15 @@
   // that have been animated in should all be associated with holding space
   // items that exist in the model.
   SetCanProcessEventsWithinSubtree(true);
-
-  return kDeleteObserver;
 }
 
-bool HoldingSpaceItemViewsSection::OnAnimateOutCompleted(
+void HoldingSpaceItemViewsSection::OnAnimateOutCompleted(
     const ui::CallbackLayerAnimationObserver& observer) {
   DCHECK(animation_state_ & AnimationState::kAnimatingOut);
   animation_state_ &= ~AnimationState::kAnimatingOut;
 
   if (observer.aborted_count())
-    return kDeleteObserver;
+    return;
 
   DCHECK_EQ(animation_state_, AnimationState::kNotAnimating);
 
@@ -437,7 +449,7 @@
 
   HoldingSpaceModel* model = HoldingSpaceController::Get()->model();
   if (!model)
-    return kDeleteObserver;
+    return;
 
   for (const auto& item : model->items()) {
     if (item->IsFinalized() && base::Contains(supported_types_, item->type())) {
@@ -457,8 +469,6 @@
   }
 
   MaybeAnimateIn();
-
-  return kDeleteObserver;
 }
 
 }  // namespace ash
diff --git a/ash/system/holding_space/holding_space_item_views_section.h b/ash/system/holding_space/holding_space_item_views_section.h
index 9b7d430..bfe6c20 100644
--- a/ash/system/holding_space/holding_space_item_views_section.h
+++ b/ash/system/holding_space/holding_space_item_views_section.h
@@ -115,10 +115,9 @@
   void AnimateOut(ui::LayerAnimationObserver* observer);
 
   // Invoked when an animate in/out of the contents of this section has been
-  // completed. These methods always return true to delete the observer which
-  // notified the event.
-  bool OnAnimateInCompleted(const ui::CallbackLayerAnimationObserver&);
-  bool OnAnimateOutCompleted(const ui::CallbackLayerAnimationObserver&);
+  // completed. Note that the provided observer will be deleted after returning.
+  void OnAnimateInCompleted(const ui::CallbackLayerAnimationObserver&);
+  void OnAnimateOutCompleted(const ui::CallbackLayerAnimationObserver&);
 
   HoldingSpaceItemViewDelegate* const delegate_;
   const std::vector<HoldingSpaceItem::Type> supported_types_;
@@ -142,8 +141,11 @@
   base::ScopedObservation<HoldingSpaceController,
                           HoldingSpaceControllerObserver>
       controller_observer_{this};
+
   base::ScopedObservation<HoldingSpaceModel, HoldingSpaceModelObserver>
       model_observer_{this};
+
+  base::WeakPtrFactory<HoldingSpaceItemViewsSection> weak_factory_{this};
 };
 
 }  // namespace ash
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 0d41908..6966cd4 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20201223.3.1
+0.20201225.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 0d41908..6966cd4 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20201223.3.1
+0.20201225.3.1
diff --git a/chrome/VERSION b/chrome/VERSION
index f40dde32..15fb5e2b 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=89
 MINOR=0
-BUILD=4367
+BUILD=4369
 PATCH=0
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutPerfTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutPerfTest.java
index b056cff..83a3c141 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutPerfTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutPerfTest.java
@@ -12,6 +12,7 @@
 import static org.chromium.base.test.util.CriteriaHelper.DEFAULT_POLLING_INTERVAL;
 import static org.chromium.components.embedder_support.util.UrlConstants.NTP_URL;
 
+import android.os.Build.VERSION_CODES;
 import android.support.test.InstrumentationRegistry;
 
 import androidx.annotation.Nullable;
@@ -30,6 +31,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.EnormousTest;
 import org.chromium.base.test.util.FlakyTest;
@@ -133,7 +135,10 @@
     @Test
     @EnormousTest
     @CommandLineFlags.Add({BASE_PARAMS + "/soft-cleanup-delay/10000/cleanup-delay/10000"})
-    public void testTabToGridFromLiveTabWith10TabsWarm() throws InterruptedException {
+    @DisableIf.Build(message = "Flaky on Android P, see https://crbug.com/1161731",
+            sdk_is_greater_than = VERSION_CODES.O_MR1, sdk_is_less_than = VERSION_CODES.Q)
+    public void
+    testTabToGridFromLiveTabWith10TabsWarm() throws InterruptedException {
         prepareTabs(10, NTP_URL);
         reportTabToGridPerf(mUrl, "Tab-to-Grid from live tab with 10 tabs (warm)");
     }
@@ -339,7 +344,10 @@
     @Test
     @EnormousTest
     @CommandLineFlags.Add({BASE_PARAMS})
-    public void testGridToTabToOtherFrozen() throws InterruptedException {
+    @DisableIf.Build(message = "Flaky on Android P, see https://crbug.com/1161731",
+            sdk_is_greater_than = VERSION_CODES.O_MR1, sdk_is_less_than = VERSION_CODES.Q)
+    public void
+    testGridToTabToOtherFrozen() throws InterruptedException {
         prepareTabs(2, mUrl);
         reportGridToTabPerf(true, true, "Grid-to-Tab to other frozen tab");
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
index 4a25aca..7d9d6df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
@@ -797,12 +797,13 @@
     // SignOutDialogListener implementation:
     @Override
     public void onSignOutClicked(boolean forceWipeUserData) {
+        final Profile profile = Profile.getLastUsedRegularProfile();
         // In case sign-out happened while the dialog was displayed, we guard the sign out so
         // we do not hit a native crash.
-        if (!IdentityServicesProvider.get().getIdentityManager().hasPrimaryAccount()) return;
+        if (!IdentityServicesProvider.get().getIdentityManager(profile).hasPrimaryAccount()) return;
 
         final DialogFragment clearDataProgressDialog = new ClearDataProgressDialog();
-        IdentityServicesProvider.get().getSigninManager().signOut(
+        IdentityServicesProvider.get().getSigninManager(profile).signOut(
                 SignoutReason.USER_CLICKED_SIGNOUT_SETTINGS, new SigninManager.SignOutCallback() {
                     @Override
                     public void preWipeData() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java
index 55c0da4..5c3e77e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java
@@ -41,7 +41,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.FlakyTest;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.app.ChromeActivity;
@@ -123,7 +123,7 @@
     @Test
     @MediumTest
     @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
-    @FlakyTest(message = "https://crbug.com/1155085")
+    @DisabledTest(message = "Flaky test, see https://crbug.com/1161737, https://crbug.com/1155085")
     public void testShowBookmarksReadLaterIPH() throws Throwable {
         mActivityTestRule.loadUrl(mTestServer.getServer().getURL(CONTEXT_MENU_TEST_URL));
         ChromeActivity activity = mActivityTestRule.getActivity();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java
index 53e575b..071060c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java
@@ -104,7 +104,7 @@
                 () -> UmaSessionStats.updateMetricsAndCrashReportingForTesting(true));
 
         // Enable a Syncing account.
-        CoreAccountInfo account = mSyncTestRule.setUpAccountAndSignInForTesting();
+        CoreAccountInfo account = mSyncTestRule.setUpAccountAndEnableSyncForTesting();
         Tab normalTab = mSyncTestRule.getActivity().getActivityTab();
         Assert.assertTrue("UKM Enabled:", isUkmEnabled(normalTab));
 
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b4321da..a0deade 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -658,6 +658,8 @@
     "login_detection/login_detection_util.h",
     "login_detection/oauth_login_detector.cc",
     "login_detection/oauth_login_detector.h",
+    "login_detection/password_store_sites.cc",
+    "login_detection/password_store_sites.h",
     "lookalikes/lookalike_url_blocking_page.cc",
     "lookalikes/lookalike_url_blocking_page.h",
     "lookalikes/lookalike_url_controller_client.cc",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index aa521b5..67665ca1 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -3349,6 +3349,7 @@
     "apps/metrics/intent_handling_metrics_unittest.cc",
     "arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc",
     "arc/accessibility/arc_accessibility_helper_bridge_unittest.cc",
+    "arc/accessibility/arc_accessibility_test_util.h",
     "arc/accessibility/arc_accessibility_util_unittest.cc",
     "arc/accessibility/auto_complete_handler_unittest.cc",
     "arc/accessibility/ax_tree_source_arc_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc
index 6f314921..1009547 100644
--- a/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h"
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_test_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h"
 #include "chrome/grit/generated_resources.h"
@@ -33,30 +34,6 @@
 using AXStringListProperty = mojom::AccessibilityStringListProperty;
 using AXStringProperty = mojom::AccessibilityStringProperty;
 
-namespace {
-
-void SetProperty(AXNodeInfoData* node, AXBooleanProperty prop, bool value) {
-  arc::SetProperty(node->boolean_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node, AXIntProperty prop, int value) {
-  arc::SetProperty(node->int_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node,
-                 AXIntListProperty prop,
-                 const std::vector<int>& value) {
-  arc::SetProperty(node->int_list_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node,
-                 AXStringProperty prop,
-                 const std::string& value) {
-  arc::SetProperty(node->string_properties, prop, value);
-}
-
-}  // namespace
-
 class AccessibilityNodeInfoDataWrapperTest : public testing::Test,
                                              public AXTreeSourceArc::Delegate {
  public:
@@ -133,7 +110,7 @@
 
 TEST_F(AccessibilityNodeInfoDataWrapperTest, Name) {
   AXNodeInfoData node;
-  SetProperty(&node, AXStringProperty::CLASS_NAME, "");
+  SetProperty(node, AXStringProperty::CLASS_NAME, "");
 
   AccessibilityNodeInfoDataWrapper wrapper(tree_source(), &node);
 
@@ -144,14 +121,14 @@
       data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
 
   // Text (empty).
-  SetProperty(&node, AXStringProperty::TEXT, "");
+  SetProperty(node, AXStringProperty::TEXT, "");
 
   data = CallSerialize(wrapper);
   ASSERT_FALSE(
       data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
 
   // Text (non-empty).
-  SetProperty(&node, AXStringProperty::TEXT, "label text");
+  SetProperty(node, AXStringProperty::TEXT, "label text");
 
   data = CallSerialize(wrapper);
   ASSERT_TRUE(
@@ -159,7 +136,7 @@
   EXPECT_EQ("label text", name);
 
   // Content description (empty), text (non-empty).
-  SetProperty(&node, AXStringProperty::CONTENT_DESCRIPTION, "");
+  SetProperty(node, AXStringProperty::CONTENT_DESCRIPTION, "");
 
   data = CallSerialize(wrapper);
   ASSERT_TRUE(
@@ -167,8 +144,8 @@
   EXPECT_EQ("label text", name);
 
   // Content description (non-empty), text (empty).
-  SetProperty(&node, AXStringProperty::TEXT, "");
-  SetProperty(&node, AXStringProperty::CONTENT_DESCRIPTION,
+  SetProperty(node, AXStringProperty::TEXT, "");
+  SetProperty(node, AXStringProperty::CONTENT_DESCRIPTION,
               "label content description");
 
   data = CallSerialize(wrapper);
@@ -177,7 +154,7 @@
   EXPECT_EQ("label content description", name);
 
   // Content description (non-empty), text (non-empty).
-  SetProperty(&node, AXStringProperty::TEXT, "label text");
+  SetProperty(node, AXStringProperty::TEXT, "label text");
 
   data = CallSerialize(wrapper);
   ASSERT_TRUE(
@@ -190,31 +167,31 @@
   root.id = 10;
   AccessibilityNodeInfoDataWrapper root_wrapper(tree_source(), &root);
   SetIdToWrapper(&root_wrapper);
-  SetProperty(&root, AXStringProperty::CLASS_NAME, "");
-  SetProperty(&root, AXBooleanProperty::IMPORTANCE, true);
-  SetProperty(&root, AXIntListProperty::CHILD_NODE_IDS,
+  SetProperty(root, AXStringProperty::CLASS_NAME, "");
+  SetProperty(root, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(root, AXIntListProperty::CHILD_NODE_IDS,
               std::vector<int>({1, 2}));
 
   AXNodeInfoData child1;
   child1.id = 1;
   AccessibilityNodeInfoDataWrapper child1_wrapper(tree_source(), &child1);
   SetIdToWrapper(&child1_wrapper);
-  SetProperty(&child1, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(child1, AXBooleanProperty::IMPORTANCE, true);
 
   AXNodeInfoData child2;
   child2.id = 2;
   AccessibilityNodeInfoDataWrapper child2_wrapper(tree_source(), &child2);
   SetIdToWrapper(&child2_wrapper);
-  SetProperty(&child2, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(child2, AXBooleanProperty::IMPORTANCE, true);
 
   SetParentId(child1.id, root.id);
   SetParentId(child2.id, root.id);
 
   // Root node has no name, but has descendants with name.
   // Name from contents can happen if a node is focusable.
-  SetProperty(&root, AXBooleanProperty::FOCUSABLE, true);
-  SetProperty(&child1, AXStringProperty::TEXT, "child1 label text");
-  SetProperty(&child2, AXStringProperty::TEXT, "child2 label text");
+  SetProperty(root, AXBooleanProperty::FOCUSABLE, true);
+  SetProperty(child1, AXStringProperty::TEXT, "child1 label text");
+  SetProperty(child2, AXStringProperty::TEXT, "child2 label text");
 
   // If the screen reader mode is off, do not compute from descendants.
   set_full_focus_mode(false);
@@ -251,13 +228,13 @@
   ASSERT_TRUE(data.IsIgnored());
 
   // Don't compute name from descendants for scrollable, e.g. ScrollView.
-  SetProperty(&root, AXBooleanProperty::SCROLLABLE, true);
+  SetProperty(root, AXBooleanProperty::SCROLLABLE, true);
 
   data = CallSerialize(root_wrapper);
   ASSERT_FALSE(
       data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
 
-  SetProperty(&root, AXBooleanProperty::SCROLLABLE, false);
+  SetProperty(root, AXBooleanProperty::SCROLLABLE, false);
 
   // Don't compute name from descendants for virtual views, e.g. WebView.
   root.is_virtual_node = true;
@@ -273,7 +250,7 @@
   child2.is_virtual_node = false;
 
   // If one child is clickable, do not use clickable child.
-  SetProperty(&child1, AXBooleanProperty::CLICKABLE, true);
+  SetProperty(child1, AXBooleanProperty::CLICKABLE, true);
 
   data = CallSerialize(root_wrapper);
   ASSERT_TRUE(
@@ -290,7 +267,7 @@
   ASSERT_TRUE(data.IsIgnored());
 
   // If both children are also clickable, do not use child properties.
-  SetProperty(&child2, AXBooleanProperty::CLICKABLE, true);
+  SetProperty(child2, AXBooleanProperty::CLICKABLE, true);
 
   data = CallSerialize(root_wrapper);
   ASSERT_FALSE(
@@ -299,7 +276,7 @@
   // If the node has a name, it should override the contents.
   child1.boolean_properties->clear();
   child2.boolean_properties->clear();
-  SetProperty(&root, AXStringProperty::TEXT, "root label text");
+  SetProperty(root, AXStringProperty::TEXT, "root label text");
 
   data = CallSerialize(root_wrapper);
   ASSERT_TRUE(
@@ -320,11 +297,11 @@
   root.id = 10;
   AccessibilityNodeInfoDataWrapper root_wrapper(tree_source(), &root);
   SetIdToWrapper(&root_wrapper);
-  SetProperty(&root, AXStringProperty::CLASS_NAME, "");
-  SetProperty(&root, AXBooleanProperty::IMPORTANCE, true);
-  SetProperty(&root, AXBooleanProperty::CLICKABLE, true);
-  SetProperty(&root, AXBooleanProperty::FOCUSABLE, true);
-  SetProperty(&root, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({1}));
+  SetProperty(root, AXStringProperty::CLASS_NAME, "");
+  SetProperty(root, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(root, AXBooleanProperty::CLICKABLE, true);
+  SetProperty(root, AXBooleanProperty::FOCUSABLE, true);
+  SetProperty(root, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({1}));
 
   AXNodeInfoData child1;
   child1.id = 1;
@@ -332,11 +309,10 @@
   SetIdToWrapper(&child1_wrapper);
 
   // Set all properties that will be used in name computation.
-  SetProperty(&child1, AXStringProperty::TEXT, "text");
-  SetProperty(&child1, AXStringProperty::CONTENT_DESCRIPTION,
+  SetProperty(child1, AXStringProperty::TEXT, "text");
+  SetProperty(child1, AXStringProperty::CONTENT_DESCRIPTION,
               "content_description");
-  SetProperty(&child1, AXStringProperty::STATE_DESCRIPTION,
-              "state_description");
+  SetProperty(child1, AXStringProperty::STATE_DESCRIPTION, "state_description");
 
   AccessibilityNodeInfoDataWrapper wrapper(tree_source(), &root);
 
@@ -349,7 +325,7 @@
   ASSERT_EQ("text", name);
 
   // Unset TEXT property, and confirm that CONTENT_DESCRIPTION is used as name.
-  SetProperty(&child1, AXStringProperty::TEXT, "");
+  SetProperty(child1, AXStringProperty::TEXT, "");
   data = CallSerialize(wrapper);
   ASSERT_TRUE(
       data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
@@ -357,7 +333,7 @@
 
   // Unset CONTENT_DESCRIPTION property, and confirm that STATE_DESCRIPTION is
   // used as name.
-  SetProperty(&child1, AXStringProperty::CONTENT_DESCRIPTION, "");
+  SetProperty(child1, AXStringProperty::CONTENT_DESCRIPTION, "");
   data = CallSerialize(wrapper);
   ASSERT_TRUE(
       data.GetStringAttribute(ax::mojom::StringAttribute::kName, &name));
@@ -366,8 +342,8 @@
 
 TEST_F(AccessibilityNodeInfoDataWrapperTest, TextFieldNameAndValue) {
   AXNodeInfoData node;
-  SetProperty(&node, AXStringProperty::CLASS_NAME, "");
-  SetProperty(&node, AXBooleanProperty::EDITABLE, true);
+  SetProperty(node, AXStringProperty::CLASS_NAME, "");
+  SetProperty(node, AXBooleanProperty::EDITABLE, true);
 
   struct AndroidState {
     std::string content_description, text, hint_text;
@@ -421,11 +397,11 @@
   };
 
   for (const auto& test_case : test_cases) {
-    SetProperty(&node, AXStringProperty::CONTENT_DESCRIPTION,
+    SetProperty(node, AXStringProperty::CONTENT_DESCRIPTION,
                 test_case.first.content_description);
-    SetProperty(&node, AXStringProperty::TEXT, test_case.first.text);
-    SetProperty(&node, AXStringProperty::HINT_TEXT, test_case.first.hint_text);
-    SetProperty(&node, AXBooleanProperty::SHOWING_HINT_TEXT,
+    SetProperty(node, AXStringProperty::TEXT, test_case.first.text);
+    SetProperty(node, AXStringProperty::HINT_TEXT, test_case.first.hint_text);
+    SetProperty(node, AXBooleanProperty::SHOWING_HINT_TEXT,
                 test_case.first.showingHint);
 
     AccessibilityNodeInfoDataWrapper wrapper(tree_source(), &node);
@@ -449,9 +425,9 @@
 TEST_F(AccessibilityNodeInfoDataWrapperTest, StringProperties) {
   AXNodeInfoData node;
   node.id = 10;
-  SetProperty(&node, AXStringProperty::CLASS_NAME, "");
-  SetProperty(&node, AXStringProperty::PACKAGE_NAME, "com.android.vending");
-  SetProperty(&node, AXStringProperty::TOOLTIP, "tooltip text");
+  SetProperty(node, AXStringProperty::CLASS_NAME, "");
+  SetProperty(node, AXStringProperty::PACKAGE_NAME, "com.android.vending");
+  SetProperty(node, AXStringProperty::TOOLTIP, "tooltip text");
 
   SetNodeRootId(node.id);
 
@@ -474,20 +450,20 @@
   AccessibilityNodeInfoDataWrapper wrapper(tree_source(), &node);
 
   // Node is checkable, but not checked.
-  SetProperty(&node, AXBooleanProperty::CHECKABLE, true);
-  SetProperty(&node, AXBooleanProperty::CHECKED, false);
+  SetProperty(node, AXBooleanProperty::CHECKABLE, true);
+  SetProperty(node, AXBooleanProperty::CHECKED, false);
 
   ui::AXNodeData data = CallSerialize(wrapper);
   EXPECT_EQ(ax::mojom::CheckedState::kFalse, data.GetCheckedState());
 
   // Make the node checked.
-  SetProperty(&node, AXBooleanProperty::CHECKED, true);
+  SetProperty(node, AXBooleanProperty::CHECKED, true);
 
   data = CallSerialize(wrapper);
   EXPECT_EQ(ax::mojom::CheckedState::kTrue, data.GetCheckedState());
 
   // Make the node expandable (i.e. collapsed).
-  SetProperty(&node, AXIntListProperty::STANDARD_ACTION_IDS,
+  SetProperty(node, AXIntListProperty::STANDARD_ACTION_IDS,
               std::vector<int>({static_cast<int>(AXActionType::EXPAND)}));
 
   data = CallSerialize(wrapper);
@@ -495,7 +471,7 @@
   EXPECT_FALSE(data.HasState(ax::mojom::State::kExpanded));
 
   // Make the node collapsible (i.e. expanded).
-  SetProperty(&node, AXIntListProperty::STANDARD_ACTION_IDS,
+  SetProperty(node, AXIntListProperty::STANDARD_ACTION_IDS,
               std::vector<int>({static_cast<int>(AXActionType::COLLAPSE)}));
 
   data = CallSerialize(wrapper);
@@ -506,7 +482,7 @@
 TEST_F(AccessibilityNodeInfoDataWrapperTest, SelectedState) {
   AXNodeInfoData grid;
   grid.id = 1;
-  SetProperty(&grid, AXIntListProperty::CHILD_NODE_IDS,
+  SetProperty(grid, AXIntListProperty::CHILD_NODE_IDS,
               std::vector<int>({10, 11, 12, 13}));
   grid.collection_info = AXCollectionInfoData::New();
   grid.collection_info->row_count = 2;
@@ -523,7 +499,7 @@
     node.collection_item_info = AXCollectionItemInfoData::New();
     node.collection_item_info->row_index = i % 2;
     node.collection_item_info->column_index = i / 2;
-    SetProperty(&node, AXBooleanProperty::SELECTED, true);
+    SetProperty(node, AXBooleanProperty::SELECTED, true);
 
     SetParentId(node.id, grid.id);
   }
@@ -539,8 +515,8 @@
   // text_node is simple static text, which doesn't supports selected state in
   // the web.
   AXNodeInfoData text_node;
-  SetProperty(&text_node, AXStringProperty::TEXT, "text.");
-  SetProperty(&text_node, AXBooleanProperty::SELECTED, true);
+  SetProperty(text_node, AXStringProperty::TEXT, "text.");
+  SetProperty(text_node, AXBooleanProperty::SELECTED, true);
 
   AccessibilityNodeInfoDataWrapper text_wrapper(tree_source(), &text_node);
 
@@ -559,8 +535,8 @@
   AccessibilityNodeInfoDataWrapper wrapper(tree_source(), &node);
 
   // Editable node is textField.
-  SetProperty(&node, AXStringProperty::CLASS_NAME, ui::kAXEditTextClassname);
-  SetProperty(&node, AXBooleanProperty::EDITABLE, true);
+  SetProperty(node, AXStringProperty::CLASS_NAME, ui::kAXEditTextClassname);
+  SetProperty(node, AXBooleanProperty::EDITABLE, true);
 
   ui::AXNodeData data = CallSerialize(wrapper);
   EXPECT_EQ(ax::mojom::Role::kTextField, data.role);
@@ -568,14 +544,14 @@
   // Non-editable node is not textField even if it has EditTextClassname.
   // When it has text and no children, it is staticText. Otherwise, it's
   // genericContainer.
-  SetProperty(&node, AXBooleanProperty::EDITABLE, false);
-  SetProperty(&node, AXStringProperty::TEXT, "text");
+  SetProperty(node, AXBooleanProperty::EDITABLE, false);
+  SetProperty(node, AXStringProperty::TEXT, "text");
 
   data = CallSerialize(wrapper);
   EXPECT_EQ(ax::mojom::Role::kStaticText, data.role);
 
   // Add a child.
-  SetProperty(&node, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({2}));
+  SetProperty(node, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({2}));
   AXNodeInfoData child;
   child.id = 2;
   AccessibilityNodeInfoDataWrapper child_wrapper(tree_source(), &node);
@@ -605,7 +581,7 @@
       &checked_state_description));
 
   // State Description without Range Value should be stored as kDescription
-  SetProperty(&node, AXStringProperty::STATE_DESCRIPTION, "state description");
+  SetProperty(node, AXStringProperty::STATE_DESCRIPTION, "state description");
 
   data = CallSerialize(wrapper);
   ASSERT_TRUE(data.GetStringAttribute(ax::mojom::StringAttribute::kDescription,
@@ -633,7 +609,7 @@
   // State Description for compound button should be stores as
   // checkedDescription.
   node.range_info.reset();
-  SetProperty(&node, AXBooleanProperty::CHECKABLE, true);
+  SetProperty(node, AXBooleanProperty::CHECKABLE, true);
 
   data = CallSerialize(wrapper);
   ASSERT_FALSE(data.GetStringAttribute(ax::mojom::StringAttribute::kDescription,
@@ -649,7 +625,7 @@
 TEST_F(AccessibilityNodeInfoDataWrapperTest, LabeledByLoop) {
   AXNodeInfoData root;
   root.id = 1;
-  SetProperty(&root, AXIntProperty::LABELED_BY, 2);
+  SetProperty(root, AXIntProperty::LABELED_BY, 2);
   AccessibilityNodeInfoDataWrapper wrapper(tree_source(), &root);
   SetIdToWrapper(&wrapper);
 
@@ -657,8 +633,8 @@
   node2.id = 2;
   AccessibilityNodeInfoDataWrapper child1_wrapper(tree_source(), &node2);
   SetIdToWrapper(&child1_wrapper);
-  SetProperty(&node2, AXStringProperty::CONTENT_DESCRIPTION, "node2");
-  SetProperty(&node2, AXIntProperty::LABELED_BY, 1);
+  SetProperty(node2, AXStringProperty::CONTENT_DESCRIPTION, "node2");
+  SetProperty(node2, AXIntProperty::LABELED_BY, 1);
 
   ui::AXNodeData data = CallSerialize(wrapper);
   std::string name;
@@ -683,9 +659,9 @@
   ASSERT_FALSE(data.GetStringAttribute(ax::mojom::StringAttribute::kDescription,
                                        &description));
 
-  SetProperty(&node, AXStringProperty::STATE_DESCRIPTION, "state description");
-  SetProperty(&node, AXBooleanProperty::SELECTED, true);
-  SetProperty(&node, AXStringProperty::TEXT, "text");
+  SetProperty(node, AXStringProperty::STATE_DESCRIPTION, "state description");
+  SetProperty(node, AXBooleanProperty::SELECTED, true);
+  SetProperty(node, AXStringProperty::TEXT, "text");
 
   data = CallSerialize(wrapper);
   ASSERT_TRUE(data.GetStringAttribute(ax::mojom::StringAttribute::kDescription,
@@ -698,10 +674,10 @@
 TEST_F(AccessibilityNodeInfoDataWrapperTest, ControlIsFocusable) {
   AXNodeInfoData root;
   root.id = 1;
-  SetProperty(&root, AXStringProperty::CLASS_NAME, ui::kAXSeekBarClassname);
-  SetProperty(&root, AXStringProperty::TEXT, "");
-  SetProperty(&root, AXBooleanProperty::FOCUSABLE, true);
-  SetProperty(&root, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(root, AXStringProperty::CLASS_NAME, ui::kAXSeekBarClassname);
+  SetProperty(root, AXStringProperty::TEXT, "");
+  SetProperty(root, AXBooleanProperty::FOCUSABLE, true);
+  SetProperty(root, AXBooleanProperty::IMPORTANCE, true);
   AccessibilityNodeInfoDataWrapper wrapper(tree_source(), &root);
 
   // Check the pre conditions required, before checking whether this
@@ -720,29 +696,28 @@
   root.id = 10;
   AccessibilityNodeInfoDataWrapper root_wrapper(tree_source(), &root);
   SetIdToWrapper(&root_wrapper);
-  SetProperty(&root, AXStringProperty::CLASS_NAME, "");
-  SetProperty(&root, AXBooleanProperty::IMPORTANCE, true);
-  SetProperty(&root, AXBooleanProperty::FOCUSABLE, true);
-  SetProperty(&root, AXBooleanProperty::CLICKABLE, true);
-  SetProperty(&root, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({1}));
+  SetProperty(root, AXStringProperty::CLASS_NAME, "");
+  SetProperty(root, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(root, AXBooleanProperty::FOCUSABLE, true);
+  SetProperty(root, AXBooleanProperty::CLICKABLE, true);
+  SetProperty(root, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({1}));
 
   AXNodeInfoData child1;
   child1.id = 1;
   AccessibilityNodeInfoDataWrapper child1_wrapper(tree_source(), &child1);
   SetIdToWrapper(&child1_wrapper);
-  SetProperty(&child1, AXBooleanProperty::IMPORTANCE, true);
-  SetProperty(&child1, AXIntListProperty::CHILD_NODE_IDS,
-              std::vector<int>({2}));
+  SetProperty(child1, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(child1, AXIntListProperty::CHILD_NODE_IDS, std::vector<int>({2}));
   SetParentId(child1.id, root.id);
 
   AXNodeInfoData child2;
   child2.id = 2;
   AccessibilityNodeInfoDataWrapper child2_wrapper(tree_source(), &child2);
   SetIdToWrapper(&child2_wrapper);
-  SetProperty(&child2, AXBooleanProperty::IMPORTANCE, true);
+  SetProperty(child2, AXBooleanProperty::IMPORTANCE, true);
   SetParentId(child2.id, child1.id);
 
-  SetProperty(&child2, AXStringProperty::CONTENT_DESCRIPTION, "test text");
+  SetProperty(child2, AXStringProperty::CONTENT_DESCRIPTION, "test text");
 
   set_full_focus_mode(true);
 
@@ -762,7 +737,7 @@
 
   // Set click and focus action to child1. child1 will be clickable and
   // focusable, and gets ax name from descendants.
-  SetProperty(&child1, AXIntListProperty::STANDARD_ACTION_IDS,
+  SetProperty(child1, AXIntListProperty::STANDARD_ACTION_IDS,
               std::vector<int>({static_cast<int>(AXActionType::CLICK),
                                 static_cast<int>(AXActionType::FOCUS)}));
 
@@ -780,7 +755,7 @@
   EXPECT_TRUE(data.HasState(ax::mojom::State::kFocusable));
 
   // Same for clear_focus action instead of focus action.
-  SetProperty(&child1, AXIntListProperty::STANDARD_ACTION_IDS,
+  SetProperty(child1, AXIntListProperty::STANDARD_ACTION_IDS,
               std::vector<int>({static_cast<int>(AXActionType::CLICK),
                                 static_cast<int>(AXActionType::CLEAR_FOCUS)}));
 
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
index 697c84f..a5df2b6 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_test_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h"
 #include "chrome/common/extensions/api/accessibility_private.h"
 #include "chrome/common/pref_names.h"
@@ -338,7 +339,7 @@
   event->node_data.push_back(arc::mojom::AccessibilityNodeInfoData::New());
   event->node_data[0]->id = 10;
   event->node_data[0]->window_id = 100;
-  SetProperty(event->node_data[0]->int_list_properties,
+  SetProperty(event->node_data[0].get(),
               mojom::AccessibilityIntListProperty::CHILD_NODE_IDS, {1, 2, 3});
   for (int i = 1; i <= 3; i++) {
     // This creates focusable nodes.
@@ -346,11 +347,11 @@
     event->node_data.push_back(arc::mojom::AccessibilityNodeInfoData::New());
     event->node_data[i]->id = i;
     event->node_data[i]->window_id = 100;
-    SetProperty(event->node_data[i]->boolean_properties,
+    SetProperty(event->node_data[i].get(),
                 mojom::AccessibilityBooleanProperty::IMPORTANCE, true);
-    SetProperty(event->node_data[i]->boolean_properties,
+    SetProperty(event->node_data[i].get(),
                 mojom::AccessibilityBooleanProperty::VISIBLE_TO_USER, true);
-    SetProperty(event->node_data[i]->string_properties,
+    SetProperty(event->node_data[i].get(),
                 mojom::AccessibilityStringProperty::CONTENT_DESCRIPTION,
                 "node" + base::NumberToString(i) + " description");
   }
@@ -361,8 +362,8 @@
       event->window_data->back().get();
   root_window->window_id = 100;
   root_window->root_node_id = 10;
-  SetProperty(root_window->boolean_properties,
-              mojom::AccessibilityWindowBooleanProperty::FOCUSED, true);
+  SetProperty(root_window, mojom::AccessibilityWindowBooleanProperty::FOCUSED,
+              true);
 
   // There's no active window.
   helper_bridge->OnAccessibilityEvent(event.Clone());
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_test_util.h b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_test_util.h
new file mode 100644
index 0000000..7d4afc1
--- /dev/null
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_test_util.h
@@ -0,0 +1,83 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_TEST_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_TEST_UTIL_H_
+
+#include "base/containers/flat_map.h"
+#include "base/optional.h"
+#include "base/stl_util.h"
+#include "components/arc/mojom/accessibility_helper.mojom.h"
+
+namespace arc {
+
+template <class PropType, class ValueType>
+void SetProperty(
+    base::Optional<base::flat_map<PropType, ValueType>>& properties,
+    PropType prop,
+    const ValueType& value) {
+  if (!properties.has_value())
+    properties = base::flat_map<PropType, ValueType>();
+
+  properties->insert_or_assign(prop, value);
+}
+
+#define DEF_SET_PROP(data_type, prop_type, data_member_name, value_type) \
+  inline void SetProperty(data_type* data, prop_type prop,               \
+                          const value_type& value) {                     \
+    SetProperty(data->data_member_name, prop, value);                    \
+  }                                                                      \
+  inline void SetProperty(data_type& data, prop_type prop,               \
+                          const value_type& value) {                     \
+    SetProperty(data.data_member_name, prop, value);                     \
+  }
+
+DEF_SET_PROP(mojom::AccessibilityEventData,
+             mojom::AccessibilityEventIntProperty,
+             int_properties,
+             int32_t)
+DEF_SET_PROP(mojom::AccessibilityEventData,
+             mojom::AccessibilityEventIntListProperty,
+             int_list_properties,
+             std::vector<int32_t>)
+
+DEF_SET_PROP(mojom::AccessibilityNodeInfoData,
+             mojom::AccessibilityBooleanProperty,
+             boolean_properties,
+             bool)
+DEF_SET_PROP(mojom::AccessibilityNodeInfoData,
+             mojom::AccessibilityIntProperty,
+             int_properties,
+             int32_t)
+DEF_SET_PROP(mojom::AccessibilityNodeInfoData,
+             mojom::AccessibilityIntListProperty,
+             int_list_properties,
+             std::vector<int32_t>)
+DEF_SET_PROP(mojom::AccessibilityNodeInfoData,
+             mojom::AccessibilityStringProperty,
+             string_properties,
+             std::string)
+
+DEF_SET_PROP(mojom::AccessibilityWindowInfoData,
+             mojom::AccessibilityWindowBooleanProperty,
+             boolean_properties,
+             bool)
+DEF_SET_PROP(mojom::AccessibilityWindowInfoData,
+             mojom::AccessibilityWindowIntProperty,
+             int_properties,
+             int32_t)
+DEF_SET_PROP(mojom::AccessibilityWindowInfoData,
+             mojom::AccessibilityWindowIntListProperty,
+             int_list_properties,
+             std::vector<int32_t>)
+DEF_SET_PROP(mojom::AccessibilityWindowInfoData,
+             mojom::AccessibilityWindowStringProperty,
+             string_properties,
+             std::string)
+
+#undef DEF_SET_PROP
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_TEST_UTIL_H_
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h
index f5d64c1..8a09868c 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h
@@ -97,20 +97,6 @@
   return !it->second.empty();
 }
 
-// Sets property to mojom struct. Used in test.
-template <class PropType, class ValueType>
-void SetProperty(
-    base::Optional<base::flat_map<PropType, ValueType>>& properties,
-    PropType prop,
-    const ValueType& value) {
-  if (!properties.has_value())
-    properties = base::flat_map<PropType, ValueType>();
-
-  auto& prop_map = properties.value();
-  base::EraseIf(prop_map, [prop](auto it) { return it.first == prop; });
-  prop_map.insert(std::make_pair(prop, value));
-}
-
 }  // namespace arc
 
 #endif  // CHROME_BROWSER_CHROMEOS_ARC_ACCESSIBILITY_ARC_ACCESSIBILITY_UTIL_H_
diff --git a/chrome/browser/chromeos/arc/accessibility/auto_complete_handler_unittest.cc b/chrome/browser/chromeos/arc/accessibility/auto_complete_handler_unittest.cc
index 0082d713..550576a0 100644
--- a/chrome/browser/chromeos/arc/accessibility/auto_complete_handler_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/auto_complete_handler_unittest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_info_data_wrapper.h"
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h"
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h"
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_test_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h"
 #include "components/arc/mojom/accessibility_helper.mojom.h"
@@ -32,38 +33,6 @@
 using AXWindowIntProperty = mojom::AccessibilityWindowIntProperty;
 using AXWindowIntListProperty = mojom::AccessibilityWindowIntListProperty;
 
-namespace {
-
-void SetProperty(AXNodeInfoData* node, AXBooleanProperty prop, bool value) {
-  arc::SetProperty(node->boolean_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node,
-                 AXIntListProperty prop,
-                 const std::vector<int>& value) {
-  arc::SetProperty(node->int_list_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node,
-                 AXStringProperty prop,
-                 const std::string& value) {
-  arc::SetProperty(node->string_properties, prop, value);
-}
-
-void SetProperty(AXWindowInfoData* window,
-                 AXWindowIntProperty prop,
-                 int value) {
-  arc::SetProperty(window->int_properties, prop, value);
-}
-
-void SetProperty(AXWindowInfoData* window,
-                 AXWindowIntListProperty prop,
-                 const std::vector<int>& value) {
-  arc::SetProperty(window->int_list_properties, prop, value);
-}
-
-}  // namespace
-
 class AutoCompleteHandlerTest : public testing::Test,
                                 public AXTreeSourceArc::Delegate {
  public:
diff --git a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
index a33b513..777a25fa 100644
--- a/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/stl_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h"
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h"
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_test_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h"
 #include "components/arc/mojom/accessibility_helper.mojom.h"
 #include "extensions/browser/api/automation_internal/automation_event_router.h"
@@ -42,64 +43,6 @@
 using AXWindowIntListProperty = mojom::AccessibilityWindowIntListProperty;
 using AXWindowStringProperty = mojom::AccessibilityWindowStringProperty;
 
-namespace {
-
-void SetProperty(AXNodeInfoData* node, AXBooleanProperty prop, bool value) {
-  arc::SetProperty(node->boolean_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node,
-                 AXStringProperty prop,
-                 const std::string& value) {
-  arc::SetProperty(node->string_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node, AXIntProperty prop, int32_t value) {
-  arc::SetProperty(node->int_properties, prop, value);
-}
-
-void SetProperty(AXWindowInfoData* window,
-                 AXWindowStringProperty prop,
-                 const std::string& value) {
-  arc::SetProperty(window->string_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node,
-                 AXIntListProperty prop,
-                 const std::vector<int>& value) {
-  arc::SetProperty(node->int_list_properties, prop, value);
-}
-
-void SetProperty(AXWindowInfoData* window,
-                 AXWindowBooleanProperty prop,
-                 bool value) {
-  arc::SetProperty(window->boolean_properties, prop, value);
-}
-
-void SetProperty(AXWindowInfoData* window,
-                 AXWindowIntProperty prop,
-                 int value) {
-  arc::SetProperty(window->int_properties, prop, value);
-}
-
-void SetProperty(AXWindowInfoData* window,
-                 AXWindowIntListProperty prop,
-                 const std::vector<int>& value) {
-  arc::SetProperty(window->int_list_properties, prop, value);
-}
-
-void SetProperty(AXEventData* event, AXEventIntProperty prop, int32_t value) {
-  arc::SetProperty(event->int_properties, prop, value);
-}
-
-void SetProperty(AXEventData* event,
-                 AXEventIntListProperty prop,
-                 const std::vector<int>& value) {
-  arc::SetProperty(event->int_list_properties, prop, value);
-}
-
-}  // namespace
-
 class MockAutomationEventRouter
     : public extensions::AutomationEventRouterInterface {
  public:
@@ -601,8 +544,7 @@
   AXWindowInfoData* root = event->window_data->back().get();
   root->window_id = 5;
   SetProperty(root, AXWindowIntListProperty::CHILD_WINDOW_IDS, {1});
-  SetProperty(root->boolean_properties,
-              mojom::AccessibilityWindowBooleanProperty::FOCUSED, true);
+  SetProperty(root, mojom::AccessibilityWindowBooleanProperty::FOCUSED, true);
 
   // Add a child window.
   event->window_data->push_back(AXWindowInfoData::New());
@@ -635,8 +577,8 @@
   AXWindowInfoData* root_window = event->window_data->back().get();
   root_window->window_id = 100;
   root_window->root_node_id = 10;
-  SetProperty(root_window->boolean_properties,
-              mojom::AccessibilityWindowBooleanProperty::FOCUSED, true);
+  SetProperty(root_window, mojom::AccessibilityWindowBooleanProperty::FOCUSED,
+              true);
 
   event->node_data.push_back(AXNodeInfoData::New());
   AXNodeInfoData* root = event->node_data.back().get();
@@ -748,8 +690,8 @@
   AXWindowInfoData* root_window = event->window_data->back().get();
   root_window->window_id = 100;
   root_window->root_node_id = 10;
-  SetProperty(root_window->boolean_properties,
-              mojom::AccessibilityWindowBooleanProperty::FOCUSED, true);
+  SetProperty(root_window, mojom::AccessibilityWindowBooleanProperty::FOCUSED,
+              true);
 
   event->node_data.push_back(AXNodeInfoData::New());
   AXNodeInfoData* root = event->node_data.back().get();
@@ -848,8 +790,8 @@
   AXWindowInfoData* root_window = event->window_data->back().get();
   root_window->window_id = 100;
   root_window->root_node_id = 10;
-  SetProperty(root_window->boolean_properties,
-              mojom::AccessibilityWindowBooleanProperty::FOCUSED, true);
+  SetProperty(root_window, mojom::AccessibilityWindowBooleanProperty::FOCUSED,
+              true);
 
   event->node_data.push_back(AXNodeInfoData::New());
   AXNodeInfoData* root = event->node_data.back().get();
@@ -1134,8 +1076,8 @@
   AXWindowInfoData* root_window = event->window_data->back().get();
   root_window->window_id = 100;
   root_window->root_node_id = 10;
-  SetProperty(root_window->boolean_properties,
-              mojom::AccessibilityWindowBooleanProperty::FOCUSED, true);
+  SetProperty(root_window, mojom::AccessibilityWindowBooleanProperty::FOCUSED,
+              true);
 
   event->node_data.push_back(AXNodeInfoData::New());
   AXNodeInfoData* root = event->node_data.back().get();
@@ -1344,8 +1286,8 @@
   AXWindowInfoData* root_window = event->window_data->back().get();
   root_window->window_id = 2;
   root_window->root_node_id = 1;
-  SetProperty(root_window->boolean_properties,
-              mojom::AccessibilityWindowBooleanProperty::FOCUSED, true);
+  SetProperty(root_window, mojom::AccessibilityWindowBooleanProperty::FOCUSED,
+              true);
 
   event->node_data.push_back(AXNodeInfoData::New());
   AXNodeInfoData* node = event->node_data.back().get();
diff --git a/chrome/browser/chromeos/arc/accessibility/drawer_layout_handler_unittest.cc b/chrome/browser/chromeos/arc/accessibility/drawer_layout_handler_unittest.cc
index aa76a3f..10907b1 100644
--- a/chrome/browser/chromeos/arc/accessibility/drawer_layout_handler_unittest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/drawer_layout_handler_unittest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_info_data_wrapper.h"
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_node_info_data_wrapper.h"
 #include "chrome/browser/chromeos/arc/accessibility/accessibility_window_info_data_wrapper.h"
+#include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_test_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_util.h"
 #include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h"
 #include "components/arc/mojom/accessibility_helper.mojom.h"
@@ -28,26 +29,6 @@
 using AXStringProperty = mojom::AccessibilityStringProperty;
 using AXWindowInfoData = mojom::AccessibilityWindowInfoData;
 
-namespace {
-
-void SetProperty(AXNodeInfoData* node, AXBooleanProperty prop, bool value) {
-  arc::SetProperty(node->boolean_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node,
-                 AXIntListProperty prop,
-                 const std::vector<int>& value) {
-  arc::SetProperty(node->int_list_properties, prop, value);
-}
-
-void SetProperty(AXNodeInfoData* node,
-                 AXStringProperty prop,
-                 const std::string& value) {
-  arc::SetProperty(node->string_properties, prop, value);
-}
-
-}  // namespace
-
 class DrawerLayoutHandlerTest : public testing::Test,
                                 public AXTreeSourceArc::Delegate {
  public:
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 5f38cc4..f55eef1 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -157,7 +157,7 @@
   {
     "name": "arc-file-picker-experiment",
     "owners": [ "niwa" ],
-    "expiry_milestone": 88
+    "expiry_milestone": 95
   },
   {
     "name": "arc-native-bridge-64bit-support-experiment",
diff --git a/chrome/browser/login_detection/login_detection_keyed_service.cc b/chrome/browser/login_detection/login_detection_keyed_service.cc
index 97a917d..90c2c0b 100644
--- a/chrome/browser/login_detection/login_detection_keyed_service.cc
+++ b/chrome/browser/login_detection/login_detection_keyed_service.cc
@@ -6,6 +6,7 @@
 
 #include "chrome/browser/login_detection/login_detection_prefs.h"
 #include "chrome/browser/login_detection/login_detection_util.h"
+#include "chrome/browser/password_manager/account_password_store_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/child_process_security_policy.h"
 #include "url/gurl.h"
@@ -29,7 +30,14 @@
 }
 
 LoginDetectionKeyedService::LoginDetectionKeyedService(Profile* profile)
-    : profile_(profile), field_trial_logged_in_sites_(GetLoggedInSites()) {}
+    : profile_(profile),
+      field_trial_logged_in_sites_(GetLoggedInSites()),
+      profile_password_sites_(PasswordStoreFactory::GetForProfile(
+          profile,
+          ServiceAccessType::EXPLICIT_ACCESS)),
+      account_password_sites_(AccountPasswordStoreFactory::GetForProfile(
+          profile,
+          ServiceAccessType::EXPLICIT_ACCESS)) {}
 
 LoginDetectionKeyedService::~LoginDetectionKeyedService() = default;
 
@@ -66,6 +74,12 @@
     return LoginDetectionType::kPreloadedPasswordSiteLogin;
   }
 
+  // Check for sites saved in the password manager.
+  if (profile_password_sites_.IsSiteInPasswordStore(url) ||
+      account_password_sites_.IsSiteInPasswordStore(url)) {
+    return LoginDetectionType::kPasswordManagerSavedSite;
+  }
+
   return LoginDetectionType::kNoLogin;
 }
 
diff --git a/chrome/browser/login_detection/login_detection_keyed_service.h b/chrome/browser/login_detection/login_detection_keyed_service.h
index 93e849c..8b9d79f 100644
--- a/chrome/browser/login_detection/login_detection_keyed_service.h
+++ b/chrome/browser/login_detection/login_detection_keyed_service.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "chrome/browser/login_detection/login_detection_type.h"
+#include "chrome/browser/login_detection/password_store_sites.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 class Profile;
@@ -43,6 +44,9 @@
   // Set of sites that should be treated as logged-in, retrieved from field
   // trial.
   const std::set<std::string, OriginComparator> field_trial_logged_in_sites_;
+
+  const PasswordStoreSites profile_password_sites_;
+  const PasswordStoreSites account_password_sites_;
 };
 
 }  // namespace login_detection
diff --git a/chrome/browser/login_detection/login_detection_keyed_service_factory.cc b/chrome/browser/login_detection/login_detection_keyed_service_factory.cc
index 29645d4..3884595 100644
--- a/chrome/browser/login_detection/login_detection_keyed_service_factory.cc
+++ b/chrome/browser/login_detection/login_detection_keyed_service_factory.cc
@@ -6,6 +6,8 @@
 
 #include "chrome/browser/login_detection/login_detection_keyed_service.h"
 #include "chrome/browser/login_detection/login_detection_util.h"
+#include "chrome/browser/password_manager/account_password_store_factory.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "content/public/browser/browser_context.h"
@@ -35,7 +37,10 @@
 LoginDetectionKeyedServiceFactory::LoginDetectionKeyedServiceFactory()
     : BrowserContextKeyedServiceFactory(
           "LoginDetectionKeyedService",
-          BrowserContextDependencyManager::GetInstance()) {}
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(AccountPasswordStoreFactory::GetInstance());
+  DependsOn(PasswordStoreFactory::GetInstance());
+}
 
 LoginDetectionKeyedServiceFactory::~LoginDetectionKeyedServiceFactory() =
     default;
diff --git a/chrome/browser/login_detection/login_detection_type.h b/chrome/browser/login_detection/login_detection_type.h
index 0c451d73..b47b3b2e 100644
--- a/chrome/browser/login_detection/login_detection_type.h
+++ b/chrome/browser/login_detection/login_detection_type.h
@@ -33,7 +33,10 @@
   // commonly logged-in.
   kFieldTrialLoggedInSite,
 
-  kMaxValue = kFieldTrialLoggedInSite
+  // The site has credentials saved in the password manager.
+  kPasswordManagerSavedSite,
+
+  kMaxValue = kPasswordManagerSavedSite
 };
 }  // namespace login_detection
 
diff --git a/chrome/browser/login_detection/password_store_sites.cc b/chrome/browser/login_detection/password_store_sites.cc
new file mode 100644
index 0000000..a86cd66
--- /dev/null
+++ b/chrome/browser/login_detection/password_store_sites.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/login_detection/password_store_sites.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/sequence_checker.h"
+#include "chrome/browser/login_detection/login_detection_util.h"
+#include "components/password_manager/core/browser/password_form.h"
+
+namespace login_detection {
+
+PasswordStoreSites::PasswordStoreSites(
+    scoped_refptr<password_manager::PasswordStore> password_store)
+    : password_store_(std::move(password_store)) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (password_store_) {
+    password_store_->AddObserver(this);
+    password_store_->GetAllLogins(this);
+  }
+}
+
+PasswordStoreSites::~PasswordStoreSites() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (password_store_)
+    password_store_->RemoveObserver(this);
+}
+
+void PasswordStoreSites::OnLoginsChanged(
+    const password_manager::PasswordStoreChangeList& changes) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Fetch the login list again.
+  password_store_->GetAllLogins(this);
+}
+
+void PasswordStoreSites::OnGetPasswordStoreResults(
+    std::vector<std::unique_ptr<password_manager::PasswordForm>> form_entries) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  password_sites_ = std::set<std::string>();
+  for (const auto& entry : form_entries) {
+    password_sites_->insert(GetSiteNameForURL(entry->url));
+  }
+}
+
+bool PasswordStoreSites::IsSiteInPasswordStore(const GURL& url) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::UmaHistogramBoolean("Login.PasswordStoreSites.InitializedBeforeQuery",
+                            password_sites_.has_value());
+  return password_sites_ && password_sites_->find(GetSiteNameForURL(url)) !=
+                                password_sites_->end();
+}
+
+}  // namespace login_detection
diff --git a/chrome/browser/login_detection/password_store_sites.h b/chrome/browser/login_detection/password_store_sites.h
new file mode 100644
index 0000000..96caf37f
--- /dev/null
+++ b/chrome/browser/login_detection/password_store_sites.h
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_LOGIN_DETECTION_PASSWORD_STORE_SITES_H_
+#define CHROME_BROWSER_LOGIN_DETECTION_PASSWORD_STORE_SITES_H_
+
+#include <set>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/optional.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+
+namespace login_detection {
+
+// Maintains the sites that are saved in password store. These sites will be
+// treated as logged-in.
+class PasswordStoreSites : public password_manager::PasswordStore::Observer,
+                           public password_manager::PasswordStoreConsumer {
+ public:
+  explicit PasswordStoreSites(
+      scoped_refptr<password_manager::PasswordStore> store);
+
+  ~PasswordStoreSites() override;
+
+  // Returns whether the site for |url| has credentials saved in this password
+  // store.
+  bool IsSiteInPasswordStore(const GURL& url) const;
+
+ private:
+  // PasswordStore::Observer:
+  void OnLoginsChanged(
+      const password_manager::PasswordStoreChangeList& changes) override;
+
+  // PasswordStoreConsumer:
+  void OnGetPasswordStoreResults(
+      std::vector<std::unique_ptr<password_manager::PasswordForm>> form_entries)
+      override;
+
+  // The password store |this| is observing site entries from.
+  scoped_refptr<password_manager::PasswordStore> password_store_;
+
+  // Set of sites saved in the password store. Will be base::nullopt until the
+  // sites are retrieved the fist time.
+  base::Optional<std::set<std::string>> password_sites_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+}  // namespace login_detection
+
+#endif  // CHROME_BROWSER_LOGIN_DETECTION_PASSWORD_STORE_SITES_H_
diff --git a/chrome/browser/login_detection/password_store_sites_browsertest.cc b/chrome/browser/login_detection/password_store_sites_browsertest.cc
new file mode 100644
index 0000000..9256d63
--- /dev/null
+++ b/chrome/browser/login_detection/password_store_sites_browsertest.cc
@@ -0,0 +1,191 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/login_detection/password_store_sites.h"
+#include "chrome/browser/password_manager/account_password_store_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.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/password_manager/core/browser/password_store.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace login_detection {
+
+class LoginDetectionPasswordStoreSitesBrowserTest
+    : public InProcessBrowserTest {
+ public:
+  LoginDetectionPasswordStoreSitesBrowserTest() = default;
+
+  scoped_refptr<password_manager::PasswordStore> GetProfilePasswordStore() {
+    return PasswordStoreFactory::GetForProfile(
+        ProfileManager::GetActiveUserProfile(),
+        ServiceAccessType::EXPLICIT_ACCESS);
+  }
+
+  scoped_refptr<password_manager::PasswordStore> GetAccountPasswordStore() {
+    return AccountPasswordStoreFactory::GetForProfile(
+        ProfileManager::GetActiveUserProfile(),
+        ServiceAccessType::EXPLICIT_ACCESS);
+  }
+
+  void AddLoginForSite(password_manager::PasswordStore* password_store,
+                       const GURL& url) {
+    password_manager::PasswordForm form;
+    form.scheme = password_manager::PasswordForm::Scheme::kHtml;
+    form.url = url;
+    form.signon_realm = "https://www.chrome.com";
+    form.username_value = base::ASCIIToUTF16("my_username");
+    form.password_value = base::ASCIIToUTF16("my_password");
+    form.blocked_by_user = false;
+    password_store->AddLogin(form);
+    WaitForPasswordStoreUpdate(password_store);
+  }
+
+  // Wait for the password store taskrunner to complete its event processing.
+  void WaitForPasswordStoreUpdate(
+      password_manager::PasswordStore* password_store) {
+    base::WaitableEvent waitable_event(
+        base::WaitableEvent::ResetPolicy::AUTOMATIC,
+        base::WaitableEvent::InitialState::NOT_SIGNALED);
+    password_store->ScheduleTask(base::BindOnce(
+        &base::WaitableEvent::Signal, base::Unretained(&waitable_event)));
+    waitable_event.Wait();
+
+    // At this point, the password store has completed its processing and posted
+    // events to the main thread.
+    base::RunLoop().RunUntilIdle();
+  }
+
+ protected:
+  base::HistogramTester histogram_tester;
+};
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#define DISABLE_ON_CHROMEOS(x) DISABLED_##x
+#else
+#define DISABLE_ON_CHROMEOS(x) x
+#endif
+
+IN_PROC_BROWSER_TEST_F(LoginDetectionPasswordStoreSitesBrowserTest,
+                       DISABLE_ON_CHROMEOS(ProfilePasswordStore)) {
+  auto password_store = GetProfilePasswordStore();
+  PasswordStoreSites password_store_sites(password_store);
+  WaitForPasswordStoreUpdate(password_store.get());
+  base::RunLoop().RunUntilIdle();
+
+  AddLoginForSite(password_store.get(), GURL("https://www.foo.com/login.html"));
+  WaitForPasswordStoreUpdate(password_store.get());
+  base::RunLoop().RunUntilIdle();
+
+  // The site and its subdomains should exist in the password store.
+  EXPECT_TRUE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://www.foo.com")));
+  EXPECT_TRUE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://foo.com")));
+  EXPECT_TRUE(password_store_sites.IsSiteInPasswordStore(
+      GURL("https://mobile.foo.com")));
+
+  EXPECT_FALSE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://www.bar.com")));
+
+  histogram_tester.ExpectUniqueSample(
+      "Login.PasswordStoreSites.InitializedBeforeQuery", true, 4);
+}
+
+IN_PROC_BROWSER_TEST_F(LoginDetectionPasswordStoreSitesBrowserTest,
+                       DISABLE_ON_CHROMEOS(AccountPasswordStore)) {
+  auto password_store = GetAccountPasswordStore();
+  PasswordStoreSites password_store_sites(password_store);
+  WaitForPasswordStoreUpdate(password_store.get());
+  base::RunLoop().RunUntilIdle();
+
+  AddLoginForSite(password_store.get(), GURL("https://www.foo.com/login.html"));
+  WaitForPasswordStoreUpdate(password_store.get());
+  base::RunLoop().RunUntilIdle();
+
+  // The site and its subdomains should exist in the password store.
+  EXPECT_TRUE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://www.foo.com")));
+  EXPECT_TRUE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://foo.com")));
+  EXPECT_TRUE(password_store_sites.IsSiteInPasswordStore(
+      GURL("https://mobile.foo.com")));
+
+  EXPECT_FALSE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://www.bar.com")));
+
+  histogram_tester.ExpectUniqueSample(
+      "Login.PasswordStoreSites.InitializedBeforeQuery", true, 4);
+}
+
+IN_PROC_BROWSER_TEST_F(LoginDetectionPasswordStoreSitesBrowserTest,
+                       DISABLE_ON_CHROMEOS(AccountPasswordStoreExistingLogin)) {
+  auto password_store = GetAccountPasswordStore();
+  AddLoginForSite(password_store.get(), GURL("https://www.foo.com/login.html"));
+  base::RunLoop().RunUntilIdle();
+
+  PasswordStoreSites password_store_sites(password_store);
+  WaitForPasswordStoreUpdate(password_store.get());
+
+  // The site and its subdomains should exist in the password store.
+  EXPECT_TRUE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://www.foo.com")));
+  EXPECT_TRUE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://foo.com")));
+  EXPECT_TRUE(password_store_sites.IsSiteInPasswordStore(
+      GURL("https://mobile.foo.com")));
+
+  histogram_tester.ExpectUniqueSample(
+      "Login.PasswordStoreSites.InitializedBeforeQuery", true, 3);
+}
+
+IN_PROC_BROWSER_TEST_F(LoginDetectionPasswordStoreSitesBrowserTest,
+                       DISABLE_ON_CHROMEOS(ProfilePasswordStoreExistingLogin)) {
+  auto password_store = GetProfilePasswordStore();
+  AddLoginForSite(password_store.get(), GURL("https://www.foo.com/login.html"));
+  base::RunLoop().RunUntilIdle();
+
+  PasswordStoreSites password_store_sites(password_store);
+  WaitForPasswordStoreUpdate(password_store.get());
+
+  // The site and its subdomains should exist in the password store.
+  EXPECT_TRUE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://www.foo.com")));
+  EXPECT_TRUE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://foo.com")));
+  EXPECT_TRUE(password_store_sites.IsSiteInPasswordStore(
+      GURL("https://mobile.foo.com")));
+
+  histogram_tester.ExpectUniqueSample(
+      "Login.PasswordStoreSites.InitializedBeforeQuery", true, 3);
+}
+
+// Tests querying the password store sites before the password store is
+// initialized.
+IN_PROC_BROWSER_TEST_F(
+    LoginDetectionPasswordStoreSitesBrowserTest,
+    DISABLE_ON_CHROMEOS(QueryBeforePasswordStoreInitialize)) {
+  auto password_store = GetProfilePasswordStore();
+  PasswordStoreSites password_store_sites(password_store);
+
+  EXPECT_FALSE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://www.foo.com")));
+  histogram_tester.ExpectUniqueSample(
+      "Login.PasswordStoreSites.InitializedBeforeQuery", false, 1);
+
+  WaitForPasswordStoreUpdate(password_store.get());
+  EXPECT_FALSE(
+      password_store_sites.IsSiteInPasswordStore(GURL("https://www.foo.com")));
+
+  histogram_tester.ExpectBucketCount(
+      "Login.PasswordStoreSites.InitializedBeforeQuery", true, 1);
+}
+
+}  // namespace login_detection
diff --git a/chrome/browser/resources/chromeos/login/debug/debug.js b/chrome/browser/resources/chromeos/login/debug/debug.js
index 8b97074..33c04221 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug.js
@@ -499,6 +499,7 @@
     {
       id: 'gaia-signin',
       kind: ScreenKind.NORMAL,
+      handledSteps: 'allowlist-error',
       states: [
         {
           id: 'allowlist-customer',
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
index 157ab180..b413deb 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.css
@@ -28,22 +28,12 @@
    * border-top-right-radius: 4px; */
   display: block;
   overflow: hidden;
-  position: static;
-}
-
-:host .step-contents {
-  -webkit-box-pack: center;
-  display: -webkit-box;
-  height: 100%;
 }
 
 #saml-notice-container {
-  align-items: center;
   background: rgb(241, 243, 244); /* #F1F3F4 */
   box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.17);
-  display: flex;
   height: 44px;
-  min-height: 0;
 }
 
 #saml-notice-recording-indicator {
@@ -56,32 +46,11 @@
   font-size: 13px;
 }
 
-.step-loading {
-  align-items: center;
-  bottom: 0;
-  display: flex;
-  justify-content: center;
-  left: 0;
-  min-height: 0;
-  position: absolute;
-  right: 0;
-  top: 0;
-}
-
 .icon20 {
   height: 20px;
   width: 20px;
 }
 
-#gaia-allowlist-error {
-  bottom: 0;
-  display: block;
-  left: 0;
-  position: absolute;
-  right: 0;
-  top: 0;
-}
-
 #signin-frame-container {
   z-index: 10;
 }
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
index a5d2d89..086512f 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
@@ -22,14 +22,14 @@
     <oobe-dialog id="signin-frame-dialog" class="gaia-dialog" role="dialog"
         has-buttons="[[!isSamlSsoVisible_]]"
         no-header no-footer-padding no-lazy
-        hidden="[[!isStep_(step_, 'online-gaia', 'gaia-loading')]]">
+        for-step="online-gaia, gaia-loading">
       <div slot="footer" class="flex layout vertical">
         <div id="signin-frame-container" transparent$="[[isLoadingUiShown_]]"
             animated-transparency$="[[!usedSaml_]]"
             hideshadows$="[[isPopUpOverlayVisible_]]"
             class="flex layout vertical">
           <div id="saml-notice-container"
-               class="layout horizontal"
+               class="layout horizontal center"
                hidden="[[!isSamlSsoVisible_]]">
             <div class="flex layout horizontal center-justified">
               <span id="saml-notice-recording-indicator"
@@ -71,12 +71,11 @@
       </div>
     </oobe-dialog>
     <security-token-pin id="pinDialog" parameters="[[pinDialogParameters_]]"
-        hidden="[[!isStep_(step_, 'pin')]]"
-        on-cancel="onPinDialogCanceled_" on-completed="onPinDialogCompleted_">
+        for-step="pin" on-cancel="onPinDialogCanceled_"
+        on-completed="onPinDialogCompleted_">
     </security-token-pin>
     <oobe-dialog id="saml-interstitial" role="dialog" has-buttons
-        title-key="loginWelcomeMessage"
-        hidden="[[!isStep_(step_, 'saml-interstitial')]]">
+        title-key="loginWelcomeMessage" for-step="saml-interstitial">
       <hd-iron-icon slot="oobe-icon" icon1x="oobe-32:googleg"
           icon2x="oobe-64:googleg">
       </hd-iron-icon>
@@ -100,12 +99,12 @@
             id="interstitial-next" class="focus-on-show"></oobe-next-button>
       </div>
     </oobe-dialog>
-    <div id="gaia-loading" class="step-loading gaia-dialog"
-        hidden="[[!isStep_(step_, 'loading', 'gaia-loading')]]">
+    <div id="gaia-loading" class="layout center center-justified vertical fit"
+        for-step="loading, gaia-loading">
       <throbber-notice text-key="gaiaLoading"></throbber-notice>
     </div>
-    <notification-card id="gaia-allowlist-error" type="fail" class="gaia-dialog"
-        hidden="[[!isStep_(step_, 'allowlist-error')]]"
+    <notification-card id="gaia-allowlist-error" type="fail" class="fit"
+        for-step="allowlist-error"
         button-label="[[i18nDynamic(locale, 'tryAgainButton')]]"
         link-label="[[i18nDynamic(locale, 'learnMoreButton')]]">
     </notification-card>
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index cb61d5cf..3be26b1 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -57,7 +57,11 @@
 Polymer({
   is: 'gaia-signin-element',
 
-  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior, LoginScreenBehavior],
+  behaviors: [
+    OobeI18nBehavior,
+    LoginScreenBehavior,
+    MultiStepBehavior,
+  ],
 
   EXTERNAL_API: [
     'loadAuthExtension',
@@ -80,16 +84,6 @@
     },
 
     /**
-     * Current step displayed.
-     * @type {DialogMode}
-     * @private
-     */
-    step_: {
-      type: String,
-      value: DialogMode.GAIA,
-    },
-
-    /**
      * Whether the screen contents are currently being loaded.
      * @private
      */
@@ -314,6 +308,12 @@
    */
   clickPrimaryActionButtonForTesting_: false,
 
+  defaultUIStep() {
+    return DialogMode.GAIA;
+  },
+
+  UI_STEPS: DialogMode,
+
   /** @override */
   ready() {
     this.authenticator_ = new cr.login.Authenticator(this.getSigninFrame_());
@@ -1357,18 +1357,6 @@
   },
 
   /**
-   * Checks if current step is one of specified steps.
-   * @param {DialogMode} currentStep Name of current step.
-   * @param {...string} stepsVarArgs List of steps to compare with.
-   * @return {boolean}
-   */
-  isStep_(currentStep, ...stepsVarArgs) {
-    if (stepsVarArgs.length < 1)
-      throw Error('At least one step to compare is required.');
-    return stepsVarArgs.some(step => currentStep === step);
-  },
-
-  /**
    * Updates current UI step based on internal state.
    * @param {number} mode
    * @param {OobeTypes.SecurityTokenPinDialogParameter} pinParams
@@ -1378,27 +1366,27 @@
    */
   refreshDialogStep_(mode, pinParams, isLoading, isAllowlistError) {
     if (pinParams !== null) {
-      this.step_ = DialogMode.PIN_DIALOG;
+      this.setUIStep(DialogMode.PIN_DIALOG);
       return;
     }
     if (isLoading) {
       if (mode == AuthMode.DEFAULT) {
-        this.step_ = DialogMode.GAIA_LOADING;
+        this.setUIStep(DialogMode.GAIA_LOADING);
       } else {
-        this.step_ = DialogMode.LOADING;
+        this.setUIStep(DialogMode.LOADING);
       }
       return;
     }
     if (isAllowlistError) {
-      this.step_ = DialogMode.GAIA_ALLOWLIST_ERROR;
+      this.setUIStep(DialogMode.GAIA_ALLOWLIST_ERROR);
       return;
     }
     switch (mode) {
       case AuthMode.DEFAULT:
-        this.step_ = DialogMode.GAIA;
+        this.setUIStep(DialogMode.GAIA);
         break;
       case AuthMode.SAML_INTERSTITIAL:
-        this.step_ = DialogMode.SAML_INTERSTITIAL;
+        this.setUIStep(DialogMode.SAML_INTERSTITIAL);
         break;
     }
   },
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index a9b0594c..96362a9 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -81,10 +81,10 @@
       "certificate_reporting_service_factory.h",
       "chrome_password_protection_service.cc",
       "chrome_password_protection_service.h",
-      "client_side_detection_host.cc",
-      "client_side_detection_host.h",
-      "client_side_detection_service.cc",
-      "client_side_detection_service.h",
+      "client_side_detection_host_delegate.cc",
+      "client_side_detection_host_delegate.h",
+      "client_side_detection_service_delegate.cc",
+      "client_side_detection_service_delegate.h",
       "client_side_detection_service_factory.cc",
       "client_side_detection_service_factory.h",
       "delayed_warning_navigation_throttle.cc",
@@ -122,6 +122,7 @@
       "//chrome/common/safe_browsing:proto",
       "//components/safe_browsing/content",
       "//components/safe_browsing/content/browser",
+      "//components/safe_browsing/content/browser:client_side_detection",
       "//components/safe_browsing/content/browser:client_side_model_loader",
       "//components/safe_browsing/content/password_protection",
       "//components/safe_browsing/content/triggers:ad_popup_trigger",
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc b/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc
index 675719c..e4f1d97e 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc
@@ -2,17 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/safe_browsing/client_side_detection_host.h"
+#include "chrome/browser/safe_browsing/client_side_detection_host_delegate.h"
 
 #include "base/run_loop.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
+#include "chrome/browser/safe_browsing/ui_manager.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
 #include "components/safe_browsing/core/proto/client_model.pb.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -112,7 +113,7 @@
 
   ASSERT_TRUE(embedded_test_server()->Start());
   std::unique_ptr<ClientSideDetectionHost> csd_host =
-      ClientSideDetectionHost::Create(
+      ClientSideDetectionHostDelegate::CreateHost(
           browser()->tab_strip_model()->GetActiveWebContents());
   csd_host->set_client_side_detection_service(&fake_csd_service);
   csd_host->SendModelToRenderFrame();
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_delegate.cc b/chrome/browser/safe_browsing/client_side_detection_host_delegate.cc
new file mode 100644
index 0000000..372e8b4
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_detection_host_delegate.cc
@@ -0,0 +1,58 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/client_side_detection_host_delegate.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/client_side_detection_service_factory.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/safe_browsing/user_interaction_observer.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/content/browser/client_side_detection_host.h"
+#include "components/safe_browsing/core/db/database_manager.h"
+
+namespace safe_browsing {
+
+// static
+std::unique_ptr<ClientSideDetectionHost>
+ClientSideDetectionHostDelegate::CreateHost(content::WebContents* tab) {
+  return ClientSideDetectionHost::Create(
+      tab, std::make_unique<ClientSideDetectionHostDelegate>(tab));
+}
+
+ClientSideDetectionHostDelegate::ClientSideDetectionHostDelegate(
+    content::WebContents* web_contents)
+    : web_contents_(web_contents) {}
+ClientSideDetectionHostDelegate::~ClientSideDetectionHostDelegate() = default;
+
+bool ClientSideDetectionHostDelegate::HasSafeBrowsingUserInteractionObserver() {
+  return SafeBrowsingUserInteractionObserver::FromWebContents(web_contents_);
+}
+
+PrefService* ClientSideDetectionHostDelegate::GetPrefs() {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
+  return profile ? profile->GetPrefs() : nullptr;
+}
+
+scoped_refptr<SafeBrowsingDatabaseManager>
+ClientSideDetectionHostDelegate::GetSafeBrowsingDBManager() {
+  SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service();
+  return sb_service ? sb_service->database_manager().get() : nullptr;
+}
+
+scoped_refptr<BaseUIManager>
+ClientSideDetectionHostDelegate::GetSafeBrowsingUIManager() {
+  SafeBrowsingService* sb_service = g_browser_process->safe_browsing_service();
+  return sb_service ? sb_service->ui_manager() : nullptr;
+}
+
+ClientSideDetectionService*
+ClientSideDetectionHostDelegate::GetClientSideDetectionService() {
+  return ClientSideDetectionServiceFactory::GetForProfile(
+      Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_delegate.h b/chrome/browser/safe_browsing/client_side_detection_host_delegate.h
new file mode 100644
index 0000000..97241ca4
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_detection_host_delegate.h
@@ -0,0 +1,39 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_DELEGATE_H_
+#define CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_DELEGATE_H_
+
+#include "components/safe_browsing/content/browser/client_side_detection_host.h"
+
+namespace safe_browsing {
+
+// Delegate class which implements chrome specific bits for configuring
+// the ClientSideDetectionHost class.
+class ClientSideDetectionHostDelegate
+    : public ClientSideDetectionHost::Delegate {
+ public:
+  static std::unique_ptr<ClientSideDetectionHost> CreateHost(
+      content::WebContents* tab);
+
+  explicit ClientSideDetectionHostDelegate(content::WebContents* web_contents);
+  ~ClientSideDetectionHostDelegate() override;
+
+  // ClientSideDetectionHost::Delegate implementation.
+  bool HasSafeBrowsingUserInteractionObserver() override;
+  PrefService* GetPrefs() override;
+  scoped_refptr<SafeBrowsingDatabaseManager> GetSafeBrowsingDBManager()
+      override;
+  scoped_refptr<BaseUIManager> GetSafeBrowsingUIManager() override;
+  ClientSideDetectionService* GetClientSideDetectionService() override;
+
+ private:
+  content::WebContents* web_contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionHostDelegate);
+};
+
+}  // namespace safe_browsing
+
+#endif  // CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_DELEGATE_H_
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index a2df3a6..25f93b6 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/safe_browsing/client_side_detection_host.h"
+#include "chrome/browser/safe_browsing/client_side_detection_host_delegate.h"
 
 #include <memory>
 #include <tuple>
@@ -19,13 +19,13 @@
 #include "base/test/gmock_move_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_tick_clock.h"
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
 #include "components/safe_browsing/content/browser/client_side_model_loader.h"
 #include "components/safe_browsing/content/common/safe_browsing.mojom-shared.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
@@ -278,7 +278,7 @@
         static_cast<safe_browsing::SafeBrowsingService*>(
             SafeBrowsingService::CreateSafeBrowsingService()));
 
-    csd_host_ = ClientSideDetectionHost::Create(web_contents());
+    csd_host_ = ClientSideDetectionHostDelegate::CreateHost(web_contents());
     csd_host_->set_client_side_detection_service(csd_service_.get());
     csd_host_->set_ui_manager(ui_manager_.get());
     csd_host_->set_database_manager(database_manager_.get());
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_browsertest.cc b/chrome/browser/safe_browsing/client_side_detection_service_browsertest.cc
index fb68b3f..57f82bc5 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
+#include "chrome/browser/safe_browsing/client_side_detection_service_delegate.h"
 
 #include "base/test/bind.h"
 #include "chrome/browser/profiles/profile.h"
@@ -11,6 +11,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
 #include "components/safe_browsing/content/common/safe_browsing.mojom.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/core/proto/client_model.pb.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_delegate.cc b/chrome/browser/safe_browsing/client_side_detection_service_delegate.cc
new file mode 100644
index 0000000..37c957ae
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_detection_service_delegate.cc
@@ -0,0 +1,52 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/client_side_detection_service_delegate.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/policy/chrome_browser_policy_connector.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/core/common/utils.h"
+
+namespace safe_browsing {
+
+ClientSideDetectionServiceDelegate::ClientSideDetectionServiceDelegate(
+    Profile* profile)
+    : profile_(profile) {}
+
+ClientSideDetectionServiceDelegate::~ClientSideDetectionServiceDelegate() =
+    default;
+
+PrefService* ClientSideDetectionServiceDelegate::GetPrefs() {
+  if (profile_) {
+    return profile_->GetPrefs();
+  }
+  return nullptr;
+}
+scoped_refptr<network::SharedURLLoaderFactory>
+ClientSideDetectionServiceDelegate::GetURLLoaderFactory() {
+  if (profile_) {
+    return profile_->GetURLLoaderFactory();
+  }
+  return nullptr;
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+ClientSideDetectionServiceDelegate::GetSafeBrowsingURLLoaderFactory() {
+  if (g_browser_process->safe_browsing_service()) {
+    return g_browser_process->safe_browsing_service()->GetURLLoaderFactory(
+        profile_);
+  }
+  return nullptr;
+}
+
+ChromeUserPopulation::ProfileManagementStatus
+ClientSideDetectionServiceDelegate::GetManagementStatus() {
+  return GetProfileManagementStatus(
+      g_browser_process->browser_policy_connector());
+}
+
+}  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_delegate.h b/chrome/browser/safe_browsing/client_side_detection_service_delegate.h
new file mode 100644
index 0000000..7720216
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_detection_service_delegate.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_DELEGATE_H_
+#define CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_DELEGATE_H_
+
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
+
+class Profile;
+
+namespace safe_browsing {
+
+// Delegate class which implements chrome specific bits for configuring
+// the ClientSideDetectionService class.
+class ClientSideDetectionServiceDelegate
+    : public ClientSideDetectionService::Delegate {
+ public:
+  explicit ClientSideDetectionServiceDelegate(Profile* profile);
+  ~ClientSideDetectionServiceDelegate() override;
+
+  // ClientSideDetectionService::Delegate implementation.
+  PrefService* GetPrefs() override;
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
+  scoped_refptr<network::SharedURLLoaderFactory>
+  GetSafeBrowsingURLLoaderFactory() override;
+  ChromeUserPopulation::ProfileManagementStatus GetManagementStatus() override;
+
+ private:
+  Profile* profile_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionServiceDelegate);
+};
+
+}  // namespace safe_browsing
+
+#endif  // CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_DELEGATE_H_
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_factory.cc b/chrome/browser/safe_browsing/client_side_detection_service_factory.cc
index ed2b9b13..e1dbb67 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_factory.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_factory.cc
@@ -7,10 +7,11 @@
 #include "base/command_line.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
+#include "chrome/browser/safe_browsing/client_side_detection_service_delegate.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/safe_browsing/buildflags.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
 #include "components/safe_browsing/core/features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -53,7 +54,8 @@
     return nullptr;
 
   Profile* profile = Profile::FromBrowserContext(context);
-  return new ClientSideDetectionService(profile);
+  return new ClientSideDetectionService(
+      std::make_unique<ClientSideDetectionServiceDelegate>(profile));
 }
 
 content::BrowserContext*
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_factory_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_factory_unittest.cc
index 99b9436..b5816b1 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_factory_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_factory_unittest.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/safe_browsing/client_side_detection_service_factory.h"
 
 #include "base/command_line.h"
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_profile.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
index 4dcf33a..dcd2e92 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
+#include "chrome/browser/safe_browsing/client_side_detection_service_delegate.h"
 
 #include <stdint.h>
 
@@ -23,6 +23,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/core/proto/client_model.pb.h"
 #include "components/safe_browsing/core/proto/csd.pb.h"
@@ -227,7 +228,8 @@
 
 TEST_F(ClientSideDetectionServiceTest, ServiceObjectDeletedBeforeCallbackDone) {
   SetModelFetchResponses();
-  csd_service_ = std::make_unique<ClientSideDetectionService>(profile_);
+  csd_service_ = std::make_unique<ClientSideDetectionService>(
+      std::make_unique<ClientSideDetectionServiceDelegate>(profile_));
   profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
   EXPECT_NE(csd_service_.get(), nullptr);
   // We delete the client-side detection service class even though the callbacks
@@ -240,7 +242,8 @@
 
 TEST_F(ClientSideDetectionServiceTest, SendClientReportPhishingRequest) {
   SetModelFetchResponses();
-  csd_service_ = std::make_unique<ClientSideDetectionService>(profile_);
+  csd_service_ = std::make_unique<ClientSideDetectionService>(
+      std::make_unique<ClientSideDetectionServiceDelegate>(profile_));
   csd_service_->SetURLLoaderFactoryForTesting(test_shared_loader_factory_);
 
   GURL url("http://a.com/");
@@ -296,7 +299,8 @@
 
 TEST_F(ClientSideDetectionServiceTest, GetNumReportTest) {
   SetModelFetchResponses();
-  csd_service_ = std::make_unique<ClientSideDetectionService>(profile_);
+  csd_service_ = std::make_unique<ClientSideDetectionService>(
+      std::make_unique<ClientSideDetectionServiceDelegate>(profile_));
 
   base::queue<base::Time>& report_times = GetPhishingReportTimes();
   base::Time now = base::Time::Now();
@@ -312,14 +316,16 @@
 
 TEST_F(ClientSideDetectionServiceTest, CacheTest) {
   SetModelFetchResponses();
-  csd_service_ = std::make_unique<ClientSideDetectionService>(profile_);
+  csd_service_ = std::make_unique<ClientSideDetectionService>(
+      std::make_unique<ClientSideDetectionServiceDelegate>(profile_));
 
   TestCache();
 }
 
 TEST_F(ClientSideDetectionServiceTest, IsPrivateIPAddress) {
   SetModelFetchResponses();
-  csd_service_ = std::make_unique<ClientSideDetectionService>(profile_);
+  csd_service_ = std::make_unique<ClientSideDetectionService>(
+      std::make_unique<ClientSideDetectionServiceDelegate>(profile_));
 
   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("10.1.2.3"));
   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("127.0.0.1"));
@@ -343,7 +349,8 @@
 TEST_F(ClientSideDetectionServiceTest, SetEnabledAndRefreshState) {
   // Check that the model isn't downloaded until the service is enabled.
   profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, false);
-  csd_service_ = std::make_unique<ClientSideDetectionService>(profile_);
+  csd_service_ = std::make_unique<ClientSideDetectionService>(
+      std::make_unique<ClientSideDetectionServiceDelegate>(profile_));
   EXPECT_FALSE(csd_service_->enabled());
   EXPECT_TRUE(csd_service_->model_loader_ == nullptr);
 
@@ -391,7 +398,8 @@
   profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled,
                                    false);
   profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false);
-  csd_service_ = std::make_unique<ClientSideDetectionService>(profile_);
+  csd_service_ = std::make_unique<ClientSideDetectionService>(
+      std::make_unique<ClientSideDetectionServiceDelegate>(profile_));
 
   // Safe Browsing is not enabled.
   EXPECT_EQ(csd_service_->model_loader_, nullptr);
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc
index 8f9c14a..2a17226 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service.cc
@@ -59,7 +59,6 @@
 #endif
 
 #if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
 #include "components/safe_browsing/content/password_protection/password_protection_service.h"
 #endif
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index 285cb1f..f314ca5 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -47,7 +47,6 @@
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
 #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
diff --git a/chrome/browser/safe_browsing/safe_browsing_tab_observer.cc b/chrome/browser/safe_browsing/safe_browsing_tab_observer.cc
index be8c216..44e5b59 100644
--- a/chrome/browser/safe_browsing/safe_browsing_tab_observer.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_tab_observer.cc
@@ -18,10 +18,11 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 
 #if BUILDFLAG(SAFE_BROWSING_AVAILABLE)
-#include "chrome/browser/safe_browsing/client_side_detection_host.h"
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
+#include "chrome/browser/safe_browsing/client_side_detection_host_delegate.h"
 #include "chrome/browser/safe_browsing/client_side_detection_service_factory.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
+#include "components/safe_browsing/content/browser/client_side_detection_host.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #endif
 
@@ -55,7 +56,7 @@
     if (IsSafeBrowsingEnabled(*prefs) &&
         g_browser_process->safe_browsing_service() && csd_service) {
       safebrowsing_detection_host_ =
-          ClientSideDetectionHost::Create(web_contents);
+          ClientSideDetectionHostDelegate::CreateHost(web_contents);
       csd_service->AddClientSideDetectionHost(
           safebrowsing_detection_host_.get());
     }
@@ -80,7 +81,7 @@
   if (safe_browsing && csd_service) {
     if (!safebrowsing_detection_host_.get()) {
       safebrowsing_detection_host_ =
-          ClientSideDetectionHost::Create(web_contents_);
+          ClientSideDetectionHostDelegate::CreateHost(web_contents_);
       csd_service->AddClientSideDetectionHost(
           safebrowsing_detection_host_.get());
     }
diff --git a/chrome/browser/safe_browsing/services_delegate_desktop.h b/chrome/browser/safe_browsing/services_delegate_desktop.h
index 00f0d802..d293a6b7 100644
--- a/chrome/browser/safe_browsing/services_delegate_desktop.h
+++ b/chrome/browser/safe_browsing/services_delegate_desktop.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
 #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
 #include "chrome/browser/safe_browsing/services_delegate.h"
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/IdentityServicesProvider.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/IdentityServicesProvider.java
index 28641b3..e5e7b2e 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/IdentityServicesProvider.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/IdentityServicesProvider.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.signin.services;
 
+import androidx.annotation.MainThread;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.ThreadUtils;
@@ -19,9 +20,7 @@
 public class IdentityServicesProvider {
     private static IdentityServicesProvider sIdentityServicesProvider;
 
-    // TODO(https://crbug.com/1152718): Change the ctor to private once the derived
-    // signin.IdentityServicesProvider is removed
-    protected IdentityServicesProvider() {}
+    private IdentityServicesProvider() {}
 
     public static IdentityServicesProvider get() {
         if (sIdentityServicesProvider == null) {
@@ -40,6 +39,7 @@
      * @param profile The profile to get regarding identity manager.
      * @return a {@link IdentityManager} instance.
      */
+    @MainThread
     public IdentityManager getIdentityManager(Profile profile) {
         ThreadUtils.assertOnUiThread();
         IdentityManager result = IdentityServicesProviderJni.get().getIdentityManager(profile);
@@ -48,19 +48,11 @@
     }
 
     /**
-     * Getter for {@link IdentityManager} instance.
-     * Deprecated, use {@link IdentityServicesProvider#getIdentityManager(Profile)} instead.
-     */
-    @Deprecated
-    public IdentityManager getIdentityManager() {
-        return getIdentityManager(Profile.getLastUsedRegularProfile());
-    }
-
-    /**
      * Getter for {@link AccountTrackerService} instance for given profile.
      * @param profile The profile to get regarding account tracker service.
      * @return a {@link AccountTrackerService} instance.
      */
+    @MainThread
     public AccountTrackerService getAccountTrackerService(Profile profile) {
         ThreadUtils.assertOnUiThread();
         AccountTrackerService result =
@@ -70,19 +62,11 @@
     }
 
     /**
-     * Getter for {@link AccountTrackerService} instance.
-     * Deprecated, use {@link IdentityServicesProvider#getAccountTrackerService(Profile)} instead.
-     */
-    @Deprecated
-    public AccountTrackerService getAccountTrackerService() {
-        return getAccountTrackerService(Profile.getLastUsedRegularProfile());
-    }
-
-    /**
      * Getter for {@link SigninManager} instance for given profile.
      * @param profile The profile to get regarding sign-in manager.
      * @return a {@link SigninManager} instance.
      */
+    @MainThread
     public SigninManager getSigninManager(Profile profile) {
         ThreadUtils.assertOnUiThread();
         SigninManager result = IdentityServicesProviderJni.get().getSigninManager(profile);
@@ -90,15 +74,6 @@
         return result;
     }
 
-    /**
-     * Getter for {@link SigninManager} instance.
-     * Deprecated, use {@link IdentityServicesProvider#getSigninManager(Profile)} instead.
-     */
-    @Deprecated
-    public SigninManager getSigninManager() {
-        return getSigninManager(Profile.getLastUsedRegularProfile());
-    }
-
     @NativeMethods
     public interface Natives {
         IdentityManager getIdentityManager(Profile profile);
diff --git a/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SignOutDialogRenderTest.java b/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SignOutDialogRenderTest.java
index 1a8c405..046a4288 100644
--- a/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SignOutDialogRenderTest.java
+++ b/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SignOutDialogRenderTest.java
@@ -4,10 +4,6 @@
 
 package org.chromium.chrome.browser.signin.ui;
 
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.pressBack;
-import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -38,6 +34,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ChromeRenderTestRule;
 import org.chromium.components.signin.GAIAServiceType;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.DisableAnimationsTestRule;
 import org.chromium.ui.test.util.DummyUiActivityTestCase;
 
@@ -69,6 +66,8 @@
     @Mock
     private Profile mProfile;
 
+    private SignOutDialogFragment mSignOutDialog;
+
     @Before
     public void setUp() {
         initMocks(this);
@@ -82,7 +81,9 @@
     public void tearDown() {
         // Since the Dialog dismiss calls native method, we need to close the dialog before the
         // Native mock SigninMetricsUtils.Natives gets removed.
-        onView(isRoot()).perform(pressBack());
+        if (mSignOutDialog != null) {
+            TestThreadUtils.runOnUiThreadBlocking(() -> mSignOutDialog.dismiss());
+        }
     }
 
     @Test
@@ -101,10 +102,9 @@
     }
 
     private View showSignOutDialog() {
-        SignOutDialogFragment signOutDialog =
-                SignOutDialogFragment.create(GAIAServiceType.GAIA_SERVICE_TYPE_NONE);
-        signOutDialog.show(getActivity().getSupportFragmentManager(), null);
+        mSignOutDialog = SignOutDialogFragment.create(GAIAServiceType.GAIA_SERVICE_TYPE_NONE);
+        mSignOutDialog.show(getActivity().getSupportFragmentManager(), null);
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        return signOutDialog.getDialog().getWindow().getDecorView();
+        return mSignOutDialog.getDialog().getWindow().getDecorView();
     }
 }
diff --git a/chrome/browser/tracing/chrome_tracing_delegate.cc b/chrome/browser/tracing/chrome_tracing_delegate.cc
index a218fbb..b2350696 100644
--- a/chrome/browser/tracing/chrome_tracing_delegate.cc
+++ b/chrome/browser/tracing/chrome_tracing_delegate.cc
@@ -131,7 +131,8 @@
 }
 
 bool ProfileAllowsScenario(const content::BackgroundTracingConfig& config,
-                           PermitMissingProfile profile_permission) {
+                           PermitMissingProfile profile_permission,
+                           bool is_crash_scenario) {
   // If the background tracing is specified on the command-line, we allow
   // any scenario to be traced.
   auto* command_line = base::CommandLine::ForCurrentProcess();
@@ -151,6 +152,27 @@
     return profile_permission != PROFILE_REQUIRED;
   }
 
+  PrefService* local_state = g_browser_process->local_state();
+  DCHECK(local_state);
+
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && defined(OFFICIAL_BUILD)
+  if (!local_state->GetBoolean(metrics::prefs::kMetricsReportingEnabled)) {
+    RecordDisallowedMetric(
+        TracingFinalizationDisallowedReason::kMetricsReportingDisabled);
+    return false;
+  }
+#endif  // !OS_CHROMEOS && OFFICIAL_BUILD
+
+  // Skip the rest of the checks, we know that the scenario does not have
+  // incognito profile and metrics reporting is enabled. Skip the upload limit
+  // checks.
+  if (is_crash_scenario) {
+    // Maybe we shouldn't skip the browser crash test when session begins (when
+    // PROFILE_NOT_REQUIRED).
+    DCHECK_EQ(PROFILE_REQUIRED, profile_permission);
+    return true;
+  }
+
 // Safeguard, in case background tracing is responsible for a crash on
 // startup.
 #if !defined(OS_ANDROID)
@@ -177,17 +199,6 @@
   }
 #endif
 
-  PrefService* local_state = g_browser_process->local_state();
-  DCHECK(local_state);
-
-#if !BUILDFLAG(IS_CHROMEOS_ASH) && defined(OFFICIAL_BUILD)
-  if (!local_state->GetBoolean(metrics::prefs::kMetricsReportingEnabled)) {
-    RecordDisallowedMetric(
-        TracingFinalizationDisallowedReason::kMetricsReportingDisabled);
-    return false;
-  }
-#endif // !OS_CHROMEOS && OFFICIAL_BUILD
-
   if (config.tracing_mode() == content::BackgroundTracingConfig::PREEMPTIVE) {
     const base::Time last_upload_time = base::Time::FromInternalValue(
         local_state->GetInt64(prefs::kBackgroundTracingLastUpload));
@@ -210,8 +221,14 @@
 bool ChromeTracingDelegate::IsAllowedToBeginBackgroundScenario(
     const content::BackgroundTracingConfig& config,
     bool requires_anonymized_data) {
-  if (!ProfileAllowsScenario(config, PROFILE_NOT_REQUIRED))
+  // For crash-triggered traces, we can only support preemptive tracing. For
+  // such preemptive traces, the profile will not be loaded yet, and calling
+  // ProfileAllowsScenario() will return true to allow the trace to start,
+  // regardless of the value of is_crash_scenario.
+  if (!ProfileAllowsScenario(config, PROFILE_NOT_REQUIRED,
+                             /*is_crash_scenario=*/false)) {
     return false;
+  }
 
   if (requires_anonymized_data && chrome::IsOffTheRecordSessionActive())
     return false;
@@ -221,7 +238,8 @@
 
 bool ChromeTracingDelegate::IsAllowedToEndBackgroundScenario(
     const content::BackgroundTracingConfig& config,
-    bool requires_anonymized_data) {
+    bool requires_anonymized_data,
+    bool is_crash_scenario) {
   if (requires_anonymized_data &&
       (incognito_launched_ || chrome::IsOffTheRecordSessionActive())) {
     RecordDisallowedMetric(
@@ -229,7 +247,7 @@
     return false;
   }
 
-  if (!ProfileAllowsScenario(config, PROFILE_REQUIRED))
+  if (!ProfileAllowsScenario(config, PROFILE_REQUIRED, is_crash_scenario))
     return false;
 
   if (config.tracing_mode() == content::BackgroundTracingConfig::PREEMPTIVE) {
diff --git a/chrome/browser/tracing/chrome_tracing_delegate.h b/chrome/browser/tracing/chrome_tracing_delegate.h
index 49e922f..df31394 100644
--- a/chrome/browser/tracing/chrome_tracing_delegate.h
+++ b/chrome/browser/tracing/chrome_tracing_delegate.h
@@ -44,7 +44,8 @@
 
   bool IsAllowedToEndBackgroundScenario(
       const content::BackgroundTracingConfig& config,
-      bool requires_anonymized_data) override;
+      bool requires_anonymized_data,
+      bool is_crash_scenario) override;
 
   bool IsProfileLoaded() override;
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index e85de09..24f9c851 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1608767282-7469f12bc7883325d24b820537c86a4836b7599a.profdata
+chrome-linux-master-1608938443-5e2a6a5509ef0c35b284bb575c523599912f115a.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 406ef383..82ffbf63 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1608767282-017d7dadc4601ccad45290574822bb174972e9d0.profdata
+chrome-mac-master-1608938443-813619ab6447ba11a985c78bfacde75dce409fd5.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 74d9b0a..95774ae 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1608692346-a5866de54d0b0b083300633703188b0bd8d56834.profdata
+chrome-win64-master-1608913095-f136f8f83569a182f681a83edbe456e8f7d0cecd.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 6f25ca93..ab655a5a 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1078,6 +1078,7 @@
       "../browser/loadtimes_extension_bindings_browsertest.cc",
       "../browser/locale_tests_browsertest.cc",
       "../browser/login_detection/login_detection_browsertest.cc",
+      "../browser/login_detection/password_store_sites_browsertest.cc",
       "../browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc",
       "../browser/media/autoplay_metrics_browsertest.cc",
       "../browser/media/cast_mirroring_performance_browsertest.cc",
diff --git a/chrome/test/data/webui/new_tab_page/utils_test.js b/chrome/test/data/webui/new_tab_page/utils_test.js
index 19ad9f9..6795d37 100644
--- a/chrome/test/data/webui/new_tab_page/utils_test.js
+++ b/chrome/test/data/webui/new_tab_page/utils_test.js
@@ -6,7 +6,7 @@
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 
 import {createScrollBorders, decodeString16, mojoString16} from 'chrome://new-tab-page/new_tab_page.js';
-import {waitAfterNextRender} from 'chrome://test/test_util.m.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://test/test_util.m.js';
 
 suite('scroll borders', () => {
   /** @type {!HTMLElement} */
@@ -44,6 +44,7 @@
     bottom = document.body.lastElementChild;
     observer = createScrollBorders(container, top, bottom, 'show');
     await waitAfterNextRender();
+    await flushTasks();
   });
 
   teardown(() => {
@@ -58,6 +59,7 @@
   test('borders shown when content available above and below', async () => {
     container.scrollTop = 10;
     await waitAfterNextRender();
+    await flushTasks();
     assertShown(top);
     assertShown(bottom);
   });
@@ -65,6 +67,7 @@
   test('bottom border hidden when no content available below', async () => {
     container.scrollTop = 200;
     await waitAfterNextRender();
+    await flushTasks();
     assertShown(top);
     assertHidden(bottom);
   });
@@ -72,6 +75,7 @@
   test('borders hidden when all content is shown', async () => {
     content.style.height = '100px';
     await waitAfterNextRender();
+    await flushTasks();
     assertHidden(top);
     assertHidden(bottom);
   });
diff --git a/chrome/test/data/webui/test_util.js b/chrome/test/data/webui/test_util.js
index e07d5f5..f0d8b88 100644
--- a/chrome/test/data/webui/test_util.js
+++ b/chrome/test/data/webui/test_util.js
@@ -132,7 +132,7 @@
     // Promises have microtask timing, so we use setTimeout to explicitly force
     // a new task.
     return new Promise(function(resolve, reject) {
-      window.setTimeout(resolve, 0);
+      window.setTimeout(resolve, 1);
     });
   }
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index d1246b1..470481e 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13681.0.0
\ No newline at end of file
+13685.0.0
\ No newline at end of file
diff --git a/chromeos/components/camera_app_ui/camera_app_ui.cc b/chromeos/components/camera_app_ui/camera_app_ui.cc
index b795142..3de983eb 100644
--- a/chromeos/components/camera_app_ui/camera_app_ui.cc
+++ b/chromeos/components/camera_app_ui/camera_app_ui.cc
@@ -6,6 +6,7 @@
 
 #include "ash/public/cpp/window_properties.h"
 #include "base/bind.h"
+#include "base/strings/string_util.h"
 #include "chromeos/components/camera_app_ui/camera_app_helper_impl.h"
 #include "chromeos/components/camera_app_ui/resources.h"
 #include "chromeos/components/camera_app_ui/url_constants.h"
@@ -63,6 +64,9 @@
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::ChildSrc,
       std::string("frame-src ") + kChromeUIUntrustedCameraAppURL + ";");
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::ObjectSrc,
+      std::string("object-src 'self';"));
 
   return source;
 }
@@ -263,12 +267,15 @@
 }
 
 const GURL& CameraAppUI::url() {
-  return web_ui()->GetWebContents()->GetURL();
+  return web_ui()->GetWebContents()->GetLastCommittedURL();
 }
 
 void CameraAppUI::DevToolsAgentHostAttached(
     content::DevToolsAgentHost* agent_host) {
-  if (agent_host->GetWebContents() != web_ui()->GetWebContents()) {
+  if (agent_host->GetWebContents() == nullptr ||
+      !base::StartsWith(
+          agent_host->GetWebContents()->GetLastCommittedURL().spec(),
+          kChromeUICameraAppMainURL)) {
     return;
   }
   app_window_manager()->SetDevToolsEnabled(true);
@@ -276,7 +283,10 @@
 
 void CameraAppUI::DevToolsAgentHostDetached(
     content::DevToolsAgentHost* agent_host) {
-  if (agent_host->GetWebContents() != web_ui()->GetWebContents()) {
+  if (agent_host->GetWebContents() == nullptr ||
+      !base::StartsWith(
+          agent_host->GetWebContents()->GetLastCommittedURL().spec(),
+          kChromeUICameraAppMainURL)) {
     return;
   }
   app_window_manager()->SetDevToolsEnabled(false);
diff --git a/chromeos/components/camera_app_ui/camera_app_window_manager.cc b/chromeos/components/camera_app_ui/camera_app_window_manager.cc
index 655faf3..6a17f68 100644
--- a/chromeos/components/camera_app_ui/camera_app_window_manager.cc
+++ b/chromeos/components/camera_app_ui/camera_app_window_manager.cc
@@ -35,7 +35,9 @@
                      base::Unretained(this), widget));
   camera_usage_monitors_.emplace(widget, std::move(remote));
 
-  widget->AddObserver(this);
+  if (!widget->HasObserver(this)) {
+    widget->AddObserver(this);
+  }
   std::move(callback).Run();
 
   if (widget->IsVisible()) {
@@ -130,11 +132,15 @@
   }
 }
 
+void CameraAppWindowManager::OnWidgetDestroying(views::Widget* widget) {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  widget->RemoveObserver(this);
+}
+
 CameraAppWindowManager::CameraAppWindowManager() = default;
 
 void CameraAppWindowManager::OnMonitorMojoConnectionError(
     views::Widget* widget) {
-  widget->RemoveObserver(this);
   camera_usage_monitors_.erase(widget);
 
   if (pending_transfer_.has_value() && widget == *pending_transfer_) {
diff --git a/chromeos/components/camera_app_ui/camera_app_window_manager.h b/chromeos/components/camera_app_ui/camera_app_window_manager.h
index 82945f8..25512e3 100644
--- a/chromeos/components/camera_app_ui/camera_app_window_manager.h
+++ b/chromeos/components/camera_app_ui/camera_app_window_manager.h
@@ -45,6 +45,7 @@
   // views::WidgetObserver:
   void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
   void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
+  void OnWidgetDestroying(views::Widget* widget) override;
 
  private:
   CameraAppWindowManager();
diff --git a/chromeos/components/camera_app_ui/resources.h b/chromeos/components/camera_app_ui/resources.h
index b157d7d..cf0ecf0 100644
--- a/chromeos/components/camera_app_ui/resources.h
+++ b/chromeos/components/camera_app_ui/resources.h
@@ -58,6 +58,7 @@
     {"barcode_copy_link_button", IDS_BARCODE_COPY_LINK_BUTTON},
     {"barcode_copy_text_button", IDS_BARCODE_COPY_TEXT_BUTTON},
     {"barcode_link_detected", IDS_BARCODE_LINK_DETECTED},
+    {"barcode_text_detected", IDS_BARCODE_TEXT_DETECTED},
     {"expert_mode_button", IDS_EXPERT_MODE_BUTTON},
     {"expert_preview_metadata", IDS_EXPERT_PREVIEW_METADATA},
     {"expert_save_metadata", IDS_EXPERT_SAVE_METADATA},
diff --git a/chromeos/components/camera_app_ui/resources/BUILD.gn b/chromeos/components/camera_app_ui/resources/BUILD.gn
index 59078aa..58ad039f4 100644
--- a/chromeos/components/camera_app_ui/resources/BUILD.gn
+++ b/chromeos/components/camera_app_ui/resources/BUILD.gn
@@ -91,6 +91,7 @@
     "js/sound.js",
     "js/state.js",
     "js/test_bridge.js",
+    "js/timer.js",
     "js/toast.js",
     "js/tooltip.js",
     "js/type.js",
diff --git a/chromeos/components/camera_app_ui/resources/camera_app_resources.grd b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
index f212340..6bc25a6 100644
--- a/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
+++ b/chromeos/components/camera_app_ui/resources/camera_app_resources.grd
@@ -77,6 +77,7 @@
       <structure name="IDR_CAMERA_TEST_BRIDGE_JS" file="js/test_bridge.js" type="chrome_html" />
       <structure name="IDR_CAMERA_TEST_HTML" file="test/test.html" type="chrome_html" />
       <structure name="IDR_CAMERA_TEST_LEGACY_HTML" file="views/test.html" type="chrome_html" />
+      <structure name="IDR_CAMERA_TIMER_JS" file="js/timer.js" type="chrome_html" />
       <structure name="IDR_CAMERA_TIMERTICK_JS" file="js/views/camera/timertick.js" type="chrome_html" />
       <structure name="IDR_CAMERA_TOAST_JS" file="js/toast.js" type="chrome_html" />
       <structure name="IDR_CAMERA_TOOLTIP_JS" file="js/tooltip.js" type="chrome_html" />
diff --git a/chromeos/components/camera_app_ui/resources/css/main.css b/chromeos/components/camera_app_ui/resources/css/main.css
index a32f445..0b88716a 100644
--- a/chromeos/components/camera_app_ui/resources/css/main.css
+++ b/chromeos/components/camera_app_ui/resources/css/main.css
@@ -3,6 +3,7 @@
  * found in the LICENSE file. */
 
 :root {
+  /* Standard CrOS styles */
   --blue-300-rgb: 138, 180, 248;
   --blue-300: rgb(var(--blue-300-rgb));
   --blue-600-dark-rgb: 37, 129, 223;
@@ -11,6 +12,18 @@
   --grey-200: rgb(232, 234, 237);
   --grey-900: rgb(32, 33, 36);
 
+  --fast1-duration: 100ms;
+  --fast2-duration: 200ms;
+  --moderate1-duration: 250ms;
+  --moderate2-duration: 500ms;
+  --slow1-duration: 600ms;
+  --slow2-duration: 1000ms;
+
+  --standard-easing: cubic-bezier(0.4, 0, 0.2, 1);
+  --enter-easing: cubic-bezier(0, 0, 0.2, 1);
+  --exit-easing: cubic-bezier(0.4, 0, 1, 1);
+
+  /* App specific settings */
   --focus-color: rgba(var(--blue-600-dark-rgb), 0.7);
 }
 
@@ -60,12 +73,12 @@
 
   border: 2px solid var(--focus-color);
   border-radius: 4px;
-  bottom: calc(0px - var(--focus-ring-size));
+  bottom: calc(-1 * var(--focus-ring-size));
   content: '';
-  left: calc(0px - var(--focus-ring-size));
+  left: calc(-1 * var(--focus-ring-size));
   position: absolute;
-  right: calc(0px - var(--focus-ring-size));
-  top: calc(0px - var(--focus-ring-size));
+  right: calc(-1 * var(--focus-ring-size));
+  top: calc(-1 * var(--focus-ring-size));
 }
 
 .cancel-animate {
@@ -134,13 +147,13 @@
   opacity: 1;
   position: absolute;
   transform: translateX(-50%);
-  transition: opacity 200ms cubic-bezier(0.4, 0, 0.2, 1);
+  transition: opacity var(--fast2-duration) var(--standard-easing);
   visibility: visible;
 }
 
 body.taking.video .left-stripe {
   opacity: 0;
-  transition: visibility 0ms 225ms, opacity 225ms cubic-bezier(0.4, 0, 0.2, 1);
+  transition: visibility 0ms 225ms, opacity 225ms var(--standard-easing);
   visibility: hidden;
 }
 
@@ -203,19 +216,41 @@
   flex-direction: column-reverse;
 }
 
-#modes-group {
+#mode-selector {
   --fade-padding: 16px;
+  --scrollbar-height: 5px;
 
-  -webkit-mask-image: linear-gradient(to left,
-    rgba(0, 0, 0, 0) 0,
-    rgba(0, 0, 0, 1) var(--fade-padding) calc(100% - var(--fade-padding)),
-    rgba(0, 0, 0, 0) 100%);
-  display: block;
   left: calc(var(--left-line) * 2);
+  right: calc(var(--right-line) * 2);
+}
+
+#mode-selector::before,
+#mode-selector::after {
+  background: linear-gradient(to right, black, transparent);
+  content: '';
+  display: block;
+  height: calc(100% - var(--scrollbar-height));
+  pointer-events: none;
+  position: absolute;
+  top: 0;
+  width: var(--fade-padding);
+  z-index: 2;
+}
+
+#mode-selector::before {
+  left: 0;
+}
+
+#mode-selector::after {
+  right: 0;
+  transform: scaleX(-1);
+}
+
+#modes-group {
+  display: block;
   overflow: auto;
   padding: 5px 0;
   pointer-events: auto;
-  right: calc(var(--right-line) * 2);
   text-align: center;
   white-space: nowrap;
 }
@@ -234,7 +269,7 @@
 }
 
 #modes-group::-webkit-scrollbar {
-  height: 5px;
+  height: var(--scrollbar-height);
   width: auto;
 }
 
@@ -297,6 +332,14 @@
   display: none;
 }
 
+#modes-group .mode-item.first {
+  margin-inline-start: var(--fade-padding);
+}
+
+#modes-group .mode-item.last {
+  margin-inline-end: var(--fade-padding);
+}
+
 div.mode-item>span {
   border-radius: 16px/50%;
   color: white;
@@ -309,10 +352,6 @@
   z-index: 0;
 }
 
-#modes-group.hide {
-  visibility: hidden;
-}
-
 .mode-item>input {
   height: 100%;
   position: absolute;
@@ -332,7 +371,7 @@
 }
 
 body.tab-navigation .mode-item>input:focus + span::after {
-  --negative-size: calc(0px - var(--focus-ring-gap));
+  --negative-size: calc(-1 * var(--focus-ring-gap));
 
   border: var(--focus-ring-border) solid var(--focus-color);
   border-radius: 23px/50%;
@@ -418,7 +457,6 @@
 
 #recordvideo,
 #pause-recordvideo {
-  --curve: cubic-bezier(0.4, 0, 0.2, 1);
   --dot-size: 25%;
   --durtaion: 180ms;
   --red: #f44336;
@@ -430,7 +468,7 @@
   border-radius: 50%;
   height: var(--size);
   position: relative;
-  transition: var(--durtaion) var(--curve);
+  transition: var(--durtaion) var(--standard-easing);
   width: var(--size);
 }
 
@@ -454,7 +492,7 @@
   position: absolute;
   top: 50%;
   transform: translate(-50%, -50%);
-  transition: var(--durtaion) var(--curve);
+  transition: var(--durtaion) var(--standard-easing);
   width: var(--dot-size);
 }
 
@@ -475,7 +513,7 @@
   opacity: 0;
   position: absolute;
   top: 50%;
-  transition: var(--durtaion) var(--square-delay) var(--curve);
+  transition: var(--durtaion) var(--square-delay) var(--standard-easing);
   width: 0;
 }
 
@@ -553,7 +591,8 @@
 #timer-tick-msg.animate {
   opacity: 0.2;
   transform: scale(1.8, 1.8);
-  transition: transform 500ms ease-out, opacity 500ms ease-out;
+  transition: transform var(--moderate1-duration) ease-out,
+              opacity var(--moderate1-duration) ease-out;
   visibility: visible;
 }
 
@@ -718,7 +757,7 @@
 body.view-warning #view-warning,
 body.view-splash #view-splash {
   opacity: 1;
-  transition: opacity 100ms;
+  transition: opacity var(--fast1-duration);
   visibility: visible;
 }
 
@@ -826,7 +865,7 @@
 }
 
 #preview-video {
-  transition: opacity 200ms ease-in-out;
+  transition: opacity var(--fast2-duration) ease-in-out;
 }
 
 #preview-video.animate {
@@ -1144,7 +1183,7 @@
 }
 
 #banner.animate {
-  animation: emerge 6000ms cubic-bezier(0.0, 0.0, 0.2, 1);
+  animation: emerge 6000ms var(--enter-easing);
   display: block;
 }
 
@@ -1155,7 +1194,7 @@
   }
   8%,
   92% {
-    animation-timing-function: cubic-bezier(0.4, 0.0, 1.0, 1.0);
+    animation-timing-function: var(--exit-easing);
     bottom: 31px;
   }
 }
@@ -1258,7 +1297,7 @@
 
 button.menu-item.animate::before,
 label.menu-item.animate::before {
-  animation: inkdrop 500ms ease-out;
+  animation: inkdrop var(--moderate1-duration) ease-out;
 }
 
 @keyframes inkdrop {
@@ -1344,7 +1383,7 @@
   left: 12px;
   right: 12px;
   top: 12px;
-  transition: border-width 100ms ease-in;
+  transition: border-width var(--fast1-duration) ease-in;
 }
 
 body.tab-navigation button.menu-item:focus::after {
@@ -1436,7 +1475,7 @@
   flex-direction: column;
   padding: 20px;
   transform: translateY(20px);
-  transition: transform 200ms;
+  transition: transform var(--fast2-duration);
 }
 
 .dialog .dialog-popup {
@@ -1564,30 +1603,18 @@
 .barcode-chip-container {
   --chip-height: 32px;
   left: 50%;
-  opacity: 0;
+  opacity: 1;
   position: absolute;
   top: 10%;
   transform: translateX(-50%);
+  transition: opacity var(--moderate1-duration) var(--standard-easing);
   z-index: 50;
 }
 
-/* TODO(b/172879638): Tune the animation according to the final motion spec. */
-.barcode-chip-container.animate {
-  /* TODO(b/172879638): This might be too short for long text. */
-  animation: 8s show-barcode-chip ease-out;
-}
-
-@keyframes show-barcode-chip {
-  0% {
-    opacity: 0;
-  }
-  3%,
-  97% {
-    opacity: 1;
-  }
-  100% {
-    opacity: 0;
-  }
+.barcode-chip-container.invisible {
+  opacity: 0;
+  transition: opacity var(--moderate1-duration) var(--standard-easing),
+              visibility 0s var(--moderate1-duration);
 }
 
 .barcode-chip-url {
@@ -1672,10 +1699,6 @@
   width: 416px;
 }
 
-#barcode-chip-text-expand.hide {
-  display: none;
-}
-
 #barcode-chip-text-expand {
   background: url(/images/barcode_chevron_down.svg) center no-repeat;
   height: 40px;
@@ -1730,3 +1753,7 @@
 .hidden {
   display: none;
 }
+
+.invisible {
+  visibility: hidden;
+}
diff --git a/chromeos/components/camera_app_ui/resources/js/BUILD.gn b/chromeos/components/camera_app_ui/resources/js/BUILD.gn
index 368415ae..e503061 100644
--- a/chromeos/components/camera_app_ui/resources/js/BUILD.gn
+++ b/chromeos/components/camera_app_ui/resources/js/BUILD.gn
@@ -98,6 +98,7 @@
     "sound.js",
     "state.js",
     "test_bridge.js",
+    "timer.js",
     "toast.js",
     "tooltip.js",
     "type.js",
diff --git a/chromeos/components/camera_app_ui/resources/js/barcode_chip.js b/chromeos/components/camera_app_ui/resources/js/barcode_chip.js
index 9505344..abdc00e 100644
--- a/chromeos/components/camera_app_ui/resources/js/barcode_chip.js
+++ b/chromeos/components/camera_app_ui/resources/js/barcode_chip.js
@@ -3,9 +3,14 @@
 // found in the LICENSE file.
 
 import {browserProxy} from './browser_proxy/browser_proxy.js';
+import {assert} from './chrome_util.js';
 import * as dom from './dom.js';
 import * as snackbar from './snackbar.js';
-import * as util from './util.js';
+import * as state from './state.js';
+import {OneShotTimer} from './timer.js';
+
+// TODO(b/172879638): Tune the duration according to the final motion spec.
+const CHIP_DURATION = 8000;
 
 /**
  * The detected string that is being shown currently.
@@ -20,14 +25,37 @@
 let currentChip = null;
 
 /**
- * Resets the variables of the current state.
+ * The countdown timer for dismissing the chip.
+ * @type {?OneShotTimer}
  */
-function resetCurrentState() {
+let currentTimer = null;
+
+/**
+ * Resets the variables of the current state and dismisses the chip.
+ */
+function deactivate() {
   if (currentChip !== null) {
-    currentChip.classList.add('hidden');
+    currentChip.classList.add('invisible');
   }
   currentCode = null;
   currentChip = null;
+  currentTimer = null;
+}
+
+/**
+ * Activates the chip on container and starts the timer.
+ * @param {!HTMLElement} container The container of the chip.
+ */
+function activate(container) {
+  container.classList.remove('invisible');
+  currentChip = container;
+
+  currentTimer = new OneShotTimer(deactivate, CHIP_DURATION);
+  if (state.get(state.State.TAB_NAVIGATION)) {
+    // Do not auto dismiss the chip when using keyboard for a11y. Screen reader
+    // might need long time to read the detected content.
+    currentTimer.stop();
+  }
 }
 
 /**
@@ -54,6 +82,7 @@
  * @param {string} content The content to be copied.
  * @param {string} snackbarLabel The label to be displayed on snackbar when the
  *     content is copied.
+ * @return {!HTMLElement} The copy button element.
  */
 function setupCopyButton(container, content, snackbarLabel) {
   const copyButton =
@@ -62,6 +91,7 @@
     await navigator.clipboard.writeText(content);
     snackbar.show(snackbarLabel);
   };
+  return copyButton;
 }
 
 /**
@@ -70,7 +100,7 @@
  */
 function showUrl(url) {
   const container = dom.get('#barcode-chip-url-container', HTMLDivElement);
-  container.classList.remove('hidden');
+  activate(container);
 
   const anchor = dom.getFrom(container, 'a', HTMLAnchorElement);
   Object.assign(anchor, {
@@ -84,9 +114,6 @@
   anchor.focus();
 
   setupCopyButton(container, url, 'snackbar_link_copied');
-
-  currentChip = container;
-  util.animateOnce(container, resetCurrentState);
 }
 
 /**
@@ -95,23 +122,30 @@
  */
 function showText(text) {
   const container = dom.get('#barcode-chip-text-container', HTMLDivElement);
-  container.classList.remove('hidden', 'expanded');
+  activate(container);
+  container.classList.remove('expanded');
 
   const textEl = dom.get('#barcode-chip-text-content', HTMLDivElement);
   textEl.textContent = text;
   const expandable = textEl.scrollWidth > textEl.clientWidth;
 
   const expandEl = dom.get('#barcode-chip-text-expand', HTMLButtonElement);
-  expandEl.classList.toggle('hide', !expandable);
+  expandEl.classList.toggle('hidden', !expandable);
   expandEl.onclick = () => {
     container.classList.toggle('expanded');
+    const expanded = container.classList.contains('expanded');
+    expandEl.setAttribute('aria-expanded', expanded.toString());
   };
 
-  setupCopyButton(container, text, 'snackbar_text_copied');
+  const copyButton = setupCopyButton(container, text, 'snackbar_text_copied');
 
-  // TODO(b/172879638): Handle a11y.
-  currentChip = container;
-  util.animateOnce(container, resetCurrentState);
+  // TODO(b/172879638): There is a race in ChromeVox which will speak the
+  // focused element twice.
+  if (expandable) {
+    expandEl.focus();
+  } else {
+    copyButton.focus();
+  }
 }
 
 /**
@@ -120,11 +154,17 @@
  */
 export async function show(code) {
   if (code === currentCode) {
+    if (currentTimer !== null) {
+      // Extend the duration by resetting the timeout.
+      currentTimer.resetTimeout();
+    }
     return;
   }
 
-  if (currentChip !== null) {
-    await util.animateCancel(currentChip);
+  if (currentTimer !== null) {
+    // Dismiss the previous chip.
+    currentTimer.fireNow();
+    assert(currentTimer === null, 'The timer should be cleared.');
   }
 
   currentCode = code;
diff --git a/chromeos/components/camera_app_ui/resources/js/timer.js b/chromeos/components/camera_app_ui/resources/js/timer.js
new file mode 100644
index 0000000..eb43d4c1
--- /dev/null
+++ b/chromeos/components/camera_app_ui/resources/js/timer.js
@@ -0,0 +1,77 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assert} from './chrome_util.js';
+
+/**
+ * A one-shot timer that is more powerful than setTimeout().
+ */
+export class OneShotTimer {
+  /**
+   * The parameters are same as the parameters of setTimeout().
+   * @param {function()} handler
+   * @param {number} timeout
+   */
+  constructor(handler, timeout) {
+    /**
+     * @type {function()}
+     * @const
+     * @private
+     */
+    this.handler_ = handler;
+
+    /**
+     * @type {number}
+     * @const
+     * @private
+     */
+    this.timeout_ = timeout;
+
+    /**
+     * @type {number}
+     * @private
+     */
+    this.timeoutId_ = 0;
+
+    this.start();
+  }
+
+  /**
+   * Starts the timer.
+   */
+  start() {
+    assert(this.timeoutId_ === 0);
+    this.timeoutId_ = setTimeout(this.handler_, this.timeout_);
+  }
+
+  /**
+   * Stops the pending timeout.
+   */
+  stop() {
+    assert(this.timeoutId_ !== 0);
+    clearTimeout(this.timeoutId_);
+    this.timeoutId_ = 0;
+  }
+
+  /**
+   * Resets the timer delay. It's a no-op if the timer is already stopped.
+   */
+  resetTimeout() {
+    if (this.timeoutId_ === 0) {
+      return;
+    }
+    this.stop();
+    this.start();
+  }
+
+  /**
+   * Stops the timer and runs the scheduled handler immediately.
+   */
+  fireNow() {
+    if (this.timeoutId_ !== 0) {
+      this.stop();
+    }
+    this.handler_();
+  }
+}
diff --git a/chromeos/components/camera_app_ui/resources/js/views/camera/mode/index.js b/chromeos/components/camera_app_ui/resources/js/views/camera/mode/index.js
index 28200ed..1a181e8 100644
--- a/chromeos/components/camera_app_ui/resources/js/views/camera/mode/index.js
+++ b/chromeos/components/camera_app_ui/resources/js/views/camera/mode/index.js
@@ -344,14 +344,25 @@
    */
   async updateModeSelectionUI(deviceId) {
     const supportedModes = await this.getSupportedModes(deviceId);
-    dom.getAll('div.mode-item', HTMLDivElement).forEach((element) => {
-      const radio = dom.getFrom(element, 'input[type=radio]', HTMLInputElement);
-      element.classList.toggle(
-          'hide',
-          !supportedModes.includes(
-              /** @type {!Mode} */ (radio.dataset['mode'])));
+    const items = dom.getAll('div.mode-item', HTMLDivElement);
+    let first = null;
+    let last = null;
+    items.forEach((el) => {
+      const radio = dom.getFrom(el, 'input[type=radio]', HTMLInputElement);
+      const supported =
+          supportedModes.includes(/** @type {!Mode} */ (radio.dataset['mode']));
+      el.classList.toggle('hide', !supported);
+      if (supported) {
+        if (first === null) {
+          first = el;
+        }
+        last = el;
+      }
     });
-    this.modesGroup_.classList.remove('hide');
+    items.forEach((el) => {
+      el.classList.toggle('first', el === first);
+      el.classList.toggle('last', el === last);
+    });
   }
 
   /**
diff --git a/chromeos/components/camera_app_ui/resources/strings/camera_strings.grd b/chromeos/components/camera_app_ui/resources/strings/camera_strings.grd
index 28c6f9f..da1d6e7d 100644
--- a/chromeos/components/camera_app_ui/resources/strings/camera_strings.grd
+++ b/chromeos/components/camera_app_ui/resources/strings/camera_strings.grd
@@ -333,9 +333,12 @@
       <message desc="Label for the copy button of the barcode text chip." name="IDS_BARCODE_COPY_TEXT_BUTTON">
         Copy text
       </message>
-      <message desc="Label for the copy button of the barcode text chip." name="IDS_BARCODE_LINK_DETECTED">
+      <message desc="Label for detected link on the barcode chip." name="IDS_BARCODE_LINK_DETECTED">
         Link detected. <ph name="hostname">$1<ex>www.google.com</ex></ph>
       </message>
+      <message desc="Label for detected text on the barcode chip." name="IDS_BARCODE_TEXT_DETECTED">
+        Text detected.
+      </message>
       <message desc="Label for the button of expert mode options." name="IDS_EXPERT_MODE_BUTTON">
         Expert mode
       </message>
diff --git a/chromeos/components/camera_app_ui/resources/strings/camera_strings_grd/IDS_BARCODE_TEXT_DETECTED.png.sha1 b/chromeos/components/camera_app_ui/resources/strings/camera_strings_grd/IDS_BARCODE_TEXT_DETECTED.png.sha1
new file mode 100644
index 0000000..330de04c
--- /dev/null
+++ b/chromeos/components/camera_app_ui/resources/strings/camera_strings_grd/IDS_BARCODE_TEXT_DETECTED.png.sha1
@@ -0,0 +1 @@
+a30cac172fff5a3100eacf1eb31e092fd2afe618
\ No newline at end of file
diff --git a/chromeos/components/camera_app_ui/resources/views/main.html b/chromeos/components/camera_app_ui/resources/views/main.html
index 67511d4b..84a2678f 100644
--- a/chromeos/components/camera_app_ui/resources/views/main.html
+++ b/chromeos/components/camera_app_ui/resources/views/main.html
@@ -102,34 +102,36 @@
           <div class="two-bars"></div>
         </button>
       </div>
-      <div id="modes-group" class="buttons bottom-stripe hide">
-        <div class="mode-item">
-          <input type="radio" name="mode"
-               data-mode="video" tabindex="0"
-               i18n-aria="switch_record_video_button">
-          <span i18n-content="label_switch_record_video_button"
-                aria-hidden="true"></span>
-        </div>
-        <div class="mode-item">
-          <input type="radio" name="mode"
-               data-mode="photo" tabindex="0"
-               i18n-aria="switch_take_photo_button">
-          <span i18n-content="label_switch_take_photo_button"
-                aria-hidden="true"></span>
-        </div>
-        <div class="mode-item">
-          <input type="radio" name="mode"
-               data-mode="square" tabindex="0"
-               i18n-aria="switch_take_square_photo_button">
-          <span i18n-content="label_switch_take_square_photo_button"
-                aria-hidden="true"></span>
-        </div>
-        <div class="mode-item hide">
-          <input type="radio" name="mode"
-               data-mode="portrait" tabindex="0"
-               i18n-aria="switch_take_portrait_photo_button">
-          <span i18n-content="label_switch_take_portrait_photo_button"
-                aria-hidden="true"></span>
+      <div id="mode-selector" class="bottom-stripe">
+        <div id="modes-group" class="buttons">
+          <div class="mode-item">
+            <input type="radio" name="mode"
+                 data-mode="video" tabindex="0"
+                 i18n-aria="switch_record_video_button">
+            <span i18n-content="label_switch_record_video_button"
+                  aria-hidden="true"></span>
+          </div>
+          <div class="mode-item">
+            <input type="radio" name="mode"
+                 data-mode="photo" tabindex="0"
+                 i18n-aria="switch_take_photo_button">
+            <span i18n-content="label_switch_take_photo_button"
+                  aria-hidden="true"></span>
+          </div>
+          <div class="mode-item">
+            <input type="radio" name="mode"
+                 data-mode="square" tabindex="0"
+                 i18n-aria="switch_take_square_photo_button">
+            <span i18n-content="label_switch_take_square_photo_button"
+                  aria-hidden="true"></span>
+          </div>
+          <div class="mode-item">
+            <input type="radio" name="mode"
+                 data-mode="portrait" tabindex="0"
+                 i18n-aria="switch_take_portrait_photo_button">
+            <span i18n-content="label_switch_take_portrait_photo_button"
+                  aria-hidden="true"></span>
+          </div>
         </div>
       </div>
       <div class="bottom-stripe right-stripe buttons circle">
@@ -404,7 +406,8 @@
       </div>
     </div>
     <div id="toast" class="centered-overlay" aria-live="polite"></div>
-    <div id="barcode-chip-url-container" class="hidden barcode-chip-container">
+    <div id="barcode-chip-url-container"
+         class="invisible barcode-chip-container">
       <div class="barcode-chip-url">
         <a target="_blank"></a>
       </div>
@@ -413,10 +416,13 @@
           i18n-label="barcode_copy_link_button"></button>
       </div>
     </div>
-    <div id="barcode-chip-text-container" class="hidden barcode-chip-container">
+    <div id="barcode-chip-text-container"
+         class="invisible barcode-chip-container"
+         role="dialog" i18n-label="barcode_text_detected"
+         aria-live="polite" aria-describedby="barcode-chip-text-content">
       <div class="barcode-chip-text">
         <div id="barcode-chip-text-content"></div>
-        <button id="barcode-chip-text-expand"></button>
+        <button id="barcode-chip-text-expand" aria-expanded="false"></button>
       </div>
       <div class="circle">
         <button class="barcode-copy-button" tabindex="0"
diff --git a/codelabs/OWNERS b/codelabs/OWNERS
index e2fe7c6..647e878 100644
--- a/codelabs/OWNERS
+++ b/codelabs/OWNERS
@@ -1,4 +1,5 @@
 *
 
 # Primary
+asully@chromium.org
 pwnall@chromium.org
\ No newline at end of file
diff --git a/components/safe_browsing/content/browser/BUILD.gn b/components/safe_browsing/content/browser/BUILD.gn
index 20befe8..2480b6fd 100644
--- a/components/safe_browsing/content/browser/BUILD.gn
+++ b/components/safe_browsing/content/browser/BUILD.gn
@@ -81,3 +81,36 @@
     "//testing/gtest",
   ]
 }
+
+source_set("client_side_detection") {
+  sources = [
+    "client_side_detection_host.cc",
+    "client_side_detection_host.h",
+    "client_side_detection_service.cc",
+    "client_side_detection_service.h",
+  ]
+  deps = [
+    ":client_side_model_loader",
+    "//base:base",
+    "//components/prefs",
+    "//components/safe_browsing:buildflags",
+    "//components/safe_browsing/content",
+    "//components/safe_browsing/content/common:interfaces",
+    "//components/safe_browsing/core:client_model_proto",
+    "//components/safe_browsing/core:csd_proto",
+    "//components/safe_browsing/core:features",
+    "//components/safe_browsing/core/common",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/safe_browsing/core/db:allowlist_checker_client",
+    "//components/safe_browsing/core/db:database_manager",
+    "//components/safe_browsing/core/db:v4_protocol_manager_util",
+    "//components/security_interstitials/content:security_interstitial_page",
+    "//components/variations",
+    "//content/public/browser",
+    "//google_apis:google_apis",
+    "//net:net",
+    "//net/traffic_annotation:traffic_annotation",
+    "//services/network/public/cpp",
+    "//url:url",
+  ]
+}
diff --git a/components/safe_browsing/content/browser/DEPS b/components/safe_browsing/content/browser/DEPS
index 3e20f42..869cfc2a 100644
--- a/components/safe_browsing/content/browser/DEPS
+++ b/components/safe_browsing/content/browser/DEPS
@@ -3,6 +3,7 @@
   "+components/safe_browsing/core/proto/csd.pb.h",
   "+components/sessions/core/session_id.h",
   "+content/public/browser",
+  "+crypto/sha2.h",
   "+ipc/ipc_message.h",
   "+net/cookies",
   "+net/extras",
@@ -12,6 +13,7 @@
   "+services/network/network_context.h",
   "+services/network/public",
   "+services/service_manager/public/cpp",
+  "+third_party/blink/public/mojom/loader",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/components/safe_browsing/content/browser/client_side_detection_host.cc
similarity index 89%
rename from chrome/browser/safe_browsing/client_side_detection_host.cc
rename to components/safe_browsing/content/browser/client_side_detection_host.cc
index 747e6f95..9529716d 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.cc
+++ b/components/safe_browsing/content/browser/client_side_detection_host.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/safe_browsing/client_side_detection_host.h"
+#include "components/safe_browsing/content/browser/client_side_detection_host.h"
 
 #include <memory>
 #include <utility>
@@ -18,18 +18,14 @@
 #include "base/sequenced_task_runner_helpers.h"
 #include "base/time/default_tick_clock.h"
 #include "base/time/tick_clock.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
-#include "chrome/browser/safe_browsing/client_side_detection_service_factory.h"
-#include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "chrome/browser/safe_browsing/user_interaction_observer.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
 #include "components/safe_browsing/content/common/safe_browsing.mojom-shared.h"
 #include "components/safe_browsing/content/common/safe_browsing.mojom.h"
 #include "components/safe_browsing/core/db/allowlist_checker_client.h"
 #include "components/safe_browsing/core/db/database_manager.h"
 #include "components/security_interstitials/content/unsafe_resource_util.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
@@ -111,15 +107,14 @@
     }
 
     // Don't start classification if |url_| is whitelisted by enterprise policy.
-    Profile* profile =
-        Profile::FromBrowserContext(web_contents_->GetBrowserContext());
-    if (profile && IsURLWhitelistedByPolicy(url_, *profile->GetPrefs())) {
-      DontClassifyForPhishing(NO_CLASSIFY_ALLOWLISTED_BY_POLICY);
+    if (host_->delegate_->GetPrefs() &&
+        IsURLWhitelistedByPolicy(url_, *host_->delegate_->GetPrefs())) {
+      DontClassifyForPhishing(NO_CLASSIFY_WHITELISTED_BY_POLICY);
     }
 
     // If the tab has a delayed warning, ignore this second verdict. We don't
     // want to immediately undelay a page that's already blocked as phishy.
-    if (SafeBrowsingUserInteractionObserver::FromWebContents(web_contents_)) {
+    if (host_->delegate_->HasSafeBrowsingUserInteractionObserver()) {
       DontClassifyForPhishing(NO_CLASSIFY_HAS_DELAYED_WARNING);
     }
 
@@ -164,7 +159,7 @@
     NO_CLASSIFY_RESULT_FROM_CACHE = 9,
     DEPRECATED_NO_CLASSIFY_NOT_HTTP_URL = 10,
     NO_CLASSIFY_SCHEME_NOT_SUPPORTED = 11,
-    NO_CLASSIFY_ALLOWLISTED_BY_POLICY = 12,
+    NO_CLASSIFY_WHITELISTED_BY_POLICY = 12,
     CLASSIFY = 13,
     NO_CLASSIFY_HAS_DELAYED_WARNING = 14,
 
@@ -172,7 +167,7 @@
   };
 
   // The destructor can be called either from the UI or the IO thread.
-  virtual ~ShouldClassifyUrlRequest() { }
+  virtual ~ShouldClassifyUrlRequest() = default;
 
   bool ShouldClassifyForPhishing() const {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -288,27 +283,29 @@
 
 // static
 std::unique_ptr<ClientSideDetectionHost> ClientSideDetectionHost::Create(
-    WebContents* tab) {
-  return base::WrapUnique(new ClientSideDetectionHost(tab));
+    content::WebContents* tab,
+    std::unique_ptr<Delegate> delegate) {
+  return base::WrapUnique(
+      new ClientSideDetectionHost(tab, std::move(delegate)));
 }
 
-ClientSideDetectionHost::ClientSideDetectionHost(WebContents* tab)
+ClientSideDetectionHost::ClientSideDetectionHost(
+    WebContents* tab,
+    std::unique_ptr<Delegate> delegate)
     : content::WebContentsObserver(tab),
       csd_service_(nullptr),
       tab_(tab),
       classification_request_(nullptr),
-      tick_clock_(base::DefaultTickClock::GetInstance()) {
+      tick_clock_(base::DefaultTickClock::GetInstance()),
+      delegate_(std::move(delegate)) {
   DCHECK(tab);
   // Note: csd_service_ and sb_service will be nullptr here in testing.
-  csd_service_ = ClientSideDetectionServiceFactory::GetForProfile(
-      Profile::FromBrowserContext(tab->GetBrowserContext()));
+  csd_service_ = delegate_->GetClientSideDetectionService();
 
-  scoped_refptr<SafeBrowsingService> sb_service =
-      g_browser_process->safe_browsing_service();
-  if (sb_service.get()) {
-    ui_manager_ = sb_service->ui_manager();
-    database_manager_ = sb_service->database_manager();
-  }
+  // ui_manager_ and database_manager_ can be null if safe browsing
+  // service is not available in the embedder.
+  ui_manager_ = delegate_->GetSafeBrowsingUIManager();
+  database_manager_ = delegate_->GetSafeBrowsingDBManager();
 }
 
 ClientSideDetectionHost::~ClientSideDetectionHost() {
@@ -428,8 +425,7 @@
   // We parse the protocol buffer here.  If we're unable to parse it we won't
   // send the verdict further.
   std::unique_ptr<ClientPhishingRequest> verdict(new ClientPhishingRequest);
-  if (csd_service_ &&
-      verdict->ParseFromString(verdict_str) &&
+  if (csd_service_ && verdict->ParseFromString(verdict_str) &&
       verdict->IsInitialized()) {
     VLOG(2) << "Phishing classification score: " << verdict->client_score();
     for (auto& match : verdict->vision_match()) {
@@ -437,10 +433,8 @@
       VLOG(2) << "Phash Score: " << match.vision_matched_phash_score();
       VLOG(2) << "EMD Score: " << match.vision_matched_emd_score();
     }
-    Profile* profile =
-        Profile::FromBrowserContext(web_contents()->GetBrowserContext());
-    if (!IsExtendedReportingEnabled(*profile->GetPrefs()) &&
-        !IsEnhancedProtectionEnabled(*profile->GetPrefs())) {
+    if (!IsExtendedReportingEnabled(*delegate_->GetPrefs()) &&
+        !IsEnhancedProtectionEnabled(*delegate_->GetPrefs())) {
       // These fields should only be set for SBER users.
       verdict->clear_screenshot_digest();
       verdict->clear_screenshot_phash();
@@ -456,11 +450,10 @@
           base::BindOnce(&ClientSideDetectionHost::MaybeShowPhishingWarning,
                          weak_factory_.GetWeakPtr(),
                          /*is_from_cache=*/false);
-      Profile* profile =
-          Profile::FromBrowserContext(web_contents()->GetBrowserContext());
       csd_service_->SendClientReportPhishingRequest(
-          std::move(verdict), IsExtendedReportingEnabled(*profile->GetPrefs()),
-          IsEnhancedProtectionEnabled(*profile->GetPrefs()),
+          std::move(verdict),
+          IsExtendedReportingEnabled(*delegate_->GetPrefs()),
+          IsEnhancedProtectionEnabled(*delegate_->GetPrefs()),
           std::move(callback));
     }
   }
@@ -510,8 +503,7 @@
   csd_service_ = service;
 }
 
-void ClientSideDetectionHost::set_ui_manager(
-    SafeBrowsingUIManager* ui_manager) {
+void ClientSideDetectionHost::set_ui_manager(BaseUIManager* ui_manager) {
   ui_manager_ = ui_manager;
 }
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.h b/components/safe_browsing/content/browser/client_side_detection_host.h
similarity index 72%
rename from chrome/browser/safe_browsing/client_side_detection_host.h
rename to components/safe_browsing/content/browser/client_side_detection_host.h
index ce343b8..0617888 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.h
+++ b/components/safe_browsing/content/browser/client_side_detection_host.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_H_
-#define CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_H_
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_CLIENT_SIDE_DETECTION_HOST_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_CLIENT_SIDE_DETECTION_HOST_H_
 
 #include <stddef.h>
 
@@ -12,8 +12,10 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "chrome/browser/safe_browsing/ui_manager.h"
+#include "components/safe_browsing/content/base_ui_manager.h"
 #include "components/safe_browsing/content/browser/client_side_model_loader.h"
+#include "components/safe_browsing/content/common/safe_browsing.mojom-shared.h"
+#include "components/safe_browsing/content/common/safe_browsing.mojom.h"
 #include "components/safe_browsing/core/db/database_manager.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -35,10 +37,29 @@
 // TODO(noelutz): move all client-side detection IPCs to this class.
 class ClientSideDetectionHost : public content::WebContentsObserver {
  public:
+  // Delegate which allows to provide embedder specific implementations.
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // Returns whether there is a SafeBrowsingUserInteractionObserver available.
+    virtual bool HasSafeBrowsingUserInteractionObserver() = 0;
+    // Returns the prefs service associated with the current embedders profile.
+    virtual PrefService* GetPrefs() = 0;
+    virtual scoped_refptr<SafeBrowsingDatabaseManager>
+    GetSafeBrowsingDBManager() = 0;
+    virtual scoped_refptr<BaseUIManager> GetSafeBrowsingUIManager() = 0;
+    virtual ClientSideDetectionService* GetClientSideDetectionService() = 0;
+  };
+
   // The caller keeps ownership of the tab object and is responsible for
   // ensuring that it stays valid until WebContentsDestroyed is called.
   static std::unique_ptr<ClientSideDetectionHost> Create(
-      content::WebContents* tab);
+      content::WebContents* tab,
+      std::unique_ptr<Delegate> delegate);
+
+  // The caller keeps ownership of the tab object and is responsible for
+  // ensuring that it stays valid until WebContentsDestroyed is called.
   ~ClientSideDetectionHost() override;
 
   // From content::WebContentsObserver.  If we navigate away we cancel all
@@ -51,14 +72,15 @@
   void SendModelToRenderFrame();
 
  protected:
-  explicit ClientSideDetectionHost(content::WebContents* tab);
+  explicit ClientSideDetectionHost(content::WebContents* tab,
+                                   std::unique_ptr<Delegate> delegate);
 
   // From content::WebContentsObserver.
   void WebContentsDestroyed() override;
   void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
 
   // Used for testing.
-  void set_ui_manager(SafeBrowsingUIManager* ui_manager);
+  void set_ui_manager(BaseUIManager* ui_manager);
   void set_database_manager(SafeBrowsingDatabaseManager* database_manager);
 
  private:
@@ -101,7 +123,7 @@
   content::WebContents* tab_;
   // These pointers may be nullptr if SafeBrowsing is disabled.
   scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
-  scoped_refptr<SafeBrowsingUIManager> ui_manager_;
+  scoped_refptr<BaseUIManager> ui_manager_;
   // Keep a handle to the latest classification request so that we can cancel
   // it if necessary.
   scoped_refptr<ShouldClassifyUrlRequest> classification_request_;
@@ -113,6 +135,9 @@
   // Records the start time of when phishing detection started.
   base::TimeTicks phishing_detection_start_time_;
   const base::TickClock* tick_clock_;
+
+  std::unique_ptr<Delegate> delegate_;
+
   base::WeakPtrFactory<ClientSideDetectionHost> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionHost);
@@ -120,4 +145,4 @@
 
 }  // namespace safe_browsing
 
-#endif  // CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_H_
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_CLIENT_SIDE_DETECTION_HOST_H_
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/components/safe_browsing/content/browser/client_side_detection_service.cc
similarity index 91%
rename from chrome/browser/safe_browsing/client_side_detection_service.cc
rename to components/safe_browsing/content/browser/client_side_detection_service.cc
index 801c28a..39cef21 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.cc
+++ b/components/safe_browsing/content/browser/client_side_detection_service.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/safe_browsing/client_side_detection_service.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
 
 #include <algorithm>
 #include <memory>
@@ -18,12 +18,8 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/policy/chrome_browser_policy_connector.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/safe_browsing/client_side_detection_host.h"
-#include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/content/browser/client_side_detection_host.h"
 #include "components/safe_browsing/content/common/safe_browsing.mojom.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/core/common/utils.h"
@@ -68,22 +64,17 @@
 ClientSideDetectionService::CacheState::CacheState(bool phish, base::Time time)
     : is_phishing(phish), timestamp(time) {}
 
-ClientSideDetectionService::ClientSideDetectionService(Profile* profile)
-    : profile_(profile),
-      enabled_(false),
-      extended_reporting_(false),
-      url_loader_factory_(nullptr) {
-  // |profile_| can be null in unit tests
-  if (!profile_)
+ClientSideDetectionService::ClientSideDetectionService(
+    std::unique_ptr<Delegate> delegate)
+    : delegate_(std::move(delegate)) {
+  // delegate and prefs can be null in unit tests.
+  if (!delegate_ || !delegate_->GetPrefs()) {
     return;
-
-  if (g_browser_process->safe_browsing_service()) {
-    url_loader_factory_ =
-        g_browser_process->safe_browsing_service()->GetURLLoaderFactory(
-            profile);
   }
 
-  pref_change_registrar_.Init(profile_->GetPrefs());
+  url_loader_factory_ = delegate_->GetSafeBrowsingURLLoaderFactory();
+
+  pref_change_registrar_.Init(delegate_->GetPrefs());
   pref_change_registrar_.Add(
       prefs::kSafeBrowsingEnabled,
       base::BindRepeating(&ClientSideDetectionService::OnPrefsUpdated,
@@ -96,7 +87,6 @@
       prefs::kSafeBrowsingScoutReportingEnabled,
       base::BindRepeating(&ClientSideDetectionService::OnPrefsUpdated,
                           base::Unretained(this)));
-
   // Do an initial check of the prefs.
   OnPrefsUpdated();
 }
@@ -111,10 +101,10 @@
 
 void ClientSideDetectionService::OnPrefsUpdated() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  bool enabled = IsSafeBrowsingEnabled(*profile_->GetPrefs());
+  bool enabled = IsSafeBrowsingEnabled(*delegate_->GetPrefs());
   bool extended_reporting =
-      IsEnhancedProtectionEnabled(*profile_->GetPrefs()) ||
-      IsExtendedReportingEnabled(*profile_->GetPrefs());
+      IsEnhancedProtectionEnabled(*delegate_->GetPrefs()) ||
+      IsExtendedReportingEnabled(*delegate_->GetPrefs());
   if (enabled == enabled_ && extended_reporting_ == extended_reporting)
     return;
 
@@ -128,7 +118,7 @@
       model_loader_ = std::make_unique<ModelLoader>(
           base::BindRepeating(&ClientSideDetectionService::SendModelToRenderers,
                               base::Unretained(this)),
-          profile_->GetURLLoaderFactory(), extended_reporting_);
+          delegate_->GetURLLoaderFactory(), extended_reporting_);
     }
     // Refresh the models when the service is enabled.  This can happen when
     // either of the preferences are toggled, or early during startup if
@@ -142,9 +132,8 @@
       model_loader_->CancelFetcher();
     }
     // Invoke pending callbacks with a false verdict.
-    for (auto it = client_phishing_reports_.begin();
-         it != client_phishing_reports_.end(); ++it) {
-      ClientPhishingReportInfo* info = it->second.get();
+    for (auto& client_phishing_report : client_phishing_reports_) {
+      ClientPhishingReportInfo* info = client_phishing_report.second.get();
       if (!info->callback.is_null())
         std::move(info->callback).Run(info->phishing_url, false);
     }
@@ -242,8 +231,7 @@
         ChromeUserPopulation::SAFE_BROWSING);
   }
   request->mutable_population()->set_profile_management_status(
-      GetProfileManagementStatus(
-          g_browser_process->browser_policy_connector()));
+      delegate_->GetManagementStatus());
 
   std::string request_data;
   request->SerializeToString(&request_data);
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.h b/components/safe_browsing/content/browser/client_side_detection_service.h
similarity index 88%
rename from chrome/browser/safe_browsing/client_side_detection_service.h
rename to components/safe_browsing/content/browser/client_side_detection_service.h
index baa0aa7c..782173b1 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.h
+++ b/components/safe_browsing/content/browser/client_side_detection_service.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
 //
@@ -10,8 +10,8 @@
 // This class is not thread-safe and expects all calls to be made on the UI
 // thread.  We also expect that the calling thread runs a message loop.
 
-#ifndef CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_H_
-#define CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_H_
+#ifndef COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_CLIENT_SIDE_DETECTION_SERVICE_H_
+#define COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_CLIENT_SIDE_DETECTION_SERVICE_H_
 
 #include <map>
 #include <memory>
@@ -30,14 +30,13 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/safe_browsing/content/browser/client_side_model_loader.h"
+#include "components/safe_browsing/core/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "url/gurl.h"
 
-class Profile;
-
 namespace network {
 class SimpleURLLoader;
 class SharedURLLoaderFactory;
@@ -55,7 +54,24 @@
   typedef base::OnceCallback<void(GURL, bool)>
       ClientReportPhishingRequestCallback;
 
-  explicit ClientSideDetectionService(Profile* profile);
+  // Delegate which allows to provide embedder specific implementations.
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // Returns the pref service associated with the current profile.
+    virtual PrefService* GetPrefs() = 0;
+    // Returns the main URLLoaderFactory.
+    virtual scoped_refptr<network::SharedURLLoaderFactory>
+    GetURLLoaderFactory() = 0;
+    virtual scoped_refptr<network::SharedURLLoaderFactory>
+    GetSafeBrowsingURLLoaderFactory() = 0;
+    // Returns the management status for current profile.
+    virtual ChromeUserPopulation::ProfileManagementStatus
+    GetManagementStatus() = 0;
+  };
+
+  explicit ClientSideDetectionService(std::unique_ptr<Delegate> delegate);
   ~ClientSideDetectionService() override;
 
   void Shutdown() override;
@@ -111,8 +127,6 @@
   // Sends a model to each renderer.
   virtual void SendModelToRenderers();
 
-  base::WeakPtr<ClientSideDetectionService> GetWeakPtr();
-
   // Get the model status for the given client-side model.
   ModelLoader::ClientModelStatus GetLastModelStatus();
 
@@ -194,16 +208,13 @@
   // Returns the URL that will be used for phishing requests.
   static GURL GetClientReportUrl(const std::string& report_url);
 
-  // The profile this ClientSideDetectionService is attached to.
-  Profile* profile_;
-
   // Whether the service is running or not.  When the service is not running,
   // it won't download the model nor report detected phishing URLs.
-  bool enabled_;
+  bool enabled_ = false;
 
   // Whether the service is in extended reporting mode or not. This affects the
   // choice of model.
-  bool extended_reporting_;
+  bool extended_reporting_ = false;
 
   std::unique_ptr<ModelLoader> model_loader_;
 
@@ -238,6 +249,8 @@
   // Factory used for constructing ModelLoaders
   base::RepeatingCallback<std::unique_ptr<ModelLoader>()> model_factory_;
 
+  std::unique_ptr<Delegate> delegate_;
+
   // Used to asynchronously call the callbacks for
   // SendClientReportPhishingRequest.
   base::WeakPtrFactory<ClientSideDetectionService> weak_factory_{this};
@@ -247,4 +260,4 @@
 
 }  // namespace safe_browsing
 
-#endif  // CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_H_
+#endif  // COMPONENTS_SAFE_BROWSING_CONTENT_BROWSER_CLIENT_SIDE_DETECTION_SERVICE_H_
diff --git a/components/signin/internal/identity_manager/accounts_mutator_impl.cc b/components/signin/internal/identity_manager/accounts_mutator_impl.cc
index 5c683a8..e31505d3 100644
--- a/components/signin/internal/identity_manager/accounts_mutator_impl.cc
+++ b/components/signin/internal/identity_manager/accounts_mutator_impl.cc
@@ -100,7 +100,7 @@
 #endif
   DCHECK(primary_account_manager_->HasPrimaryAccount(ConsentLevel::kSync));
   CoreAccountInfo primary_account_info =
-      primary_account_manager_->GetAuthenticatedAccountInfo();
+      primary_account_manager_->GetPrimaryAccountInfo(ConsentLevel::kSync);
   AddOrUpdateAccount(primary_account_info.gaia, primary_account_info.email,
                      GaiaConstants::kInvalidRefreshToken,
                      primary_account_info.is_under_advanced_protection, source);
diff --git a/components/signin/internal/identity_manager/primary_account_manager.cc b/components/signin/internal/identity_manager/primary_account_manager.cc
index 50a8037..c85f7433b 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager.cc
@@ -113,10 +113,10 @@
       account_tracker_service_->GetAccountInfo(account_id);
   if (consented) {
     DCHECK(!account_info.account_id.empty());
-    // First reset the state, because SetAuthenticatedAccountInfo can only be
-    // called if the user is not already signed in.
-    SetPrimaryAccountInternal(CoreAccountInfo(), /*consented=*/false);
-    SetAuthenticatedAccountInfo(account_info);
+    // First reset the state, because SetSyncPrimaryAccountInternal() can
+    // only be called if there is no primary account.
+    SetPrimaryAccountInternal(CoreAccountInfo(), /*consented_to_sync=*/false);
+    SetSyncPrimaryAccountInternal(account_info);
   } else {
     SetPrimaryAccountInternal(account_info, consented);
   }
@@ -127,50 +127,41 @@
   // It is important to only load credentials after starting to observe the
   // token service.
   token_service_->AddObserver(this);
-  token_service_->LoadCredentials(GetAuthenticatedAccountId());
+  token_service_->LoadCredentials(
+      GetPrimaryAccountId(signin::ConsentLevel::kSync));
 }
 
 bool PrimaryAccountManager::IsInitialized() const {
   return initialized_;
 }
 
-CoreAccountInfo PrimaryAccountManager::GetAuthenticatedAccountInfo() const {
-  if (!HasPrimaryAccount(signin::ConsentLevel::kSync))
+CoreAccountInfo PrimaryAccountManager::GetPrimaryAccountInfo(
+    signin::ConsentLevel consent_level) const {
+  if (!HasPrimaryAccount(consent_level))
     return CoreAccountInfo();
   return primary_account_info();
 }
 
-CoreAccountId PrimaryAccountManager::GetAuthenticatedAccountId() const {
-  return GetAuthenticatedAccountInfo().account_id;
-}
-
-CoreAccountInfo PrimaryAccountManager::GetUnconsentedPrimaryAccountInfo()
-    const {
-  return primary_account_info();
+CoreAccountId PrimaryAccountManager::GetPrimaryAccountId(
+    signin::ConsentLevel consent_level) const {
+  return GetPrimaryAccountInfo(consent_level).account_id;
 }
 
 void PrimaryAccountManager::SetUnconsentedPrimaryAccountInfo(
-    CoreAccountInfo account_info) {
+    const CoreAccountInfo& account_info) {
   if (HasPrimaryAccount(signin::ConsentLevel::kSync)) {
-    DCHECK_EQ(account_info, GetAuthenticatedAccountInfo());
+    DCHECK_EQ(account_info, GetPrimaryAccountInfo(signin::ConsentLevel::kSync));
     return;
   }
 
   bool account_changed = account_info != primary_account_info();
-  PrimaryAccountChangeEvent::State previous_state(
-      primary_account_info(), signin::ConsentLevel::kNotRequired);
+  PrimaryAccountChangeEvent::State previous_state = GetPrimaryAccountState();
   SetPrimaryAccountInternal(account_info, /*consented_to_sync=*/false);
-
-  if (account_changed) {
-    PrimaryAccountChangeEvent::State current_state(
-        account_info, signin::ConsentLevel::kNotRequired);
-    PrimaryAccountChangeEvent event_details(previous_state, current_state);
-    for (Observer& observer : observers_)
-      observer.UnconsentedPrimaryAccountChanged(event_details);
-  }
+  if (account_changed)
+    FirePrimaryAccountChanged(previous_state);
 }
 
-void PrimaryAccountManager::SetAuthenticatedAccountInfo(
+void PrimaryAccountManager::SetSyncPrimaryAccountInternal(
     const CoreAccountInfo& account_info) {
   DCHECK(!account_info.account_id.empty());
   DCHECK(!HasPrimaryAccount(signin::ConsentLevel::kSync));
@@ -199,7 +190,7 @@
   client_->GetPrefs()->SetString(prefs::kGoogleServicesLastUsername,
                                  account_info.email);
 
-  // Commit authenticated account info immediately so that it does not get lost
+  // Commit primary sync account info immediately so that it does not get lost
   // if Chrome crashes before the next commit interval.
   client_->GetPrefs()->CommitPendingWrite();
 }
@@ -237,38 +228,29 @@
   }
 }
 
-void PrimaryAccountManager::SignIn(const std::string& username) {
-  CoreAccountInfo info =
-      account_tracker_service_->FindAccountInfoByEmail(username);
-  DCHECK(!info.gaia.empty());
-  DCHECK(!info.email.empty());
-  DCHECK(!info.account_id.empty());
+void PrimaryAccountManager::SetSyncPrimaryAccountInfo(
+    const CoreAccountInfo& account_info) {
+#if DCHECK_IS_ON()
+  DCHECK(!account_info.account_id.empty());
+  DCHECK(!account_info.gaia.empty());
+  DCHECK(!account_info.email.empty());
+  DCHECK(!account_tracker_service_->GetAccountInfo(account_info.account_id)
+              .IsEmpty())
+      << "Account should have been seeded before being set as primary account";
+#endif
   if (HasPrimaryAccount(signin::ConsentLevel::kSync)) {
-    DCHECK_EQ(info.account_id, GetAuthenticatedAccountId())
-        << "Changing the authenticated account while it is not allowed.";
+    DCHECK_EQ(account_info.account_id,
+              GetPrimaryAccountId(signin::ConsentLevel::kSync))
+        << "Changing the primary sync account is not allowed.";
     return;
   }
 
-  bool account_changed = info != primary_account_info();
-  PrimaryAccountChangeEvent::State previous_state(
-      primary_account_info(), signin::ConsentLevel::kNotRequired);
-  PrimaryAccountChangeEvent::State current_state(info,
-                                                 signin::ConsentLevel::kSync);
-  PrimaryAccountChangeEvent event_details(previous_state, current_state);
-
-  SetAuthenticatedAccountInfo(info);
-
-  for (Observer& observer : observers_) {
-    // TODO(https://crbug.com/1158855): Remove call to
-    // UnconsentedPrimaryAccountChanged() after IdentityManager::Observer
-    // migration has been completed.
-    if (account_changed)
-      observer.UnconsentedPrimaryAccountChanged(event_details);
-    observer.GoogleSigninSucceeded(event_details);
-  }
+  PrimaryAccountChangeEvent::State previous_state = GetPrimaryAccountState();
+  SetSyncPrimaryAccountInternal(account_info);
+  FirePrimaryAccountChanged(previous_state);
 }
 
-void PrimaryAccountManager::UpdateAuthenticatedAccountInfo() {
+void PrimaryAccountManager::UpdateSyncPrimaryAccountInfo() {
   DCHECK(!primary_account_info().account_id.empty());
   DCHECK(HasPrimaryAccount(signin::ConsentLevel::kSync));
   const CoreAccountInfo info = account_tracker_service_->GetAccountInfo(
@@ -350,39 +332,52 @@
     return;
   }
 
-  const CoreAccountInfo account_info = primary_account_info();
-  const bool has_sync_consent = HasPrimaryAccount(signin::ConsentLevel::kSync);
+  PrimaryAccountChangeEvent::State previous_state = GetPrimaryAccountState();
   client_->GetPrefs()->ClearPref(prefs::kGoogleServicesHostedDomain);
-  // Revoke the sync consent.
-  if (has_sync_consent)
-    SetPrimaryAccountInternal(account_info, /*consented_to_sync=*/false);
 
-  DCHECK(!HasPrimaryAccount(signin::ConsentLevel::kSync));
   // Revoke all tokens before sending signed_out notification, because there
   // may be components that don't listen for token service events when the
   // profile is not connected to an account.
   switch (remove_option) {
     case RemoveAccountsOption::kRemoveAllAccounts:
       VLOG(0) << "Revoking all refresh tokens on server. Reason: sign out";
-      SetUnconsentedPrimaryAccountInfo(CoreAccountInfo());
+      SetPrimaryAccountInternal(CoreAccountInfo(), /*consented_to_sync=*/false);
       token_service_->RevokeAllCredentials(
           signin_metrics::SourceForRefreshTokenOperation::
               kPrimaryAccountManager_ClearAccount);
       break;
     case RemoveAccountsOption::kKeepAllAccounts:
-      // Do nothing.
+      SetPrimaryAccountInternal(primary_account_info(),
+                                /*consented_to_sync=*/false);
       break;
   }
 
-  PrimaryAccountChangeEvent::State previous_state;
-  previous_state.primary_account = account_info;
-  previous_state.consent_level = has_sync_consent
-                                     ? signin::ConsentLevel::kSync
-                                     : signin::ConsentLevel::kNotRequired;
-  PrimaryAccountChangeEvent event_details(previous_state,
-                                          PrimaryAccountChangeEvent::State());
+  DCHECK(!HasPrimaryAccount(signin::ConsentLevel::kSync));
+  FirePrimaryAccountChanged(previous_state);
+}
+
+PrimaryAccountChangeEvent::State PrimaryAccountManager::GetPrimaryAccountState()
+    const {
+  PrimaryAccountChangeEvent::State state(primary_account_info(),
+                                         signin::ConsentLevel::kNotRequired);
+  if (HasPrimaryAccount(signin::ConsentLevel::kSync))
+    state.consent_level = signin::ConsentLevel::kSync;
+  return state;
+}
+
+void PrimaryAccountManager::FirePrimaryAccountChanged(
+    const PrimaryAccountChangeEvent::State& previous_state) {
+  PrimaryAccountChangeEvent::State current_state = GetPrimaryAccountState();
+  PrimaryAccountChangeEvent event_details(previous_state, current_state);
+
+  DCHECK(event_details.GetEventTypeFor(signin::ConsentLevel::kSync) !=
+             PrimaryAccountChangeEvent::Type::kNone ||
+         event_details.GetEventTypeFor(signin::ConsentLevel::kNotRequired) !=
+             PrimaryAccountChangeEvent::Type::kNone)
+      << "PrimaryAccountChangeEvent with no change: " << event_details;
+
   for (Observer& observer : observers_)
-    observer.GoogleSignedOut(event_details);
+    observer.OnPrimaryAccountChanged(event_details);
 }
 
 void PrimaryAccountManager::OnRefreshTokensLoaded() {
@@ -397,9 +392,10 @@
   if (token_service_->HasLoadCredentialsFinishedWithNoErrors()) {
     std::vector<AccountInfo> accounts_in_tracker_service =
         account_tracker_service_->GetAccounts();
-    const CoreAccountId authenticated_account_id = GetAuthenticatedAccountId();
+    const CoreAccountId sync_account_id =
+        GetPrimaryAccountId(signin::ConsentLevel::kSync);
     for (const auto& account : accounts_in_tracker_service) {
-      if (authenticated_account_id != account.account_id &&
+      if (sync_account_id != account.account_id &&
           !token_service_->RefreshTokenIsAvailable(account.account_id)) {
         VLOG(0) << "Removed account from account tracker service: "
                 << account.account_id;
diff --git a/components/signin/internal/identity_manager/primary_account_manager.h b/components/signin/internal/identity_manager/primary_account_manager.h
index b05fb4c..52c3472 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.h
+++ b/components/signin/internal/identity_manager/primary_account_manager.h
@@ -47,19 +47,10 @@
  public:
   class Observer : public base::CheckedObserver {
    public:
-    // Called whenever a user signs into Google services such as sync.
-    // Not called during a reauth.
-    virtual void GoogleSigninSucceeded(
-        const signin::PrimaryAccountChangeEvent& event_details) {}
-
-    // Called whenever the unconsented primary account changes. This includes
-    // the changes for the consented primary account as well.
-    virtual void UnconsentedPrimaryAccountChanged(
-        const signin::PrimaryAccountChangeEvent& event_details) {}
-
-    // Called whenever the currently signed-in user has been signed out.
-    virtual void GoogleSignedOut(
-        const signin::PrimaryAccountChangeEvent& event_details) {}
+    // Called when there is a change in the primary account or in the consent
+    // level for the primary account.
+    virtual void OnPrimaryAccountChanged(
+        const signin::PrimaryAccountChangeEvent& event_details) = 0;
   };
 
   // Used to remove accounts from the token service and the account tracker.
@@ -87,48 +78,52 @@
   void Initialize(PrefService* local_state);
   bool IsInitialized() const;
 
-  // If a user has previously signed in (and has not signed out), this returns
-  // the know information of the account. Otherwise, it returns an empty struct.
-  CoreAccountInfo GetAuthenticatedAccountInfo() const;
-
-  // If a user has previously signed in (and has not signed out), this returns
-  // the account id. Otherwise, it returns an empty CoreAccountId.  This id is
-  // the G+/Focus obfuscated gaia id of the user. It can be used to uniquely
-  // identify an account, so for example as a key to map accounts to data. For
-  // code that needs a unique id to represent the connected account, call this
-  // method. Example: the AccountStatusMap type in
-  // MutableProfileOAuth2TokenService. For code that needs to know the
-  // normalized email address of the connected account, use
-  // GetAuthenticatedAccountInfo().email.  Example: to show the string
-  // "Signed in as XXX" in the hotdog menu.
-  CoreAccountId GetAuthenticatedAccountId() const;
-
   // Returns whether the user's primary account is available. If consent is
   // |ConsentLevel::kSync| then true implies that the user has blessed this
   // account for sync.
   bool HasPrimaryAccount(signin::ConsentLevel consent_level) const;
 
+  // Provides access to the core information of the user's primary account.
+  // The primary account may or may not be blessed with the sync consent.
+  // Returns an empty struct if no such info is available, either because there
+  // is no primary account yet or because the user signed out or the |consent|
+  // level required |ConsentLevel::kSync| was not granted.
+  // Returns a non-empty struct if the primary account exists and was granted
+  // the required consent level.
+  CoreAccountInfo GetPrimaryAccountInfo(
+      signin::ConsentLevel consent_level) const;
+
+  // Provides access to the account ID of the user's primary account. Simple
+  // convenience wrapper over GetPrimaryAccountInfo().account_id.
+  CoreAccountId GetPrimaryAccountId(signin::ConsentLevel consent_level) const;
+
   // Signs a user in. PrimaryAccountManager assumes that |username| can be used
   // to look up the corresponding account_id and gaia_id for this email.
-  void SignIn(const std::string& username);
+  void SetSyncPrimaryAccountInfo(const CoreAccountInfo& account_info);
 
-  // Updates the authenticated account information from AccountTrackerService.
-  void UpdateAuthenticatedAccountInfo();
+  // Sets the unconsented primary account. The unconsented primary account can
+  // only be changed if the user has not consented for sync If the user has
+  // consented for sync already, then use ClearPrimaryAccount() or RevokeSync()
+  // instead.
+  void SetUnconsentedPrimaryAccountInfo(const CoreAccountInfo& account_info);
+
+  // Updates the primary account information from AccountTrackerService.
+  void UpdateSyncPrimaryAccountInfo();
 
   // Signout API surfaces (not supported on ChromeOS, where signout is not
   // permitted).
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-  // Signs a user out, removing the preference, erasing all keys
-  // associated with the authenticated user, and canceling all auth in progress.
-  // It removes all accounts from Chrome by revoking all refresh tokens.
+  // Clears the primary account, erasing all keys associated with the primary
+  // account (also cancels all auth in progress).
+  // It removes all accounts from the identity manager by revoking all refresh
+  // tokens.
   void ClearPrimaryAccount(signin_metrics::ProfileSignout signout_source_metric,
                            signin_metrics::SignoutDelete signout_delete_metric);
 
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
-  // Signs a user out, removing the preference, erasing all keys
-  // associated with the authenticated user, and canceling all auth in progress.
-  // Does not remove the accounts from the token service.
+  // Rovokes the sync consent but leaves the primary account and the rest of
+  // the accounts untouched.
   void RevokeSyncConsent(signin_metrics::ProfileSignout signout_source_metric,
                          signin_metrics::SignoutDelete signout_delete_metric);
 
@@ -136,25 +131,14 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
-  // Provides access to the core information of the user's unconsented primary
-  // account. Returns an empty info, if there is no such account.
-  CoreAccountInfo GetUnconsentedPrimaryAccountInfo() const;
-
-  // Sets the unconsented primary account. The unconsented primary account can
-  // only be changed if the user is not authenticated. If the user is
-  // authenticated, use Signout() instead.
-  void SetUnconsentedPrimaryAccountInfo(CoreAccountInfo account_info);
-
  private:
-  // Sets the authenticated user's account id, when the user has consented to
-  // sync.
-  // If the user is already authenticated with the same account id, then this
-  // method is a no-op.
-  // It is forbidden to call this method if the user is already authenticated
-  // with a different account (this method will DCHECK in that case).
-  // |account_id| must not be empty. To log the user out, use
-  // ClearAuthenticatedAccountId() instead.
-  void SetAuthenticatedAccountInfo(const CoreAccountInfo& account_info);
+  // Sets the primary account id, when the user has consented to sync.
+  // If the user has consented for sync with the same account, then this method
+  // is a no-op.
+  // It is forbidden to call this method if the user has already consented for
+  // sync  with a different account (this method will DCHECK in that case).
+  // |account_id| must not be empty.
+  void SetSyncPrimaryAccountInternal(const CoreAccountInfo& account_info);
 
   // Sets |primary_account_info_| and updates the associated preferences.
   void SetPrimaryAccountInternal(const CoreAccountInfo& account_info,
@@ -175,6 +159,13 @@
       bool assert_signout_allowed,
       SigninClient::SignoutDecision signout_decision);
 
+  // Returns the current state of the primary account.
+  signin::PrimaryAccountChangeEvent::State GetPrimaryAccountState() const;
+
+  // Fires OnPrimaryAccountChanged() notifications on all observers.
+  void FirePrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent::State& previous_state);
+
   // ProfileOAuth2TokenServiceObserver:
   void OnRefreshTokensLoaded() override;
 
@@ -191,8 +182,8 @@
 
   bool initialized_ = false;
 
-  // Account id after successful authentication. The account may or may not be
-  // consented to Sync.
+  // The primary account information. The account may or may not be consented
+  // for Sync.
   // Must be kept in sync with prefs. Use SetPrimaryAccountInternal() to change
   // this field.
   CoreAccountInfo primary_account_info_;
diff --git a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
index dd98841..89a4638 100644
--- a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
@@ -111,24 +111,43 @@
 
   void ExpectSignInWithRefreshTokenSuccess() {
     EXPECT_TRUE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
-    EXPECT_FALSE(manager_->GetAuthenticatedAccountId().empty());
-    EXPECT_FALSE(manager_->GetAuthenticatedAccountInfo().email.empty());
+    EXPECT_FALSE(manager_->GetPrimaryAccountId(ConsentLevel::kSync).empty());
+    EXPECT_FALSE(
+        manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email.empty());
 
     EXPECT_TRUE(token_service_.RefreshTokenIsAvailable(
-        manager_->GetAuthenticatedAccountId()));
+        manager_->GetPrimaryAccountId(ConsentLevel::kSync)));
 
     // Should go into token service and stop.
     EXPECT_EQ(1, num_successful_signins_);
   }
 
-  void GoogleSigninSucceeded(
-      const signin::PrimaryAccountChangeEvent& account_info) override {
-    num_successful_signins_++;
-  }
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event_details) override {
+    DCHECK(event_details.GetEventTypeFor(signin::ConsentLevel::kSync) !=
+               signin::PrimaryAccountChangeEvent::Type::kNone ||
+           event_details.GetEventTypeFor(signin::ConsentLevel::kNotRequired) !=
+               signin::PrimaryAccountChangeEvent::Type::kNone)
+        << "PrimaryAccountChangeEvent with no change: " << event_details;
 
-  void UnconsentedPrimaryAccountChanged(
-      const signin::PrimaryAccountChangeEvent& info) override {
-    num_unconsented_account_changed_++;
+    switch (event_details.GetEventTypeFor(ConsentLevel::kSync)) {
+      case signin::PrimaryAccountChangeEvent::Type::kSet:
+        num_successful_signins_++;
+        break;
+      case signin::PrimaryAccountChangeEvent::Type::kCleared:
+        // ignored
+        break;
+      case signin::PrimaryAccountChangeEvent::Type::kNone:
+        break;
+    }
+    switch (event_details.GetEventTypeFor(ConsentLevel::kNotRequired)) {
+      case signin::PrimaryAccountChangeEvent::Type::kSet:
+      case signin::PrimaryAccountChangeEvent::Type::kCleared:
+        num_unconsented_account_changed_++;
+        break;
+      case signin::PrimaryAccountChangeEvent::Type::kNone:
+        break;
+    }
   }
 
   base::test::TaskEnvironment task_environment_;
@@ -153,20 +172,25 @@
   CreatePrimaryAccountManager();
   CoreAccountId main_account_id =
       AddToAccountTracker("account_id", "user@gmail.com");
-  manager_->SignIn("user@gmail.com");
+  manager_->SetSyncPrimaryAccountInfo(
+      account_tracker()->GetAccountInfo(main_account_id));
   manager_->ClearPrimaryAccount(signin_metrics::SIGNOUT_TEST,
                                 signin_metrics::SignoutDelete::IGNORE_METRIC);
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().email.empty());
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty());
-  EXPECT_TRUE(manager_->GetUnconsentedPrimaryAccountInfo().IsEmpty());
+  EXPECT_TRUE(
+      manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email.empty());
+  EXPECT_TRUE(manager_->GetPrimaryAccountId(ConsentLevel::kSync).empty());
+  EXPECT_TRUE(
+      manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired).IsEmpty());
   // Should not be persisted anymore
   ShutDownManager();
   CreatePrimaryAccountManager();
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().email.empty());
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty());
-  EXPECT_TRUE(manager_->GetUnconsentedPrimaryAccountInfo().IsEmpty());
+  EXPECT_TRUE(
+      manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email.empty());
+  EXPECT_TRUE(manager_->GetPrimaryAccountId(ConsentLevel::kSync).empty());
+  EXPECT_TRUE(
+      manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired).IsEmpty());
 }
 
 TEST_F(PrimaryAccountManagerTest, SignOutRevoke) {
@@ -177,9 +201,11 @@
       AddToAccountTracker("other_id", "other@gmail.com");
   token_service_.UpdateCredentials(main_account_id, "token");
   token_service_.UpdateCredentials(other_account_id, "token");
-  manager_->SignIn("user@gmail.com");
+  manager_->SetSyncPrimaryAccountInfo(
+      account_tracker()->GetAccountInfo(main_account_id));
   EXPECT_TRUE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
-  EXPECT_EQ(main_account_id, manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ(main_account_id,
+            manager_->GetPrimaryAccountId(ConsentLevel::kSync));
 
   manager_->ClearPrimaryAccount(signin_metrics::SIGNOUT_TEST,
                                 signin_metrics::SignoutDelete::IGNORE_METRIC);
@@ -192,11 +218,14 @@
 TEST_F(PrimaryAccountManagerTest, SignOutWhileProhibited) {
   CreatePrimaryAccountManager();
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().email.empty());
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty());
+  EXPECT_TRUE(
+      manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email.empty());
+  EXPECT_TRUE(manager_->GetPrimaryAccountId(ConsentLevel::kSync).empty());
 
-  AddToAccountTracker("gaia_id", "user@gmail.com");
-  manager_->SignIn("user@gmail.com");
+  CoreAccountId main_account_id =
+      AddToAccountTracker("gaia_id", "user@gmail.com");
+  manager_->SetSyncPrimaryAccountInfo(
+      account_tracker()->GetAccountInfo(main_account_id));
   signin_client()->set_is_signout_allowed(false);
   manager_->ClearPrimaryAccount(signin_metrics::SIGNOUT_TEST,
                                 signin_metrics::SignoutDelete::IGNORE_METRIC);
@@ -210,8 +239,9 @@
 TEST_F(PrimaryAccountManagerTest, UnconsentedSignOutWhileProhibited) {
   CreatePrimaryAccountManager();
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().email.empty());
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty());
+  EXPECT_TRUE(
+      manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email.empty());
+  EXPECT_TRUE(manager_->GetPrimaryAccountId(ConsentLevel::kSync).empty());
 
   CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
   CoreAccountInfo account_info = account_tracker()->GetAccountInfo(account_id);
@@ -231,61 +261,72 @@
                          ".*@google.com");
   CreatePrimaryAccountManager();
   // Currently signed in user is prohibited by policy, so should be signed out.
-  EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(CoreAccountId(), manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ("", manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(CoreAccountId(),
+            manager_->GetPrimaryAccountId(ConsentLevel::kSync));
 }
 
 TEST_F(PrimaryAccountManagerTest, ProhibitedAfterStartup) {
   CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
   user_prefs_.SetString(prefs::kGoogleServicesAccountId, account_id.ToString());
   CreatePrimaryAccountManager();
-  EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ("user@gmail.com",
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(account_id, manager_->GetPrimaryAccountId(ConsentLevel::kSync));
   // Update the profile - user should be signed out.
   local_state_.SetString(prefs::kGoogleServicesUsernamePattern,
                          ".*@google.com");
-  EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(CoreAccountId(), manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ("", manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(CoreAccountId(),
+            manager_->GetPrimaryAccountId(ConsentLevel::kSync));
 }
 #endif
 
 TEST_F(PrimaryAccountManagerTest, SignIn) {
   CreatePrimaryAccountManager();
-  EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(CoreAccountId(), manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ("", manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(CoreAccountId(),
+            manager_->GetPrimaryAccountId(ConsentLevel::kSync));
   EXPECT_EQ(0, num_successful_signins_);
   EXPECT_EQ(0, num_unconsented_account_changed_);
 
   CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
-  manager_->SignIn("user@gmail.com");
+  manager_->SetSyncPrimaryAccountInfo(
+      account_tracker()->GetAccountInfo(account_id));
   EXPECT_EQ(1, num_successful_signins_);
   EXPECT_EQ(1, num_unconsented_account_changed_);
-  EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
-  EXPECT_EQ(manager_->GetUnconsentedPrimaryAccountInfo(),
-            manager_->GetAuthenticatedAccountInfo());
+  EXPECT_EQ("user@gmail.com",
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(account_id, manager_->GetPrimaryAccountId(ConsentLevel::kSync));
+  EXPECT_EQ(manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync));
 }
 
 TEST_F(PrimaryAccountManagerTest,
        ExternalSignIn_ReauthShouldNotSendNotification) {
   CreatePrimaryAccountManager();
-  EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(CoreAccountId(), manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ("", manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(CoreAccountId(),
+            manager_->GetPrimaryAccountId(ConsentLevel::kSync));
   EXPECT_EQ(0, num_successful_signins_);
   EXPECT_EQ(0, num_unconsented_account_changed_);
 
   CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
-  manager_->SignIn("user@gmail.com");
+  manager_->SetSyncPrimaryAccountInfo(
+      account_tracker()->GetAccountInfo(account_id));
   EXPECT_EQ(1, num_successful_signins_);
   EXPECT_EQ(1, num_unconsented_account_changed_);
-  EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ("user@gmail.com",
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(account_id, manager_->GetPrimaryAccountId(ConsentLevel::kSync));
 
-  manager_->SignIn("user@gmail.com");
+  manager_->SetSyncPrimaryAccountInfo(
+      account_tracker()->GetAccountInfo(account_id));
   EXPECT_EQ(1, num_successful_signins_);
   EXPECT_EQ(1, num_unconsented_account_changed_);
-  EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ("user@gmail.com",
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(account_id, manager_->GetPrimaryAccountId(ConsentLevel::kSync));
 }
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
@@ -296,8 +337,8 @@
   user_prefs_.SetBoolean(prefs::kSigninAllowed, false);
   CreatePrimaryAccountManager();
   // Currently signing in is prohibited by policy, so should be signed out.
-  EXPECT_EQ("", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountId().empty());
+  EXPECT_EQ("", manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_TRUE(manager_->GetPrimaryAccountId(ConsentLevel::kSync).empty());
 }
 #endif
 
@@ -330,7 +371,8 @@
 
   CreatePrimaryAccountManager();
 
-  EXPECT_EQ(CoreAccountId(gaia_id), manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ(CoreAccountId(gaia_id),
+            manager_->GetPrimaryAccountId(ConsentLevel::kSync));
   EXPECT_EQ(gaia_id, user_prefs_.GetString(prefs::kGoogleServicesAccountId));
 }
 
@@ -362,7 +404,8 @@
   client_prefs->SetString(prefs::kGoogleServicesAccountId, gaia_id);
 
   CreatePrimaryAccountManager();
-  EXPECT_EQ(CoreAccountId(gaia_id), manager_->GetAuthenticatedAccountId());
+  EXPECT_EQ(CoreAccountId(gaia_id),
+            manager_->GetPrimaryAccountId(ConsentLevel::kSync));
   EXPECT_EQ(gaia_id, user_prefs_.GetString(prefs::kGoogleServicesAccountId));
 
   base::RunLoop().RunUntilIdle();
@@ -375,10 +418,11 @@
   user_prefs_.SetString(prefs::kGoogleServicesAccountId, account_id.ToString());
   user_prefs_.SetBoolean(prefs::kGoogleServicesConsentedToSync, true);
   CreatePrimaryAccountManager();
-  EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
-  EXPECT_EQ(manager_->GetUnconsentedPrimaryAccountInfo(),
-            manager_->GetAuthenticatedAccountInfo());
+  EXPECT_EQ("user@gmail.com",
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(account_id, manager_->GetPrimaryAccountId(ConsentLevel::kSync));
+  EXPECT_EQ(manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync));
 }
 
 TEST_F(PrimaryAccountManagerTest, RestoreFromPrefsUnconsented) {
@@ -387,10 +431,11 @@
   user_prefs_.SetBoolean(prefs::kGoogleServicesConsentedToSync, false);
   CreatePrimaryAccountManager();
   EXPECT_EQ("user@gmail.com",
-            manager_->GetUnconsentedPrimaryAccountInfo().email);
-  EXPECT_EQ(account_id,
-            manager_->GetUnconsentedPrimaryAccountInfo().account_id);
-  EXPECT_TRUE(manager_->GetAuthenticatedAccountInfo().IsEmpty());
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired).email);
+  EXPECT_EQ(
+      account_id,
+      manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired).account_id);
+  EXPECT_TRUE(manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).IsEmpty());
 }
 
 // If kGoogleServicesConsentedToSync is missing, the account is fully
@@ -406,15 +451,17 @@
 
   CreatePrimaryAccountManager();
   EXPECT_TRUE(user_prefs_.GetBoolean(prefs::kGoogleServicesConsentedToSync));
-  EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
-  EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
-  EXPECT_EQ(manager_->GetUnconsentedPrimaryAccountInfo(),
-            manager_->GetAuthenticatedAccountInfo());
+  EXPECT_EQ("user@gmail.com",
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
+  EXPECT_EQ(account_id, manager_->GetPrimaryAccountId(ConsentLevel::kSync));
+  EXPECT_EQ(manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync));
 }
 
 TEST_F(PrimaryAccountManagerTest, SetUnconsentedPrimaryAccountInfo) {
   CreatePrimaryAccountManager();
-  EXPECT_EQ(CoreAccountInfo(), manager_->GetUnconsentedPrimaryAccountInfo());
+  EXPECT_EQ(CoreAccountInfo(),
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
   EXPECT_EQ(0, num_unconsented_account_changed_);
   EXPECT_EQ(0, num_successful_signins_);
 
@@ -426,15 +473,19 @@
   manager_->SetUnconsentedPrimaryAccountInfo(account_info);
   EXPECT_EQ(0, num_successful_signins_);
   EXPECT_EQ(1, num_unconsented_account_changed_);
-  EXPECT_EQ(account_info, manager_->GetUnconsentedPrimaryAccountInfo());
-  EXPECT_EQ(CoreAccountInfo(), manager_->GetAuthenticatedAccountInfo());
+  EXPECT_EQ(account_info,
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
+  EXPECT_EQ(CoreAccountInfo(),
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync));
 
   // Set the same account again.
   manager_->SetUnconsentedPrimaryAccountInfo(account_info);
   EXPECT_EQ(0, num_successful_signins_);
   EXPECT_EQ(1, num_unconsented_account_changed_);
-  EXPECT_EQ(account_info, manager_->GetUnconsentedPrimaryAccountInfo());
-  EXPECT_EQ(CoreAccountInfo(), manager_->GetAuthenticatedAccountInfo());
+  EXPECT_EQ(account_info,
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
+  EXPECT_EQ(CoreAccountInfo(),
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync));
 
   // Change the email to another equivalent email. The account is updated but
   // observers are not notified.
@@ -442,36 +493,43 @@
   manager_->SetUnconsentedPrimaryAccountInfo(account_info);
   EXPECT_EQ(0, num_successful_signins_);
   EXPECT_EQ(1, num_unconsented_account_changed_);
-  EXPECT_EQ(account_info, manager_->GetUnconsentedPrimaryAccountInfo());
-  EXPECT_EQ(CoreAccountInfo(), manager_->GetAuthenticatedAccountInfo());
+  EXPECT_EQ(account_info,
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
+  EXPECT_EQ(CoreAccountInfo(),
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync));
 
   // Clear it.
   manager_->SetUnconsentedPrimaryAccountInfo(CoreAccountInfo());
   EXPECT_EQ(0, num_successful_signins_);
   EXPECT_EQ(2, num_unconsented_account_changed_);
-  EXPECT_EQ(CoreAccountInfo(), manager_->GetUnconsentedPrimaryAccountInfo());
-  EXPECT_EQ(CoreAccountInfo(), manager_->GetAuthenticatedAccountInfo());
+  EXPECT_EQ(CoreAccountInfo(),
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
+  EXPECT_EQ(CoreAccountInfo(),
+            manager_->GetPrimaryAccountInfo(ConsentLevel::kSync));
 }
 
 TEST_F(PrimaryAccountManagerTest, RevokeSyncConsent) {
   CreatePrimaryAccountManager();
   CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
-  manager_->SignIn("user@gmail.com");
+  manager_->SetSyncPrimaryAccountInfo(
+      account_tracker()->GetAccountInfo(account_id));
   EXPECT_TRUE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
 
   manager_->RevokeSyncConsent(signin_metrics::ProfileSignout::SIGNOUT_TEST,
                               signin_metrics::SignoutDelete::IGNORE_METRIC);
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
   EXPECT_TRUE(manager_->HasPrimaryAccount(ConsentLevel::kNotRequired));
-  EXPECT_EQ(account_id,
-            manager_->GetUnconsentedPrimaryAccountInfo().account_id);
+  EXPECT_EQ(
+      account_id,
+      manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired).account_id);
 }
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
 TEST_F(PrimaryAccountManagerTest, ClearPrimaryAccount) {
   CreatePrimaryAccountManager();
   CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
-  manager_->SignIn("user@gmail.com");
+  manager_->SetSyncPrimaryAccountInfo(
+      account_tracker()->GetAccountInfo(account_id));
   EXPECT_TRUE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
 
   manager_->ClearPrimaryAccount(signin_metrics::ProfileSignout::SIGNOUT_TEST,
diff --git a/components/signin/internal/identity_manager/primary_account_mutator_impl.cc b/components/signin/internal/identity_manager/primary_account_mutator_impl.cc
index 1e6d2f8..fb06b6a 100644
--- a/components/signin/internal/identity_manager/primary_account_mutator_impl.cc
+++ b/components/signin/internal/identity_manager/primary_account_mutator_impl.cc
@@ -62,7 +62,7 @@
   // TODO(crbug.com/889899): should check that the account email is allowed.
 #endif
 
-  primary_account_manager_->SignIn(account_info.email);
+  primary_account_manager_->SetSyncPrimaryAccountInfo(account_info);
   return true;
 }
 
@@ -101,7 +101,7 @@
       // TODO(msarda): The logic in this function is platform specific and we
       // should consider moving it to |SigninManager|.
       return token_service_->RefreshTokenHasError(
-          primary_account_manager_->GetAuthenticatedAccountId());
+          primary_account_manager_->GetPrimaryAccountId(ConsentLevel::kSync));
     case AccountConsistencyMethod::kDisabled:
     case AccountConsistencyMethod::kMirror:
       return true;
diff --git a/components/signin/internal/identity_manager/primary_account_policy_manager_impl.cc b/components/signin/internal/identity_manager/primary_account_policy_manager_impl.cc
index a272877..29c3c25 100644
--- a/components/signin/internal/identity_manager/primary_account_policy_manager_impl.cc
+++ b/components/signin/internal/identity_manager/primary_account_policy_manager_impl.cc
@@ -40,8 +40,8 @@
           &PrimaryAccountPolicyManagerImpl::OnSigninAllowedPrefChanged,
           base::Unretained(this), primary_account_manager));
 
-  CoreAccountInfo account_info =
-      primary_account_manager->GetAuthenticatedAccountInfo();
+  CoreAccountInfo account_info = primary_account_manager->GetPrimaryAccountInfo(
+      signin::ConsentLevel::kSync);
   if (!account_info.account_id.empty() &&
       (!IsAllowedUsername(account_info.email) || !IsSigninAllowed())) {
     // User is signed in, but the username is invalid or signin is no longer
@@ -73,7 +73,9 @@
     PrimaryAccountManager* primary_account_manager) {
   if (primary_account_manager->HasPrimaryAccount(signin::ConsentLevel::kSync) &&
       !IsAllowedUsername(
-          primary_account_manager->GetAuthenticatedAccountInfo().email)) {
+          primary_account_manager
+              ->GetPrimaryAccountInfo(signin::ConsentLevel::kSync)
+              .email)) {
     // Signed in user is invalid according to the current policy so sign
     // the user out.
     primary_account_manager->ClearPrimaryAccount(
diff --git a/components/signin/public/identity_manager/identity_manager.cc b/components/signin/public/identity_manager/identity_manager.cc
index 53c6ee0..fedd035 100644
--- a/components/signin/public/identity_manager/identity_manager.cc
+++ b/components/signin/public/identity_manager/identity_manager.cc
@@ -115,10 +115,7 @@
 // TODO(862619) change return type to base::Optional<CoreAccountInfo>
 CoreAccountInfo IdentityManager::GetPrimaryAccountInfo(
     ConsentLevel consent) const {
-  if (consent == ConsentLevel::kNotRequired) {
-    return primary_account_manager_->GetUnconsentedPrimaryAccountInfo();
-  }
-  return primary_account_manager_->GetAuthenticatedAccountInfo();
+  return primary_account_manager_->GetPrimaryAccountInfo(consent);
 }
 
 CoreAccountId IdentityManager::GetPrimaryAccountId(ConsentLevel consent) const {
@@ -479,35 +476,90 @@
   return account_info;
 }
 
-void IdentityManager::GoogleSigninSucceeded(
+void IdentityManager::OnPrimaryAccountChanged(
+    const PrimaryAccountChangeEvent& event_details) {
+  // TODO(crbug.com/1158855): Remove this switch statement once all observers
+  // are converted to OnPrimaryAccountChanged().
+  switch (event_details.GetEventTypeFor(ConsentLevel::kSync)) {
+    case PrimaryAccountChangeEvent::Type::kSet:
+      FirePrimaryAccountSet(event_details);
+      break;
+    case PrimaryAccountChangeEvent::Type::kCleared:
+      FirePrimaryAccountCleared(event_details);
+      break;
+    case PrimaryAccountChangeEvent::Type::kNone:
+      break;
+  }
+  switch (event_details.GetEventTypeFor(ConsentLevel::kNotRequired)) {
+    case PrimaryAccountChangeEvent::Type::kSet:
+    case PrimaryAccountChangeEvent::Type::kCleared:
+      FireUnconsentedPrimaryAccountChanged(event_details);
+      break;
+    case PrimaryAccountChangeEvent::Type::kNone:
+      break;
+  }
+
+  for (auto& observer : observer_list_)
+    observer.OnPrimaryAccountChanged(event_details);
+
+#if defined(OS_ANDROID)
+  if (!java_identity_manager_)
+    return;
+  JNIEnv* env = base::android::AttachCurrentThread();
+  switch (event_details.GetEventTypeFor(ConsentLevel::kSync)) {
+    case PrimaryAccountChangeEvent::Type::kSet:
+      Java_IdentityManager_onPrimaryAccountSet(
+          env, java_identity_manager_,
+          ConvertToJavaCoreAccountInfo(
+              env, event_details.GetCurrentState().primary_account));
+      return;
+    case PrimaryAccountChangeEvent::Type::kCleared:
+      Java_IdentityManager_onPrimaryAccountCleared(
+          env, java_identity_manager_,
+          ConvertToJavaCoreAccountInfo(
+              env, event_details.GetPreviousState().primary_account));
+      return;
+    case PrimaryAccountChangeEvent::Type::kNone:
+      break;
+  }
+  switch (event_details.GetEventTypeFor(ConsentLevel::kNotRequired)) {
+    // TODO(http://crbug.com/1158855): This is a hack as the Java code expects
+    // a call to onPrimaryAccountCleared() when the unconsented primary account
+    // is cleared. This does not match the intent of OnPrimaryAccountCleared
+    // which is supposed to be fired only when sync account is being cleared.
+    // This hack *must* be removed quickly as it has a high misusage risk.
+    case PrimaryAccountChangeEvent::Type::kCleared:
+      Java_IdentityManager_onPrimaryAccountCleared(
+          env, java_identity_manager_,
+          ConvertToJavaCoreAccountInfo(
+              env, event_details.GetPreviousState().primary_account));
+      break;
+    case PrimaryAccountChangeEvent::Type::kSet:
+    case PrimaryAccountChangeEvent::Type::kNone:
+      break;
+  }
+#endif
+}
+
+void IdentityManager::FirePrimaryAccountSet(
     const PrimaryAccountChangeEvent& event_details) {
   const CoreAccountInfo& account_info =
       event_details.GetCurrentState().primary_account;
   for (auto& observer : observer_list_) {
     observer.OnPrimaryAccountSet(account_info);
-    observer.OnPrimaryAccountChanged(event_details);
   }
-#if defined(OS_ANDROID)
-  if (java_identity_manager_) {
-    JNIEnv* env = base::android::AttachCurrentThread();
-    Java_IdentityManager_onPrimaryAccountSet(
-        env, java_identity_manager_,
-        ConvertToJavaCoreAccountInfo(env, account_info));
-  }
-#endif
 }
 
-void IdentityManager::UnconsentedPrimaryAccountChanged(
+void IdentityManager::FireUnconsentedPrimaryAccountChanged(
     const PrimaryAccountChangeEvent& event_details) {
   const CoreAccountInfo& account_info =
       event_details.GetCurrentState().primary_account;
   for (auto& observer : observer_list_) {
     observer.OnUnconsentedPrimaryAccountChanged(account_info);
-    observer.OnPrimaryAccountChanged(event_details);
   }
 }
 
-void IdentityManager::GoogleSignedOut(
+void IdentityManager::FirePrimaryAccountCleared(
     const PrimaryAccountChangeEvent& event_details) {
   const CoreAccountInfo& account_info =
       event_details.GetPreviousState().primary_account;
@@ -519,17 +571,7 @@
 
   for (auto& observer : observer_list_) {
     observer.OnPrimaryAccountCleared(account_info);
-    observer.OnPrimaryAccountChanged(event_details);
   }
-
-#if defined(OS_ANDROID)
-  if (java_identity_manager_) {
-    JNIEnv* env = base::android::AttachCurrentThread();
-    Java_IdentityManager_onPrimaryAccountCleared(
-        env, java_identity_manager_,
-        ConvertToJavaCoreAccountInfo(env, account_info));
-  }
-#endif
 }
 
 void IdentityManager::OnRefreshTokenAvailable(const CoreAccountId& account_id) {
@@ -638,7 +680,7 @@
   if (HasPrimaryAccount()) {
     const CoreAccountId primary_account_id = GetPrimaryAccountId();
     if (primary_account_id == info.account_id) {
-      primary_account_manager_->UpdateAuthenticatedAccountInfo();
+      primary_account_manager_->UpdateSyncPrimaryAccountInfo();
     }
   }
 
diff --git a/components/signin/public/identity_manager/identity_manager.h b/components/signin/public/identity_manager/identity_manager.h
index ece5290..2fe9f4bb 100644
--- a/components/signin/public/identity_manager/identity_manager.h
+++ b/components/signin/public/identity_manager/identity_manager.h
@@ -635,11 +635,8 @@
       const CoreAccountId& account_id) const;
 
   // PrimaryAccountManager::Observer:
-  void GoogleSigninSucceeded(
+  void OnPrimaryAccountChanged(
       const PrimaryAccountChangeEvent& event_details) override;
-  void UnconsentedPrimaryAccountChanged(
-      const PrimaryAccountChangeEvent& event_details) override;
-  void GoogleSignedOut(const PrimaryAccountChangeEvent& event_details) override;
 
   // ProfileOAuth2TokenServiceObserver:
   void OnRefreshTokenAvailable(const CoreAccountId& account_id) override;
@@ -679,6 +676,14 @@
   void OnAccountUpdated(const AccountInfo& info);
   void OnAccountRemoved(const AccountInfo& info);
 
+  // Fire the deprecated observer methods for settings and clearing the primary
+  // account.
+  void FirePrimaryAccountSet(const PrimaryAccountChangeEvent& event_details);
+  void FireUnconsentedPrimaryAccountChanged(
+      const PrimaryAccountChangeEvent& event_details);
+  void FirePrimaryAccountCleared(
+      const PrimaryAccountChangeEvent& event_details);
+
   // Backing signin classes.
   std::unique_ptr<AccountTrackerService> account_tracker_service_;
   std::unique_ptr<ProfileOAuth2TokenService> token_service_;
diff --git a/components/signin/public/identity_manager/identity_manager_unittest.cc b/components/signin/public/identity_manager/identity_manager_unittest.cc
index 99a1003..2170b44 100644
--- a/components/signin/public/identity_manager/identity_manager_unittest.cc
+++ b/components/signin/public/identity_manager/identity_manager_unittest.cc
@@ -410,8 +410,10 @@
 
     if (primary_account_manager_setup ==
         PrimaryAccountManagerSetup::kWithAuthenticatedAccout) {
-      account_tracker_service->SeedAccountInfo(kTestGaiaId, kTestEmail);
-      primary_account_manager->SignIn(kTestEmail);
+      CoreAccountId account_id =
+          account_tracker_service->SeedAccountInfo(kTestGaiaId, kTestEmail);
+      primary_account_manager->SetSyncPrimaryAccountInfo(
+          account_tracker_service->GetAccountInfo(account_id));
     }
 
     IdentityManager::InitParameters init_params;
@@ -1219,7 +1221,8 @@
 
   identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId,
                                                                   kTestEmail);
-  identity_manager()->GetPrimaryAccountManager()->SignIn(kTestEmail);
+  identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount(
+      primary_account_id());
   UpdateCredentials(primary_account_id(), kTestGaiaId, kTestEmail,
                     "refresh_token");
 
@@ -1260,7 +1263,8 @@
 
   identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId,
                                                                   kTestEmail);
-  identity_manager()->GetPrimaryAccountManager()->SignIn(kTestEmail);
+  identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount(
+      primary_account_id());
   UpdateCredentials(primary_account_id(), kTestGaiaId, kTestEmail,
                     "refresh_token");
 
@@ -1352,7 +1356,8 @@
 
   identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId,
                                                                   kTestEmail);
-  identity_manager()->GetPrimaryAccountManager()->SignIn(kTestEmail);
+  identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount(
+      primary_account_id());
   UpdateCredentials(primary_account_id(), kTestGaiaId, kTestEmail,
                     "refresh_token");
 
@@ -1407,7 +1412,8 @@
 
   identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId,
                                                                   kTestEmail);
-  identity_manager()->GetPrimaryAccountManager()->SignIn(kTestEmail);
+  identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount(
+      primary_account_id());
   UpdateCredentials(primary_account_id(), kTestGaiaId, kTestEmail,
                     "refresh_token");
   token_service()->set_auto_post_fetch_response_on_message_loop(true);
@@ -2097,7 +2103,8 @@
        BatchChangeObserversAreNotifiedOnCredentialsUpdate) {
   identity_manager()->GetAccountTrackerService()->SeedAccountInfo(kTestGaiaId,
                                                                   kTestEmail);
-  identity_manager()->GetPrimaryAccountManager()->SignIn(kTestEmail);
+  identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount(
+      primary_account_id());
   UpdateCredentials(primary_account_id(), kTestGaiaId, kTestEmail,
                     "refresh_token");
 
diff --git a/components/signin/public/identity_manager/identity_test_environment.cc b/components/signin/public/identity_manager/identity_test_environment.cc
index 0c0e623..23cc5f8 100644
--- a/components/signin/public/identity_manager/identity_test_environment.cc
+++ b/components/signin/public/identity_manager/identity_test_environment.cc
@@ -398,11 +398,11 @@
   AccountInfo account_info = MakeAccountAvailable(email);
   identity_manager()->GetPrimaryAccountMutator()->SetUnconsentedPrimaryAccount(
       account_info.account_id);
-#elif defined(OS_IOS) || defined(OS_ANDROID)
-  // iOS and Android only support the primary account.
+#elif defined(OS_IOS)
+  // iOS only support the primary account.
   AccountInfo account_info = MakePrimaryAccountAvailable(email);
 #else
-  // Desktop platforms.
+  // Android and Desktop platforms.
   AccountInfo account_info =
       MakeAccountAvailableWithCookies(email, GetTestGaiaIdForEmail(email));
   base::RunLoop().RunUntilIdle();
diff --git a/components/signin/public/identity_manager/identity_test_utils.cc b/components/signin/public/identity_manager/identity_test_utils.cc
index 5f68ed8..588ff69 100644
--- a/components/signin/public/identity_manager/identity_test_utils.cc
+++ b/components/signin/public/identity_manager/identity_test_utils.cc
@@ -125,7 +125,7 @@
       EnsureAccountExists(identity_manager->GetAccountTrackerService(), email);
   DCHECK(!account_info.gaia.empty());
 
-  primary_account_manager->SignIn(email);
+  primary_account_manager->SetSyncPrimaryAccountInfo(account_info);
 
   DCHECK(primary_account_manager->HasPrimaryAccount(ConsentLevel::kSync));
   DCHECK(identity_manager->HasPrimaryAccount());
diff --git a/components/signin/public/identity_manager/primary_account_change_event.cc b/components/signin/public/identity_manager/primary_account_change_event.cc
index 890ef755..a89f5bb 100644
--- a/components/signin/public/identity_manager/primary_account_change_event.cc
+++ b/components/signin/public/identity_manager/primary_account_change_event.cc
@@ -32,15 +32,14 @@
   if (previous_state_ == current_state_)
     return Type::kNone;
 
-  if (previous_state_.consent_level == ConsentLevel::kSync) {
-    // Cannot change the Sync account without signing out first.
-    DCHECK(previous_state_.primary_account == current_state_.primary_account ||
-           current_state_.primary_account.IsEmpty());
-  }
-  if (previous_state_.primary_account == current_state_.primary_account) {
-    // Cannot change the consent level for the empty account.
-    DCHECK(!previous_state_.primary_account.IsEmpty());
-  }
+  // Cannot change the Sync account without clearing the primary account first.
+  DCHECK(previous_state_.consent_level != ConsentLevel::kSync ||
+         previous_state_.primary_account == current_state_.primary_account ||
+         current_state_.primary_account.IsEmpty());
+
+  // Cannot change the consent level for the empty account.
+  DCHECK(previous_state_.primary_account != current_state_.primary_account ||
+         !previous_state_.primary_account.IsEmpty());
 
   switch (consent_level) {
     case ConsentLevel::kNotRequired:
@@ -55,7 +54,8 @@
                    ? Type::kSet
                    : Type::kCleared;
       }
-      // Cannot change the Sync account without signing out first.
+      // Cannot change the Sync account without clearing the primary account
+      // first.
       DCHECK_EQ(current_state_.consent_level, ConsentLevel::kNotRequired);
       return Type::kNone;
   }
@@ -77,4 +77,21 @@
          lhs.consent_level == rhs.consent_level;
 }
 
+std::ostream& operator<<(std::ostream& os,
+                         const PrimaryAccountChangeEvent::State& state) {
+  os << "{ primary_account: " << state.primary_account.account_id << ", "
+     << "consent_level:"
+     << (state.consent_level == ConsentLevel::kNotRequired ? "NotRequired"
+                                                           : "Sync")
+     << " }";
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const PrimaryAccountChangeEvent& event) {
+  os << "{ previous_state: " << event.GetPreviousState() << ", "
+     << "current_state: " << event.GetCurrentState() << " }";
+  return os;
+}
+
 }  // namespace signin
\ No newline at end of file
diff --git a/components/signin/public/identity_manager/primary_account_change_event.h b/components/signin/public/identity_manager/primary_account_change_event.h
index 8ca54ae..1da4052b 100644
--- a/components/signin/public/identity_manager/primary_account_change_event.h
+++ b/components/signin/public/identity_manager/primary_account_change_event.h
@@ -55,6 +55,10 @@
 bool operator==(const PrimaryAccountChangeEvent::State& lhs,
                 const PrimaryAccountChangeEvent::State& rhs);
 
+std::ostream& operator<<(std::ostream& os,
+                         const PrimaryAccountChangeEvent::State& state);
+std::ostream& operator<<(std::ostream& os,
+                         const PrimaryAccountChangeEvent& event);
 }  // namespace signin
 
 #endif  // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_CHANGE_EVENT_H_
diff --git a/content/browser/tracing/background_tracing_active_scenario.cc b/content/browser/tracing/background_tracing_active_scenario.cc
index 1d11ae2..da5210e1 100644
--- a/content/browser/tracing/background_tracing_active_scenario.cc
+++ b/content/browser/tracing/background_tracing_active_scenario.cc
@@ -121,11 +121,13 @@
   }
 
   void BeginFinalizing(base::OnceClosure on_success,
-                       base::OnceClosure on_failure) {
+                       base::OnceClosure on_failure,
+                       bool is_crash_scenario) {
     // If the finalization was already in progress, ignore this call.
     if (!tracing_session_)
       return;
-    if (!BackgroundTracingManagerImpl::GetInstance()->IsAllowedFinalization()) {
+    if (!BackgroundTracingManagerImpl::GetInstance()->IsAllowedFinalization(
+            is_crash_scenario)) {
       auto on_failure_cb =
           base::MakeRefCounted<base::RefCountedData<base::OnceClosure>>(
               std::move(on_failure));
@@ -401,7 +403,8 @@
       weak_ptr_factory_.GetWeakPtr(), run_callback);
 
   tracing_session_->BeginFinalizing(std::move(on_begin_finalization_success),
-                                    std::move(on_begin_finalization_failure));
+                                    std::move(on_begin_finalization_failure),
+                                    last_triggered_rule_->is_crash());
 }
 
 void BackgroundTracingActiveScenario::OnJSONDataComplete(
diff --git a/content/browser/tracing/background_tracing_manager_impl.cc b/content/browser/tracing/background_tracing_manager_impl.cc
index 3f501d26..239c39b 100644
--- a/content/browser/tracing/background_tracing_manager_impl.cc
+++ b/content/browser/tracing/background_tracing_manager_impl.cc
@@ -454,12 +454,14 @@
   }
 }
 
-bool BackgroundTracingManagerImpl::IsAllowedFinalization() const {
+bool BackgroundTracingManagerImpl::IsAllowedFinalization(
+    bool is_crash_scenario) const {
   return !delegate_ ||
          (active_scenario_ &&
           delegate_->IsAllowedToEndBackgroundScenario(
               *active_scenario_->GetConfig(),
-              active_scenario_->GetConfig()->requires_anonymized_data()));
+              active_scenario_->GetConfig()->requires_anonymized_data(),
+              is_crash_scenario));
 }
 
 std::unique_ptr<base::DictionaryValue>
diff --git a/content/browser/tracing/background_tracing_manager_impl.h b/content/browser/tracing/background_tracing_manager_impl.h
index be3e560..4c6b78dc 100644
--- a/content/browser/tracing/background_tracing_manager_impl.h
+++ b/content/browser/tracing/background_tracing_manager_impl.h
@@ -133,7 +133,7 @@
 
   void AddMetadataGeneratorFunction();
 
-  bool IsAllowedFinalization() const;
+  bool IsAllowedFinalization(bool is_crash_scenario) const;
 
   // Called by BackgroundTracingActiveScenario
   void OnStartTracingDone(BackgroundTracingConfigImpl::CategoryPreset preset);
diff --git a/content/browser/tracing/background_tracing_rule.cc b/content/browser/tracing/background_tracing_rule.cc
index 8647c03b..eebbb6f 100644
--- a/content/browser/tracing/background_tracing_rule.cc
+++ b/content/browser/tracing/background_tracing_rule.cc
@@ -34,6 +34,7 @@
     "stop_tracing_on_repeated_reactive";
 const char kConfigRuleArgsKey[] = "args";
 const char kConfigRuleIdKey[] = "rule_id";
+const char kConfigIsCrashKey[] = "is_crash";
 
 const char kConfigRuleHistogramNameKey[] = "histogram_name";
 const char kConfigRuleHistogramValueOldKey[] = "histogram_value";
@@ -67,19 +68,11 @@
 
 namespace content {
 
-BackgroundTracingRule::BackgroundTracingRule()
-    : trigger_chance_(1.0),
-      trigger_delay_(-1),
-      stop_tracing_on_repeated_reactive_(false),
-      category_preset_(BackgroundTracingConfigImpl::CATEGORY_PRESET_UNSET) {}
-
+BackgroundTracingRule::BackgroundTracingRule() = default;
 BackgroundTracingRule::BackgroundTracingRule(int trigger_delay)
-    : trigger_chance_(1.0),
-      trigger_delay_(trigger_delay),
-      stop_tracing_on_repeated_reactive_(false),
-      category_preset_(BackgroundTracingConfigImpl::CATEGORY_PRESET_UNSET) {}
+    : trigger_delay_(trigger_delay) {}
 
-BackgroundTracingRule::~BackgroundTracingRule() {}
+BackgroundTracingRule::~BackgroundTracingRule() = default;
 
 bool BackgroundTracingRule::ShouldTriggerNamedEvent(
     const std::string& named_event) const {
@@ -115,6 +108,10 @@
         kConfigCategoryKey,
         BackgroundTracingConfigImpl::CategoryPresetToString(category_preset_));
   }
+
+  if (is_crash_) {
+    dict->SetBoolean(kConfigIsCrashKey, is_crash_);
+  }
 }
 
 void BackgroundTracingRule::GenerateMetadataProto(
@@ -130,6 +127,7 @@
   } else {
     rule_id_ = GetDefaultRuleId();
   }
+  dict->GetBoolean(kConfigIsCrashKey, &is_crash_);
 }
 
 namespace {
diff --git a/content/browser/tracing/background_tracing_rule.h b/content/browser/tracing/background_tracing_rule.h
index 299019b..0b6b0fc 100644
--- a/content/browser/tracing/background_tracing_rule.h
+++ b/content/browser/tracing/background_tracing_rule.h
@@ -65,17 +65,21 @@
 
   const std::string& rule_id() const { return rule_id_; }
 
+  bool is_crash() const { return is_crash_; }
+
  protected:
   virtual std::string GetDefaultRuleId() const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(BackgroundTracingRule);
 
-  double trigger_chance_;
-  int trigger_delay_;
-  bool stop_tracing_on_repeated_reactive_;
+  double trigger_chance_ = 1.0;
+  int trigger_delay_ = -1;
+  bool stop_tracing_on_repeated_reactive_ = false;
   std::string rule_id_;
-  BackgroundTracingConfigImpl::CategoryPreset category_preset_;
+  BackgroundTracingConfigImpl::CategoryPreset category_preset_ =
+      BackgroundTracingConfigImpl::CATEGORY_PRESET_UNSET;
+  bool is_crash_ = false;
   std::unique_ptr<base::DictionaryValue> args_;
 };
 
diff --git a/content/public/browser/tracing_delegate.cc b/content/public/browser/tracing_delegate.cc
index c216979..eb1e110 100644
--- a/content/public/browser/tracing_delegate.cc
+++ b/content/public/browser/tracing_delegate.cc
@@ -16,7 +16,8 @@
 
 bool TracingDelegate::IsAllowedToEndBackgroundScenario(
     const content::BackgroundTracingConfig& config,
-    bool requires_anonymized_data) {
+    bool requires_anonymized_data,
+    bool is_crash_scenario) {
   return false;
 }
 
diff --git a/content/public/browser/tracing_delegate.h b/content/public/browser/tracing_delegate.h
index 0aede03..cd17a08 100644
--- a/content/public/browser/tracing_delegate.h
+++ b/content/public/browser/tracing_delegate.h
@@ -40,7 +40,8 @@
 
   virtual bool IsAllowedToEndBackgroundScenario(
       const content::BackgroundTracingConfig& config,
-      bool requires_anonymized_data);
+      bool requires_anonymized_data,
+      bool is_crash_scenario);
 
   virtual bool IsProfileLoaded();
 
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 2214ae2..d94fba5b 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-98740986d373cb954788b10f66dbce6f2fd921ba
\ No newline at end of file
+36b5262321411db1fde3fef494c819dff7ee1bce
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 25a51b2..94c3dd1 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-deaf9fa5754950cd3c6cc1add204c616f546b312
\ No newline at end of file
+e6219d58d76eabdcfde3a4e4a64821cbbac00d0f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index daaeb53..597106f 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-d1c9346d6a428bc471837e7ff2805a6508283745
\ No newline at end of file
+c21209e3f14d5f49551481b8a7cdcea317a2f78a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index ea0eb784..c57bc18 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-b986e70d8be5ab9be5fafe51b55068d9e5fd6b84
\ No newline at end of file
+de42fae3da12db766861111cf754385671552273
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index 0f331fb..1dedc26c 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-85a310ae25145eb6195145788968f3aec83a6c5e
\ No newline at end of file
+3c0d9708365385b6badbb13255d6cb8fab95aa3d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 7a315e5..0923327b 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-d486c191e0dbb93fc72dd8c74c8867d6196dae10
\ No newline at end of file
+c3c4931568b06c2fe81a9f127a66aa68dadc9eef
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 221b2e2..4ccad41 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-a4be0f2eba02c425775de37328dc26517fbcaf2f
\ No newline at end of file
+701a0e408c9ca8f312881e5cc1f7600498e72e50
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 315d811..878622e8 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-c4270b410a20961de46ed32413b33795989abaff
\ No newline at end of file
+2adf62f62e95e284449fb069e432a3cb9f93d3e1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index 76e6c8f..851a3da 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-1899d265d945c71118d5e3f9f1301efecf0e8326
\ No newline at end of file
+603e8ad156d67e9c8bce74c1b4626009c002912a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index 492ce390..3114cce 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-3765977ed9b7b19280ba8c783746f202f3e0952d
\ No newline at end of file
+bf49209ebc31fd09f8fdde046c93af91d0258acf
\ No newline at end of file
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 8c9807b6..695dd49 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -240,11 +240,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.126"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.128"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.126",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.128",
         "resultdb": {
           "enable": true
         },
@@ -254,7 +254,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.126"
+              "revision": "version:87.0.4280.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -317,11 +317,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.60"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.62"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.60",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.62",
         "resultdb": {
           "enable": true
         },
@@ -331,7 +331,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.60"
+              "revision": "version:88.0.4324.62"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -394,11 +394,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.126"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.128"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.126",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.128",
         "resultdb": {
           "enable": true
         },
@@ -408,7 +408,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.126"
+              "revision": "version:87.0.4280.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -471,11 +471,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.60"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.62"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.60",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.62",
         "resultdb": {
           "enable": true
         },
@@ -485,7 +485,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.60"
+              "revision": "version:88.0.4324.62"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -769,11 +769,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.126"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.128"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.126",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.128",
         "resultdb": {
           "enable": true
         },
@@ -783,7 +783,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.126"
+              "revision": "version:87.0.4280.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -846,11 +846,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.60"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.62"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.60",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.62",
         "resultdb": {
           "enable": true
         },
@@ -860,7 +860,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.60"
+              "revision": "version:88.0.4324.62"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -923,11 +923,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.126"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.128"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.126",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.128",
         "resultdb": {
           "enable": true
         },
@@ -937,7 +937,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.126"
+              "revision": "version:87.0.4280.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1000,11 +1000,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.60"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.62"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.60",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.62",
         "resultdb": {
           "enable": true
         },
@@ -1014,7 +1014,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.60"
+              "revision": "version:88.0.4324.62"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1298,11 +1298,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.126"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.128"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.126",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.128",
         "resultdb": {
           "enable": true
         },
@@ -1312,7 +1312,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.126"
+              "revision": "version:87.0.4280.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1375,11 +1375,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.60"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.62"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.60",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.62",
         "resultdb": {
           "enable": true
         },
@@ -1389,7 +1389,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.60"
+              "revision": "version:88.0.4324.62"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1452,11 +1452,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.126"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.128"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.126",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.128",
         "resultdb": {
           "enable": true
         },
@@ -1466,7 +1466,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.126"
+              "revision": "version:87.0.4280.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1529,11 +1529,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.60"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.62"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.60",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.62",
         "resultdb": {
           "enable": true
         },
@@ -1543,7 +1543,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.60"
+              "revision": "version:88.0.4324.62"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1827,11 +1827,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.126"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.128"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.126",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.128",
         "resultdb": {
           "enable": true
         },
@@ -1841,7 +1841,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.126"
+              "revision": "version:87.0.4280.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1904,11 +1904,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.60"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.62"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.60",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.62",
         "resultdb": {
           "enable": true
         },
@@ -1918,7 +1918,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.60"
+              "revision": "version:88.0.4324.62"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1981,11 +1981,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.126"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.128"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.126",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.128",
         "resultdb": {
           "enable": true
         },
@@ -1995,7 +1995,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.126"
+              "revision": "version:87.0.4280.128"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -2058,11 +2058,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.60"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.62"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.60",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.62",
         "resultdb": {
           "enable": true
         },
@@ -2072,7 +2072,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.60"
+              "revision": "version:88.0.4324.62"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 8a8eeed0..b79d107 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -319,13 +319,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=88',
     ],
-    'identifier': 'Implementation Tests For 88.0.4324.60',
+    'identifier': 'Implementation Tests For 88.0.4324.62',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.60',
+          'revision': 'version:88.0.4324.62',
         }
       ],
     },
@@ -342,13 +342,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=87',
     ],
-    'identifier': 'Implementation Tests For 87.0.4280.126',
+    'identifier': 'Implementation Tests For 87.0.4280.128',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M87',
-          'revision': 'version:87.0.4280.126',
+          'revision': 'version:87.0.4280.128',
         }
       ],
     },
@@ -388,13 +388,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=88',
     ],
-    'identifier': 'Client Tests For 88.0.4324.60',
+    'identifier': 'Client Tests For 88.0.4324.62',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.60',
+          'revision': 'version:88.0.4324.62',
         }
       ],
     },
@@ -411,13 +411,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=87',
     ],
-    'identifier': 'Client Tests For 87.0.4280.126',
+    'identifier': 'Client Tests For 87.0.4280.128',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M87',
-          'revision': 'version:87.0.4280.126',
+          'revision': 'version:87.0.4280.128',
         }
       ],
     },
diff --git a/third_party/blink/renderer/platform/heap/impl/member.h b/third_party/blink/renderer/platform/heap/impl/member.h
index 66f2e23..17dd6db 100644
--- a/third_party/blink/renderer/platform/heap/impl/member.h
+++ b/third_party/blink/renderer/platform/heap/impl/member.h
@@ -348,7 +348,7 @@
 // heap allocated objects.
 // However instead of creating a strong pointer to the object, the WeakMember
 // creates a weak pointer, which does not keep the pointee alive. Hence if all
-// pointers to to a heap allocated object are weak the object will be garbage
+// pointers to a heap allocated object are weak the object will be garbage
 // collected. At the time of GC the weak pointers will automatically be set to
 // null.
 template <typename T>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 61e504f..00e45c7 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -46616,6 +46616,7 @@
   <int value="3" label="Password entered login"/>
   <int value="4" label="Preloaded password sites list"/>
   <int value="5" label="Field trial logged-in site"/>
+  <int value="6" label="Password manager saved site"/>
 </enum>
 
 <enum name="LoginFailureReason">
diff --git a/tools/metrics/histograms/histograms_xml/login/histograms.xml b/tools/metrics/histograms/histograms_xml/login/histograms.xml
index e354f999..b843f2ce 100644
--- a/tools/metrics/histograms/histograms_xml/login/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/login/histograms.xml
@@ -176,6 +176,18 @@
   </summary>
 </histogram>
 
+<histogram name="Login.PasswordStoreSites.InitializedBeforeQuery"
+    enum="Boolean" expires_after="2021-11-30">
+  <owner>rajendrant@chromium.org</owner>
+  <owner>mcrouse@chromium.org</owner>
+  <summary>
+    Records whether the passworded sites list is initialized when the query for
+    sites happens. This is used to measure how many queries happened before the
+    list got populated the first time. Recorded on every query to the password
+    store.
+  </summary>
+</histogram>
+
 <histogram name="Login.PolicyFilesStatePerBoot" enum="LoginPolicyFilesState"
     expires_after="M81">
   <owner>cmasone@chromium.org</owner>
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index c04bb9c95a..f7264ec2 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -294,7 +294,7 @@
  <item id="safe_browsing_certificate_error_reporting" added_in_milestone="62" hash_code="66590631" type="0" content_hash_code="26108454" os_list="linux,windows" file_path="chrome/browser/ssl/certificate_error_reporter.cc"/>
  <item id="safe_browsing_chunk_backup_request" added_in_milestone="62" hash_code="79957943" type="0" deprecated="2018-08-14" content_hash_code="133850277" file_path=""/>
  <item id="safe_browsing_client_side_malware_detector" added_in_milestone="62" hash_code="102935425" type="0" deprecated="2019-12-07" content_hash_code="79591279" file_path=""/>
- <item id="safe_browsing_client_side_phishing_detector" added_in_milestone="62" hash_code="1313982" type="0" content_hash_code="50199143" os_list="linux,windows" file_path="chrome/browser/safe_browsing/client_side_detection_service.cc"/>
+ <item id="safe_browsing_client_side_phishing_detector" added_in_milestone="62" hash_code="1313982" type="0" content_hash_code="50199143" os_list="linux,windows" file_path="components/safe_browsing/content/browser/client_side_detection_service.cc"/>
  <item id="safe_browsing_extended_reporting" added_in_milestone="62" hash_code="42848942" type="0" content_hash_code="81193513" os_list="linux,windows" file_path="components/safe_browsing/core/ping_manager.cc"/>
  <item id="safe_browsing_feedback" added_in_milestone="62" hash_code="44583821" type="0" content_hash_code="27116846" os_list="linux,windows" file_path="chrome/browser/safe_browsing/download_protection/download_feedback.cc"/>
  <item id="safe_browsing_get_full_hash" added_in_milestone="62" hash_code="68745894" type="0" deprecated="2018-08-14" content_hash_code="21739198" file_path=""/>
diff --git a/ui/accessibility/OWNERS b/ui/accessibility/OWNERS
index 815bea4..717b0e5 100644
--- a/ui/accessibility/OWNERS
+++ b/ui/accessibility/OWNERS
@@ -1,10 +1,10 @@
+abigailbklein@google.com
+aboxhall@chromium.org
+aleventhal@chromium.org
 dmazzoni@chromium.org
 dtseng@chromium.org
-aboxhall@chromium.org
-nektar@chromium.org
-dougt@chromium.org
-aleventhal@chromium.org
 katie@chromium.org
+nektar@chromium.org
 
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/weblayer/BUILD.gn b/weblayer/BUILD.gn
index a5bfc19..5e457ee2 100644
--- a/weblayer/BUILD.gn
+++ b/weblayer/BUILD.gn
@@ -414,6 +414,7 @@
     "//components/policy/core/browser",
     "//components/pref_registry:pref_registry",
     "//components/prefs",
+    "//components/safe_browsing/content/browser:client_side_detection",
     "//components/safe_browsing/content/common:interfaces",
     "//components/safe_browsing/content/renderer:throttles",
     "//components/safe_browsing/content/renderer/phishing_classifier",
@@ -567,6 +568,12 @@
       "browser/new_tab_callback_proxy.h",
       "browser/proxying_url_loader_factory_impl.cc",
       "browser/proxying_url_loader_factory_impl.h",
+      "browser/safe_browsing/client_side_detection_host_delegate.cc",
+      "browser/safe_browsing/client_side_detection_host_delegate.h",
+      "browser/safe_browsing/client_side_detection_service_delegate.cc",
+      "browser/safe_browsing/client_side_detection_service_delegate.h",
+      "browser/safe_browsing/client_side_detection_service_factory.cc",
+      "browser/safe_browsing/client_side_detection_service_factory.h",
       "browser/safe_browsing/real_time_url_lookup_service_factory.cc",
       "browser/safe_browsing/real_time_url_lookup_service_factory.h",
       "browser/safe_browsing/safe_browsing_blocking_page.cc",
@@ -577,6 +584,8 @@
       "browser/safe_browsing/safe_browsing_service.h",
       "browser/safe_browsing/safe_browsing_subresource_helper.cc",
       "browser/safe_browsing/safe_browsing_subresource_helper.h",
+      "browser/safe_browsing/safe_browsing_tab_observer.cc",
+      "browser/safe_browsing/safe_browsing_tab_observer.h",
       "browser/safe_browsing/safe_browsing_ui_manager.cc",
       "browser/safe_browsing/safe_browsing_ui_manager.h",
       "browser/safe_browsing/url_checker_delegate_impl.cc",
@@ -641,11 +650,15 @@
       "//components/safe_browsing/android:safe_browsing_api_handler",
       "//components/safe_browsing/content",
       "//components/safe_browsing/content/browser",
+      "//components/safe_browsing/content/browser:client_side_model_loader",
+      "//components/safe_browsing/core:client_model_proto",
+      "//components/safe_browsing/core:csd_proto",
       "//components/safe_browsing/core:ping_manager",
       "//components/safe_browsing/core:verdict_cache_manager",
       "//components/safe_browsing/core/browser",
       "//components/safe_browsing/core/browser:network_context",
       "//components/safe_browsing/core/common",
+      "//components/safe_browsing/core/db:allowlist_checker_client",
       "//components/safe_browsing/core/db:database_manager",
       "//components/safe_browsing/core/realtime:policy_engine",
       "//components/safe_browsing/core/realtime:url_lookup_service",
diff --git a/weblayer/browser/safe_browsing/DEPS b/weblayer/browser/safe_browsing/DEPS
index 56c6be6..73c4bc5 100644
--- a/weblayer/browser/safe_browsing/DEPS
+++ b/weblayer/browser/safe_browsing/DEPS
@@ -1,4 +1,3 @@
 include_rules = [
   "+components/safe_browsing",
- ]
- 
\ No newline at end of file
+]
\ No newline at end of file
diff --git a/weblayer/browser/safe_browsing/client_side_detection_host_delegate.cc b/weblayer/browser/safe_browsing/client_side_detection_host_delegate.cc
new file mode 100644
index 0000000..7ef047a
--- /dev/null
+++ b/weblayer/browser/safe_browsing/client_side_detection_host_delegate.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "weblayer/browser/safe_browsing/client_side_detection_host_delegate.h"
+
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/android/remote_database_manager.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/browser_process.h"
+#include "weblayer/browser/safe_browsing/client_side_detection_service_factory.h"
+#include "weblayer/browser/safe_browsing/safe_browsing_service.h"
+
+namespace weblayer {
+
+ClientSideDetectionHostDelegate::ClientSideDetectionHostDelegate(
+    content::WebContents* web_contents)
+    : web_contents_(web_contents) {}
+
+ClientSideDetectionHostDelegate::~ClientSideDetectionHostDelegate() = default;
+
+bool ClientSideDetectionHostDelegate::HasSafeBrowsingUserInteractionObserver() {
+  return false;
+}
+
+PrefService* ClientSideDetectionHostDelegate::GetPrefs() {
+  BrowserContextImpl* browser_context_impl =
+      static_cast<BrowserContextImpl*>(web_contents_->GetBrowserContext());
+  DCHECK(browser_context_impl);
+  return browser_context_impl->pref_service();
+}
+
+scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
+ClientSideDetectionHostDelegate::GetSafeBrowsingDBManager() {
+  SafeBrowsingService* sb_service =
+      BrowserProcess::GetInstance()->GetSafeBrowsingService();
+  return sb_service ? sb_service->GetSafeBrowsingDBManager() : nullptr;
+}
+
+scoped_refptr<safe_browsing::BaseUIManager>
+ClientSideDetectionHostDelegate::GetSafeBrowsingUIManager() {
+  SafeBrowsingService* sb_service =
+      BrowserProcess::GetInstance()->GetSafeBrowsingService();
+  return sb_service ? sb_service->GetSafeBrowsingUIManager() : nullptr;
+}
+
+safe_browsing::ClientSideDetectionService*
+ClientSideDetectionHostDelegate::GetClientSideDetectionService() {
+  return ClientSideDetectionServiceFactory::GetForBrowserContext(
+      web_contents_->GetBrowserContext());
+}
+
+}  // namespace weblayer
diff --git a/weblayer/browser/safe_browsing/client_side_detection_host_delegate.h b/weblayer/browser/safe_browsing/client_side_detection_host_delegate.h
new file mode 100644
index 0000000..ed6b467
--- /dev/null
+++ b/weblayer/browser/safe_browsing/client_side_detection_host_delegate.h
@@ -0,0 +1,36 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBLAYER_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_DELEGATE_H_
+#define WEBLAYER_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_DELEGATE_H_
+
+#include "components/safe_browsing/content/browser/client_side_detection_host.h"
+
+namespace weblayer {
+
+class ClientSideDetectionHostDelegate
+    : public safe_browsing::ClientSideDetectionHost::Delegate {
+ public:
+  explicit ClientSideDetectionHostDelegate(content::WebContents* web_contents);
+  ~ClientSideDetectionHostDelegate() override;
+
+  // ClientSideDetectionHost::Delegate implementation.
+  bool HasSafeBrowsingUserInteractionObserver() override;
+  PrefService* GetPrefs() override;
+  scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
+  GetSafeBrowsingDBManager() override;
+  scoped_refptr<safe_browsing::BaseUIManager> GetSafeBrowsingUIManager()
+      override;
+  safe_browsing::ClientSideDetectionService* GetClientSideDetectionService()
+      override;
+
+ private:
+  content::WebContents* web_contents_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionHostDelegate);
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_DELEGATE_H_
diff --git a/weblayer/browser/safe_browsing/client_side_detection_service_browsertest.cc b/weblayer/browser/safe_browsing/client_side_detection_service_browsertest.cc
new file mode 100644
index 0000000..c087e680
--- /dev/null
+++ b/weblayer/browser/safe_browsing/client_side_detection_service_browsertest.cc
@@ -0,0 +1,123 @@
+// 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 "weblayer/browser/safe_browsing/client_side_detection_service_factory.h"
+
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
+#include "components/safe_browsing/content/common/safe_browsing.mojom.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/safe_browsing/core/proto/client_model.pb.h"
+#include "content/public/test/browser_test.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/profile_impl.h"
+#include "weblayer/browser/tab_impl.h"
+#include "weblayer/common/features.h"
+#include "weblayer/shell/browser/shell.h"
+#include "weblayer/test/weblayer_browser_test.h"
+#include "weblayer/test/weblayer_browser_test_utils.h"
+
+namespace weblayer {
+
+using safe_browsing::ClientSideDetectionService;
+using safe_browsing::ClientSideModel;
+using safe_browsing::ModelLoader;
+using ::testing::_;
+using ::testing::ReturnRef;
+using ::testing::StrictMock;
+
+namespace {
+
+class FakeModelLoader : public ModelLoader {
+ public:
+  explicit FakeModelLoader(const std::string& model_str)
+      : ModelLoader(base::RepeatingClosure(),
+                    nullptr,
+                    /*is_extended_reporting=*/false) {
+    model_str_ = model_str;
+  }
+  ~FakeModelLoader() override = default;
+
+  void ScheduleFetch(int64_t delay) override {}
+  void CancelFetcher() override {}
+};
+
+std::unique_ptr<ModelLoader> CreateFakeModelLoader(std::string model_str) {
+  return std::make_unique<FakeModelLoader>(model_str);
+}
+
+}  // namespace
+
+class ClientSideDetectionServiceBrowserTest : public WebLayerBrowserTest {
+ public:
+  ClientSideDetectionServiceBrowserTest() {
+    feature_list_.InitAndEnableFeature(
+        features::kWebLayerClientSidePhishingDetection);
+  }
+  content::WebContents* GetWebContents() {
+    return static_cast<TabImpl*>(shell()->tab())->web_contents();
+  }
+
+ private:
+  void SetUpOnMainThread() override {
+    NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+  }
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(ClientSideDetectionServiceBrowserTest,
+                       NewHostGetsModel) {
+  PrefService* prefs = GetProfile()->GetBrowserContext()->pref_service();
+  prefs->SetBoolean(::prefs::kSafeBrowsingEnabled, false);
+  ClientSideDetectionService* csd_service =
+      ClientSideDetectionServiceFactory::GetForBrowserContext(
+          GetProfile()->GetBrowserContext());
+
+  ClientSideModel model;
+  model.set_max_words_per_term(0);
+  std::string model_str;
+  model.SerializeToString(&model_str);
+
+  csd_service->SetModelLoaderFactoryForTesting(
+      base::BindRepeating(&CreateFakeModelLoader, model_str));
+
+  // Enable Safe Browsing and the CSD service.
+  prefs->SetBoolean(::prefs::kSafeBrowsingEnabled, true);
+
+  base::RunLoop run_loop;
+
+  content::RenderFrameHost* rfh = GetWebContents()->GetMainFrame();
+  mojo::Remote<safe_browsing::mojom::PhishingDetector> phishing_detector;
+  rfh->GetRemoteInterfaces()->GetInterface(
+      phishing_detector.BindNewPipeAndPassReceiver());
+
+  safe_browsing::mojom::PhishingDetectorResult result;
+  std::string verdict;
+  phishing_detector->StartPhishingDetection(
+      GURL("about:blank"),
+      base::BindOnce(
+          [](base::RepeatingClosure quit_closure,
+             safe_browsing::mojom::PhishingDetectorResult* out_result,
+             std::string* out_verdict,
+             safe_browsing::mojom::PhishingDetectorResult result,
+             const std::string& verdict) {
+            *out_result = result;
+            *out_verdict = verdict;
+            quit_closure.Run();
+          },
+          run_loop.QuitClosure(), &result, &verdict));
+
+  run_loop.Run();
+
+  // The model classification will run, but will return an invalid score.
+  EXPECT_EQ(result,
+            safe_browsing::mojom::PhishingDetectorResult::INVALID_SCORE);
+}
+
+}  // namespace weblayer
\ No newline at end of file
diff --git a/weblayer/browser/safe_browsing/client_side_detection_service_delegate.cc b/weblayer/browser/safe_browsing/client_side_detection_service_delegate.cc
new file mode 100644
index 0000000..7aaeba0fd
--- /dev/null
+++ b/weblayer/browser/safe_browsing/client_side_detection_service_delegate.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "weblayer/browser/safe_browsing/client_side_detection_service_delegate.h"
+
+#include "components/safe_browsing/core/common/utils.h"
+#include "content/public/browser/storage_partition.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/browser_process.h"
+#include "weblayer/browser/safe_browsing/safe_browsing_service.h"
+
+namespace weblayer {
+
+ClientSideDetectionServiceDelegate::ClientSideDetectionServiceDelegate(
+    BrowserContextImpl* browser_context)
+    : browser_context_(browser_context) {}
+
+ClientSideDetectionServiceDelegate::~ClientSideDetectionServiceDelegate() =
+    default;
+
+PrefService* ClientSideDetectionServiceDelegate::GetPrefs() {
+  DCHECK(browser_context_);
+  return browser_context_->pref_service();
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+ClientSideDetectionServiceDelegate::GetURLLoaderFactory() {
+  return content::BrowserContext::GetDefaultStoragePartition(browser_context_)
+      ->GetURLLoaderFactoryForBrowserProcess();
+}
+
+scoped_refptr<network::SharedURLLoaderFactory>
+ClientSideDetectionServiceDelegate::GetSafeBrowsingURLLoaderFactory() {
+  SafeBrowsingService* sb_service =
+      BrowserProcess::GetInstance()->GetSafeBrowsingService();
+  return sb_service ? sb_service->GetURLLoaderFactory() : nullptr;
+}
+
+safe_browsing::ChromeUserPopulation::ProfileManagementStatus
+ClientSideDetectionServiceDelegate::GetManagementStatus() {
+  // corresponds to unmanaged "unavailable" status on android
+  return safe_browsing::GetProfileManagementStatus(nullptr);
+}
+
+}  // namespace weblayer
diff --git a/weblayer/browser/safe_browsing/client_side_detection_service_delegate.h b/weblayer/browser/safe_browsing/client_side_detection_service_delegate.h
new file mode 100644
index 0000000..afe14cc
--- /dev/null
+++ b/weblayer/browser/safe_browsing/client_side_detection_service_delegate.h
@@ -0,0 +1,35 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBLAYER_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_DELEGATE_H_
+#define WEBLAYER_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_DELEGATE_H_
+
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
+
+namespace weblayer {
+
+class ClientSideDetectionServiceDelegate
+    : public safe_browsing::ClientSideDetectionService::Delegate {
+ public:
+  explicit ClientSideDetectionServiceDelegate(
+      BrowserContextImpl* browser_context);
+  ~ClientSideDetectionServiceDelegate() override;
+
+  // ClientSideDetectionService::Delegate implementation.
+  PrefService* GetPrefs() override;
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
+  scoped_refptr<network::SharedURLLoaderFactory>
+  GetSafeBrowsingURLLoaderFactory() override;
+  safe_browsing::ChromeUserPopulation::ProfileManagementStatus
+  GetManagementStatus() override;
+
+ private:
+  BrowserContextImpl* browser_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionServiceDelegate);
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_DELEGATE_H_
diff --git a/weblayer/browser/safe_browsing/client_side_detection_service_factory.cc b/weblayer/browser/safe_browsing/client_side_detection_service_factory.cc
new file mode 100644
index 0000000..9d22cabda
--- /dev/null
+++ b/weblayer/browser/safe_browsing/client_side_detection_service_factory.cc
@@ -0,0 +1,59 @@
+// 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 "weblayer/browser/safe_browsing/client_side_detection_service_factory.h"
+
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
+#include "content/public/browser/browser_context.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/browser_process.h"
+#include "weblayer/browser/feature_list_creator.h"
+#include "weblayer/browser/safe_browsing/client_side_detection_service_delegate.h"
+#include "weblayer/common/features.h"
+
+namespace weblayer {
+
+// static
+safe_browsing::ClientSideDetectionService*
+ClientSideDetectionServiceFactory::GetForBrowserContext(
+    content::BrowserContext* browser_context) {
+  if (base::FeatureList::IsEnabled(
+          features::kWebLayerClientSidePhishingDetection)) {
+    return static_cast<safe_browsing::ClientSideDetectionService*>(
+        GetInstance()->GetServiceForBrowserContext(browser_context,
+                                                   /* create= */ true));
+  }
+  return nullptr;
+}
+
+// static
+ClientSideDetectionServiceFactory*
+ClientSideDetectionServiceFactory::GetInstance() {
+  static base::NoDestructor<ClientSideDetectionServiceFactory> factory;
+  return factory.get();
+}
+
+ClientSideDetectionServiceFactory::ClientSideDetectionServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "ClientSideDetectionService",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+ClientSideDetectionServiceFactory::~ClientSideDetectionServiceFactory() =
+    default;
+
+KeyedService* ClientSideDetectionServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  return new safe_browsing::ClientSideDetectionService(
+      std::make_unique<ClientSideDetectionServiceDelegate>(
+          static_cast<BrowserContextImpl*>(context)));
+}
+
+content::BrowserContext*
+ClientSideDetectionServiceFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return context;
+}
+
+}  // namespace weblayer
diff --git a/weblayer/browser/safe_browsing/client_side_detection_service_factory.h b/weblayer/browser/safe_browsing/client_side_detection_service_factory.h
new file mode 100644
index 0000000..f8e491e
--- /dev/null
+++ b/weblayer/browser/safe_browsing/client_side_detection_service_factory.h
@@ -0,0 +1,56 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBLAYER_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_FACTORY_H_
+#define WEBLAYER_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class KeyedService;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace safe_browsing {
+class ClientSideDetectionService;
+}
+
+namespace weblayer {
+
+// Singleton that owns ClientSideDetectionServiceFactory objects and associates
+// them them with BrowserContextImpl instances.
+class ClientSideDetectionServiceFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  ClientSideDetectionServiceFactory(const ClientSideDetectionServiceFactory&) =
+      delete;
+  ClientSideDetectionServiceFactory& operator=(
+      const ClientSideDetectionServiceFactory&) = delete;
+
+  // Creates the service if it doesn't exist already for the given
+  // |browser_context|. If the service already exists, return its pointer.
+  static safe_browsing::ClientSideDetectionService* GetForBrowserContext(
+      content::BrowserContext* browser_context);
+
+  // Get the singleton instance.
+  static ClientSideDetectionServiceFactory* GetInstance();
+
+ private:
+  friend class base::NoDestructor<ClientSideDetectionServiceFactory>;
+
+  ClientSideDetectionServiceFactory();
+  ~ClientSideDetectionServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_SERVICE_FACTORY_H_
diff --git a/weblayer/browser/safe_browsing/client_side_detection_service_factory_browsertest.cc b/weblayer/browser/safe_browsing/client_side_detection_service_factory_browsertest.cc
new file mode 100644
index 0000000..29448aab
--- /dev/null
+++ b/weblayer/browser/safe_browsing/client_side_detection_service_factory_browsertest.cc
@@ -0,0 +1,34 @@
+// 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 "weblayer/browser/safe_browsing/client_side_detection_service_factory.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/profile_impl.h"
+#include "weblayer/common/features.h"
+#include "weblayer/test/weblayer_browser_test.h"
+
+namespace weblayer {
+
+class ClientSideDetectionServiceFactoryBrowserTest
+    : public WebLayerBrowserTest {
+ public:
+  ClientSideDetectionServiceFactoryBrowserTest() {
+    feature_list_.InitAndDisableFeature(
+        features::kWebLayerClientSidePhishingDetection);
+  }
+
+ private:
+  void SetUpOnMainThread() override {}
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(ClientSideDetectionServiceFactoryBrowserTest,
+                       ClientDetectionServiceNullWhenDisabled) {
+  EXPECT_EQ(nullptr, ClientSideDetectionServiceFactory::GetForBrowserContext(
+                         GetProfile()->GetBrowserContext()));
+}
+
+}  // namespace weblayer
diff --git a/weblayer/browser/safe_browsing/safe_browsing_service.cc b/weblayer/browser/safe_browsing/safe_browsing_service.cc
index adb19d19..c85d8bd 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_service.cc
+++ b/weblayer/browser/safe_browsing/safe_browsing_service.cc
@@ -127,7 +127,7 @@
 SafeBrowsingService::CreateSafeBrowsingNavigationThrottle(
     content::NavigationHandle* handle) {
   return std::make_unique<SafeBrowsingNavigationThrottle>(
-      handle, GetSafeBrowsingUIManager());
+      handle, GetSafeBrowsingUIManager().get());
 }
 
 scoped_refptr<safe_browsing::UrlCheckerDelegate>
@@ -142,16 +142,17 @@
   return safe_browsing_url_checker_delegate_;
 }
 
-safe_browsing::RemoteSafeBrowsingDatabaseManager*
+scoped_refptr<safe_browsing::RemoteSafeBrowsingDatabaseManager>
 SafeBrowsingService::GetSafeBrowsingDBManager() {
   if (!safe_browsing_db_manager_) {
     CreateAndStartSafeBrowsingDBManager();
   }
-  return safe_browsing_db_manager_.get();
+  return safe_browsing_db_manager_;
 }
 
-SafeBrowsingUIManager* SafeBrowsingService::GetSafeBrowsingUIManager() {
-  return ui_manager_.get();
+scoped_refptr<SafeBrowsingUIManager>
+SafeBrowsingService::GetSafeBrowsingUIManager() {
+  return ui_manager_;
 }
 
 void SafeBrowsingService::CreateSafeBrowsingUIManager() {
diff --git a/weblayer/browser/safe_browsing/safe_browsing_service.h b/weblayer/browser/safe_browsing/safe_browsing_service.h
index d6a55bd..2c006d07 100644
--- a/weblayer/browser/safe_browsing/safe_browsing_service.h
+++ b/weblayer/browser/safe_browsing/safe_browsing_service.h
@@ -67,11 +67,12 @@
 
   // May be called on the UI or IO thread. The instance returned should be
   // *accessed* only on the IO thread.
-  safe_browsing::RemoteSafeBrowsingDatabaseManager* GetSafeBrowsingDBManager();
+  scoped_refptr<safe_browsing::RemoteSafeBrowsingDatabaseManager>
+  GetSafeBrowsingDBManager();
+
+  scoped_refptr<SafeBrowsingUIManager> GetSafeBrowsingUIManager();
 
  private:
-  SafeBrowsingUIManager* GetSafeBrowsingUIManager();
-
   // Executed on IO thread
   scoped_refptr<safe_browsing::UrlCheckerDelegate>
   GetSafeBrowsingUrlCheckerDelegate();
diff --git a/weblayer/browser/safe_browsing/safe_browsing_tab_observer.cc b/weblayer/browser/safe_browsing/safe_browsing_tab_observer.cc
new file mode 100644
index 0000000..15af219
--- /dev/null
+++ b/weblayer/browser/safe_browsing/safe_browsing_tab_observer.cc
@@ -0,0 +1,75 @@
+// 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 "weblayer/browser/safe_browsing/safe_browsing_tab_observer.h"
+
+#include "base/bind.h"
+#include "components/prefs/pref_service.h"
+#include "components/safe_browsing/content/browser/client_side_detection_host.h"
+#include "components/safe_browsing/content/browser/client_side_detection_service.h"
+#include "content/public/browser/web_contents.h"
+#include "weblayer/browser/browser_context_impl.h"
+#include "weblayer/browser/browser_process.h"
+#include "weblayer/browser/safe_browsing/client_side_detection_host_delegate.h"
+#include "weblayer/browser/safe_browsing/client_side_detection_service_factory.h"
+
+namespace weblayer {
+
+SafeBrowsingTabObserver::SafeBrowsingTabObserver(
+    content::WebContents* web_contents)
+    : web_contents_(web_contents) {
+  BrowserContextImpl* browser_context =
+      static_cast<BrowserContextImpl*>(web_contents->GetBrowserContext());
+  PrefService* prefs = browser_context->pref_service();
+  if (prefs) {
+    pref_change_registrar_.Init(prefs);
+    pref_change_registrar_.Add(
+        ::prefs::kSafeBrowsingEnabled,
+        base::BindRepeating(
+            &SafeBrowsingTabObserver::UpdateSafebrowsingDetectionHost,
+            base::Unretained(this)));
+
+    safe_browsing::ClientSideDetectionService* csd_service =
+        ClientSideDetectionServiceFactory::GetForBrowserContext(
+            browser_context);
+    if (safe_browsing::IsSafeBrowsingEnabled(*prefs) &&
+        BrowserProcess::GetInstance()->GetSafeBrowsingService() &&
+        csd_service) {
+      safebrowsing_detection_host_ =
+          safe_browsing::ClientSideDetectionHost::Create(
+              web_contents,
+              std::make_unique<ClientSideDetectionHostDelegate>(web_contents));
+      csd_service->AddClientSideDetectionHost(
+          safebrowsing_detection_host_.get());
+    }
+  }
+}
+
+SafeBrowsingTabObserver::~SafeBrowsingTabObserver() {}
+
+void SafeBrowsingTabObserver::UpdateSafebrowsingDetectionHost() {
+  BrowserContextImpl* browser_context =
+      static_cast<BrowserContextImpl*>(web_contents_->GetBrowserContext());
+  PrefService* prefs = browser_context->pref_service();
+
+  bool safe_browsing = safe_browsing::IsSafeBrowsingEnabled(*prefs);
+  safe_browsing::ClientSideDetectionService* csd_service =
+      ClientSideDetectionServiceFactory::GetForBrowserContext(browser_context);
+  if (safe_browsing && csd_service) {
+    if (!safebrowsing_detection_host_.get()) {
+      safebrowsing_detection_host_ =
+          safe_browsing::ClientSideDetectionHost::Create(
+              web_contents_,
+              std::make_unique<ClientSideDetectionHostDelegate>(web_contents_));
+      csd_service->AddClientSideDetectionHost(
+          safebrowsing_detection_host_.get());
+    }
+  } else {
+    safebrowsing_detection_host_.reset();
+  }
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(SafeBrowsingTabObserver)
+
+}  // namespace weblayer
\ No newline at end of file
diff --git a/weblayer/browser/safe_browsing/safe_browsing_tab_observer.h b/weblayer/browser/safe_browsing/safe_browsing_tab_observer.h
new file mode 100644
index 0000000..c4bac09
--- /dev/null
+++ b/weblayer/browser/safe_browsing/safe_browsing_tab_observer.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 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 WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_TAB_OBSERVER_H_
+#define WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_TAB_OBSERVER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace safe_browsing {
+class ClientSideDetectionHost;
+}
+
+namespace weblayer {
+
+// Per-tab class to handle safe-browsing functionality.
+class SafeBrowsingTabObserver
+    : public content::WebContentsUserData<SafeBrowsingTabObserver> {
+ public:
+  ~SafeBrowsingTabObserver() override;
+
+ private:
+  explicit SafeBrowsingTabObserver(content::WebContents* web_contents);
+  friend class content::WebContentsUserData<SafeBrowsingTabObserver>;
+
+  // Create or destroy SafebrowsingDetectionHost as needed if the user's
+  // safe browsing preference has changed.
+  void UpdateSafebrowsingDetectionHost();
+
+  // Handles IPCs.
+  std::unique_ptr<safe_browsing::ClientSideDetectionHost>
+      safebrowsing_detection_host_;
+
+  // Our owning WebContents.
+  content::WebContents* web_contents_;
+
+  PrefChangeRegistrar pref_change_registrar_;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingTabObserver);
+};
+
+}  // namespace weblayer
+
+#endif  // WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_TAB_OBSERVER_H_
\ No newline at end of file
diff --git a/weblayer/browser/tab_impl.cc b/weblayer/browser/tab_impl.cc
index 61e611a..6de70e8 100644
--- a/weblayer/browser/tab_impl.cc
+++ b/weblayer/browser/tab_impl.cc
@@ -113,10 +113,12 @@
 #include "weblayer/browser/java/jni/TabImpl_jni.h"
 #include "weblayer/browser/javascript_tab_modal_dialog_manager_delegate_android.h"
 #include "weblayer/browser/js_communication/web_message_host_factory_proxy.h"
+#include "weblayer/browser/safe_browsing/safe_browsing_tab_observer.h"
 #include "weblayer/browser/translate_client_impl.h"
 #include "weblayer/browser/url_bar/trusted_cdn_observer.h"
 #include "weblayer/browser/weblayer_factory_impl_android.h"
 #include "weblayer/browser/webrtc/media_stream_manager.h"
+#include "weblayer/common/features.h"
 #endif
 
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
@@ -355,6 +357,11 @@
           web_contents_.get(), this);
 
   TrustedCDNObserver::CreateForWebContents(web_contents_.get());
+
+  if (base::FeatureList::IsEnabled(
+          features::kWebLayerClientSidePhishingDetection)) {
+    SafeBrowsingTabObserver::CreateForWebContents(web_contents_.get());
+  }
 #endif
 
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
diff --git a/weblayer/test/BUILD.gn b/weblayer/test/BUILD.gn
index d9d11dd10..551bdd9 100644
--- a/weblayer/test/BUILD.gn
+++ b/weblayer/test/BUILD.gn
@@ -192,6 +192,8 @@
       "../browser/android/metrics/metrics_test_helper.cc",
       "../browser/android/metrics/metrics_test_helper.h",
       "../browser/android/metrics/ukm_browsertest.cc",
+      "../browser/safe_browsing/client_side_detection_service_browsertest.cc",
+      "../browser/safe_browsing/client_side_detection_service_factory_browsertest.cc",
       "../browser/safe_browsing/safe_browsing_browsertest.cc",
       "../browser/url_bar/page_info_browsertest.cc",
       "../shell/android/browsertests_apk/translate_test_bridge.cc",
@@ -211,7 +213,12 @@
       "//components/page_info/android",
       "//components/safe_browsing/android:safe_browsing_api_handler",
       "//components/safe_browsing/content",
+      "//components/safe_browsing/content/browser:client_side_detection",
+      "//components/safe_browsing/content/browser:client_side_model_loader",
+      "//components/safe_browsing/content/common:interfaces",
+      "//components/safe_browsing/core:client_model_proto",
       "//components/safe_browsing/core:features",
+      "//components/safe_browsing/core/common",
       "//components/viz/service:service_java",
       "//content/public/test/android:android_test_message_pump_support_java",
       "//content/test:android_test_message_pump_support",