diff --git a/DEPS b/DEPS
index 565fafbb..3444d34 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': 'e2893f2df8b26bf6805c1888b43f33346d91a7be',
+  'skia_revision': 'c5d2a2fe532148ffe5282ba46a3314c42fe36370',
   # 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': '7bb48090258056285eefddebf26d55a22121fe5a',
+  'v8_revision': 'cc40f8a7ae361efa2c1d3602a4a2f0ca464fa2cb',
   # 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': 'ad80b184a00d71d0f677c748fde192ffb5279cde',
+  'angle_revision': '39b5e771cd8ed1f906a9a744f070384d31b3e447',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -219,7 +219,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '538433c720648f67cf6eb2f3ba108ffd97df9eb3',
+  'pdfium_revision': 'a58a676ab96a459703d02f9f3ea8e06b13ce484d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -270,7 +270,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': 'ad59e7a16b5dee5ad8fc50a66c809e9044698e8a',
+  'devtools_frontend_revision': '166d4461bb51ff1bb8f525f097877daaf1e29ff2',
   # 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.
@@ -306,7 +306,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.
-  'spv_tools_revision': 'c2553a315f5c78d73a808526782596d4e3870082',
+  'spv_tools_revision': '5c64374dd6cbfff1294ec78cdae1bc9de870a07d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -900,12 +900,12 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '16aafbd27d09c779e67f11291d8cb34b7bc7abb4',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'ebddc236e489a2dbb8a8c1f5be6bff4238ea7942',
       'condition': 'checkout_linux',
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'aa45aa62c3c62a3eefc011ea3d6f038ec7bd58fd',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6e970e597b25a3a74a560746711d1811133841d2',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1036,7 +1036,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + '18e09b9197a3b1d771c077c530d1a4ebad04c167',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'aba3f0dfeeddc0c69730ec28ef423543b8a62693',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '84fb34e2d110871c3414183b9f6c0745453b02ad',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -1258,7 +1258,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '0150a791fcb7070c81c7e43ce5a7de06eaa4b1cf',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '1bf2339dc0f885ad869bddd21a99186885f4a65b',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1336,7 +1336,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': '7vSUW_nuKSjSwu_SJlXmDCOkdOAMe1nyjgN02vO04jEC'
+              'version': 'YbCLJ5Z-CyNAlCrGyHQbqIyNd28ROl7YsrGANbNQ8TAC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1562,7 +1562,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cae3bad7926244b553485a8b697a7ecc94876ec5',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@45f024b330f818d0b1cee0e55c5b2a4fc02a4e4f',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index e4e5262..55fed24 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -224,9 +224,9 @@
     // SurfaceControl is not supported on webview.
     features.DisableIfNotSet(::features::kAndroidSurfaceControl);
 
-    // TODO(https://crbug.com/963653): SmsReceiver is not yet supported on
+    // TODO(https://crbug.com/963653): WebOTP is not yet supported on
     // WebView.
-    features.DisableIfNotSet(::features::kSmsReceiver);
+    features.DisableIfNotSet(::features::kWebOTP);
 
     // TODO(https://crbug.com/1012899): WebXR is not yet supported on WebView.
     features.DisableIfNotSet(::features::kWebXr);
diff --git a/ash/hud_display/hud_settings_view.cc b/ash/hud_display/hud_settings_view.cc
index 6726bf3..c506026 100644
--- a/ash/hud_display/hud_settings_view.cc
+++ b/ash/hud_display/hud_settings_view.cc
@@ -25,6 +25,12 @@
 
 namespace ash {
 namespace hud_display {
+namespace {
+
+ui::ScopedAnimationDurationScaleMode* scoped_animation_duration_scale_mode =
+    nullptr;
+
+}  // anonymous namespace
 
 class HUDCheckboxHandler {
  public:
@@ -207,9 +213,6 @@
   // Map slider values to animation scale.
   using SliderValuesMap = base::flat_map<float, float>;
 
-  std::unique_ptr<ui::ScopedAnimationDurationScaleMode>
-      scoped_animation_duration_scale_mode_;
-
   views::View* hints_container_ = nullptr;  // not owned.
   AnimationSpeedSlider* slider_ = nullptr;  // not owned.
 
@@ -306,10 +309,11 @@
   // There could be only one instance of the scoped modifier at a time.
   // So we need to destroy the existing one before we can create a
   // new one.
-  scoped_animation_duration_scale_mode_.reset();
+  delete scoped_animation_duration_scale_mode;
+  scoped_animation_duration_scale_mode = nullptr;
   if (multiplier != 1) {
-    scoped_animation_duration_scale_mode_ =
-        std::make_unique<ui::ScopedAnimationDurationScaleMode>(multiplier);
+    scoped_animation_duration_scale_mode =
+        new ui::ScopedAnimationDurationScaleMode(multiplier);
   }
 }
 
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 4157907..46edc74 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -49,6 +49,7 @@
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/logging.h"
+#include "base/optional.h"
 #include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/components/proximity_auth/public/mojom/auth_type.mojom.h"
@@ -1144,11 +1145,19 @@
 void LockContentsView::OnSetTpmLockedState(const AccountId& user,
                                            bool is_locked,
                                            base::TimeDelta time_left) {
+  LockContentsView::UserState* state = FindStateForUser(user);
+  if (!state) {
+    LOG(ERROR) << "Unable to find user when setting TPM lock state";
+    return;
+  }
+
+  state->time_until_tpm_unlock =
+      is_locked ? base::make_optional(time_left) : base::nullopt;
+
   LoginBigUserView* big_user =
-      TryToFindBigUser(user, false /*require_auth_active*/);
+      TryToFindBigUser(user, true /*require_auth_active*/);
   if (big_user && big_user->auth_user()) {
     LayoutAuth(big_user, nullptr /*opt_to_hide*/, true /*animate*/);
-    big_user->auth_user()->SetTpmLockedState(is_locked, time_left);
   }
 }
 
@@ -1879,7 +1888,12 @@
           view->auth_user()->current_user().basic_user_info.account_id);
       uint32_t to_update_auth;
       LoginAuthUserView::AuthMethodsMetadata auth_metadata;
-      if (state->force_online_sign_in) {
+      if (state->time_until_tpm_unlock.has_value()) {
+        // TPM is locked
+        to_update_auth = LoginAuthUserView::AUTH_DISABLED_TPM_LOCKED;
+        auth_metadata.time_until_tpm_unlock =
+            state->time_until_tpm_unlock.value();
+      } else if (state->force_online_sign_in) {
         to_update_auth = LoginAuthUserView::AUTH_ONLINE_SIGN_IN;
       } else if (state->disable_auth) {
         to_update_auth = LoginAuthUserView::AUTH_DISABLED;
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index 30b73a2..4c6fd1d9 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -248,8 +248,10 @@
     bool disable_auth = false;
     bool show_pin_pad_for_password = false;
     size_t autosubmit_pin_length = 0;
-    base::Optional<EasyUnlockIconOptions> easy_unlock_state;
+    base::Optional<EasyUnlockIconOptions> easy_unlock_state = base::nullopt;
     FingerprintState fingerprint_state;
+    // When present, indicates that the TPM is locked.
+    base::Optional<base::TimeDelta> time_until_tpm_unlock = base::nullopt;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(UserState);
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index d4f4cf6..c5a0fcd2 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -891,6 +891,11 @@
   views::ImageView* message_icon_;
 };
 
+LoginAuthUserView::AuthMethodsMetadata::AuthMethodsMetadata() = default;
+LoginAuthUserView::AuthMethodsMetadata::~AuthMethodsMetadata() = default;
+LoginAuthUserView::AuthMethodsMetadata::AuthMethodsMetadata(
+    const AuthMethodsMetadata&) = default;
+
 struct LoginAuthUserView::UiState {
   explicit UiState(const LoginAuthUserView* view) {
     has_password = view->ShouldShowPasswordField();
@@ -901,7 +906,8 @@
     has_challenge_response =
         view->HasAuthMethod(LoginAuthUserView::AUTH_CHALLENGE_RESPONSE);
     auth_disabled = view->HasAuthMethod(LoginAuthUserView::AUTH_DISABLED);
-    tpm_is_locked = view->tpm_is_locked();
+    tpm_is_locked =
+        view->HasAuthMethod(LoginAuthUserView::AUTH_DISABLED_TPM_LOCKED);
     force_online_sign_in =
         view->HasAuthMethod(LoginAuthUserView::AUTH_ONLINE_SIGN_IN);
 
@@ -1187,8 +1193,9 @@
 
 LoginAuthUserView::~LoginAuthUserView() = default;
 
-void LoginAuthUserView::SetAuthMethods(uint32_t auth_methods,
-                                       AuthMethodsMetadata auth_metadata) {
+void LoginAuthUserView::SetAuthMethods(
+    uint32_t auth_methods,
+    const AuthMethodsMetadata& auth_metadata) {
   // It is an error to call this method without storing the previous state.
   DCHECK(previous_state_);
 
@@ -1201,6 +1208,10 @@
   online_sign_in_message_->SetVisible(current_state.force_online_sign_in);
   disabled_auth_message_->SetVisible(current_state.auth_disabled);
   locked_tpm_message_view_->SetVisible(current_state.tpm_is_locked);
+  if (current_state.tpm_is_locked &&
+      auth_metadata.time_until_tpm_unlock.has_value())
+    locked_tpm_message_view_->SetRemainingTime(
+        auth_metadata.time_until_tpm_unlock.value());
 
   // Adjust the PIN keyboard visibility before the password textfield's one, so
   // that when both are about to be hidden the focus doesn't jump to the "1"
@@ -1214,6 +1225,8 @@
   password_view_->layer()->SetOpacity(current_state.has_password ? 1 : 0);
 
   pin_input_view_->UpdateLength(auth_metadata_.autosubmit_pin_length);
+  pin_input_view_->SetAuthenticateWithEmptyPinOnReturnKey(
+      HasAuthMethod(AUTH_TAP));
   pin_input_view_->SetVisible(current_state.has_pin_input);
 
   pin_password_toggle_->SetVisible(current_state.has_toggle);
@@ -1469,17 +1482,6 @@
   Layout();
 }
 
-void LoginAuthUserView::SetTpmLockedState(bool is_locked,
-                                          base::TimeDelta time_left) {
-  if (is_locked)
-    locked_tpm_message_view_->SetRemainingTime(time_left);
-  if (tpm_is_locked_ != is_locked) {
-    tpm_is_locked_ = is_locked;
-    // Update auth methods which are available.
-    SetAuthMethods(auth_methods_, auth_metadata_);
-  }
-}
-
 const LoginUserInfo& LoginAuthUserView::current_user() const {
   return user_view_->current_user();
 }
@@ -1620,7 +1622,7 @@
 }
 
 bool LoginAuthUserView::HasAuthMethod(AuthMethods auth_method) const {
-  return (auth_methods_ & auth_method) != 0 && !tpm_is_locked_;
+  return (auth_methods_ & auth_method) != 0;
 }
 
 void LoginAuthUserView::AttemptAuthenticateWithChallengeResponse() {
diff --git a/ash/login/ui/login_auth_user_view.h b/ash/login/ui/login_auth_user_view.h
index e34b513..dcb9ad8 100644
--- a/ash/login/ui/login_auth_user_view.h
+++ b/ash/login/ui/login_auth_user_view.h
@@ -53,17 +53,24 @@
                                        // protocol using security token.
     AUTH_DISABLED = 1 << 6,  // Disable all the auth methods and show a
                              // message to user.
+    AUTH_DISABLED_TPM_LOCKED = 1 << 7,  // Disable all the auth methods due
+                                        // to the TPM being locked
   };
 
   // Extra control parameters to be passed when setting the auth methods.
   struct AuthMethodsMetadata {
-    explicit AuthMethodsMetadata() {}
+    AuthMethodsMetadata();
+    ~AuthMethodsMetadata();
+    AuthMethodsMetadata(const AuthMethodsMetadata&);
+
     // If the virtual keyboard is visible, the pinpad is hidden.
     bool virtual_keyboard_visible = false;
     // Whether to show the pinpad for the password field.
     bool show_pinpad_for_pw = false;
     // User's pin length to use for autosubmit.
     size_t autosubmit_pin_length = 0;
+    // Only present when the TPM is locked.
+    base::Optional<base::TimeDelta> time_until_tpm_unlock = base::nullopt;
   };
 
   // Possible states that the input fields (PasswordView & PinInputView)
@@ -135,7 +142,7 @@
   // `CaptureStateForAnimationPreLayout` and `ApplyAnimationPostLayout`.
   void SetAuthMethods(
       uint32_t auth_methods,
-      AuthMethodsMetadata auth_metadata = AuthMethodsMetadata());
+      const AuthMethodsMetadata& auth_metadata = AuthMethodsMetadata());
   AuthMethods auth_methods() const { return auth_methods_; }
   InputFieldMode input_field_mode() const { return input_field_mode_; }
 
@@ -164,8 +171,6 @@
   // auth method is |AUTH_DISABLED|.
   void SetAuthDisabledMessage(const AuthDisabledData& auth_disabled_data);
 
-  void SetTpmLockedState(bool is_locked, base::TimeDelta time_left);
-
   const LoginUserInfo& current_user() const;
 
   // Provides the view that should be the anchor to message bubbles. Either the
@@ -174,8 +179,6 @@
   LoginPasswordView* password_view() { return password_view_; }
   LoginUserView* user_view() { return user_view_; }
 
-  bool tpm_is_locked() const { return tpm_is_locked_; }
-
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
   void RequestFocus() override;
@@ -286,8 +289,6 @@
   // `ApplyAnimationPostLayout`.
   std::unique_ptr<UiState> previous_state_;
 
-  bool tpm_is_locked_ = false;
-
   base::WeakPtrFactory<LoginAuthUserView> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(LoginAuthUserView);
diff --git a/ash/login/ui/login_auth_user_view_unittest.cc b/ash/login/ui/login_auth_user_view_unittest.cc
index 6976994..a35dca0 100644
--- a/ash/login/ui/login_auth_user_view_unittest.cc
+++ b/ash/login/ui/login_auth_user_view_unittest.cc
@@ -19,6 +19,8 @@
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/controls/textfield/textfield.h"
@@ -209,6 +211,39 @@
   base::RunLoop().RunUntilIdle();
 }
 
+// Verifies that pressing return on the pin input field when tap-to-unlock
+// is enabled attempts unlock.
+TEST_P(LoginAuthUserViewUnittest, PressReturnOnPinWithTapToUnlockEnabled) {
+  auto client = std::make_unique<MockLoginScreenClient>();
+
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  LoginAuthUserView::TestApi auth_test(view_);
+  LoginUserView* user_view(auth_test.user_view());
+  LoginPinInputView* pin_input(auth_test.pin_input_view());
+
+  SetUserCount(1);
+
+  // One call using focus, and one direct call to the view.
+  EXPECT_CALL(*client,
+              AuthenticateUserWithEasyUnlock(
+                  user_view->current_user().basic_user_info.account_id))
+      .Times(2);
+  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD |
+                     LoginAuthUserView::AUTH_PIN | LoginAuthUserView::AUTH_TAP,
+                 /*show_pinpad_for_pw=*/false,
+                 /*virtual_keyboard_visible=*/false,
+                 /*autosubmit_pin_length=*/6);
+
+  // Call through correct focus.
+  pin_input->RequestFocus();
+  generator->PressKey(ui::KeyboardCode::VKEY_RETURN, ui::EF_NONE);
+
+  // Call the view directly as well.
+  pin_input->OnKeyPressed(ui::KeyEvent(
+      ui::ET_KEY_PRESSED, ui::KeyboardCode::VKEY_RETURN, ui::EF_NONE));
+  base::RunLoop().RunUntilIdle();
+}
+
 TEST_P(LoginAuthUserViewUnittest, OnlineSignInMessage) {
   auto client = std::make_unique<MockLoginScreenClient>();
   LoginAuthUserView::TestApi auth_test(view_);
@@ -278,7 +313,6 @@
   view_->UpdateForUser(another_user);
   EXPECT_TRUE(password_test.textfield()->GetText().empty());
 
-  // TODO(tellier) - Check that this test is doing what it is intended to
   if (display_password_feature_enabled_) {
     password_test.textfield()->SetTextInputType(ui::TEXT_INPUT_TYPE_NULL);
     EXPECT_EQ(password_test.textfield()->GetTextInputType(),
diff --git a/ash/login/ui/login_password_view_test.cc b/ash/login/ui/login_password_view_test.cc
index 3420796..0b7b975 100644
--- a/ash/login/ui/login_password_view_test.cc
+++ b/ash/login/ui/login_password_view_test.cc
@@ -14,6 +14,7 @@
 #include "base/timer/mock_timer.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "ui/base/ime/text_input_type.h"
+#include "ui/events/event_constants.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/textfield/textfield.h"
@@ -190,6 +191,25 @@
   EXPECT_EQ(base::ASCIIToUTF16("abc1"), *password_);
 }
 
+// Verifies that pressing 'Return' on the password field triggers an
+// unlock attempt by calling OnSubmit with an empty password.
+TEST_F(LoginPasswordViewTest, PressingReturnTriggersUnlockWithEmptyPassword) {
+  LoginPasswordView::TestApi test_api(view_);
+
+  // Hitting enter on an empty password should not submit an empty password
+  // when not allowed to.
+  view_->SetEnabledOnEmptyPassword(false);
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  generator->PressKey(ui::KeyboardCode::VKEY_RETURN, ui::EF_NONE);
+  ASSERT_FALSE(password_.has_value());
+
+  // When allowed, hitting enter should submit an empty password.
+  view_->SetEnabledOnEmptyPassword(true);
+  generator->PressKey(ui::KeyboardCode::VKEY_RETURN, ui::EF_NONE);
+  ASSERT_TRUE(password_.has_value());
+  EXPECT_EQ(base::ASCIIToUTF16(""), *password_);
+}
+
 // Verifies that text is not cleared after submitting a password.
 TEST_F(LoginPasswordViewTest, PasswordSubmitClearsPassword) {
   LoginPasswordView::TestApi test_api(view_);
diff --git a/ash/login/ui/login_pin_input_view.cc b/ash/login/ui/login_pin_input_view.cc
index 057609c..e872f60 100644
--- a/ash/login/ui/login_pin_input_view.cc
+++ b/ash/login/ui/login_pin_input_view.cc
@@ -28,7 +28,6 @@
 // Total height of the view.
 constexpr const int kPinInputTotalHeightDp = 37;
 // Default length
-constexpr const int kDefaultLength = 6;
 constexpr const int kPinAutosubmitMinLength = 6;
 constexpr const int kPinAutosubmitMaxLength = 12;
 }  // namespace
@@ -48,6 +47,8 @@
                         const ui::MouseEvent& mouse_event) override;
   bool HandleGestureEvent(views::Textfield* sender,
                           const ui::GestureEvent& gesture_event) override;
+  bool HandleKeyEvent(views::Textfield* sender,
+                      const ui::KeyEvent& key_event) override;
   // views::view
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
@@ -112,6 +113,18 @@
   return true;
 }
 
+bool LoginPinInput::HandleKeyEvent(views::Textfield* sender,
+                                   const ui::KeyEvent& key_event) {
+  // Let the parent view handle the 'Return' key. Triggers SmartLock login.
+  if (key_event.type() == ui::ET_KEY_PRESSED &&
+      key_event.key_code() == ui::VKEY_RETURN) {
+    return false;
+  }
+
+  // Delegate all other key events to the base class.
+  return FixedLengthCodeInput::HandleKeyEvent(sender, key_event);
+}
+
 void LoginPinInput::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   FixedLengthCodeInput::GetAccessibleNodeData(node_data);
   const int inserted_digits = active_input_index();
@@ -122,6 +135,8 @@
           IDS_ASH_LOGIN_POD_PASSWORD_PIN_INPUT_ACCESSIBLE_NAME));
 }
 
+const int LoginPinInputView::kDefaultLength = 6;
+
 LoginPinInputView::TestApi::TestApi(LoginPinInputView* view) : view_(view) {
   DCHECK(view_);
 }
@@ -182,10 +197,15 @@
                           base::Unretained(this)),
       base::BindRepeating(&LoginPinInputView::OnChanged,
                           base::Unretained(this))));
+  is_read_only_ = false;
   Layout();
   SetVisible(true);
 }
 
+void LoginPinInputView::SetAuthenticateWithEmptyPinOnReturnKey(bool enabled) {
+  authenticate_with_empty_pin_on_return_key_ = enabled;
+}
+
 void LoginPinInputView::Reset() {
   DCHECK(code_input_);
   code_input_->ClearInput();
@@ -202,6 +222,7 @@
 }
 
 void LoginPinInputView::SetReadOnly(bool read_only) {
+  is_read_only_ = read_only;
   code_input_->SetReadOnly(read_only);
 }
 
@@ -217,6 +238,20 @@
   code_input_->RequestFocus();
 }
 
+bool LoginPinInputView::OnKeyPressed(const ui::KeyEvent& event) {
+  // The 'Return' key has no relationship to the digits inserted in this view.
+  // It just performs an unlock attempt with an empty PIN, which triggers a
+  // SmartLock attempt in LoginAuthUserView. The user's PIN is only submitted
+  // when the last digit is inserted.
+  if (event.key_code() == ui::KeyboardCode::VKEY_RETURN && !is_read_only_ &&
+      authenticate_with_empty_pin_on_return_key_) {
+    SubmitPin(base::UTF8ToUTF16(""));
+    return true;
+  }
+
+  return false;
+}
+
 void LoginPinInputView::OnChanged(bool is_empty) {
   if (on_changed_)
     on_changed_.Run(is_empty);
diff --git a/ash/login/ui/login_pin_input_view.h b/ash/login/ui/login_pin_input_view.h
index 9095723..848893d4 100644
--- a/ash/login/ui/login_pin_input_view.h
+++ b/ash/login/ui/login_pin_input_view.h
@@ -34,6 +34,8 @@
   using OnPinSubmit = base::RepeatingCallback<void(const base::string16& pin)>;
   using OnPinChanged = base::RepeatingCallback<void(bool is_empty)>;
 
+  static const int kDefaultLength;
+
   class ASH_EXPORT TestApi {
    public:
     explicit TestApi(LoginPinInputView* view);
@@ -61,6 +63,10 @@
   // Updates the length of the field. Used when switching users.
   void UpdateLength(const size_t pin_length);
 
+  // When set, hitting return will attempt an unlock with an empty PIN.
+  // LoginAuthUserView interprets such attempts as a SmartLock unlock.
+  void SetAuthenticateWithEmptyPinOnReturnKey(bool enabled);
+
   void Reset();
   void Backspace();
   void InsertDigit(int digit);
@@ -72,6 +78,7 @@
   // views::View
   gfx::Size CalculatePreferredSize() const override;
   void RequestFocus() override;
+  bool OnKeyPressed(const ui::KeyEvent& event) override;
 
  private:
   // The code input will call this when all digits are in.
@@ -81,11 +88,17 @@
   void OnChanged(bool is_empty);
 
   // Current field length.
-  size_t length_;
+  size_t length_ = kDefaultLength;
+
+  // Whether the field is read only.
+  bool is_read_only_ = false;
 
   // The input field owned by this view.
   LoginPinInput* code_input_ = nullptr;
 
+  // Whether the 'Return' key should trigger an unlock with an empty PIN.
+  bool authenticate_with_empty_pin_on_return_key_ = false;
+
   OnPinSubmit on_submit_;
   OnPinChanged on_changed_;
 };
diff --git a/ash/login/ui/login_pin_input_view_unittest.cc b/ash/login/ui/login_pin_input_view_unittest.cc
index fe88679..27a9ae1 100644
--- a/ash/login/ui/login_pin_input_view_unittest.cc
+++ b/ash/login/ui/login_pin_input_view_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string16.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
 #include "ui/accessibility/ax_node_data.h"
+#include "ui/events/test/event_generator.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
@@ -80,6 +81,23 @@
   base::Optional<bool> is_empty_;
 };
 
+// Verifies that pressing 'Return' on the PIN input field triggers an
+// unlock attempt by calling OnSubmit with an empty PIN.
+TEST_P(LoginPinInputViewTest, PressingReturnTriggersUnlockWithEmptyPin) {
+  // Hitting 'Return' should not trigger 'OnSubmit' with an empty PIN when not
+  // allowed.
+  view_->SetAuthenticateWithEmptyPinOnReturnKey(false);
+  ui::test::EventGenerator* generator = GetEventGenerator();
+  generator->PressKey(ui::KeyboardCode::VKEY_RETURN, 0);
+  ASSERT_FALSE(submitted_pin_.has_value());
+
+  // Hitting 'Return' should trigger 'OnSubmit' with an empty PIN.
+  view_->SetAuthenticateWithEmptyPinOnReturnKey(true);
+  generator->PressKey(ui::KeyboardCode::VKEY_RETURN, 0);
+  ASSERT_TRUE(submitted_pin_.has_value());
+  EXPECT_EQ(base::ASCIIToUTF16(""), *submitted_pin_);
+}
+
 // Tests that ChromeVox announces "Enter your PIN" when the
 // field gets focused
 TEST_P(LoginPinInputViewTest, AccessibleName) {
diff --git a/ash/login/ui/login_public_account_user_view.cc b/ash/login/ui/login_public_account_user_view.cc
index 230efa8..c9dd2ef 100644
--- a/ash/login/ui/login_public_account_user_view.cc
+++ b/ash/login/ui/login_public_account_user_view.cc
@@ -154,6 +154,12 @@
                                                const ui::Event& event) {
   if (sender == arrow_button_) {
     DCHECK(arrow_button_);
+    // If the pod isn't active, activate it first.
+    if (!auth_enabled_) {
+      OnUserViewTap();
+    }
+
+    DCHECK(auth_enabled_);
     on_public_account_tap_.Run();
   }
 }
diff --git a/ash/login/ui/login_public_account_user_view_unittest.cc b/ash/login/ui/login_public_account_user_view_unittest.cc
index f27974e..6d83832 100644
--- a/ash/login/ui/login_public_account_user_view_unittest.cc
+++ b/ash/login/ui/login_public_account_user_view_unittest.cc
@@ -62,7 +62,15 @@
   int public_account_tap_count_ = 0;
 
  private:
-  void OnUserViewTapped() { ++user_view_tap_count_; }
+  void OnUserViewTapped() {
+    ++user_view_tap_count_;
+    // Simulate the pod becoming active as it does when it is a part of
+    // LoginAuthUserView.
+    public_account_view_->SetAuthEnabled(/*enabled=*/true, /*animate=*/false);
+    // 'on_tap' must run before 'public_account_tap'
+    ASSERT_TRUE(public_account_tap_count_ == 0);
+  }
+
   void OnPublicAccountTapped() { ++public_account_tap_count_; }
 
   DISALLOW_COPY_AND_ASSIGN(LoginPublicAccountUserViewTest);
@@ -126,12 +134,34 @@
   GetEventGenerator()->ClickLeftButton();
   EXPECT_EQ(user_view_tap_count_, 1);
 
-  // Tap on the arrow button and verify on_public_account_tapped callback is
-  // run.
+  // Now that the pod is active, clicking on the arrow should not trigger
+  // another 'on_tap' callback. Only on_public_account_tapped should be called.
+  user_view_tap_count_ = 0;
+
   GetEventGenerator()->MoveMouseTo(
       public_account_test.arrow_button()->GetBoundsInScreen().CenterPoint());
   GetEventGenerator()->ClickLeftButton();
   EXPECT_EQ(public_account_tap_count_, 1);
+  EXPECT_EQ(user_view_tap_count_, 0);
+}
+
+// Verifies that tapping on the arrow when the pod isn't active will first call
+// the 'on_tap' callback to make the pod active, and then run the public
+// account tap callback.
+TEST_F(LoginPublicAccountUserViewTest, OnTapIsCalledWhenNotActive) {
+  LoginPublicAccountUserView::TestApi public_account_test(public_account_view_);
+
+  // Disable auth to simulate the pod being inactive.
+  public_account_view_->SetAuthEnabled(false, /*animate=*/false);
+
+  // Tap on the arrow and verify that 'on_tap' will be called before
+  // 'on_public_account_tapped' is called. The temporal aspect of this
+  // expectation is verified in OnUserViewTapped.
+  GetEventGenerator()->MoveMouseTo(
+      public_account_test.arrow_button()->GetBoundsInScreen().CenterPoint());
+  GetEventGenerator()->ClickLeftButton();
+  EXPECT_EQ(user_view_tap_count_, 1);
+  EXPECT_EQ(public_account_tap_count_, 1);
 }
 
 // Verifies that displaying the arrow button does not change the view bounds.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 3983a6cd..f8e2c5b 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -255,6 +255,8 @@
     "files/file_util.h",
     "files/important_file_writer.cc",
     "files/important_file_writer.h",
+    "files/important_file_writer_cleaner.cc",
+    "files/important_file_writer_cleaner.h",
     "files/memory_mapped_file.cc",
     "files/memory_mapped_file.h",
     "files/platform_file.h",
@@ -896,8 +898,6 @@
       "files/file_path_watcher_win.cc",
       "files/file_util_win.cc",
       "files/file_win.cc",
-      "files/important_file_writer_cleaner.cc",
-      "files/important_file_writer_cleaner.h",
       "files/memory_mapped_file_win.cc",
       "logging_win.cc",
       "logging_win.h",
@@ -1682,6 +1682,8 @@
       "files/file_proxy.cc",
       "files/important_file_writer.cc",
       "files/important_file_writer.h",
+      "files/important_file_writer_cleaner.cc",
+      "files/important_file_writer_cleaner.h",
       "files/scoped_temp_dir.cc",
       "memory/discardable_memory.cc",
       "memory/discardable_memory.h",
@@ -2736,6 +2738,7 @@
     "files/file_proxy_unittest.cc",
     "files/file_unittest.cc",
     "files/file_util_unittest.cc",
+    "files/important_file_writer_cleaner_unittest.cc",
     "files/important_file_writer_unittest.cc",
     "files/memory_mapped_file_unittest.cc",
     "files/scoped_temp_dir_unittest.cc",
@@ -3028,7 +3031,6 @@
     sources += [
       "debug/gdi_debug_util_win_unittest.cc",
       "file_version_info_win_unittest.cc",
-      "files/important_file_writer_cleaner_unittest.cc",
       "process/launch_unittest_win.cc",
       "test/test_reg_util_win_unittest.cc",
       "threading/platform_thread_win_unittest.cc",
diff --git a/base/allocator/partition_allocator/partition_alloc.cc b/base/allocator/partition_allocator/partition_alloc.cc
index 0852843..fac80c3c 100644
--- a/base/allocator/partition_allocator/partition_alloc.cc
+++ b/base/allocator/partition_allocator/partition_alloc.cc
@@ -397,9 +397,7 @@
   }
 #endif
 
-  page->set_raw_size(raw_size);
-  PA_DCHECK(page->get_raw_size() == raw_size);
-
+  page->SetRawSize(raw_size);
   page->bucket->slot_size = new_slot_size;
   return true;
 }
@@ -473,16 +471,19 @@
       // Trying to allocate a block of size |new_size| would give us a block of
       // the same size as the one we've already got, so re-use the allocation
       // after updating statistics (and cookies, if present).
-      size_t new_raw_size =
-          internal::PartitionSizeAdjustAdd(allow_extras, new_size);
-      page->set_raw_size(new_raw_size);
+      if (page->CanStoreRawSize()) {
+        size_t new_raw_size =
+            internal::PartitionSizeAdjustAdd(allow_extras, new_size);
+        page->SetRawSize(new_raw_size);
 #if DCHECK_IS_ON()
-      // Write a new trailing cookie when it is possible to keep track of
-      // |new_size| via the raw size pointer.
-      if (page->get_raw_size_ptr() && allow_extras) {
-        internal::PartitionCookieWriteValue(static_cast<char*>(ptr) + new_size);
-      }
+        // Write a new trailing cookie only when it is possible to keep track
+        // raw size (otherwise we wouldn't know where to look for it later).
+        if (allow_extras) {
+          internal::PartitionCookieWriteValue(static_cast<char*>(ptr) +
+                                              new_size);
+        }
 #endif
+      }
       return ptr;
     }
   }
@@ -517,9 +518,9 @@
   size_t bucket_num_slots = bucket->get_slots_per_span();
   size_t discardable_bytes = 0;
 
-  size_t raw_size = page->get_raw_size();
-  if (raw_size) {
-    uint32_t used_bytes = static_cast<uint32_t>(RoundUpToSystemPage(raw_size));
+  if (page->CanStoreRawSize()) {
+    uint32_t used_bytes =
+        static_cast<uint32_t>(RoundUpToSystemPage(page->GetRawSize()));
     discardable_bytes = bucket->slot_size - used_bytes;
     if (discardable_bytes && discard) {
       char* ptr = reinterpret_cast<char*>(
@@ -712,9 +713,8 @@
 
   stats_out->discardable_bytes += PartitionPurgePage(page, false);
 
-  size_t raw_size = page->get_raw_size();
-  if (raw_size) {
-    stats_out->active_bytes += static_cast<uint32_t>(raw_size);
+  if (page->CanStoreRawSize()) {
+    stats_out->active_bytes += static_cast<uint32_t>(page->GetRawSize());
   } else {
     stats_out->active_bytes +=
         (page->num_allocated_slots * stats_out->bucket_slot_size);
diff --git a/base/allocator/partition_allocator/partition_alloc.h b/base/allocator/partition_allocator/partition_alloc.h
index 8742b6b..87e0f1de 100644
--- a/base/allocator/partition_allocator/partition_alloc.h
+++ b/base/allocator/partition_allocator/partition_alloc.h
@@ -394,7 +394,7 @@
 
     // All large allocations must go through the slow path to correctly update
     // the size metadata.
-    PA_DCHECK(!page->get_raw_size_ptr());  // doesn't have raw size
+    PA_DCHECK(!page->CanStoreRawSize());
     internal::PartitionFreelistEntry* new_head =
         internal::EncodedPartitionFreelistEntry::Decode(
             page->freelist_head->next);
@@ -526,6 +526,8 @@
 #endif
 
 #if ENABLE_REF_COUNT_FOR_BACKUP_REF_PTR
+      size_t usable_size =
+          internal::PartitionSizeAdjustSubtract(true, utilized_slot_size);
       internal::PartitionRefCount* ref_count =
           internal::PartitionRefCountPointerNoOffset(ptr);
       // If we are holding the last reference to the allocation, it can be freed
@@ -816,7 +818,7 @@
       Page* page = Page::FromPointerNoAlignmentCheck(ret);
       // All large allocations must go through the RawAlloc path to correctly
       // set |utilized_slot_size|.
-      PA_DCHECK(!page->get_raw_size_ptr());  // doesn't have raw size
+      PA_DCHECK(!page->CanStoreRawSize());
       PA_DCHECK(IsValidPage(page));
       PA_DCHECK(page->bucket == &buckets[bucket_index]);
     }
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc
index 3c97793..1e8fd81 100644
--- a/base/allocator/partition_allocator/partition_bucket.cc
+++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -665,7 +665,8 @@
 
   PA_DCHECK(new_page_bucket != &root->sentinel_bucket);
   new_page_bucket->active_pages_head = new_page;
-  new_page->set_raw_size(raw_size);
+  if (new_page->CanStoreRawSize())
+    new_page->SetRawSize(raw_size);
 
   // If we found an active page with free slots, or an empty page, we have a
   // usable freelist head.
diff --git a/base/allocator/partition_allocator/partition_page.cc b/base/allocator/partition_allocator/partition_page.cc
index f19d86ca..7969b83 100644
--- a/base/allocator/partition_allocator/partition_page.cc
+++ b/base/allocator/partition_allocator/partition_page.cc
@@ -126,8 +126,8 @@
       bucket->SetNewActivePage();
     PA_DCHECK(bucket->active_pages_head != this);
 
-    set_raw_size(0);
-    PA_DCHECK(!get_raw_size());
+    if (CanStoreRawSize())
+      SetRawSize(0);
 
     PartitionRegisterEmptyPage(this);
   } else {
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h
index a3c7fbd..2f64498a 100644
--- a/base/allocator/partition_allocator/partition_page.h
+++ b/base/allocator/partition_allocator/partition_page.h
@@ -123,30 +123,26 @@
   ALWAYS_INLINE static PartitionPage* FromPointerNoAlignmentCheck(void* ptr);
   ALWAYS_INLINE static PartitionPage* FromPointer(void* ptr);
 
+  // Checks if it is feasible to store raw_size.
+  ALWAYS_INLINE bool CanStoreRawSize() const;
+  // The caller is responsible for ensuring that raw_size can be stored before
+  // calling Set/GetRawSize.
+  ALWAYS_INLINE void SetRawSize(size_t raw_size);
+  ALWAYS_INLINE size_t GetRawSize() const;
+
   // Returns size of the region used within a slot. The used region comprises
   // of actual allocated data, extras and possibly empty space.
   ALWAYS_INLINE size_t GetUtilizedSlotSize() const {
     // The returned size can be:
     // - The slot size for small buckets.
     // - Exact needed size to satisfy allocation (incl. extras), for large
-    //   buckets and direct-mapped allocations (see the comment in
-    //   get_raw_size_ptr() for more info).
-    size_t result = bucket->slot_size;
-    if (UNLIKELY(get_raw_size_ptr()))  // has raw size.
-      result = get_raw_size();
-
-    return result;
+    //   buckets and direct-mapped allocations (see also the comment in
+    //   CanStoreRawSize() for more info).
+    if (LIKELY(!CanStoreRawSize()))
+      return bucket->slot_size;
+    return GetRawSize();
   }
 
-  ALWAYS_INLINE const size_t* get_raw_size_ptr() const;
-  ALWAYS_INLINE size_t* get_raw_size_ptr() {
-    return const_cast<size_t*>(
-        const_cast<const PartitionPage*>(this)->get_raw_size_ptr());
-  }
-
-  ALWAYS_INLINE size_t get_raw_size() const;
-  ALWAYS_INLINE void set_raw_size(size_t size);
-
   ALWAYS_INLINE void Reset();
 
   // TODO(ajwong): Can this be made private?  https://crbug.com/787153
@@ -270,29 +266,39 @@
 }
 
 template <bool thread_safe>
-ALWAYS_INLINE const size_t* PartitionPage<thread_safe>::get_raw_size_ptr()
-    const {
-  // For direct-map as well as single-slot buckets which span more than
-  // |kMaxPartitionPagesPerSlotSpan| partition pages, we have some spare
-  // metadata space to store the raw size needed to satisfy the allocation
-  // (requested size + extras). We can use this to report better statistics.
+ALWAYS_INLINE bool PartitionPage<thread_safe>::CanStoreRawSize() const {
+  // Raw size is the size needed to satisfy the allocation (requested size +
+  // extras). If available, it can be used to report better statistics or to
+  // bring protective cookie closer to the allocated memory.
+  //
+  // For direct-map as well as single-slot slot spans (recognized by checking
+  // against |kMaxPartitionPagesPerSlotSpan|), we have some spare metadata space
+  // in subsequent PartitionPage to store the raw size. It isn't only metadata
+  // space though, slot spans that have more than one slot can't have raw size
+  // stored, because we wouldn't know which slot it applies to.
   if (LIKELY(bucket->slot_size <=
              MaxSystemPagesPerSlotSpan() * SystemPageSize()))
-    return nullptr;
+    return false;
 
   PA_DCHECK((bucket->slot_size % SystemPageSize()) == 0);
   PA_DCHECK(bucket->is_direct_mapped() || bucket->get_slots_per_span() == 1);
 
-  const PartitionPage* the_next_page = this + 1;
-  return reinterpret_cast<const size_t*>(&the_next_page->freelist_head);
+  return true;
 }
 
 template <bool thread_safe>
-ALWAYS_INLINE size_t PartitionPage<thread_safe>::get_raw_size() const {
-  const size_t* ptr = get_raw_size_ptr();
-  if (UNLIKELY(ptr != nullptr))
-    return *ptr;
-  return 0;
+ALWAYS_INLINE void PartitionPage<thread_safe>::SetRawSize(size_t raw_size) {
+  PA_DCHECK(CanStoreRawSize());
+  PartitionPage* the_next_page = this + 1;
+  the_next_page->freelist_head =
+      reinterpret_cast<PartitionFreelistEntry*>(raw_size);
+}
+
+template <bool thread_safe>
+ALWAYS_INLINE size_t PartitionPage<thread_safe>::GetRawSize() const {
+  PA_DCHECK(CanStoreRawSize());
+  const PartitionPage* the_next_page = this + 1;
+  return reinterpret_cast<size_t>(the_next_page->freelist_head);
 }
 
 template <bool thread_safe>
@@ -316,8 +322,8 @@
     return FreeSlowPath();
   } else {
     // All single-slot allocations must go through the slow path to
-    // correctly update the size metadata.
-    PA_DCHECK(get_raw_size() == 0);
+    // correctly update the raw size.
+    PA_DCHECK(!CanStoreRawSize());
   }
   return {};
 }
@@ -362,13 +368,6 @@
 }
 
 template <bool thread_safe>
-ALWAYS_INLINE void PartitionPage<thread_safe>::set_raw_size(size_t size) {
-  size_t* raw_size_ptr = get_raw_size_ptr();
-  if (UNLIKELY(raw_size_ptr != nullptr))
-    *raw_size_ptr = size;
-}
-
-template <bool thread_safe>
 ALWAYS_INLINE void PartitionPage<thread_safe>::Reset() {
   PA_DCHECK(is_decommitted());
 
diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc
index f8ecef4..bf4a9458 100644
--- a/base/files/important_file_writer.cc
+++ b/base/files/important_file_writer.cc
@@ -186,10 +186,8 @@
                                                   StringPiece data,
                                                   StringPiece histogram_suffix,
                                                   bool from_instance) {
-#if defined(OS_WIN)
   if (!from_instance)
     ImportantFileWriterCleaner::AddDirectory(path.DirName());
-#endif
 
 #if defined(OS_WIN) && DCHECK_IS_ON()
   // In https://crbug.com/920174, we have cases where CreateTemporaryFileInDir
@@ -311,9 +309,7 @@
       commit_interval_(interval),
       histogram_suffix_(histogram_suffix ? histogram_suffix : "") {
   DCHECK(task_runner_);
-#if defined(OS_WIN)
   ImportantFileWriterCleaner::AddDirectory(path.DirName());
-#endif
 }
 
 ImportantFileWriter::~ImportantFileWriter() {
diff --git a/base/files/important_file_writer_cleaner.cc b/base/files/important_file_writer_cleaner.cc
index 83ba867..90e27d9d 100644
--- a/base/files/important_file_writer_cleaner.cc
+++ b/base/files/important_file_writer_cleaner.cc
@@ -17,9 +17,28 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
 
 namespace base {
 
+namespace {
+
+base::Time GetUpperBoundTime() {
+#if defined(OS_ANDROID) || defined(OS_IOS) || defined(OS_FUCHSIA)
+  // If process creation time is not available then use instance creation
+  // time as the upper-bound for old files. Modification times may be
+  // rounded-down to coarse-grained increments, e.g. FAT has 2s granularity,
+  // so it is necessary to set the upper-bound earlier than Now() by at least
+  // that margin to account for modification times being rounded-down.
+  return Time::Now() - TimeDelta::FromSeconds(2);
+#else
+  return Process::Current().CreationTime() - TimeDelta::FromSeconds(2);
+#endif
+}
+
+}  // namespace
+
 // static
 ImportantFileWriterCleaner& ImportantFileWriterCleaner::GetInstance() {
   static NoDestructor<ImportantFileWriterCleaner> instance;
@@ -97,8 +116,12 @@
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
+base::Time ImportantFileWriterCleaner::GetUpperBoundTimeForTest() const {
+  return upper_bound_time_;
+}
+
 ImportantFileWriterCleaner::ImportantFileWriterCleaner()
-    : upper_bound_time_(Process::Current().CreationTime()) {
+    : upper_bound_time_(GetUpperBoundTime()) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
diff --git a/base/files/important_file_writer_cleaner.h b/base/files/important_file_writer_cleaner.h
index 1cea741..3e4466c1e 100644
--- a/base/files/important_file_writer_cleaner.h
+++ b/base/files/important_file_writer_cleaner.h
@@ -77,6 +77,10 @@
   // main thread task runner.
   void UninitializeForTesting();
 
+  // Returns the upper-bound time. Files with modification times older than this
+  // are assumed to have been orphaned by a previous instance of the process.
+  base::Time GetUpperBoundTimeForTest() const;
+
  private:
   friend class NoDestructor<ImportantFileWriterCleaner>;
 
diff --git a/base/files/important_file_writer_cleaner_unittest.cc b/base/files/important_file_writer_cleaner_unittest.cc
index d8121f4..6c2a921e 100644
--- a/base/files/important_file_writer_cleaner_unittest.cc
+++ b/base/files/important_file_writer_cleaner_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/optional.h"
-#include "base/process/process.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/thread_pool.h"
 #include "base/test/bind_test_util.h"
@@ -27,6 +26,12 @@
 namespace base {
 
 class ImportantFileWriterCleanerTest : public ::testing::Test {
+ public:
+  ImportantFileWriterCleanerTest()
+      : old_file_time_(ImportantFileWriterCleaner::GetInstance()
+                           .GetUpperBoundTimeForTest() -
+                       TimeDelta::FromMilliseconds(1)) {}
+
  protected:
   // Initializes and Starts the global cleaner at construction and Stops it
   // at destruction. ("Lifetime" refers to its activity rather than existence.)
@@ -72,11 +77,9 @@
   }
 
   void CreateOldFile(const FilePath& path) {
-    const Time old_time =
-        Process::Current().CreationTime() - TimeDelta::FromSeconds(1);
     File file(path, File::FLAG_CREATE | File::FLAG_WRITE);
     ASSERT_TRUE(file.IsValid());
-    ASSERT_TRUE(file.SetTimes(Time::Now(), old_time));
+    ASSERT_TRUE(file.SetTimes(Time::Now(), old_file_time_));
   }
 
   ScopedTempDir temp_dir_;
@@ -84,6 +87,7 @@
   HistogramTester histogram_tester_;
 
  private:
+  const Time old_file_time_;
   FilePath dir_1_;
   FilePath dir_2_;
   FilePath dir_1_file_new_;
diff --git a/base/memory/checked_ptr_unittest.cc b/base/memory/checked_ptr_unittest.cc
index 52669c27..8f5d9bc 100644
--- a/base/memory/checked_ptr_unittest.cc
+++ b/base/memory/checked_ptr_unittest.cc
@@ -890,7 +890,7 @@
     !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
 TEST(BackupRefPtrImpl, Basic) {
   // This test works only if GigaCage is enabled. Bail out otherwise.
-  if (!IsPartitionAllocGigaCageEnabled())
+  if (!features::IsPartitionAllocGigaCageEnabled())
     return;
 
   // TODO(bartekn): Avoid using PartitionAlloc API directly. Switch to
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 53f2abca..7d7fc21 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20201013.3.1
+0.20201014.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 53f2abca..f870b4f 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20201013.3.1
+0.20201014.0.1
diff --git a/chrome/VERSION b/chrome/VERSION
index 3581564..1359219 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=88
 MINOR=0
-BUILD=4292
+BUILD=4293
 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 3d65157..8443874 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1093,6 +1093,9 @@
   "java/src/org/chromium/chrome/browser/omnibox/ChromeAutocompleteSchemeClassifier.java",
   "java/src/org/chromium/chrome/browser/omnibox/KeyboardHideHelper.java",
   "java/src/org/chromium/chrome/browser/omnibox/LocationBar.java",
+  "java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java",
+  "java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorPhone.java",
+  "java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorTablet.java",
   "java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java",
   "java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java",
   "java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 42e00b6..a8b4113 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -519,7 +519,7 @@
   "javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java",
   "javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTestUtils.java",
   "javatests/src/org/chromium/chrome/browser/site_settings/WebsitePermissionsFetcherTest.java",
-  "javatests/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBarTest.java",
+  "javatests/src/org/chromium/chrome/browser/sms/WebOTPServiceInfoBarTest.java",
   "javatests/src/org/chromium/chrome/browser/ssl/CaptivePortalTest.java",
   "javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java",
   "javatests/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorViewBinderTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DataReductionProxyFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DataReductionProxyFirstRunFragment.java
index bc220ef0..9108631f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DataReductionProxyFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DataReductionProxyFirstRunFragment.java
@@ -11,6 +11,7 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
 import android.widget.TextView;
 
@@ -92,6 +93,12 @@
     }
 
     @Override
+    public void setInitialA11yFocus() {
+        final View title = getView().findViewById(R.id.title);
+        title.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    }
+
+    @Override
     public void onStart() {
         super.onStart();
         DataReductionPromoUtils.saveFreOrSecondRunPromoDisplayed();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
index 502df14..f1786dff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultSearchEngineFirstRunFragment.java
@@ -8,6 +8,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
 
 import androidx.fragment.app.Fragment;
@@ -69,6 +70,12 @@
     }
 
     @Override
+    public void setInitialA11yFocus() {
+        final View title = getView().findViewById(R.id.chooser_title);
+        title.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    }
+
+    @Override
     public void setUserVisibleHint(boolean isVisibleToUser) {
         super.setUserVisibleHint(isVisibleToUser);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
index 11744a6..00dca72 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
@@ -378,7 +378,7 @@
         if (mPager.getCurrentItem() == 0) {
             abortFirstRunExperience();
         } else {
-            mPager.setCurrentItem(mPager.getCurrentItem() - 1, false);
+            setCurrentItemForPager(mPager.getCurrentItem() - 1);
         }
     }
 
@@ -545,12 +545,34 @@
         if (!didAcceptTermsOfService()) {
             return position == 0;
         }
+        if (!setCurrentItemForPager(position)) {
+            return false;
+        }
+        recordFreProgressHistogram(mFreProgressStates.get(position));
+        return true;
+    }
+
+    private boolean setCurrentItemForPager(int position) {
         if (position >= mPagerAdapter.getCount()) {
             completeFirstRunExperience();
             return false;
         }
+
         mPager.setCurrentItem(position, false);
-        recordFreProgressHistogram(mFreProgressStates.get(position));
+
+        // Set A11y focus if possible. See https://crbug.com/1094064 for more context.
+        // * Screen reader can lose focus when switching between pages with ViewPager;
+        // * FragmentPagerStateAdapter is trying to limit access for the real fragment that we are
+        // creating / created;
+        // * Note that despite the function name and javadoc,
+        // FragmentPagerStateAdapter#instantiateItem returns cached fragments when possible. This
+        // should always be the case here as ViewPager#setCurrentItem will trigger instantiation if
+        // needed. This function call to #instantiateItem is not creating new fragment here but
+        // rather reading the ones already created.
+        Object currentFragment = mPagerAdapter.instantiateItem(mPager, position);
+        if (currentFragment instanceof FirstRunFragment) {
+            ((FirstRunFragment) currentFragment).setInitialA11yFocus();
+        }
         return true;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFragment.java
index 06a883d..6d49d6d8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFragment.java
@@ -28,6 +28,16 @@
     FragmentActivity getActivity();
 
     /**
+     * Set the a11y focus when the fragment is shown on the screen.
+     *
+     * Android ViewPager cannot always assign the correct a11y focus automatically when switching
+     * between pages.
+     *
+     * See https://crbug.com/1094064 for more detail.
+     */
+    void setInitialA11yFocus();
+
+    /**
      * Convenience method to get {@link FirstRunPageDelegate}.
      */
     default FirstRunPageDelegate getPageDelegate() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java
index 04df2387..148709b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java
@@ -6,6 +6,8 @@
 
 import android.content.Context;
 import android.os.Bundle;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
 
 import androidx.fragment.app.Fragment;
 
@@ -83,4 +85,10 @@
     protected int getNegativeButtonTextId() {
         return R.string.no_thanks;
     }
+
+    @Override
+    public void setInitialA11yFocus() {
+        final View title = getView().findViewById(R.id.signin_title);
+        title.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
index ba3fc17..f1b3fc5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
@@ -11,6 +11,7 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.TextView;
@@ -136,6 +137,11 @@
     }
 
     @Override
+    public void setInitialA11yFocus() {
+        mTitle.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    }
+
+    @Override
     public void setUserVisibleHint(boolean isVisibleToUser) {
         super.setUserVisibleHint(isVisibleToUser);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
index c4949db..a4e9efb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBar.java
@@ -4,15 +4,10 @@
 
 package org.chromium.chrome.browser.omnibox;
 
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
 import android.app.Activity;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
-import android.widget.FrameLayout;
 
 import androidx.annotation.IntDef;
 
@@ -21,6 +16,7 @@
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.WindowDelegate;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
+import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.ntp.FakeboxDelegate;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate;
@@ -36,12 +32,11 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.List;
 
 /**
  * Container that holds the {@link UrlBar} and SSL state related with the current {@link Tab}.
  */
-public interface LocationBar extends UrlBarDelegate, FakeboxDelegate {
+public interface LocationBar extends UrlBarDelegate, FakeboxDelegate, Destroyable {
     /** A means of tracking which mechanism is being used to focus the omnibox. */
     @IntDef({OmniboxFocusReason.OMNIBOX_TAP, OmniboxFocusReason.OMNIBOX_LONG_PRESS,
             OmniboxFocusReason.FAKE_BOX_TAP, OmniboxFocusReason.FAKE_BOX_LONG_PRESS,
@@ -72,11 +67,6 @@
     }
 
     /**
-     * Cleanup resources when this goes out of scope.
-     */
-    void destroy();
-
-    /**
      * Handle all necessary tasks that can be delayed until initialization completes.
      */
     default void onDeferredStartup() {}
@@ -230,276 +220,4 @@
      * @param profileSupplier The supplier of the active profile.
      */
     void setProfileSupplier(ObservableSupplier<Profile> profileSupplier);
-
-    /**
-     * Public methods of LocationBar exclusive to smaller devices.
-     */
-    interface Phone {
-        /**
-         * Returns width of child views before the first view that would be visible when location
-         * bar is focused. The first visible, focused view should be either url bar or status icon.
-         */
-        int getOffsetOfFirstVisibleFocusedView();
-
-        /**
-         * Populates fade animators of status icon for location bar focus change animation.
-         *
-         * @param animators The target list to add animators to.
-         * @param startDelayMs Start delay of fade animation in milliseconds.
-         * @param durationMs Duration of fade animation in milliseconds.
-         * @param targetAlpha Target alpha value.
-         */
-        void populateFadeAnimations(
-                List<Animator> animators, long startDelayMs, long durationMs, float targetAlpha);
-
-        /**
-         * Calculates the offset required for the focused LocationBar to appear as it's still
-         * unfocused so it can animate to a focused state.
-         *
-         * @param hasFocus True if the LocationBar has focus, this will be true between the focus
-         *         animation starting and the unfocus animation starting.
-         * @return The offset for the location bar when showing the DSE/loupe icon.
-         */
-        int getLocationBarOffsetForFocusAnimation(boolean hasFocus);
-
-        /**
-         * Function used to position the URL bar inside the location bar during omnibox animation.
-         *
-         * @param urlExpansionFraction The current expansion progress, 1 is fully focused and 0 is
-         *         completely unfocused.
-         * @param hasFocus True if the LocationBar has focus, this will be true between the focus
-         *         animation starting and the unfocus animation starting.
-         * @return The number of pixels of horizontal translation for the URL bar, used in the
-         *         toolbar animation.
-         */
-        float getUrlBarTranslationXForToolbarAnimation(
-                float urlExpansionFraction, boolean hasFocus);
-
-        /**
-         * Handles any actions to be performed after all other actions triggered by the URL focus
-         * change. This will be called after any animations are performed to transition from one
-         * focus state to the other.
-         *
-         * @param hasFocus Whether the URL field has gained focus.
-         */
-        void finishUrlFocusChange(boolean hasFocus);
-
-        /**
-         * Sets whether the url bar should be focusable.
-         */
-        void setUrlBarFocusable(boolean focusable);
-
-        /**
-         * Returns {@link FrameLayout.LayoutParams} of the LocationBar view.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getLayoutParams()
-         */
-        FrameLayout.LayoutParams getFrameLayoutParams();
-
-        /**
-         * The opacity of the view.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getAlpha()
-         */
-        float getAlpha();
-
-        /**
-         * Bottom position of this view relative to its parent.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getBottom()
-         * @return The bottom of this view, in pixels.
-         */
-        int getBottom();
-
-        /**
-         * Returns the resolved layout direction for this view.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getLayoutDirection()
-         * @return {@link View#LAYOUT_DIRECTION_LTR}, or {@link View#LAYOUT_DIRECTION_RTL}.
-         */
-        int getLayoutDirection();
-
-        /**
-         * Returns the end padding of this view.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getPaddingEnd()
-         * @return The end padding in pixels.
-         */
-        int getPaddingEnd();
-
-        /**
-         * Returns the start padding of this view.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getPaddingStart()
-         * @return The start padding in pixels.
-         */
-        int getPaddingStart();
-
-        /**
-         * Top position of this view relative to its parent.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getTop()
-         * @return The top of this view, in pixels.
-         */
-        int getTop();
-
-        /**
-         * The vertical location of this view relative to its top position, in pixels.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getTranslationY()
-         */
-        float getTranslationY();
-
-        /**
-         * Returns the visibility status for this view.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getVisibility()
-         */
-        int getVisibility();
-
-        /**
-         * Returns true if this view has focus itself, or is the ancestor of the view that has
-         * focus.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#hasFocus()
-         */
-        boolean hasFocus();
-
-        /**
-         * Invalidate the whole view.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#invalidate()
-         */
-        void invalidate();
-
-        /**
-         * Sets the opacity of the view.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#setAlpha(float)
-         */
-        void setAlpha(float alpha);
-
-        /**
-         * Sets the padding.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#setPadding(int, int, int, int)
-         */
-        void setPadding(int left, int top, int right, int bottom);
-
-        /**
-         * Sets the horizontal location of this view relative to its left position.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#setTranslationX(float)
-         */
-        void setTranslationX(float translationX);
-
-        /**
-         * Sets the vertical location of this view relative to its top position.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#setTranslationY(float)
-         */
-        void setTranslationY(float translationY);
-
-        /**
-         * Returns the LocationBar view for use in drawing.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see ViewGroup#drawChild(Canvas, View, long)
-         */
-        View getViewForDrawing();
-    }
-
-    /**
-     * Public methods of LocationBar exclusive to larger devices.
-     */
-    interface Tablet {
-        /**
-         * @param button The {@link View} of the button to hide.
-         * @return An animator to run for the given view when hiding buttons in the unfocused
-         *         location bar. This should also be used to create animators for hiding toolbar
-         *         buttons.
-         */
-        ObjectAnimator createHideButtonAnimator(View button);
-
-        /**
-         * @param button The {@link View} of the button to show.
-         * @return An animator to run for the given view when showing buttons in the unfocused
-         *         location bar. This should also be used to create animators for showing toolbar
-         *         buttons.
-         */
-        ObjectAnimator createShowButtonAnimator(View button);
-
-        /**
-         * Creates animators for hiding buttons in the unfocused location bar. The buttons fade out
-         * while width of the location bar gets larger. There are toolbar buttons that also hide at
-         * the same time, causing the width of the location bar to change.
-         *
-         * @param toolbarStartPaddingDifference The difference in the toolbar's start padding
-         *         between the beginning and end of the animation.
-         * @return A list of animators to run.
-         */
-        List<Animator> getHideButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference);
-
-        /**
-         * Creates animators for showing buttons in the unfocused location bar. The buttons fade in
-         * while width of the location bar gets smaller. There are toolbar buttons that also show at
-         * the same time, causing the width of the location bar to change.
-         *
-         * @param toolbarStartPaddingDifference The difference in the toolbar's start padding
-         *         between the beginning and end of the animation.
-         * @return A list of animators to run.
-         */
-        List<Animator> getShowButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference);
-
-        /**
-         * @param shouldShowButtons Whether buttons should be displayed in the URL bar when it's not
-         *         focused.
-         */
-        void setShouldShowButtonsWhenUnfocused(boolean shouldShowButtons);
-
-        /**
-         * Updates the visibility of the buttons inside the location bar.
-         */
-        void updateButtonVisibility();
-
-        /**
-         * Gets the background drawable.
-         *
-         * <p>TODO(1133482): Hide this View interaction if possible.
-         *
-         * @see View#getBackground()
-         */
-        Drawable getBackground();
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
new file mode 100644
index 0000000..ac6260df2
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinator.java
@@ -0,0 +1,297 @@
+// 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.
+
+package org.chromium.chrome.browser.omnibox;
+
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.Supplier;
+import org.chromium.chrome.browser.ActivityTabProvider;
+import org.chromium.chrome.browser.WindowDelegate;
+import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
+import org.chromium.chrome.browser.lifecycle.Destroyable;
+import org.chromium.chrome.browser.ntp.NewTabPage;
+import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.share.ShareDelegate;
+import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider;
+import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
+import org.chromium.chrome.browser.toolbar.top.ToolbarActionModeCallback;
+import org.chromium.chrome.browser.ui.native_page.NativePage;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+
+import java.util.List;
+
+/**
+ * The public API of the location bar component. Location bar responsibilities are:
+ * <ul>
+ *   <li>Display the current URL.
+ *   <li>Display Status.
+ *   <li>Handle omnibox input.
+ * </ul>
+ *
+ * <p>The coordinator creates and owns elements within this component.
+ */
+public final class LocationBarCoordinator implements LocationBar {
+    /** Identifies coordinators with methods specific to a device type. */
+    public interface SubCoordinator extends Destroyable {}
+
+    private LocationBarLayout mLocationBarLayout;
+    @Nullable
+    private SubCoordinator mSubCoordinator;
+
+    /**
+     * Creates {@link LocationBarCoordinator} and its subcoordinator: {@link
+     * LocationBarCoordinatorPhone} or {@link LocationBarCoordinatorTablet}, depending on the type
+     * of {@code locationBarLayout}.
+     * {@code LocationBarCoordinator} owns the subcoordinator. Destroying the former destroys the
+     * latter.
+     *
+     * @param locationBarLayout Inflated {@link LocationBarPhone} or {@link LocationBarTablet}.
+     *         {@code LocationBarCoordinator} takes ownership and will destroy this object.
+     * @throws IllegalArgumentException if the view is neither {@link LocationBarPhone} nor {@link
+     *         LocationBarTablet}.
+     */
+    public LocationBarCoordinator(View locationBarLayout) {
+        mLocationBarLayout = (LocationBarLayout) locationBarLayout;
+
+        if (locationBarLayout instanceof LocationBarPhone) {
+            mSubCoordinator = new LocationBarCoordinatorPhone((LocationBarPhone) locationBarLayout);
+        } else if (locationBarLayout instanceof LocationBarTablet) {
+            mSubCoordinator =
+                    new LocationBarCoordinatorTablet((LocationBarTablet) locationBarLayout);
+        } else {
+            assert false : "Expected LocationBarPhone or LocationBarTablet, got "
+                           + locationBarLayout.getClass();
+            throw new IllegalArgumentException(locationBarLayout.getClass().toString());
+        }
+    }
+
+    /**
+     * Returns the {@link LocationBarCoordinatorPhone} for this coordinator.
+     *
+     * @throws ClassCastException if this coordinator holds a {@link SubCoordinator} of a different
+     *         type.
+     */
+    @NonNull
+    public LocationBarCoordinatorPhone getPhoneCoordinator() {
+        assert mSubCoordinator != null;
+        return (LocationBarCoordinatorPhone) mSubCoordinator;
+    }
+
+    /**
+     * Returns the {@link LocationBarCoordinatorTablet} for this coordinator.
+     *
+     * @throws ClassCastException if this coordinator holds a {@link SubCoordinator} of a different
+     *         type.
+     */
+    @NonNull
+    public LocationBarCoordinatorTablet getTabletCoordinator() {
+        assert mSubCoordinator != null;
+        return (LocationBarCoordinatorTablet) mSubCoordinator;
+    }
+
+    @Override
+    public void destroy() {
+        if (mSubCoordinator != null) {
+            mSubCoordinator.destroy();
+            mSubCoordinator = null;
+        }
+        if (mLocationBarLayout != null) {
+            mLocationBarLayout.destroy();
+            mLocationBarLayout = null;
+        }
+    }
+
+    @Override
+    public void onDeferredStartup() {
+        mLocationBarLayout.onDeferredStartup();
+    }
+
+    @Override
+    public void onNativeLibraryReady() {
+        mLocationBarLayout.onNativeLibraryReady();
+    }
+
+    @Override
+    public void onTabLoadingNTP(NewTabPage ntp) {
+        mLocationBarLayout.onTabLoadingNTP(ntp);
+    }
+
+    @Override
+    public void updateVisualsForState() {
+        mLocationBarLayout.updateVisualsForState();
+    }
+
+    @Override
+    public void setUrlFocusChangeFraction(float fraction) {
+        mLocationBarLayout.setUrlFocusChangeFraction(fraction);
+    }
+
+    @Override
+    public void setUrlToPageUrl() {
+        mLocationBarLayout.setUrlToPageUrl();
+    }
+
+    @Override
+    public void setTitleToPageTitle() {
+        mLocationBarLayout.setTitleToPageTitle();
+    }
+
+    @Override
+    public void setShowTitle(boolean showTitle) {
+        mLocationBarLayout.setShowTitle(showTitle);
+    }
+
+    @Override
+    public void updateLoadingState(boolean updateUrl) {
+        mLocationBarLayout.updateLoadingState(updateUrl);
+    }
+
+    @Override
+    public void setToolbarDataProvider(ToolbarDataProvider dataProvider) {
+        mLocationBarLayout.setToolbarDataProvider(dataProvider);
+    }
+
+    @Override
+    public void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
+        mLocationBarLayout.setOverviewModeBehavior(overviewModeBehavior);
+    }
+
+    @Override
+    public ToolbarDataProvider getToolbarDataProvider() {
+        return mLocationBarLayout.getToolbarDataProvider();
+    }
+
+    @Override
+    public void initializeControls(WindowDelegate windowDelegate, WindowAndroid windowAndroid,
+            ActivityTabProvider activityTabProvider,
+            Supplier<ModalDialogManager> modalDialogManagerSupplier,
+            Supplier<ShareDelegate> shareDelegateSupplier,
+            IncognitoStateProvider incognitoStateProvider) {
+        mLocationBarLayout.initializeControls(windowDelegate, windowAndroid, activityTabProvider,
+                modalDialogManagerSupplier, shareDelegateSupplier, incognitoStateProvider);
+    }
+
+    @Override
+    public void showUrlBarCursorWithoutFocusAnimations() {
+        mLocationBarLayout.showUrlBarCursorWithoutFocusAnimations();
+    }
+
+    @Override
+    public void selectAll() {
+        mLocationBarLayout.selectAll();
+    }
+
+    @Override
+    public void revertChanges() {
+        mLocationBarLayout.revertChanges();
+    }
+
+    @Override
+    public void updateStatusIcon() {
+        mLocationBarLayout.updateStatusIcon();
+    }
+
+    @Override
+    public View getContainerView() {
+        return mLocationBarLayout.getContainerView();
+    }
+
+    @Override
+    public View getSecurityIconView() {
+        return mLocationBarLayout.getSecurityIconView();
+    }
+
+    @Override
+    public void updateMicButtonState() {
+        mLocationBarLayout.updateMicButtonState();
+    }
+
+    @Override
+    public void setDefaultTextEditActionModeCallback(ToolbarActionModeCallback callback) {
+        mLocationBarLayout.setDefaultTextEditActionModeCallback(callback);
+    }
+
+    @Override
+    public void setUnfocusedWidth(int unfocusedWidth) {
+        mLocationBarLayout.setUnfocusedWidth(unfocusedWidth);
+    }
+
+    @Override
+    public void setProfileSupplier(ObservableSupplier<Profile> profileSupplier) {
+        mLocationBarLayout.setProfileSupplier(profileSupplier);
+    }
+
+    @Nullable
+    @Override
+    public View getViewForUrlBackFocus() {
+        return mLocationBarLayout.getViewForUrlBackFocus();
+    }
+
+    @Override
+    public boolean allowKeyboardLearning() {
+        return mLocationBarLayout.allowKeyboardLearning();
+    }
+
+    @Override
+    public void backKeyPressed() {
+        mLocationBarLayout.backKeyPressed();
+    }
+
+    @Override
+    public boolean shouldForceLTR() {
+        return mLocationBarLayout.shouldForceLTR();
+    }
+
+    @Override
+    public boolean shouldCutCopyVerbatim() {
+        return mLocationBarLayout.shouldCutCopyVerbatim();
+    }
+
+    @Override
+    public void gestureDetected(boolean isLongPress) {
+        mLocationBarLayout.gestureDetected(isLongPress);
+    }
+
+    @Override
+    public void setUrlBarFocus(boolean shouldBeFocused, @Nullable String pastedText, int reason) {
+        mLocationBarLayout.setUrlBarFocus(shouldBeFocused, pastedText, reason);
+    }
+
+    @Override
+    public void performSearchQuery(String query, List<String> searchParams) {
+        mLocationBarLayout.performSearchQuery(query, searchParams);
+    }
+
+    @Override
+    public boolean isUrlBarFocused() {
+        return mLocationBarLayout.isUrlBarFocused();
+    }
+
+    @Override
+    public boolean isCurrentPage(NativePage nativePage) {
+        return mLocationBarLayout.isCurrentPage(nativePage);
+    }
+
+    @Override
+    public VoiceRecognitionHandler getVoiceRecognitionHandler() {
+        return mLocationBarLayout.getVoiceRecognitionHandler();
+    }
+
+    @Override
+    public void addUrlFocusChangeListener(UrlFocusChangeListener listener) {
+        mLocationBarLayout.addUrlFocusChangeListener(listener);
+    }
+
+    @Override
+    public void removeUrlFocusChangeListener(UrlFocusChangeListener listener) {
+        mLocationBarLayout.removeUrlFocusChangeListener(listener);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorPhone.java
new file mode 100644
index 0000000..df2b0e8e
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorPhone.java
@@ -0,0 +1,274 @@
+// 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.
+
+package org.chromium.chrome.browser.omnibox;
+
+import android.animation.Animator;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import java.util.List;
+
+/**
+ * A supplement to {@link LocationBarCoordinator} with methods specific to smaller devices.
+ */
+public class LocationBarCoordinatorPhone implements LocationBarCoordinator.SubCoordinator {
+    private LocationBarPhone mLocationBarPhone;
+
+    public LocationBarCoordinatorPhone(LocationBarPhone phoneLayout) {
+        mLocationBarPhone = phoneLayout;
+    }
+
+    @Override
+    public void destroy() {
+        mLocationBarPhone = null;
+    }
+
+    /**
+     * Returns width of child views before the first view that would be visible when location
+     * bar is focused. The first visible, focused view should be either url bar or status icon.
+     */
+    public int getOffsetOfFirstVisibleFocusedView() {
+        return mLocationBarPhone.getOffsetOfFirstVisibleFocusedView();
+    }
+
+    /**
+     * Populates fade animators of status icon for location bar focus change animation.
+     *
+     * @param animators The target list to add animators to.
+     * @param startDelayMs Start delay of fade animation in milliseconds.
+     * @param durationMs Duration of fade animation in milliseconds.
+     * @param targetAlpha Target alpha value.
+     */
+    public void populateFadeAnimations(
+            List<Animator> animators, long startDelayMs, long durationMs, float targetAlpha) {
+        mLocationBarPhone.populateFadeAnimations(animators, startDelayMs, durationMs, targetAlpha);
+    }
+
+    /**
+     * Calculates the offset required for the focused LocationBar to appear as it's still
+     * unfocused so it can animate to a focused state.
+     *
+     * @param hasFocus True if the LocationBar has focus, this will be true between the focus
+     *         animation starting and the unfocus animation starting.
+     * @return The offset for the location bar when showing the DSE/loupe icon.
+     */
+    public int getLocationBarOffsetForFocusAnimation(boolean hasFocus) {
+        return mLocationBarPhone.getLocationBarOffsetForFocusAnimation(hasFocus);
+    }
+
+    /**
+     * Function used to position the URL bar inside the location bar during omnibox animation.
+     *
+     * @param urlExpansionFraction The current expansion progress, 1 is fully focused and 0 is
+     *         completely unfocused.
+     * @param hasFocus True if the LocationBar has focus, this will be true between the focus
+     *         animation starting and the unfocus animation starting.
+     * @return The number of pixels of horizontal translation for the URL bar, used in the
+     *         toolbar animation.
+     */
+    public float getUrlBarTranslationXForToolbarAnimation(
+            float urlExpansionFraction, boolean hasFocus) {
+        return mLocationBarPhone.getUrlBarTranslationXForToolbarAnimation(
+                urlExpansionFraction, hasFocus);
+    }
+
+    /**
+     * Handles any actions to be performed after all other actions triggered by the URL focus
+     * change. This will be called after any animations are performed to transition from one
+     * focus state to the other.
+     *
+     * @param hasFocus Whether the URL field has gained focus.
+     */
+    public void finishUrlFocusChange(boolean hasFocus) {
+        mLocationBarPhone.finishUrlFocusChange(hasFocus);
+    }
+
+    /** Sets whether the url bar should be focusable. */
+    public void setUrlBarFocusable(boolean focusable) {
+        mLocationBarPhone.setUrlBarFocusable(focusable);
+    }
+
+    /**
+     * Returns {@link FrameLayout.LayoutParams} of the LocationBar view.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getLayoutParams()
+     */
+    public FrameLayout.LayoutParams getFrameLayoutParams() {
+        return mLocationBarPhone.getFrameLayoutParams();
+    }
+
+    /**
+     * The opacity of the view.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getAlpha()
+     */
+    public float getAlpha() {
+        return mLocationBarPhone.getAlpha();
+    }
+
+    /**
+     * Bottom position of this view relative to its parent.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getBottom()
+     * @return The bottom of this view, in pixels.
+     */
+    public int getBottom() {
+        return mLocationBarPhone.getBottom();
+    }
+
+    /**
+     * Returns the resolved layout direction for this view.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getLayoutDirection()
+     * @return {@link View#LAYOUT_DIRECTION_LTR}, or {@link View#LAYOUT_DIRECTION_RTL}.
+     */
+    public int getLayoutDirection() {
+        return mLocationBarPhone.getLayoutDirection();
+    }
+
+    /**
+     * Returns the end padding of this view.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getPaddingEnd()
+     * @return The end padding in pixels.
+     */
+    public int getPaddingEnd() {
+        return mLocationBarPhone.getPaddingEnd();
+    }
+
+    /**
+     * Returns the start padding of this view.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getPaddingStart()
+     * @return The start padding in pixels.
+     */
+    public int getPaddingStart() {
+        return mLocationBarPhone.getPaddingStart();
+    }
+
+    /**
+     * Top position of this view relative to its parent.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getTop()
+     * @return The top of this view, in pixels.
+     */
+    public int getTop() {
+        return mLocationBarPhone.getTop();
+    }
+
+    /**
+     * The vertical location of this view relative to its top position, in pixels.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getTranslationY()
+     */
+    public float getTranslationY() {
+        return mLocationBarPhone.getTranslationY();
+    }
+
+    /**
+     * Returns the visibility status for this view.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getVisibility()
+     */
+    public int getVisibility() {
+        return mLocationBarPhone.getVisibility();
+    }
+
+    /**
+     * Returns true if this view has focus itself, or is the ancestor of the view that has
+     * focus.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#hasFocus()
+     */
+    public boolean hasFocus() {
+        return mLocationBarPhone.hasFocus();
+    }
+
+    /**
+     * Invalidate the whole view.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#invalidate()
+     */
+    public void invalidate() {
+        mLocationBarPhone.invalidate();
+    }
+
+    /**
+     * Sets the opacity of the view.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#setAlpha(float)
+     */
+    public void setAlpha(float alpha) {
+        mLocationBarPhone.setAlpha(alpha);
+    }
+
+    /**
+     * Sets the padding.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#setPadding(int, int, int, int)
+     */
+    public void setPadding(int left, int top, int right, int bottom) {
+        mLocationBarPhone.setPadding(left, top, right, bottom);
+    }
+
+    /**
+     * Sets the horizontal location of this view relative to its left position.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#setTranslationX(float)
+     */
+    public void setTranslationX(float translationX) {
+        mLocationBarPhone.setTranslationX(translationX);
+    }
+
+    /**
+     * Sets the vertical location of this view relative to its top position.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#setTranslationY(float)
+     */
+    public void setTranslationY(float translationY) {
+        mLocationBarPhone.setTranslationY(translationY);
+    }
+
+    /**
+     * Returns the LocationBar view for use in drawing.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see ViewGroup#drawChild(Canvas, View, long)
+     */
+    public View getViewForDrawing() {
+        return mLocationBarPhone;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorTablet.java
new file mode 100644
index 0000000..3f669a2
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorTablet.java
@@ -0,0 +1,97 @@
+// 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.
+
+package org.chromium.chrome.browser.omnibox;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import java.util.List;
+
+/**
+ * A supplement to {@link LocationBarCoordinator} with methods specific to larger devices.
+ */
+public class LocationBarCoordinatorTablet implements LocationBarCoordinator.SubCoordinator {
+    private LocationBarTablet mLocationBarTablet;
+
+    public LocationBarCoordinatorTablet(LocationBarTablet tabletLayout) {
+        mLocationBarTablet = tabletLayout;
+    }
+
+    @Override
+    public void destroy() {
+        mLocationBarTablet = null;
+    }
+
+    /**
+     * @param button The {@link View} of the button to hide.
+     * @return An animator to run for the given view when hiding buttons in the unfocused
+     *         location bar. This should also be used to create animators for hiding toolbar
+     *         buttons.
+     */
+    public ObjectAnimator createHideButtonAnimator(View button) {
+        return mLocationBarTablet.createHideButtonAnimator(button);
+    }
+
+    /**
+     * @param button The {@link View} of the button to show.
+     * @return An animator to run for the given view when showing buttons in the unfocused
+     *         location bar. This should also be used to create animators for showing toolbar
+     *         buttons.
+     */
+    public ObjectAnimator createShowButtonAnimator(View button) {
+        return mLocationBarTablet.createShowButtonAnimator(button);
+    }
+
+    /**
+     * Creates animators for hiding buttons in the unfocused location bar. The buttons fade out
+     * while width of the location bar gets larger. There are toolbar buttons that also hide at
+     * the same time, causing the width of the location bar to change.
+     *
+     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding
+     *         between the beginning and end of the animation.
+     * @return A list of animators to run.
+     */
+    public List<Animator> getHideButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference) {
+        return mLocationBarTablet.getHideButtonsWhenUnfocusedAnimators(
+                toolbarStartPaddingDifference);
+    }
+
+    /**
+     * Creates animators for showing buttons in the unfocused location bar. The buttons fade in
+     * while width of the location bar gets smaller. There are toolbar buttons that also show at
+     * the same time, causing the width of the location bar to change.
+     *
+     * @param toolbarStartPaddingDifference The difference in the toolbar's start padding
+     *         between the beginning and end of the animation.
+     * @return A list of animators to run.
+     */
+    public List<Animator> getShowButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference) {
+        return mLocationBarTablet.getShowButtonsWhenUnfocusedAnimators(
+                toolbarStartPaddingDifference);
+    }
+
+    /** Sets, whether buttons should be displayed in the URL bar when it's not focused. */
+    public void setShouldShowButtonsWhenUnfocused(boolean shouldShowButtons) {
+        mLocationBarTablet.setShouldShowButtonsWhenUnfocused(shouldShowButtons);
+    }
+
+    /** Updates the visibility of the buttons inside the location bar. */
+    public void updateButtonVisibility() {
+        mLocationBarTablet.updateButtonVisibility();
+    }
+
+    /**
+     * Gets the background drawable.
+     *
+     * <p>TODO(1133482): Hide this View interaction if possible.
+     *
+     * @see View#getBackground()
+     */
+    public Drawable getBackground() {
+        return mLocationBarTablet.getBackground();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index e44f1cc..644d3da7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -112,7 +112,8 @@
     protected AutocompleteCoordinator mAutocompleteCoordinator;
 
     protected ToolbarDataProvider mToolbarDataProvider;
-    private ObserverList<UrlFocusChangeListener> mUrlFocusChangeListeners = new ObserverList<>();
+    private final ObserverList<UrlFocusChangeListener> mUrlFocusChangeListeners =
+            new ObserverList<>();
 
     private final List<Runnable> mDeferredNativeRunnables = new ArrayList<Runnable>();
 
@@ -244,6 +245,8 @@
 
     @Override
     public void destroy() {
+        mUrlFocusChangeListeners.clear();
+
         if (mAutocompleteCoordinator != null) {
             removeUrlFocusChangeListener(mAutocompleteCoordinator);
             mAutocompleteCoordinator.destroy();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
index be00425..6c0c794 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java
@@ -24,7 +24,7 @@
 /**
  * A location bar implementation specific for smaller/phone screens.
  */
-public class LocationBarPhone extends LocationBarLayout implements LocationBar.Phone {
+class LocationBarPhone extends LocationBarLayout {
     private static final int ACTION_BUTTON_TOUCH_OVERFLOW_LEFT = 15;
 
     private View mFirstVisibleFocusedView;
@@ -95,7 +95,6 @@
      * @return Width of child views before the first view that would be visible when location bar is
      *         focused. The first visible, focused view should be either url bar or status icon.
      */
-    @Override
     public int getOffsetOfFirstVisibleFocusedView() {
         int visibleWidth = 0;
         for (int i = 0; i < getChildCount(); i++) {
@@ -114,7 +113,6 @@
      * @param durationMs Duration of fade animation in milliseconds.
      * @param targetAlpha Target alpha value.
      */
-    @Override
     public void populateFadeAnimations(
             List<Animator> animators, long startDelayMs, long durationMs, float targetAlpha) {
         for (int i = 0; i < getChildCount(); i++) {
@@ -136,7 +134,6 @@
      *                 animation starting and the unfocus animation starting.
      * @return The offset for the location bar when showing the dse icon.
      */
-    @Override
     public int getLocationBarOffsetForFocusAnimation(boolean hasFocus) {
         if (mStatusCoordinator == null) return 0;
 
@@ -170,7 +167,6 @@
      *                 animation starting and the unfocus animation starting.
      *  @return The X translation for the URL bar, used in the toolbar animation.
      */
-    @Override
     public float getUrlBarTranslationXForToolbarAnimation(
             float urlExpansionPercent, boolean hasFocus) {
         // This will be called before status view is ready.
@@ -282,7 +278,6 @@
         mStatusCoordinator.onUrlAnimationFinished(hasFocus);
     }
 
-    @Override
     public FrameLayout.LayoutParams getFrameLayoutParams() {
         return (FrameLayout.LayoutParams) getLayoutParams();
     }
@@ -338,11 +333,6 @@
         }
     }
 
-    @Override
-    public View getViewForDrawing() {
-        return this;
-    }
-
     /** Update the status visibility according to the current state held in LocationBar. */
     private void updateStatusVisibility() {
         boolean incognito = getToolbarDataProvider().isIncognito();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
index 8b552af..7916b3af 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarTablet.java
@@ -29,7 +29,7 @@
 /**
  * Location bar for tablet form factors.
  */
-public class LocationBarTablet extends LocationBarLayout implements LocationBar.Tablet {
+class LocationBarTablet extends LocationBarLayout {
     private static final long MAX_NTP_KEYBOARD_FOCUS_DURATION_MS = 200;
 
     private static final int ICON_FADE_ANIMATION_DURATION_MS = 150;
@@ -187,7 +187,6 @@
      * @param shouldShowButtons Whether buttons should be displayed in the URL bar when it's not
      *                          focused.
      */
-    @Override
     public void setShouldShowButtonsWhenUnfocused(boolean shouldShowButtons) {
         mShouldShowButtonsWhenUnfocused = shouldShowButtons;
         updateButtonVisibility();
@@ -268,7 +267,6 @@
      * @return An animator to run for the given view when showing buttons in the unfocused location
      *         bar. This should also be used to create animators for showing toolbar buttons.
      */
-    @Override
     public ObjectAnimator createShowButtonAnimator(View button) {
         if (button.getVisibility() != View.VISIBLE) {
             button.setAlpha(0.f);
@@ -285,7 +283,6 @@
      * @return An animator to run for the given view when hiding buttons in the unfocused location
      *         bar. This should also be used to create animators for hiding toolbar buttons.
      */
-    @Override
     public ObjectAnimator createHideButtonAnimator(View button) {
         ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(button, View.ALPHA, 0.f);
         buttonAnimator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
@@ -302,7 +299,6 @@
      *                                      the beginning and end of the animation.
      * @return An ArrayList of animators to run.
      */
-    @Override
     public List<Animator> getShowButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference) {
         mToolbarStartPaddingDifference = toolbarStartPaddingDifference;
 
@@ -357,7 +353,6 @@
      *                                      the beginning and end of the animation.
      * @return An ArrayList of animators to run.
      */
-    @Override
     public List<Animator> getHideButtonsWhenUnfocusedAnimators(int toolbarStartPaddingDifference) {
         mToolbarStartPaddingDifference = toolbarStartPaddingDifference;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountConsistencyPromoAction.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountConsistencyPromoAction.java
index a103016..cb1ab0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountConsistencyPromoAction.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountConsistencyPromoAction.java
@@ -25,6 +25,7 @@
         AccountConsistencyPromoAction.SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT,
         AccountConsistencyPromoAction.SHOWN,
         AccountConsistencyPromoAction.SUPPRESSED_SIGNIN_NOT_ALLOWED,
+        AccountConsistencyPromoAction.SIGNED_IN_WITH_ADDED_ACCOUNT,
 })
 @Retention(RetentionPolicy.SOURCE)
 public @interface AccountConsistencyPromoAction {
@@ -71,5 +72,12 @@
      */
     int SUPPRESSED_SIGNIN_NOT_ALLOWED = 7;
 
-    int MAX = 8;
+    /**
+     * User has added an account and signed in with this account.
+     * When this metric is recorded, we won't record SIGNED_IN_WITH_DEFAULT_ACCOUNT or
+     * SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT.
+     */
+    int SIGNED_IN_WITH_ADDED_ACCOUNT = 8;
+
+    int MAX = 9;
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
index 3a935e6..d8195d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerBottomSheetMediator.java
@@ -41,6 +41,7 @@
     private final AccountsChangeObserver mAccountsChangeObserver = this::onAccountListUpdated;
     private @Nullable String mSelectedAccountName;
     private @Nullable String mDefaultAccountName;
+    private @Nullable String mAddedAccountName;
 
     AccountPickerBottomSheetMediator(Context context, AccountPickerDelegate accountPickerDelegate) {
         mAccountPickerDelegate = accountPickerDelegate;
@@ -53,6 +54,7 @@
 
         mAccountManagerFacade = AccountManagerFacadeProvider.getInstance();
         mAccountManagerFacade.addObserver(mAccountsChangeObserver);
+        mAddedAccountName = null;
         onAccountListUpdated();
     }
 
@@ -80,7 +82,10 @@
     public void addAccount() {
         AccountPickerDelegate.recordAccountConsistencyPromoAction(
                 AccountConsistencyPromoAction.ADD_ACCOUNT);
-        mAccountPickerDelegate.addAccount(accountName -> onAccountSelected(accountName, false));
+        mAccountPickerDelegate.addAccount(accountName -> {
+            mAddedAccountName = accountName;
+            onAccountSelected(accountName, false);
+        });
     }
 
     /**
@@ -206,10 +211,16 @@
 
     private void signIn() {
         mModel.set(AccountPickerBottomSheetProperties.VIEW_STATE, ViewState.SIGNIN_IN_PROGRESS);
-        AccountPickerDelegate.recordAccountConsistencyPromoAction(
-                TextUtils.equals(mSelectedAccountName, mDefaultAccountName)
-                        ? AccountConsistencyPromoAction.SIGNED_IN_WITH_DEFAULT_ACCOUNT
-                        : AccountConsistencyPromoAction.SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT);
+        if (TextUtils.equals(mSelectedAccountName, mAddedAccountName)) {
+            AccountPickerDelegate.recordAccountConsistencyPromoAction(
+                    AccountConsistencyPromoAction.SIGNED_IN_WITH_ADDED_ACCOUNT);
+        } else if (TextUtils.equals(mSelectedAccountName, mDefaultAccountName)) {
+            AccountPickerDelegate.recordAccountConsistencyPromoAction(
+                    AccountConsistencyPromoAction.SIGNED_IN_WITH_DEFAULT_ACCOUNT);
+        } else {
+            AccountPickerDelegate.recordAccountConsistencyPromoAction(
+                    AccountConsistencyPromoAction.SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT);
+        }
         new AsyncTask<String>() {
             @Override
             protected String doInBackground() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 05c711e5..52b370bcc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -901,10 +901,6 @@
             mBottomControlsCoordinator = null;
         }
 
-        if (mLocationBar != null) {
-            mLocationBar.removeUrlFocusChangeListener(this);
-        }
-
         mToolbar.removeUrlExpansionObserver(mActivity.getStatusBarColorController());
         mToolbar.destroy();
 
@@ -921,10 +917,6 @@
         mHandler.removeCallbacksAndMessages(null); // Cancel delayed tasks.
         mBrowserControlsSizer.removeObserver(mBrowserControlsObserver);
         mFullscreenManager.removeObserver(mFullscreenObserver);
-        if (mLocationBar != null) {
-            mLocationBar.removeUrlFocusChangeListener(mLocationBarFocusHandler);
-            mLocationBarFocusHandler = null;
-        }
 
         if (mTabThemeColorProvider != null) mTabThemeColorProvider.removeThemeColorObserver(this);
         if (mAppThemeColorProvider != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
index 9d61264..5368b08a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarLayout.java
@@ -153,8 +153,6 @@
             mThemeColorProvider.removeThemeColorObserver(this);
             mThemeColorProvider = null;
         }
-
-        getLocationBar().destroy();
     }
 
     /**
@@ -377,6 +375,7 @@
     /**
      * @return The {@link ProgressBar} this layout uses.
      */
+    @Nullable
     protected ToolbarProgressBar getProgressBar() {
         return mProgressBar;
     }
@@ -486,6 +485,7 @@
      * @return The name of the publisher of the content if it can be reliably extracted, or null
      *         otherwise.
      */
+    @Nullable
     protected String getContentPublisher() {
         return null;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 6edd3d4e..677d157 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -54,6 +54,7 @@
 import org.chromium.chrome.browser.homepage.HomepageManager;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.LocationBar;
+import org.chromium.chrome.browser.omnibox.LocationBarCoordinator;
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -123,8 +124,7 @@
 
     private TabCountProvider mTabCountProvider;
 
-    protected LocationBar mLocationBar;
-    protected LocationBar.Phone mLocationBarPhone;
+    protected LocationBarCoordinator mLocationBar;
 
     private ViewGroup mToolbarButtonsContainer;
     protected @Nullable ToggleTabStackButton mToggleTabStackButton;
@@ -337,17 +337,11 @@
     public void onFinishInflate() {
         try (TraceEvent te = TraceEvent.scoped("ToolbarPhone.onFinishInflate")) {
             super.onFinishInflate();
-            mLocationBar = (LocationBar) findViewById(R.id.location_bar);
-            mLocationBarPhone = (LocationBar.Phone) mLocationBar;
-
+            mLocationBar = new LocationBarCoordinator(findViewById(R.id.location_bar));
             mToolbarButtonsContainer = (ViewGroup) findViewById(R.id.toolbar_buttons);
-
             mHomeButton = findViewById(R.id.home_button);
-
             mUrlBar = (TextView) findViewById(R.id.url_bar);
-
             mUrlActionContainer = findViewById(R.id.url_action_container);
-
             mToolbarBackground =
                     new ColorDrawable(getToolbarColorForVisualState(VisualState.NORMAL));
 
@@ -369,9 +363,16 @@
 
     @Override
     void destroy() {
-        super.destroy();
-        if (mHomeButton != null) mHomeButton.destroy();
+        if (mHomeButton != null) {
+            mHomeButton.destroy();
+            mHomeButton = null;
+        }
+        if (mLocationBar != null) {
+            mLocationBar.destroy();
+            mLocationBar = null;
+        }
         cancelAnimations();
+        super.destroy();
     }
 
     /**
@@ -384,7 +385,7 @@
         mLocationBarBackground = createModernLocationBarBackground(getResources());
 
         int lateralPadding = res.getDimensionPixelOffset(R.dimen.location_bar_lateral_padding);
-        mLocationBarPhone.setPadding(lateralPadding, 0, lateralPadding, 0);
+        mLocationBar.getPhoneCoordinator().setPadding(lateralPadding, 0, lateralPadding, 0);
 
         mActiveLocationBarBackground = mLocationBarBackground;
     }
@@ -498,7 +499,8 @@
     public boolean onTouchEvent(MotionEvent ev) {
         // Forward touch events to the NTP if the toolbar is moved away but the search box hasn't
         // reached the top of the page yet.
-        if (mNtpSearchBoxTranslation.y < 0 && mLocationBarPhone.getTranslationY() > 0) {
+        if (mNtpSearchBoxTranslation.y < 0
+                && mLocationBar.getPhoneCoordinator().getTranslationY() > 0) {
             NewTabPage newTabPage = getToolbarDataProvider().getNewTabPageForCurrentTab();
 
             // No null check -- the toolbar should not be moved if we are not on an NTP.
@@ -511,8 +513,9 @@
     @Override
     public void onClick(View v) {
         // Don't allow clicks while the omnibox is being focused.
-        if (mLocationBarPhone != null && mLocationBarPhone.hasFocus()) return;
-
+        if (mLocationBar != null && mLocationBar.getPhoneCoordinator().hasFocus()) {
+            return;
+        }
         if (mHomeButton != null && mHomeButton == v) {
             openHomepage();
             if (isNativeLibraryReady()
@@ -594,7 +597,8 @@
         if (mLayoutLocationBarInFocusedMode
                 || (mVisualState == VisualState.NEW_TAB_NORMAL
                         && mTabSwitcherState == STATIC_TAB)) {
-            int priorVisibleWidth = mLocationBarPhone.getOffsetOfFirstVisibleFocusedView();
+            int priorVisibleWidth =
+                    mLocationBar.getPhoneCoordinator().getOffsetOfFirstVisibleFocusedView();
             width = getFocusedLocationBarWidth(containerWidth, priorVisibleWidth);
             leftMargin = getFocusedLocationBarLeftMargin(priorVisibleWidth);
         } else {
@@ -637,7 +641,7 @@
      */
     private int getFocusedLocationBarLeftMargin(int priorVisibleWidth) {
         int baseMargin = mToolbarSidePadding;
-        if (mLocationBarPhone.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+        if (mLocationBar.getPhoneCoordinator().getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
             return baseMargin;
         } else {
             return baseMargin - priorVisibleWidth;
@@ -741,7 +745,8 @@
         }
 
         if (mLocationBarBackground != null
-                && (mLocationBarPhone.getVisibility() == VISIBLE || mTextureCaptureMode)) {
+                && (mLocationBar.getPhoneCoordinator().getVisibility() == VISIBLE
+                        || mTextureCaptureMode)) {
             updateLocationBarBackgroundBounds(mLocationBarBackgroundBounds, mVisualState);
         }
 
@@ -811,9 +816,11 @@
         // - The right most visible location bar child view.
         // - The bottom of the viewport is aligned with the bottom of the location bar.
         // Additional padding can be applied for use during animations.
-        out.set(leftViewPosition, mLocationBarPhone.getTop() + mLocationBarBackgroundVerticalInset,
+        out.set(leftViewPosition,
+                mLocationBar.getPhoneCoordinator().getTop() + mLocationBarBackgroundVerticalInset,
                 rightViewPosition,
-                mLocationBarPhone.getBottom() - mLocationBarBackgroundVerticalInset);
+                mLocationBar.getPhoneCoordinator().getBottom()
+                        - mLocationBarBackgroundVerticalInset);
     }
 
     /**
@@ -951,7 +958,8 @@
      */
     private void updateLocationBarLayoutForExpansionAnimation() {
         TraceEvent.begin("ToolbarPhone.updateLocationBarLayoutForExpansionAnimation");
-        FrameLayout.LayoutParams locationBarLayoutParams = mLocationBarPhone.getFrameLayoutParams();
+        FrameLayout.LayoutParams locationBarLayoutParams =
+                mLocationBar.getPhoneCoordinator().getFrameLayoutParams();
         int currentLeftMargin = locationBarLayoutParams.leftMargin;
         int currentWidth = locationBarLayoutParams.width;
 
@@ -981,10 +989,12 @@
         boolean isIncognito = getToolbarDataProvider().isIncognito();
         if (SearchEngineLogoUtils.shouldShowSearchEngineLogo(isIncognito)) {
             locationBarBaseTranslationX +=
-                    mLocationBarPhone.getLocationBarOffsetForFocusAnimation(hasFocus());
+                    mLocationBar.getPhoneCoordinator().getLocationBarOffsetForFocusAnimation(
+                            hasFocus());
         }
 
-        boolean isLocationBarRtl = mLocationBarPhone.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        boolean isLocationBarRtl =
+                mLocationBar.getPhoneCoordinator().getLayoutDirection() == LAYOUT_DIRECTION_RTL;
         if (isLocationBarRtl) {
             locationBarBaseTranslationX += mUnfocusedLocationBarLayoutWidth - currentWidth;
         }
@@ -1021,14 +1031,15 @@
             locationBarTranslationX = locationBarBaseTranslationX + mLocationBarNtpOffsetLeft;
         }
 
-        mLocationBarPhone.setTranslationX(locationBarTranslationX);
+        mLocationBar.getPhoneCoordinator().setTranslationX(locationBarTranslationX);
 
         // When the dse icon is enabled, the UrlBar needs additional translation to compensate for
         // the additional translation applied to the LocationBar. See comments in
         // LocationBar#getUrlBarTranslationXForToolbarAnimation() for implementation details.
         if (SearchEngineLogoUtils.shouldShowSearchEngineLogo(isIncognito)) {
-            mUrlBar.setTranslationX(mLocationBarPhone.getUrlBarTranslationXForToolbarAnimation(
-                    mUrlExpansionFraction, hasFocus()));
+            mUrlBar.setTranslationX(
+                    mLocationBar.getPhoneCoordinator().getUrlBarTranslationXForToolbarAnimation(
+                            mUrlExpansionFraction, hasFocus()));
         } else if (SearchEngineLogoUtils.isSearchEngineLogoEnabled()) {
             mUrlBar.setTranslationX(0);
         }
@@ -1056,7 +1067,7 @@
 
         // Force an invalidation of the location bar to properly handle the clipping of the URL
         // bar text as a result of the URL action container translations.
-        mLocationBarPhone.invalidate();
+        mLocationBar.getPhoneCoordinator().invalidate();
         invalidate();
         TraceEvent.end("ToolbarPhone.updateLocationBarLayoutForExpansionAnimation");
     }
@@ -1097,7 +1108,7 @@
         mLocationBarBackgroundNtpOffset.setEmpty();
         mActiveLocationBarBackground = mLocationBarBackground;
         mNtpSearchBoxTranslation.set(0, 0);
-        mLocationBarPhone.setTranslationY(0);
+        mLocationBar.getPhoneCoordinator().setTranslationY(0);
         if (!mUrlFocusChangeInProgress) {
             mToolbarButtonsContainer.setTranslationY(0);
             if (mHomeButton != null) mHomeButton.setTranslationY(0);
@@ -1107,12 +1118,12 @@
             mToolbarShadow.setAlpha(mUrlBar.hasFocus() ? 0.f : 1.f);
         }
 
-        mLocationBarPhone.setAlpha(1);
+        mLocationBar.getPhoneCoordinator().setAlpha(1);
         mForceDrawLocationBarBackground = false;
         mLocationBarBackgroundAlpha = 255;
         if (isIncognito()
                 || (mUnfocusedLocationBarUsesTransparentBg && !mUrlFocusChangeInProgress
-                        && !mLocationBarPhone.hasFocus())) {
+                        && !mLocationBar.getPhoneCoordinator().hasFocus())) {
             mLocationBarBackgroundAlpha = LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA;
         }
 
@@ -1141,9 +1152,9 @@
 
         NewTabPage ntp = getToolbarDataProvider().getNewTabPageForCurrentTab();
         ntp.getSearchBoxBounds(mNtpSearchBoxBounds, mNtpSearchBoxTranslation);
-        int locationBarTranslationY =
-                Math.max(0, (mNtpSearchBoxBounds.top - mLocationBarPhone.getTop()));
-        mLocationBarPhone.setTranslationY(locationBarTranslationY);
+        int locationBarTranslationY = Math.max(
+                0, (mNtpSearchBoxBounds.top - mLocationBar.getPhoneCoordinator().getTop()));
+        mLocationBar.getPhoneCoordinator().setTranslationY(locationBarTranslationY);
 
         updateButtonsTranslationY();
 
@@ -1170,7 +1181,7 @@
         mLocationBarBackgroundAlpha = isExpanded ? 255 : 0;
         mForceDrawLocationBarBackground = mLocationBarBackgroundAlpha > 0;
         float relativeAlpha = mLocationBarBackgroundAlpha / 255f;
-        mLocationBarPhone.setAlpha(relativeAlpha);
+        mLocationBar.getPhoneCoordinator().setAlpha(relativeAlpha);
 
         // The search box on the NTP is visible if our omnibox is invisible, and vice-versa.
         ntp.setSearchBoxAlpha(1f - relativeAlpha);
@@ -1235,13 +1246,13 @@
         }
 
         // Draw the location/URL bar.
-        previousAlpha = mLocationBarPhone.getAlpha();
-        mLocationBarPhone.setAlpha(previousAlpha * floatAlpha);
+        previousAlpha = mLocationBar.getPhoneCoordinator().getAlpha();
+        mLocationBar.getPhoneCoordinator().setAlpha(previousAlpha * floatAlpha);
         // If the location bar is now fully transparent, do not bother drawing it.
-        if (mLocationBarPhone.getAlpha() != 0 && isLocationBarCurrentlyShown()) {
+        if (mLocationBar.getPhoneCoordinator().getAlpha() != 0 && isLocationBarCurrentlyShown()) {
             drawLocationBar(canvas, SystemClock.uptimeMillis());
         }
-        mLocationBarPhone.setAlpha(previousAlpha);
+        mLocationBar.getPhoneCoordinator().setAlpha(previousAlpha);
 
         // Translate to draw end toolbar buttons.
         ViewUtils.translateCanvasToView(this, mToolbarButtonsContainer, canvas);
@@ -1318,13 +1329,16 @@
 
     @Override
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        if (child == mLocationBar) return drawLocationBar(canvas, drawingTime);
+        if (mLocationBar != null
+                && child == mLocationBar.getPhoneCoordinator().getViewForDrawing()) {
+            return drawLocationBar(canvas, drawingTime);
+        }
         boolean clipped = false;
 
         if (mLocationBarBackground != null) {
             canvas.save();
 
-            int translationY = (int) mLocationBarPhone.getTranslationY();
+            int translationY = (int) mLocationBar.getPhoneCoordinator().getTranslationY();
             int clipTop = mLocationBarBackgroundBounds.top + translationY;
             if (mUrlExpansionFraction != 0f && clipTop < child.getBottom()) {
                 // For other child views, use the inverse clipping of the URL viewport.
@@ -1400,6 +1414,11 @@
             float locationBarClipBottom =
                     mLocationBarBackgroundBounds.bottom + mLocationBarBackgroundNtpOffset.bottom;
 
+            final int locationBarPaddingStart =
+                    mLocationBar.getPhoneCoordinator().getPaddingStart();
+            final int locationBarPaddingEnd = mLocationBar.getPhoneCoordinator().getPaddingEnd();
+            final int locationBarDirection =
+                    mLocationBar.getPhoneCoordinator().getLayoutDirection();
             // When unexpanded, the location bar's visible content boundaries are inset from the
             // viewport used to draw the background.  During expansion transitions, compensation
             // is applied to increase the clip regions such that when the location bar converts
@@ -1417,17 +1436,17 @@
                 // When the defocus animation is running, the location bar padding needs to be
                 // subtracted from the clip bounds so that the location bar text width in the last
                 // frame of the animation matches the text width of the unfocused location bar.
-                if (mLocationBarPhone.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
-                    locationBarClipLeft += mLocationBarPhone.getPaddingStart() * remainingFraction;
+                if (locationBarDirection == LAYOUT_DIRECTION_RTL) {
+                    locationBarClipLeft += locationBarPaddingStart * remainingFraction;
                 } else {
-                    locationBarClipRight -= mLocationBarPhone.getPaddingEnd() * remainingFraction;
+                    locationBarClipRight -= locationBarPaddingEnd * remainingFraction;
                 }
             }
             if (mOptionalButtonAnimationRunning) {
-                if (mLocationBarPhone.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
-                    locationBarClipLeft += mLocationBarPhone.getPaddingStart();
+                if (locationBarDirection == LAYOUT_DIRECTION_RTL) {
+                    locationBarClipLeft += locationBarPaddingStart;
                 } else {
-                    locationBarClipRight -= mLocationBarPhone.getPaddingEnd();
+                    locationBarClipRight -= locationBarPaddingEnd;
                 }
             }
 
@@ -1435,10 +1454,10 @@
             // omnibox background when animating in.
             if (SearchEngineLogoUtils.shouldShowSearchEngineLogo(isIncognito())
                     && isLocationBarShownInNTP() && urlHasFocus() && mUrlFocusChangeInProgress) {
-                if (mLocationBarPhone.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
-                    locationBarClipRight -= mLocationBarPhone.getPaddingStart();
+                if (locationBarDirection == LAYOUT_DIRECTION_RTL) {
+                    locationBarClipRight -= locationBarPaddingStart;
                 } else {
-                    locationBarClipLeft += mLocationBarPhone.getPaddingStart();
+                    locationBarClipLeft += locationBarPaddingStart;
                 }
             }
 
@@ -1449,8 +1468,8 @@
         }
 
         // TODO(1133482): Hide this View interaction if possible.
-        boolean retVal =
-                super.drawChild(canvas, mLocationBarPhone.getViewForDrawing(), drawingTime);
+        boolean retVal = super.drawChild(
+                canvas, mLocationBar.getPhoneCoordinator().getViewForDrawing(), drawingTime);
 
         if (clipped) canvas.restore();
         TraceEvent.end("ToolbarPhone.drawLocationBar");
@@ -1462,7 +1481,8 @@
      *         {@link #drawLocationBar(Canvas, long)}.
      */
     private boolean shouldDrawLocationBarBackground() {
-        return (mLocationBarPhone.getAlpha() > 0 || mForceDrawLocationBarBackground)
+        return (mLocationBar.getPhoneCoordinator().getAlpha() > 0
+                       || mForceDrawLocationBarBackground)
                 && !mTextureCaptureMode;
     }
 
@@ -1782,7 +1802,7 @@
         // crbug.com/974745.
         requestLayout();
 
-        mLocationBarPhone.setUrlBarFocusable(false);
+        mLocationBar.getPhoneCoordinator().setUrlBarFocusable(false);
 
         finishAnimations();
 
@@ -1865,7 +1885,7 @@
 
         // Detect what was being transitioned from and set the new state appropriately.
         if (mTabSwitcherState == EXITING_TAB_SWITCHER) {
-            mLocationBarPhone.setUrlBarFocusable(true);
+            mLocationBar.getPhoneCoordinator().setUrlBarFocusable(true);
             mTabSwitcherState = STATIC_TAB;
             updateVisualsForLocationBarState();
         }
@@ -1957,7 +1977,7 @@
         animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
         animators.add(animator);
 
-        mLocationBarPhone.populateFadeAnimations(
+        mLocationBar.getPhoneCoordinator().populateFadeAnimations(
                 animators, 0, URL_FOCUS_CHANGE_ANIMATION_DURATION_MS, 0);
 
         float density = getContext().getResources().getDisplayMetrics().density;
@@ -2047,7 +2067,7 @@
             animators.add(animator);
         }
 
-        mLocationBarPhone.populateFadeAnimations(
+        mLocationBar.getPhoneCoordinator().populateFadeAnimations(
                 animators, URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS, URL_CLEAR_FOCUS_MENU_DELAY_MS, 1);
 
         if (isLocationBarShownInNTP() && mNtpSearchBoxScrollFraction == 0f) return;
@@ -2117,7 +2137,7 @@
                     mLayoutLocationBarInFocusedMode = false;
                     requestLayout();
                 }
-                mLocationBarPhone.finishUrlFocusChange(hasFocus);
+                mLocationBar.getPhoneCoordinator().finishUrlFocusChange(hasFocus);
                 mUrlFocusChangeInProgress = false;
 
                 if (getToolbarDataProvider().shouldShowLocationBarInOverviewMode()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index f226385..381f15f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -32,6 +32,7 @@
 import org.chromium.chrome.browser.homepage.HomepageManager;
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.omnibox.LocationBar;
+import org.chromium.chrome.browser.omnibox.LocationBarCoordinator;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -84,8 +85,7 @@
     private NavigationPopup mNavigationPopup;
 
     private Boolean mIsIncognito;
-    private LocationBar mLocationBar;
-    private LocationBar.Tablet mLocationBarTablet;
+    private LocationBarCoordinator mLocationBar;
 
     private final int mStartPaddingWithButtons;
     private final int mStartPaddingWithoutButtons;
@@ -110,9 +110,7 @@
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
-        mLocationBar = (LocationBar) findViewById(R.id.location_bar);
-        mLocationBarTablet = (LocationBar.Tablet) mLocationBar;
-
+        mLocationBar = new LocationBarCoordinator(findViewById(R.id.location_bar));
         mHomeButton = findViewById(R.id.home_button);
         mBackButton = findViewById(R.id.back_button);
         mForwardButton = findViewById(R.id.forward_button);
@@ -151,8 +149,15 @@
 
     @Override
     void destroy() {
+        if (mHomeButton != null) {
+            mHomeButton.destroy();
+            mHomeButton = null;
+        }
+        if (mLocationBar != null) {
+            mLocationBar.destroy();
+            mLocationBar = null;
+        }
         super.destroy();
-        mHomeButton.destroy();
     }
 
     /**
@@ -392,7 +397,8 @@
         setBackgroundColor(color);
         final int textBoxColor = ToolbarColors.getTextBoxColorForToolbarBackgroundInNonNativePage(
                 getResources(), color, isIncognito());
-        mLocationBarTablet.getBackground().setColorFilter(textBoxColor, PorterDuff.Mode.SRC_IN);
+        mLocationBar.getTabletCoordinator().getBackground().setColorFilter(
+                textBoxColor, PorterDuff.Mode.SRC_IN);
 
         mLocationBar.updateVisualsForState();
     }
@@ -429,7 +435,7 @@
 
     @Override
     void updateButtonVisibility() {
-        mLocationBarTablet.updateButtonVisibility();
+        mLocationBar.getTabletCoordinator().updateButtonVisibility();
     }
 
     @Override
@@ -617,7 +623,7 @@
             for (ImageButton button : mToolbarButtons) {
                 button.setVisibility(visible ? View.VISIBLE : View.GONE);
             }
-            mLocationBarTablet.setShouldShowButtonsWhenUnfocused(visible);
+            mLocationBar.getTabletCoordinator().setShouldShowButtonsWhenUnfocused(visible);
             setStartPaddingBasedOnButtonVisibility(visible);
         }
     }
@@ -658,11 +664,11 @@
 
         // Create animators for all of the toolbar buttons.
         for (ImageButton button : mToolbarButtons) {
-            animators.add(mLocationBarTablet.createShowButtonAnimator(button));
+            animators.add(mLocationBar.getTabletCoordinator().createShowButtonAnimator(button));
         }
 
         // Add animators for location bar.
-        animators.addAll(mLocationBarTablet.getShowButtonsWhenUnfocusedAnimators(
+        animators.addAll(mLocationBar.getTabletCoordinator().getShowButtonsWhenUnfocusedAnimators(
                 getStartPaddingDifferenceForButtonVisibilityAnimation()));
 
         AnimatorSet set = new AnimatorSet();
@@ -693,11 +699,11 @@
 
         // Create animators for all of the toolbar buttons.
         for (ImageButton button : mToolbarButtons) {
-            animators.add(mLocationBarTablet.createHideButtonAnimator(button));
+            animators.add(mLocationBar.getTabletCoordinator().createHideButtonAnimator(button));
         }
 
         // Add animators for location bar.
-        animators.addAll(mLocationBarTablet.getHideButtonsWhenUnfocusedAnimators(
+        animators.addAll(mLocationBar.getTabletCoordinator().getHideButtonsWhenUnfocusedAnimators(
                 getStartPaddingDifferenceForButtonVisibilityAnimation()));
 
         AnimatorSet set = new AnimatorSet();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
index 67118bb1..64544c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarCoordinator.java
@@ -302,6 +302,7 @@
         return mMenuButtonCoordinator.getMenuButton();
     }
 
+    @Nullable
     @Override
     public ToolbarProgressBar getProgressBar() {
         return mToolbarLayout.getProgressBar();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sms/README.md b/chrome/android/javatests/src/org/chromium/chrome/browser/sms/README.md
index b3f08a9..3d745acc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sms/README.md
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sms/README.md
@@ -1,3 +1,3 @@
-# SMS Receiver API
+# WebOTP Service API
 
-This directory contains test code for the android specific implementation of the SMS Receiver API user interface. For more details, refer to [this README file](https://cs.chromium.org/chromium/src/content/browser/sms/README.md).
+This directory contains test code for the android specific implementation of the WebOTP API user interface. For more details, refer to [this README file](https://cs.chromium.org/chromium/src/content/browser/sms/README.md).
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sms/WebOTPServiceInfoBarTest.java
similarity index 80%
rename from chrome/android/javatests/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBarTest.java
rename to chrome/android/javatests/src/org/chromium/chrome/browser/sms/WebOTPServiceInfoBarTest.java
index b3e4523..b150261 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sms/SmsReceiverInfoBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sms/WebOTPServiceInfoBarTest.java
@@ -24,18 +24,18 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.InfoBarUtil;
-import org.chromium.components.browser_ui.sms.SmsReceiverInfoBar;
-import org.chromium.components.browser_ui.sms.SmsReceiverUma;
+import org.chromium.components.browser_ui.sms.WebOTPServiceInfoBar;
+import org.chromium.components.browser_ui.sms.WebOTPServiceUma;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 
 /**
- * Tests for the SmsReceiverInfoBar class.
+ * Tests for the WebOTPServiceInfoBar class.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class SmsReceiverInfoBarTest {
+public class WebOTPServiceInfoBarTest {
     @Rule
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
@@ -50,10 +50,10 @@
         mActivity = mActivityTestRule.getActivity();
     }
 
-    private SmsReceiverInfoBar createInfoBar() {
+    private WebOTPServiceInfoBar createInfoBar() {
         return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
             Tab tab = mActivity.getActivityTab();
-            SmsReceiverInfoBar infoBar = SmsReceiverInfoBar.create(
+            WebOTPServiceInfoBar infoBar = WebOTPServiceInfoBar.create(
                     mActivity.getWindowAndroid(), /*enumeratedIconId=*/0, "title", "message", "ok");
             InfoBarContainer.get(tab).addInfoBarForTesting(infoBar);
             return infoBar;
@@ -73,32 +73,32 @@
     @MediumTest
     @Feature({"InfoBars", "UiCatalogue"})
     public void testSmsInfoBarOk() {
-        SmsReceiverInfoBar infoBar = createInfoBar();
+        WebOTPServiceInfoBar infoBar = createInfoBar();
 
         Assert.assertFalse(InfoBarUtil.hasSecondaryButton(infoBar));
 
         // Click primary button.
         Assert.assertTrue(InfoBarUtil.clickPrimaryButton(infoBar));
 
-        assertHistogramRecordedCount(INFOBAR_HISTOGRAM, SmsReceiverUma.InfobarAction.SHOWN, 1);
+        assertHistogramRecordedCount(INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN, 1);
         assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM, SmsReceiverUma.InfobarAction.KEYBOARD_DISMISSED, 0);
+                INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED, 0);
     }
 
     @Test
     @MediumTest
     @Feature({"InfoBars", "UiCatalogue"})
     public void testSmsInfoBarClose() {
-        SmsReceiverInfoBar infoBar = createInfoBar();
+        WebOTPServiceInfoBar infoBar = createInfoBar();
 
         Assert.assertFalse(InfoBarUtil.hasSecondaryButton(infoBar));
 
         // Close infobar.
         Assert.assertTrue(InfoBarUtil.clickCloseButton(infoBar));
 
-        assertHistogramRecordedCount(INFOBAR_HISTOGRAM, SmsReceiverUma.InfobarAction.SHOWN, 1);
+        assertHistogramRecordedCount(INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN, 1);
         assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM, SmsReceiverUma.InfobarAction.KEYBOARD_DISMISSED, 0);
+                INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED, 0);
     }
 
     @Test
@@ -119,15 +119,15 @@
         CriteriaHelper.pollUiThread(
                 () -> keyboardVisibilityDelegate.isKeyboardShowing(mActivity, editText));
 
-        SmsReceiverInfoBar infoBar = createInfoBar();
+        WebOTPServiceInfoBar infoBar = createInfoBar();
 
         // Keyboard is hidden after info bar is created and shown.
         CriteriaHelper.pollUiThread(
                 () -> !keyboardVisibilityDelegate.isKeyboardShowing(mActivity, editText));
 
-        assertHistogramRecordedCount(INFOBAR_HISTOGRAM, SmsReceiverUma.InfobarAction.SHOWN, 1);
+        assertHistogramRecordedCount(INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN, 1);
         assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM, SmsReceiverUma.InfobarAction.KEYBOARD_DISMISSED, 1);
+                INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED, 1);
         assertHistogramRecordedCount(TIME_CANCEL_ON_KEYBOARD_DISMISSAL_HISTOGRAM, 0);
     }
 
@@ -149,7 +149,7 @@
         CriteriaHelper.pollUiThread(
                 () -> keyboardVisibilityDelegate.isKeyboardShowing(mActivity, editText));
 
-        SmsReceiverInfoBar infoBar = createInfoBar();
+        WebOTPServiceInfoBar infoBar = createInfoBar();
 
         // Keyboard is hidden after info bar is created and shown.
         CriteriaHelper.pollUiThread(
@@ -158,9 +158,9 @@
         // Close info bar.
         InfoBarUtil.clickCloseButton(infoBar);
 
-        assertHistogramRecordedCount(INFOBAR_HISTOGRAM, SmsReceiverUma.InfobarAction.SHOWN, 1);
+        assertHistogramRecordedCount(INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN, 1);
         assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM, SmsReceiverUma.InfobarAction.KEYBOARD_DISMISSED, 1);
+                INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED, 1);
         assertHistogramRecordedCount(TIME_CANCEL_ON_KEYBOARD_DISMISSAL_HISTOGRAM, 1);
     }
 }
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index f1fe09d..7e346f1 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -281,7 +281,7 @@
     Languages for apps and websites
   </message>
   <message name="IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION" desc="Description for section that lets users choose the language for apps and websites. The web content languages will be served in the order of the list.">
-    Apps and websites that support multiple languages will use the first supported language from this list. Language preferences sync to Chrome browser. <ph name="BEGIN_LINK_LEARN_MORE">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK_LEARN_MORE">&lt;/a&gt;</ph>
+    Apps and websites that are available in multiple languages will use the first supported language from this list. These preferences are synced with your browser settings. <ph name="BEGIN_LINK_LEARN_MORE">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK_LEARN_MORE">&lt;/a&gt;</ph>
   </message>
   <message name="IDS_OS_SETTINGS_LANGUAGES_TRANSLATE_TARGET_LABEL" desc="The label of the toggle that enables the prompt to translate a page to users.">
     Language used when translating pages
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION.png.sha1
index 09614ff..88add76 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION.png.sha1
@@ -1 +1 @@
-1b2aec8cf7557affd2d82a3357d2dbde92311c1d
\ No newline at end of file
+300b1de537007bb5a5e650b54417374d864bd258
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 694a5c7b..5ccc937 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2575,10 +2575,9 @@
      kOsLinux | kOsCrOS | kOsWin | kOsAndroid,
      ENABLE_DISABLE_VALUE_TYPE(switches::kEnableSmoothScrolling,
                                switches::kDisableSmoothScrolling)},
-    {"sms-receiver-cross-device",
-     flag_descriptions::kSmsReceiverCrossDeviceName,
-     flag_descriptions::kSmsReceiverCrossDeviceDescription, kOsAll,
-     FEATURE_VALUE_TYPE(kSmsReceiverCrossDevice)},
+    {"sms-receiver-cross-device", flag_descriptions::kWebOTPCrossDeviceName,
+     flag_descriptions::kWebOTPCrossDeviceDescription, kOsAll,
+     FEATURE_VALUE_TYPE(kWebOTPCrossDevice)},
     {"fractional-scroll-offsets",
      flag_descriptions::kFractionalScrollOffsetsName,
      flag_descriptions::kFractionalScrollOffsetsDescription, kOsAll,
@@ -4044,10 +4043,6 @@
      kOsAll,
      FEATURE_VALUE_TYPE(omnibox::kSpeculativeServiceWorkerStartOnQueryInput)},
 
-    {"enable-service-worker-on-ui", flag_descriptions::kServiceWorkerOnUIName,
-     flag_descriptions::kServiceWorkerOnUIDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kServiceWorkerOnUI)},
-
 #if defined(OS_CHROMEOS)
     {"scheduler-configuration", flag_descriptions::kSchedulerConfigurationName,
      flag_descriptions::kSchedulerConfigurationDescription, kOsCrOS,
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 95f8404..1db984c 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -105,7 +105,6 @@
 #include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
 #include "services/network/public/cpp/features.h"
 #include "third_party/blink/public/common/input/web_mouse_event.h"
-#include "ui/accessibility/ax_action_data.h"
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/compositor_observer.h"
 #include "ui/display/display_switches.h"
@@ -3740,41 +3739,6 @@
             node_data.GetStringAttribute(ax::mojom::StringAttribute::kName));
 }
 
-IN_PROC_BROWSER_TEST_F(WebViewAccessibilityTest, FocusActionAccessibility) {
-  LoadAppWithGuest("web_view/focus_accessibility");
-  content::WebContents* web_contents = GetFirstAppWindowWebContents();
-  content::EnableAccessibilityForWebContents(web_contents);
-  content::WebContents* guest_web_contents = GetGuestWebContents();
-  content::EnableAccessibilityForWebContents(guest_web_contents);
-
-  // Wait for focus to land on the "root web area" role, representing
-  // focus on the main document itself.
-  while (content::GetFocusedAccessibilityNodeInfo(web_contents).role !=
-         ax::mojom::Role::kRootWebArea) {
-    content::WaitForAccessibilityFocusChange();
-  }
-
-  // Find the button in the guest web contents and perform a focus action.
-  content::FindAccessibilityNodeCriteria find_criteria;
-  find_criteria.role = ax::mojom::Role::kButton;
-  ui::AXPlatformNodeDelegate* button =
-      content::FindAccessibilityNode(guest_web_contents, find_criteria);
-  ASSERT_TRUE(button);
-
-  ui::AXActionData data;
-  data.action = ax::mojom::Action::kFocus;
-  button->AccessibilityPerformAction(data);
-
-  content::WaitForAccessibilityFocusChange();
-
-  // Verify that accessibility focus moved to the correct node.
-  ui::AXNodeData node_data =
-      content::GetFocusedAccessibilityNodeInfo(web_contents);
-  EXPECT_EQ(node_data.role, ax::mojom::Role::kButton);
-  EXPECT_EQ("Guest button",
-            node_data.GetStringAttribute(ax::mojom::StringAttribute::kName));
-}
-
 class WebContentsAccessibilityEventWatcher
     : public content::WebContentsObserver {
  public:
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 9e233a4..a9df3a9 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -917,6 +917,8 @@
     "child_accounts/event_based_status_reporting_service_factory.h",
     "child_accounts/family_features.cc",
     "child_accounts/family_features.h",
+    "child_accounts/family_user_app_metrics.cc",
+    "child_accounts/family_user_app_metrics.h",
     "child_accounts/family_user_metrics_service.cc",
     "child_accounts/family_user_metrics_service.h",
     "child_accounts/family_user_metrics_service_factory.cc",
@@ -3254,6 +3256,7 @@
     "certificate_provider/certificate_provider_service_unittest.cc",
     "child_accounts/child_user_service_unittest.cc",
     "child_accounts/event_based_status_reporting_service_unittest.cc",
+    "child_accounts/family_user_app_metrics_unittest.cc",
     "child_accounts/family_user_metrics_service_unittest.cc",
     "child_accounts/family_user_session_metrics_unittest.cc",
     "child_accounts/parent_access_code/authenticator_unittest.cc",
diff --git a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
index 4f958dc5..f90402e 100644
--- a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
+++ b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
@@ -100,7 +100,7 @@
   bool IsAccountWithNonDummyTokenPresentInAccountManager(
       const AccountManager::AccountKey& account) const {
     return base::Contains(account_manager_accounts_, account) &&
-           !account_manager_->HasDummyGaiaToken(account);
+           !account_manager_->HasDummyGaiaTokenSync(account);
   }
 
   bool IsAccountManagerEmpty() const {
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
index 5315d67..6a09638 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.cc
@@ -261,9 +261,9 @@
 
   for (const char* const pref_name : kCaptionStylePrefsToObserve) {
     pref_change_registrar_->Add(
-        pref_name,
-        base::Bind(&ArcAccessibilityHelperBridge::UpdateCaptionSettings,
-                   base::Unretained(this)));
+        pref_name, base::BindRepeating(
+                       &ArcAccessibilityHelperBridge::UpdateCaptionSettings,
+                       base::Unretained(this)));
   }
 
   arc_bridge_service_->accessibility_helper()->SetHost(this);
diff --git a/chrome/browser/chromeos/arc/cast_receiver/arc_cast_receiver_service.cc b/chrome/browser/chromeos/arc/cast_receiver/arc_cast_receiver_service.cc
index f7bc06d..f1ccd20 100644
--- a/chrome/browser/chromeos/arc/cast_receiver/arc_cast_receiver_service.cc
+++ b/chrome/browser/chromeos/arc/cast_receiver/arc_cast_receiver_service.cc
@@ -66,14 +66,15 @@
   // because we own |pref_change_registrar_|.
   pref_change_registrar_->Add(
       prefs::kCastReceiverEnabled,
-      base::Bind(&ArcCastReceiverService::OnCastReceiverEnabledChanged,
-                 base::Unretained(this)));
+      base::BindRepeating(&ArcCastReceiverService::OnCastReceiverEnabledChanged,
+                          base::Unretained(this)));
 
   receiver_name_subscription_ =
       chromeos::CrosSettings::Get()->AddSettingsObserver(
           chromeos::kCastReceiverName,
-          base::Bind(&ArcCastReceiverService::OnCastReceiverNameChanged,
-                     base::Unretained(this)));
+          base::BindRepeating(
+              &ArcCastReceiverService::OnCastReceiverNameChanged,
+              base::Unretained(this)));
 }
 
 ArcCastReceiverService::~ArcCastReceiverService() {
diff --git a/chrome/browser/chromeos/arc/file_system_watcher/arc_file_system_watcher_service.cc b/chrome/browser/chromeos/arc/file_system_watcher/arc_file_system_watcher_service.cc
index b1fcc08d..68aa455 100644
--- a/chrome/browser/chromeos/arc/file_system_watcher/arc_file_system_watcher_service.cc
+++ b/chrome/browser/chromeos/arc/file_system_watcher/arc_file_system_watcher_service.cc
@@ -258,8 +258,8 @@
   watcher_ = std::make_unique<base::FilePathWatcher>();
   // On Linux, base::FilePathWatcher::Watch() always returns true.
   watcher_->Watch(cros_dir_, true,
-                  base::Bind(&FileSystemWatcher::OnFilePathChanged,
-                             weak_ptr_factory_.GetWeakPtr()));
+                  base::BindRepeating(&FileSystemWatcher::OnFilePathChanged,
+                                      weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ArcFileSystemWatcherService::FileSystemWatcher::OnFilePathChanged(
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root_unittest.cc
index c5e3c4d3..5f918d2 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_root_unittest.cc
@@ -1168,7 +1168,7 @@
 
 TEST_F(ArcDocumentsProviderRootTest, WatchChanged) {
   int num_called = 0;
-  auto watcher_callback = base::Bind(
+  auto watcher_callback = base::BindRepeating(
       [](int* num_called, ChangeType type) {
         EXPECT_EQ(ChangeType::CHANGED, type);
         ++(*num_called);
@@ -1218,7 +1218,7 @@
 
 TEST_F(ArcDocumentsProviderRootTest, WatchDeleted) {
   int num_called = 0;
-  auto watcher_callback = base::Bind(
+  auto watcher_callback = base::BindRepeating(
       [](int* num_called, ChangeType type) {
         EXPECT_EQ(ChangeType::DELETED, type);
         ++(*num_called);
diff --git a/chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.cc b/chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.cc
index 947f4fc..0ef8227c 100644
--- a/chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.cc
+++ b/chrome/browser/chromeos/arc/optin/arc_optin_preference_handler.cc
@@ -27,19 +27,21 @@
 void ArcOptInPreferenceHandler::Start() {
   reporting_consent_subscription_ =
       chromeos::StatsReportingController::Get()->AddObserver(
-          base::Bind(&ArcOptInPreferenceHandler::OnMetricsPreferenceChanged,
-                     base::Unretained(this)));
+          base::BindRepeating(
+              &ArcOptInPreferenceHandler::OnMetricsPreferenceChanged,
+              base::Unretained(this)));
 
   pref_change_registrar_.Init(pref_service_);
   pref_change_registrar_.Add(
       prefs::kArcBackupRestoreEnabled,
-      base::Bind(
+      base::BindRepeating(
           &ArcOptInPreferenceHandler::OnBackupAndRestorePreferenceChanged,
           base::Unretained(this)));
   pref_change_registrar_.Add(
       prefs::kArcLocationServiceEnabled,
-      base::Bind(&ArcOptInPreferenceHandler::OnLocationServicePreferenceChanged,
-                 base::Unretained(this)));
+      base::BindRepeating(
+          &ArcOptInPreferenceHandler::OnLocationServicePreferenceChanged,
+          base::Unretained(this)));
 
   // Send current state.
   SendMetricsMode();
diff --git a/chrome/browser/chromeos/arc/session/arc_play_store_enabled_preference_handler.cc b/chrome/browser/chromeos/arc/session/arc_play_store_enabled_preference_handler.cc
index cc91520..9394b43 100644
--- a/chrome/browser/chromeos/arc/session/arc_play_store_enabled_preference_handler.cc
+++ b/chrome/browser/chromeos/arc/session/arc_play_store_enabled_preference_handler.cc
@@ -51,8 +51,9 @@
   pref_change_registrar_.Init(profile_->GetPrefs());
   pref_change_registrar_.Add(
       prefs::kArcEnabled,
-      base::Bind(&ArcPlayStoreEnabledPreferenceHandler::OnPreferenceChanged,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindRepeating(
+          &ArcPlayStoreEnabledPreferenceHandler::OnPreferenceChanged,
+          weak_ptr_factory_.GetWeakPtr()));
 
   const bool is_play_store_enabled = IsArcPlayStoreEnabledForProfile(profile_);
   VLOG(1) << "Start observing Google Play Store enabled preference. "
diff --git a/chrome/browser/chromeos/child_accounts/family_user_app_metrics.cc b/chrome/browser/chromeos/child_accounts/family_user_app_metrics.cc
new file mode 100644
index 0000000..20ba950
--- /dev/null
+++ b/chrome/browser/chromeos/child_accounts/family_user_app_metrics.cc
@@ -0,0 +1,156 @@
+// 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/chromeos/child_accounts/family_user_app_metrics.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/time/time.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/manifest.h"
+
+namespace chromeos {
+
+namespace {
+constexpr base::TimeDelta k28Days = base::TimeDelta::FromDays(28);
+}  // namespace
+
+// static
+// UMA metrics for a snapshot count of installed and enabled extensions for a
+// given family user.
+const char FamilyUserAppMetrics::kInstalledExtensionsCountHistogramName[] =
+    "FamilyUser.InstalledExtensionsCount";
+const char FamilyUserAppMetrics::kEnabledExtensionsCountHistogramName[] =
+    "FamilyUser.EnabledExtensionsCount";
+
+// UMA metrics for a snapshot count of installed apps for a given family user.
+const char FamilyUserAppMetrics::kOtherAppsCountHistogramName[] =
+    "FamilyUser.OtherAppsCount";
+const char FamilyUserAppMetrics::kArcAppsCountHistogramName[] =
+    "FamilyUser.ArcAppsCount";
+const char FamilyUserAppMetrics::kBorealisAppsCountHistogramName[] =
+    "FamilyUser.BorealisAppsCount";
+const char FamilyUserAppMetrics::kCrostiniAppsCountHistogramName[] =
+    "FamilyUser.CrostiniAppsCount";
+const char FamilyUserAppMetrics::kExtensionAppsCountHistogramName[] =
+    "FamilyUser.ExtensionAppsCount";
+const char FamilyUserAppMetrics::kWebAppsCountHistogramName[] =
+    "FamilyUser.WebAppsCount";
+// Sum of the above.
+const char FamilyUserAppMetrics::kTotalAppsCountHistogramName[] =
+    "FamilyUser.TotalAppsCount";
+
+FamilyUserAppMetrics::FamilyUserAppMetrics(Profile* profile)
+    : extension_registry_(extensions::ExtensionRegistry::Get(profile)),
+      app_registry_(&apps::AppServiceProxyFactory::GetForProfile(profile)
+                         ->AppRegistryCache()) {
+  DCHECK(extension_registry_);
+  DCHECK(app_registry_);
+}
+
+FamilyUserAppMetrics::~FamilyUserAppMetrics() {
+  if (on_new_day_) {
+    RecordInstalledExtensionsCount();
+    RecordEnabledExtensionsCount();
+    RecordRecentlyUsedAppsCount();
+  }
+}
+
+void FamilyUserAppMetrics::OnNewDay() {
+  on_new_day_ = true;
+}
+
+void FamilyUserAppMetrics::RecordInstalledExtensionsCount() {
+  int counter = 0;
+  std::unique_ptr<extensions::ExtensionSet> all_installed_extensions =
+      extension_registry_->GenerateInstalledExtensionsSet();
+  for (const auto& extension : *all_installed_extensions) {
+    if (extensions::Manifest::IsComponentLocation(extension->location()))
+      continue;
+    if (extension->is_extension() || extension->is_theme())
+      counter++;
+  }
+  // If a family user has more than a thousand extensions installed, then that
+  // count is going into an overflow bucket. We don't expect this scenario to
+  // happen often.
+  base::UmaHistogramCounts1000(kInstalledExtensionsCountHistogramName, counter);
+}
+
+void FamilyUserAppMetrics::RecordEnabledExtensionsCount() {
+  int counter = 0;
+  for (const auto& extension : extension_registry_->enabled_extensions()) {
+    if (extensions::Manifest::IsComponentLocation(extension->location()))
+      continue;
+    if (extension->is_extension() || extension->is_theme())
+      counter++;
+  }
+  // If a family user has more than a thousand extensions enabled, then that
+  // count is going into an overflow bucket. We don't expect this scenario to
+  // happen often.
+  base::UmaHistogramCounts1000(kEnabledExtensionsCountHistogramName, counter);
+}
+
+void FamilyUserAppMetrics::RecordRecentlyUsedAppsCount() {
+  int other_counter, arc_counter, borealis_counter, crostini_counter,
+      extension_counter, web_counter, total_counter;
+  other_counter = arc_counter = borealis_counter = crostini_counter =
+      extension_counter = web_counter = total_counter = 0;
+  app_registry_->ForEachApp([&other_counter, &arc_counter, &borealis_counter,
+                             &crostini_counter, &extension_counter,
+                             &web_counter,
+                             &total_counter](const apps::AppUpdate& update) {
+    // Only count apps that have been used recently.
+    if (base::Time::Now() - update.LastLaunchTime() > k28Days)
+      return;
+    switch (update.AppType()) {
+      case apps::mojom::AppType::kArc:
+        arc_counter++;
+        break;
+      case apps::mojom::AppType::kBorealis:
+        borealis_counter++;
+        break;
+      case apps::mojom::AppType::kCrostini:
+        crostini_counter++;
+        break;
+      case apps::mojom::AppType::kExtension:
+        // The InstalledExtensionsCount only includes regular browser
+        // extensions and themes. This counter only includes apps. The two
+        // counters are mutually exclusive.
+        extension_counter++;
+        break;
+      case apps::mojom::AppType::kWeb:
+        web_counter++;
+        break;
+      default:
+        // We're not interested in tracking other app types in detail.
+        other_counter++;
+        break;
+    }
+    total_counter++;
+  });
+  // If a family user has more than a thousand apps installed, then that count
+  // is going into an overflow bucket. We don't expect this scenario to happen
+  // often.
+  base::UmaHistogramCounts1000(kOtherAppsCountHistogramName, other_counter);
+  base::UmaHistogramCounts1000(kArcAppsCountHistogramName, arc_counter);
+  base::UmaHistogramCounts1000(kBorealisAppsCountHistogramName,
+                               borealis_counter);
+  base::UmaHistogramCounts1000(kCrostiniAppsCountHistogramName,
+                               crostini_counter);
+  base::UmaHistogramCounts1000(kExtensionAppsCountHistogramName,
+                               extension_counter);
+  base::UmaHistogramCounts1000(kWebAppsCountHistogramName, web_counter);
+  base::UmaHistogramCounts1000(kTotalAppsCountHistogramName, total_counter);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/family_user_app_metrics.h b/chrome/browser/chromeos/child_accounts/family_user_app_metrics.h
new file mode 100644
index 0000000..56c9ecbe0
--- /dev/null
+++ b/chrome/browser/chromeos/child_accounts/family_user_app_metrics.h
@@ -0,0 +1,67 @@
+// 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_CHILD_ACCOUNTS_FAMILY_USER_APP_METRICS_H_
+#define CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_FAMILY_USER_APP_METRICS_H_
+
+#include "chrome/browser/chromeos/child_accounts/family_user_metrics_service.h"
+
+class Profile;
+
+namespace extensions {
+class ExtensionRegistry;
+}  // namespace extensions
+
+namespace apps {
+class AppRegistryCache;
+}  // namespace apps
+
+namespace chromeos {
+
+class FamilyUserAppMetrics : public FamilyUserMetricsService::Observer {
+ public:
+  // UMA metrics for a snapshot count of installed and enabled extensions for a
+  // given family user.
+  static const char kInstalledExtensionsCountHistogramName[];
+  static const char kEnabledExtensionsCountHistogramName[];
+
+  // UMA metrics for a snapshot count of recently used apps for a given family
+  // user.
+  static const char kOtherAppsCountHistogramName[];
+  static const char kArcAppsCountHistogramName[];
+  static const char kBorealisAppsCountHistogramName[];
+  static const char kCrostiniAppsCountHistogramName[];
+  static const char kExtensionAppsCountHistogramName[];
+  static const char kWebAppsCountHistogramName[];
+  // Sum of the above metrics for a given snapshot.
+  static const char kTotalAppsCountHistogramName[];
+
+  explicit FamilyUserAppMetrics(Profile* profile);
+  FamilyUserAppMetrics(const FamilyUserAppMetrics&) = delete;
+  FamilyUserAppMetrics& operator=(const FamilyUserAppMetrics&) = delete;
+  ~FamilyUserAppMetrics() override;
+
+ private:
+  // FamilyUserMetricsService::Observer:
+  void OnNewDay() override;
+
+  // Records the number of non-component extensions that the family user has
+  // installed. This count is a superset of the enabled extensions count.
+  void RecordInstalledExtensionsCount();
+
+  // Records the number of non-component extensions that the family user has
+  // enabled. This count is a subset of the installed extensions count.
+  void RecordEnabledExtensionsCount();
+
+  // Records the number of apps that the family user has recently used.
+  void RecordRecentlyUsedAppsCount();
+
+  const extensions::ExtensionRegistry* const extension_registry_;
+  apps::AppRegistryCache* const app_registry_;
+
+  bool on_new_day_ = false;
+};
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_FAMILY_USER_APP_METRICS_H_
diff --git a/chrome/browser/chromeos/child_accounts/family_user_app_metrics_unittest.cc b/chrome/browser/chromeos/child_accounts/family_user_app_metrics_unittest.cc
new file mode 100644
index 0000000..b168f90
--- /dev/null
+++ b/chrome/browser/chromeos/child_accounts/family_user_app_metrics_unittest.cc
@@ -0,0 +1,294 @@
+// 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/chromeos/child_accounts/family_user_app_metrics.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/chromeos/child_accounts/family_user_metrics_service.h"
+#include "chrome/browser/extensions/extension_service_test_with_install.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chromeos/dbus/power/fake_power_manager_client.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+#include "components/session_manager/core/session_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_set.h"
+#include "extensions/common/manifest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace {
+
+constexpr base::TimeDelta kOneDay = base::TimeDelta::FromDays(1);
+constexpr char kStartTime[] = "1 Jan 2020 21:15";
+
+apps::mojom::AppPtr MakeApp(const char* app_id,
+                            const char* name,
+                            base::Time last_launch_time,
+                            apps::mojom::InstallSource install_source,
+                            apps::mojom::AppType app_type) {
+  apps::mojom::AppPtr app = apps::mojom::App::New();
+  app->app_id = app_id;
+  app->name = name;
+  app->last_launch_time = last_launch_time;
+  app->install_source = install_source;
+  app->app_type = app_type;
+  return app;
+}
+
+}  // namespace
+
+// Tests for family user app metrics.
+class FamilyUserAppMetricsTest
+    : public extensions::ExtensionServiceTestWithInstall,
+      public testing::WithParamInterface<bool> {
+ public:
+  FamilyUserAppMetricsTest()
+      : extensions::ExtensionServiceTestWithInstall(
+            std::make_unique<content::BrowserTaskEnvironment>(
+                base::test::TaskEnvironment::MainThreadType::IO,
+                content::BrowserTaskEnvironment::TimeSource::MOCK_TIME)) {}
+
+  void SetUp() override {
+    base::Time start_time;
+    EXPECT_TRUE(base::Time::FromString(kStartTime, &start_time));
+    base::TimeDelta forward_by = start_time - base::Time::Now();
+    EXPECT_LT(base::TimeDelta(), forward_by);
+    task_environment()->AdvanceClock(forward_by);
+
+    bool profile_is_supervised = GetParam();
+    ExtensionServiceInitParams params = CreateDefaultInitParams();
+    params.profile_is_supervised = profile_is_supervised;
+    InitializeExtensionService(params);
+
+    EXPECT_EQ(profile_is_supervised, profile()->IsChild());
+
+    supervised_user_service()->Init();
+    supervised_user_service()
+        ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(true);
+
+    PowerManagerClient::InitializeFake();
+    ConstructFamilyUserMetricsService();
+  }
+
+  void TearDown() override {
+    ShutdownFamilyUserMetricsService();
+    PowerManagerClient::Shutdown();
+  }
+
+  void InstallExtensions() {
+    // Install and enable a theme, which doesn't require parent approval.
+    base::FilePath path = data_dir().AppendASCII("theme.crx");
+    const extensions::Extension* extension1 = InstallCRX(path, INSTALL_NEW);
+    ASSERT_TRUE(extension1);
+    EXPECT_TRUE(registry()->enabled_extensions().Contains(extension1->id()));
+    EXPECT_FALSE(
+        extensions::Manifest::IsComponentLocation(extension1->location()));
+
+    // Install an extension, but keep it disabled pending parent approval if the
+    // current user is supervised.
+    path = data_dir().AppendASCII("good.crx");
+    bool profile_is_supervised = GetParam();
+    InstallState expected_state =
+        profile_is_supervised ? INSTALL_WITHOUT_LOAD : INSTALL_NEW;
+    const extensions::Extension* extension2 = InstallCRX(path, expected_state);
+    ASSERT_TRUE(extension2);
+    EXPECT_EQ(profile_is_supervised,
+              registry()->disabled_extensions().Contains(extension2->id()));
+    EXPECT_NE(profile_is_supervised,
+              registry()->enabled_extensions().Contains(extension2->id()));
+    EXPECT_FALSE(
+        extensions::Manifest::IsComponentLocation(extension2->location()));
+
+    // Install an extension, and approve it if the current user is supervised.
+    path = data_dir().AppendASCII("good2048.crx");
+    const extensions::Extension* extension3 = InstallCRX(path, expected_state);
+    ASSERT_TRUE(extension3);
+    if (profile_is_supervised) {
+      supervised_user_service()->UpdateApprovedExtensionForTesting(
+          extension3->id(),
+          SupervisedUserService::ApprovedExtensionChange::kAdd);
+    }
+    EXPECT_TRUE(registry()->enabled_extensions().Contains(extension3->id()));
+    EXPECT_FALSE(
+        extensions::Manifest::IsComponentLocation(extension3->location()));
+  }
+
+  void InstallApps() {
+    std::vector<apps::mojom::AppPtr> deltas;
+    apps::AppRegistryCache& cache =
+        apps::AppServiceProxyFactory::GetForProfile(profile())
+            ->AppRegistryCache();
+    deltas.push_back(MakeApp(/*app_id=*/"a", /*app_name=*/"apple",
+                             /*last_launch_time=*/base::Time::Now(),
+                             apps::mojom::InstallSource::kUser,
+                             apps::mojom::AppType::kArc));
+    deltas.push_back(MakeApp(/*app_id=*/"b", /*app_name=*/"banana",
+                             /*last_launch_time=*/base::Time::Now() - kOneDay,
+                             apps::mojom::InstallSource::kUser,
+                             apps::mojom::AppType::kCrostini));
+    deltas.push_back(MakeApp(
+        /*app_id=*/"c", /*app_name=*/"cherry",
+        /*last_launch_time=*/base::Time::Now() - 7 * kOneDay,
+        apps::mojom::InstallSource::kUser, apps::mojom::AppType::kExtension));
+    deltas.push_back(MakeApp(
+        /*app_id=*/"d", /*app_name=*/"dragon",
+        /*last_launch_time=*/base::Time::Now() - 14 * kOneDay,
+        apps::mojom::InstallSource::kUser, apps::mojom::AppType::kWeb));
+    deltas.push_back(MakeApp(
+        /*app_id=*/"e", /*app_name=*/"elderberry",
+        /*last_launch_time=*/base::Time::Now() - 21 * kOneDay,
+        apps::mojom::InstallSource::kUser, apps::mojom::AppType::kBorealis));
+    deltas.push_back(MakeApp(
+        /*app_id=*/"f", /*app_name=*/"fig",
+        /*last_launch_time=*/base::Time::Now() - 27 * kOneDay,
+        apps::mojom::InstallSource::kUser, apps::mojom::AppType::kUnknown));
+    deltas.push_back(MakeApp(
+        /*app_id=*/"g", /*app_name=*/"grape",
+        /*last_launch_time=*/base::Time::Now(),
+        apps::mojom::InstallSource::kSystem, apps::mojom::AppType::kBuiltIn));
+    // Not recorded. This app was launched one day too long ago.
+    deltas.push_back(MakeApp(
+        /*app_id=*/"h", /*app_name=*/"huckleberry",
+        /*last_launch_time=*/base::Time::Now() - 28 * kOneDay,
+        apps::mojom::InstallSource::kUser, apps::mojom::AppType::kLacros));
+    cache.OnApps(std::move(deltas));
+  }
+
+  void ConstructFamilyUserMetricsService() {
+    family_user_metrics_service_ =
+        std::make_unique<FamilyUserMetricsService>(profile());
+  }
+
+  void ShutdownFamilyUserMetricsService() {
+    family_user_metrics_service_->Shutdown();
+  }
+
+  SupervisedUserService* supervised_user_service() {
+    return SupervisedUserServiceFactory::GetForProfile(profile());
+  }
+
+ private:
+  // We need this member variable, even if it's unused, so
+  // FamilyUserSessionMetrics doesn't crash.
+  session_manager::SessionManager session_manager_;
+  std::unique_ptr<FamilyUserMetricsService> family_user_metrics_service_;
+};
+
+// Tests the UMA metrics that count the number of installed and enabled
+// extensions and themes.
+TEST_P(FamilyUserAppMetricsTest, CountInstalledAndEnabledExtensions) {
+  base::HistogramTester histogram_tester;
+
+  InstallExtensions();
+  task_environment()->FastForwardBy(kOneDay);
+  ShutdownFamilyUserMetricsService();
+
+  // There should be 2 installed extensions and one theme.
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kInstalledExtensionsCountHistogramName,
+      /*sample=*/3, /*expected_count=*/1);
+
+  bool profile_is_supervised = GetParam();
+  if (profile_is_supervised) {
+    // There should be 1 enabled extension and a theme.
+    histogram_tester.ExpectUniqueSample(
+        FamilyUserAppMetrics::kEnabledExtensionsCountHistogramName,
+        /*sample=*/2, /*expected_count=*/1);
+  } else {
+    // Regular user case.
+    // There should be 2 enabled extensions and a theme.
+    histogram_tester.ExpectUniqueSample(
+        FamilyUserAppMetrics::kEnabledExtensionsCountHistogramName,
+        /*sample=*/3, /*expected_count=*/1);
+  }
+}
+
+// Tests the UMA metrics that count the number of recently used apps for
+// supervised and regular users.
+TEST_P(FamilyUserAppMetricsTest, CountRecentlyUsedApps) {
+  base::HistogramTester histogram_tester;
+
+  InstallApps();
+  task_environment()->FastForwardBy(kOneDay);
+  ShutdownFamilyUserMetricsService();
+
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kOtherAppsCountHistogramName,
+      /*sample=*/2, /*expected_counter=*/1);
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kArcAppsCountHistogramName, /*sample=*/1,
+      /*expected_counter=*/1);
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kBorealisAppsCountHistogramName, /*sample=*/1,
+      /*expected_counter=*/1);
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kCrostiniAppsCountHistogramName,
+      /*sample=*/1, /*expected_counter=*/1);
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kExtensionAppsCountHistogramName,
+      /*sample=*/1, /*expected_counter=*/1);
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kWebAppsCountHistogramName, /*sample=*/1,
+      /*expected_counter=*/1);
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kTotalAppsCountHistogramName,
+      /*sample=*/7, /*expected_counter=*/1);
+}
+
+// Tests that metrics recording only happens on sign out, and not necessarily
+// once per day. Tests that metrics recording happens at most once per day.
+TEST_P(FamilyUserAppMetricsTest, FastForwardTwoDays) {
+  base::HistogramTester histogram_tester;
+
+  InstallExtensions();
+  InstallApps();
+
+  // End time is 3 Jan 2020 21:15.
+  task_environment()->FastForwardBy(kOneDay * 2);
+
+  // Metrics recorded here.
+  ShutdownFamilyUserMetricsService();
+
+  // Only one snapshot was recorded.
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kInstalledExtensionsCountHistogramName,
+      /*sample=*/3, /*expected_count=*/1);
+  // One app has not been used within 28 days and dropped from the count.
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kTotalAppsCountHistogramName,
+      /*sample=*/6, /*expected_counter=*/1);
+
+  // User signs in and out again one hour later.
+  task_environment()->FastForwardBy(base::TimeDelta::FromHours(1));
+  ConstructFamilyUserMetricsService();
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(1));
+  // Should not trigger recording.
+  ShutdownFamilyUserMetricsService();
+
+  // No additional metrics were recorded.
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kInstalledExtensionsCountHistogramName,
+      /*sample=*/3, /*expected_count=*/1);
+  // One app has not been used within 28 days and dropped from the count.
+  histogram_tester.ExpectUniqueSample(
+      FamilyUserAppMetrics::kTotalAppsCountHistogramName,
+      /*sample=*/6, /*expected_counter=*/1);
+}
+
+INSTANTIATE_TEST_SUITE_P(, FamilyUserAppMetricsTest, testing::Bool());
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/child_accounts/family_user_metrics_service.cc b/chrome/browser/chromeos/child_accounts/family_user_metrics_service.cc
index 9f09179..8d59708 100644
--- a/chrome/browser/chromeos/child_accounts/family_user_metrics_service.cc
+++ b/chrome/browser/chromeos/child_accounts/family_user_metrics_service.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/child_accounts/family_user_metrics_service.h"
 
 #include "base/logging.h"
+#include "chrome/browser/chromeos/child_accounts/family_user_app_metrics.h"
 #include "chrome/browser/chromeos/child_accounts/family_user_session_metrics.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
@@ -42,11 +43,15 @@
   DCHECK(pref_service_);
   family_user_metrics_.push_back(
       std::make_unique<FamilyUserSessionMetrics>(pref_service_));
+  Profile* profile = Profile::FromBrowserContext(context);
+  family_user_metrics_.push_back(
+      std::make_unique<FamilyUserAppMetrics>(profile));
 
   for (auto& family_user_metric : family_user_metrics_)
     AddObserver(family_user_metric.get());
 
-  // Check for a new day every |kTimerInterval|.
+  CheckForNewDay();
+  // Check for a new day every |kTimerInterval| as well.
   timer_.Start(FROM_HERE, kTimerInterval, this,
                &FamilyUserMetricsService::CheckForNewDay);
 }
diff --git a/chrome/browser/chromeos/child_accounts/family_user_metrics_service_factory.cc b/chrome/browser/chromeos/child_accounts/family_user_metrics_service_factory.cc
index 251edad..3900bb6 100644
--- a/chrome/browser/chromeos/child_accounts/family_user_metrics_service_factory.cc
+++ b/chrome/browser/chromeos/child_accounts/family_user_metrics_service_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/chromeos/child_accounts/family_user_metrics_service_factory.h"
 
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/child_accounts/family_user_metrics_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "content/public/browser/browser_context.h"
@@ -27,7 +28,9 @@
 FamilyUserMetricsServiceFactory::FamilyUserMetricsServiceFactory()
     : BrowserContextKeyedServiceFactory(
           "FamilyUserMetricsServiceFactory",
-          BrowserContextDependencyManager::GetInstance()) {}
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(apps::AppServiceProxyFactory::GetInstance());
+}
 
 FamilyUserMetricsServiceFactory::~FamilyUserMetricsServiceFactory() = default;
 
diff --git a/chrome/browser/chromeos/child_accounts/family_user_metrics_service_unittest.cc b/chrome/browser/chromeos/child_accounts/family_user_metrics_service_unittest.cc
index 6cbd251..52fc52cb 100644
--- a/chrome/browser/chromeos/child_accounts/family_user_metrics_service_unittest.cc
+++ b/chrome/browser/chromeos/child_accounts/family_user_metrics_service_unittest.cc
@@ -51,12 +51,11 @@
     EXPECT_TRUE(base::Time::FromString(kStartTime, &start_time));
     base::TimeDelta forward_by = start_time - base::Time::Now();
     EXPECT_LT(base::TimeDelta(), forward_by);
-    task_environment_.FastForwardBy(forward_by);
+    task_environment_.AdvanceClock(forward_by);
 
     PowerManagerClient::InitializeFake();
     family_user_metrics_service_ =
         std::make_unique<FamilyUserMetricsService>(&testing_profile_);
-    SetDayIdPref(FamilyUserMetricsService::GetDayIdForTesting(start_time));
 
     family_user_metrics_service_->AddObserver(&mock_observer_);
   }
@@ -76,10 +75,6 @@
     return GetPrefService()->GetInteger(prefs::kFamilyUserMetricsDayId);
   }
 
-  void SetDayIdPref(int day_id) {
-    GetPrefService()->SetInteger(prefs::kFamilyUserMetricsDayId, day_id);
-  }
-
   content::BrowserTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
@@ -98,7 +93,8 @@
 // Tests OnNewDay() is called after more than one day passes.
 TEST_F(DetectingNewDayTest, MoreThanOneDay) {
   EXPECT_CALL(mock_observer_, OnNewDay()).Times(1);
-  task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
+  task_environment_.FastForwardBy(base::TimeDelta::FromDays(1) +
+                                  base::TimeDelta::FromHours(1));
   EXPECT_EQ(FamilyUserMetricsService::GetDayIdForTesting(base::Time::Now()),
             GetDayIdPref());
 }
@@ -119,8 +115,8 @@
             GetDayIdPref());
 }
 
-// Tests OnNewDay() is called after more than one day passes, even when the
-// device is idle.
+// Tests OnNewDay() is called after one day passes, even when the device is
+// idle.
 TEST_F(DetectingNewDayTest, MoreThanOneDayDeviceIdle) {
   EXPECT_CALL(mock_observer_, OnNewDay()).Times(1);
   SetScreenOff(true);
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 9bdb7e7..69f6ff6d 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -680,7 +680,8 @@
 
   arc_data_snapshotd_manager_ =
       std::make_unique<arc::data_snapshotd::ArcDataSnapshotdManager>(
-          g_browser_process->local_state());
+          g_browser_process->local_state(),
+          base::BindOnce(chrome::AttemptUserExit));
   if (base::FeatureList::IsEnabled(::features::kWilcoDtc))
     wilco_dtc_supportd_manager_ = std::make_unique<WilcoDtcSupportdManager>();
 
diff --git a/chrome/browser/chromeos/file_manager/devtools_listener.cc b/chrome/browser/chromeos/file_manager/devtools_listener.cc
index e702456..9360eca 100644
--- a/chrome/browser/chromeos/file_manager/devtools_listener.cc
+++ b/chrome/browser/chromeos/file_manager/devtools_listener.cc
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "base/containers/span.h"
 #include "base/files/file_util.h"
 #include "base/hash/md5.h"
 #include "base/json/json_reader.h"
@@ -17,22 +18,26 @@
 #include "base/logging.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
-#include "base/threading/thread_restrictions.h"
 #include "url/url_util.h"
 
 namespace file_manager {
 
 namespace {
 
-base::span<const uint8_t> StringToSpan(const std::string& str) {
-  return base::as_bytes(base::make_span(str));
+base::span<const uint8_t> StringToSpan(const std::string& s) {
+  return base::as_bytes(base::make_span(s));
 }
 
-std::string EncodedURL(const std::string& url) {
-  url::RawCanonOutputT<char> canonical_url;
-  url::EncodeURIComponent(url.c_str(), url.size(), &canonical_url);
-  return std::string(canonical_url.data(), canonical_url.length());
+base::StringPiece SpanToStringPiece(const base::span<const uint8_t>& s) {
+  return {reinterpret_cast<const char*>(s.data()), s.size()};
+}
+
+std::string EncodeURIComponent(const std::string& component) {
+  url::RawCanonOutputT<char> encoded;
+  url::EncodeURIComponent(component.c_str(), component.size(), &encoded);
+  return std::string(encoded.data(), encoded.length());
 }
 
 }  // namespace
@@ -151,7 +156,7 @@
   }
 
   const std::string url = host->GetURL().spec();
-  CHECK(result->SetString("encodedHostURL", EncodedURL(url)));
+  CHECK(result->SetString("encodedHostURL", EncodeURIComponent(url)));
   CHECK(result->SetString("hostTitle", host->GetTitle()));
   CHECK(result->SetString("hostType", host->GetType()));
   CHECK(result->SetString("hostTest", test));
@@ -213,14 +218,14 @@
 
     base::DictionaryValue* script = nullptr;
     CHECK(script_[i]->GetDictionary("params", &script));
-    CHECK(script->SetString("encodedURL", EncodedURL(url)));
+    CHECK(script->SetString("encodedURL", EncodeURIComponent(url)));
     CHECK(script->SetString("hash", hash));
     CHECK(script->SetString("text", text));
     CHECK(script->SetString("url", url));
 
     base::FilePath path = store.AppendASCII(hash.append(".js.json"));
     CHECK(base::JSONWriter::Write(*script, &text));
-    if (!base::PathExists(path))  // Deduplication
+    if (!base::PathExists(path))  // script de-duplication
       base::WriteFile(path, text.data(), text.size());
   }
 }
@@ -236,23 +241,20 @@
 
 void DevToolsListener::DispatchProtocolMessage(
     content::DevToolsAgentHost* host,
-    base::span<const uint8_t> span_message) {
+    base::span<const uint8_t> message) {
   if (!navigated_)
     return;
 
-  std::string message(reinterpret_cast<const char*>(span_message.data()),
-                      span_message.size());
-
-  std::unique_ptr<base::DictionaryValue> response =
-      base::DictionaryValue::From(base::JSONReader::ReadDeprecated(message));
+  std::unique_ptr<base::DictionaryValue> response = base::DictionaryValue::From(
+      base::JSONReader::ReadDeprecated(SpanToStringPiece(message)));
   CHECK(response);
 
   std::string* method = response->FindStringPath("method");
   if (method) {
-    if (*method == "Debugger.scriptParsed")
-      script_.push_back(std::move(response));
-    else if (*method == "Runtime.executionContextsCreated")
+    if (*method == "Runtime.executionContextsCreated")
       script_.clear();
+    else if (*method == "Debugger.scriptParsed")
+      script_.push_back(std::move(response));
     return;
   }
 
diff --git a/chrome/browser/chromeos/file_manager/devtools_listener.h b/chrome/browser/chromeos/file_manager/devtools_listener.h
index c5ce2e9..5b995332 100644
--- a/chrome/browser/chromeos/file_manager/devtools_listener.h
+++ b/chrome/browser/chromeos/file_manager/devtools_listener.h
@@ -66,7 +66,7 @@
 
   // Receives CDP messages sent by host.
   void DispatchProtocolMessage(content::DevToolsAgentHost* host,
-                               base::span<const uint8_t> span_message) override;
+                               base::span<const uint8_t> message) override;
 
   // Returns true if URL should be attached to.
   bool MayAttachToURL(const GURL& url, bool is_webui) override;
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 37fd8ec..ae41556 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -706,7 +706,10 @@
         TestCase("transferDeletedFile"),
         TestCase("transferInfoIsRemembered"),
         TestCase("transferToUsbHasDestinationText"),
-        TestCase("transferDismissedErrorIsRemembered")));
+        TestCase("transferDismissedErrorIsRemembered"),
+        TestCase("transferNotSupportedOperationHasNoRemainingTimeText"),
+        TestCase("transferUpdateSamePanelItem"),
+        TestCase("transferShowPendingMessageForZeroRemainingTime")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     RestorePrefs, /* restore_prefs.js */
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index c96998fe..83e6c18 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -15,6 +15,7 @@
 #include "base/bind_helpers.h"
 #include "base/containers/circular_deque.h"
 #include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_value_converter.h"
 #include "base/json/json_writer.h"
@@ -108,6 +109,7 @@
 #include "ui/shell_dialogs/select_file_dialog.h"
 #include "ui/shell_dialogs/select_file_dialog_factory.h"
 #include "ui/shell_dialogs/select_file_policy.h"
+#include "url/url_util.h"
 
 using ::testing::_;
 
@@ -1278,16 +1280,26 @@
   ~DocumentsProviderTestVolume() override = default;
 
   virtual void CreateEntry(const AddEntriesMessage::TestEntryInfo& entry) {
-    // Register a document to the fake FileSystemInstance.
+    // Create and add an entry Document to the fake arc::FileSystemInstance.
     arc::FakeFileSystemInstance::Document document(
         authority_, entry.name_text, root_document_id_, entry.name_text,
         GetMimeType(entry), GetFileSize(entry),
         entry.last_modified_time.ToJavaTime(), entry.capabilities.can_delete,
         entry.capabilities.can_rename, entry.capabilities.can_add_children);
     file_system_instance_->AddDocument(document);
+
+    if (entry.type != AddEntriesMessage::FILE)
+      return;
+
+    std::string canonical_url = base::StrCat(
+        {"content://", authority_, "/document/", EncodeURI(entry.name_text)});
+    file_system_instance_->AddFile(arc::FakeFileSystemInstance::File(
+        canonical_url, GetTestFileContent(entry.source_file_name),
+        GetMimeType(entry), arc::FakeFileSystemInstance::File::Seekable::NO));
   }
 
   virtual bool Mount(Profile* profile) {
+    // Register the volume root document.
     RegisterRoot();
 
     // Tell VolumeManager that a new DocumentsProvider volume is added.
@@ -1303,12 +1315,10 @@
   const std::string root_document_id_;
   const bool read_only_;
 
-  // Register a root document of this volume.
   void RegisterRoot() {
-    arc::FakeFileSystemInstance::Document document(
-        authority_, root_document_id_, "", "", arc::kAndroidDirectoryMimeType,
-        0, 0);
-    file_system_instance_->AddDocument(document);
+    const auto* root_mime_type = arc::kAndroidDirectoryMimeType;
+    file_system_instance_->AddDocument(arc::FakeFileSystemInstance::Document(
+        authority_, root_document_id_, "", "", root_mime_type, 0, 0));
   }
 
  private:
@@ -1329,6 +1339,21 @@
                : arc::kAndroidDirectoryMimeType;
   }
 
+  std::string GetTestFileContent(const std::string& test_file_name) {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    std::string contents;
+    base::FilePath path = TestVolume::GetTestDataFilePath(test_file_name);
+    CHECK(base::ReadFileToString(path, &contents))
+        << "failed reading test data file " << test_file_name;
+    return contents;
+  }
+
+  std::string EncodeURI(const std::string& component) {
+    url::RawCanonOutputT<char> encoded;
+    url::EncodeURIComponent(component.c_str(), component.size(), &encoded);
+    return std::string(encoded.data(), encoded.length());
+  }
+
   DISALLOW_COPY_AND_ASSIGN(DocumentsProviderTestVolume);
 };
 
@@ -1751,9 +1776,8 @@
                                 base::Unretained(this)));
 
     if (arc::IsArcAvailable()) {
-      // When ARC is marked as available, we create fake FileSystemInstance and
-      // register it so that ARC-related services can work without real ARC
-      // container.
+      // When ARC is available, create and register a fake FileSystemInstance
+      // so ARC-related services work without a real ARC container.
       arc_file_system_instance_ =
           std::make_unique<arc::FakeFileSystemInstance>();
       arc::ArcServiceManager::Get()
@@ -1853,7 +1877,7 @@
 
   base::FilePath store;
   CHECK(base::PathService::Get(base::DIR_EXE, &store));
-  store = store.AppendASCII("coverage").AppendASCII("file_manager");
+  store = store.AppendASCII("coverage").AppendASCII("devtools_code_coverage");
   CHECK(base::CreateDirectory(store));
 
   base::FilePath tests = store.AppendASCII("tests");
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index e1ab31c3..941d44f 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -94,6 +94,7 @@
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "components/account_id/account_id.h"
 #include "components/arc/arc_util.h"
+#include "components/arc/enterprise/arc_data_snapshotd_manager.h"
 #include "components/google/core/common/google_util.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
@@ -345,6 +346,42 @@
   return offline_signin_limit - (now - last_online_signin);
 }
 
+// Returns account ID of a public session account if it is unique, otherwise
+// returns invalid account ID.
+AccountId GetArcDataSnapshotAutoLoginAccountId(
+    const std::vector<policy::DeviceLocalAccount>& device_local_accounts) {
+  AccountId auto_login_account_id = EmptyAccountId();
+  for (std::vector<policy::DeviceLocalAccount>::const_iterator it =
+           device_local_accounts.begin();
+       it != device_local_accounts.end(); ++it) {
+    if (it->type == policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION) {
+      // Do not perform ARC data snapshot auto-login if more than one public
+      // session account is configured.
+      if (auto_login_account_id.is_valid())
+        return EmptyAccountId();
+      auto_login_account_id = AccountId::FromUserEmail(it->user_id);
+      VLOG(2) << "PublicSession autologin found: " << it->user_id;
+    }
+  }
+  return auto_login_account_id;
+}
+
+// Returns account ID if a corresponding to |auto_login_account_id| device local
+// account exists, otherwise returns invalid account ID.
+AccountId GetPublicSessionAutoLoginAccountId(
+    const std::vector<policy::DeviceLocalAccount>& device_local_accounts,
+    const std::string& auto_login_account_id) {
+  for (std::vector<policy::DeviceLocalAccount>::const_iterator it =
+           device_local_accounts.begin();
+       it != device_local_accounts.end(); ++it) {
+    if (it->account_id == auto_login_account_id) {
+      VLOG(2) << "PublicSession autologin found: " << it->user_id;
+      return AccountId::FromUserEmail(it->user_id);
+    }
+  }
+  return EmptyAccountId();
+}
+
 class AutoLaunchNotificationDelegate
     : public message_center::HandleNotificationClickDelegate {
  public:
@@ -1555,16 +1592,17 @@
       policy::GetDeviceLocalAccounts(cros_settings_);
   const bool show_update_required_screen = IsUpdateRequiredDeadlineReached();
 
-  public_session_auto_login_account_id_ = EmptyAccountId();
-  for (std::vector<policy::DeviceLocalAccount>::const_iterator it =
-           device_local_accounts.begin();
-       it != device_local_accounts.end(); ++it) {
-    if (it->account_id == auto_login_account_id) {
-      public_session_auto_login_account_id_ =
-          AccountId::FromUserEmail(it->user_id);
-      VLOG(2) << "PublicSession autologin found: " << it->user_id;
-      break;
-    }
+  auto* data_snapshotd_manager =
+      arc::data_snapshotd::ArcDataSnapshotdManager::Get();
+  bool is_arc_data_snapshot_autologin =
+      (data_snapshotd_manager &&
+       data_snapshotd_manager->IsAutoLoginConfigured());
+  if (is_arc_data_snapshot_autologin) {
+    public_session_auto_login_account_id_ =
+        GetArcDataSnapshotAutoLoginAccountId(device_local_accounts);
+  } else {
+    public_session_auto_login_account_id_ = GetPublicSessionAutoLoginAccountId(
+        device_local_accounts, auto_login_account_id);
   }
 
   const user_manager::User* public_session_user =
@@ -1576,7 +1614,8 @@
     public_session_auto_login_account_id_ = EmptyAccountId();
   }
 
-  if (!cros_settings_->GetInteger(kAccountsPrefDeviceLocalAccountAutoLoginDelay,
+  if (is_arc_data_snapshot_autologin ||
+      !cros_settings_->GetInteger(kAccountsPrefDeviceLocalAccountAutoLoginDelay,
                                   &auto_login_delay_)) {
     auto_login_delay_ = 0;
   }
@@ -1655,6 +1694,20 @@
     StopAutoLoginTimer();
   }
 
+  // Block auto-login flow until ArcDataSnapshotdManager is ready to enter an
+  // auto-login session.
+  // ArcDataSnapshotdManager stores a reset auto-login callback to fire it once
+  // it is ready.
+  auto* data_snapshotd_manager =
+      arc::data_snapshotd::ArcDataSnapshotdManager::Get();
+  if (data_snapshotd_manager && !data_snapshotd_manager->IsAutoLoginAllowed() &&
+      data_snapshotd_manager->IsAutoLoginConfigured()) {
+    data_snapshotd_manager->set_reset_autologin_callback(
+        base::BindOnce(&ExistingUserController::ResetAutoLoginTimer,
+                       weak_factory_.GetWeakPtr()));
+    return;
+  }
+
   // Start the auto-login timer.
   if (!auto_login_timer_)
     auto_login_timer_.reset(new base::OneShotTimer);
diff --git a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
index 770fa04..933606a 100644
--- a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
@@ -59,6 +59,7 @@
 #include "chromeos/network/network_state_test_helper.h"
 #include "chromeos/settings/cros_settings_names.h"
 #include "chromeos/settings/cros_settings_provider.h"
+#include "components/arc/enterprise/arc_data_snapshotd_manager.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
@@ -108,6 +109,8 @@
 const char kHash[] = "test_hash";
 
 const char kPublicSessionUserEmail[] = "public_session_user@localhost";
+const char kPublicSessionSecondUserEmail[] =
+    "public_session_second_user@localhost";
 const int kAutoLoginNoDelay = 0;
 const int kAutoLoginShortDelay = 1;
 const int kAutoLoginLongDelay = 10000;
@@ -143,6 +146,10 @@
   return path.Append("kerberos").Append("krb5cc");
 }
 
+arc::data_snapshotd::ArcDataSnapshotdManager* arc_data_snapshotd_manager() {
+  return arc::data_snapshotd::ArcDataSnapshotdManager::Get();
+}
+
 }  // namespace
 
 class ExistingUserControllerTest : public policy::DevicePolicyCrosBrowserTest {
@@ -328,6 +335,12 @@
   void SetUpOnMainThread() override {
     ExistingUserControllerTest::SetUpOnMainThread();
 
+    // By default ArcDataSnapshotdManager does not influence an auto login
+    // flow.
+    EXPECT_TRUE(arc_data_snapshotd_manager());
+    EXPECT_TRUE(arc_data_snapshotd_manager()->IsAutoLoginAllowed());
+    EXPECT_FALSE(arc_data_snapshotd_manager()->IsAutoLoginConfigured());
+
     // Wait for the public session user to be created.
     if (!user_manager::UserManager::Get()->IsKnownUser(
             public_session_account_id_)) {
@@ -706,6 +719,98 @@
   FireAutoLogin();
 }
 
+IN_PROC_BROWSER_TEST_F(ExistingUserControllerPublicSessionTest,
+                       ArcDataSnapshotdAutoLogin) {
+  arc_data_snapshotd_manager()->set_state_for_testing(
+      arc::data_snapshotd::ArcDataSnapshotdManager::State::kBlockedUi);
+  EXPECT_FALSE(arc_data_snapshotd_manager()->IsAutoLoginAllowed());
+  EXPECT_TRUE(arc_data_snapshotd_manager()->IsAutoLoginConfigured());
+
+  ConfigureAutoLogin();
+  existing_user_controller()->OnSigninScreenReady();
+
+  // Do not start an auto-login public account session when in blocked UI mode.
+  EXPECT_TRUE(auto_login_account_id().is_valid());
+  EXPECT_EQ(public_session_account_id_, auto_login_account_id());
+  EXPECT_EQ(0, auto_login_delay());
+  EXPECT_FALSE(auto_login_timer());
+
+  // Allow to launch public account session (MGS).
+  arc_data_snapshotd_manager()->set_state_for_testing(
+      arc::data_snapshotd::ArcDataSnapshotdManager::State::kMgsToLaunch);
+  EXPECT_TRUE(arc_data_snapshotd_manager()->IsAutoLoginAllowed());
+  EXPECT_TRUE(arc_data_snapshotd_manager()->IsAutoLoginConfigured());
+
+  // Set up mocks to check login success.
+  UserContext user_context(user_manager::USER_TYPE_PUBLIC_ACCOUNT,
+                           public_session_account_id_);
+  user_context.SetUserIDHash(user_context.GetAccountId().GetUserEmail());
+  ExpectSuccessfulLogin(user_context);
+  existing_user_controller()->OnSigninScreenReady();
+
+  // Start auto-login and wait for login tasks to complete.
+  content::RunAllPendingInMessageLoop();
+
+  arc_data_snapshotd_manager()->OnSessionStateChanged();
+
+  EXPECT_TRUE(auto_login_account_id().is_valid());
+  EXPECT_EQ(0, auto_login_delay());
+  EXPECT_TRUE(auto_login_timer());
+  EXPECT_EQ(arc::data_snapshotd::ArcDataSnapshotdManager::State::kMgsLaunched,
+            arc_data_snapshotd_manager()->state());
+}
+
+class ExistingUserControllerSecondPublicSessionTest
+    : public ExistingUserControllerPublicSessionTest {
+ public:
+  void SetUpInProcessBrowserTestFixture() override {
+    ExistingUserControllerPublicSessionTest::SetUpInProcessBrowserTestFixture();
+    AddSecondPublicSessionAccount();
+  }
+
+ private:
+  void AddSecondPublicSessionAccount() {
+    // Setup the device policy.
+    em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
+    em::DeviceLocalAccountInfoProto* account =
+        proto.mutable_device_local_accounts()->add_account();
+    account->set_account_id(kPublicSessionSecondUserEmail);
+    account->set_type(
+        em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION);
+    RefreshDevicePolicy();
+
+    // Setup the device local account policy.
+    policy::UserPolicyBuilder device_local_account_policy;
+    device_local_account_policy.policy_data().set_username(
+        kPublicSessionSecondUserEmail);
+    device_local_account_policy.policy_data().set_policy_type(
+        policy::dm_protocol::kChromePublicAccountPolicyType);
+    device_local_account_policy.policy_data().set_settings_entity_id(
+        kPublicSessionSecondUserEmail);
+    device_local_account_policy.Build();
+    session_manager_client()->set_device_local_account_policy(
+        kPublicSessionSecondUserEmail, device_local_account_policy.GetBlob());
+  }
+};
+// Test that if two public session accounts are configured for the device, auto
+// login is not happening.
+IN_PROC_BROWSER_TEST_F(ExistingUserControllerSecondPublicSessionTest,
+                       ArcDataSnapshotdTwoAccounts) {
+  // Allow to launch public account session (MGS).
+  arc_data_snapshotd_manager()->set_state_for_testing(
+      arc::data_snapshotd::ArcDataSnapshotdManager::State::kMgsToLaunch);
+  EXPECT_TRUE(arc_data_snapshotd_manager()->IsAutoLoginAllowed());
+  EXPECT_TRUE(arc_data_snapshotd_manager()->IsAutoLoginConfigured());
+
+  ConfigureAutoLogin();
+  existing_user_controller()->OnSigninScreenReady();
+
+  // Do not configure auto login if more than one public session is configured.
+  EXPECT_FALSE(auto_login_account_id().is_valid());
+  EXPECT_EQ(0, auto_login_delay());
+  EXPECT_FALSE(auto_login_timer());
+}
+
 class ExistingUserControllerActiveDirectoryTest
     : public ExistingUserControllerTest {
  public:
diff --git a/chrome/browser/chromeos/login/signin/signin_error_notifier_ash.cc b/chrome/browser/chromeos/login/signin/signin_error_notifier_ash.cc
index 6d64a14..ab274f61 100644
--- a/chrome/browser/chromeos/login/signin/signin_error_notifier_ash.cc
+++ b/chrome/browser/chromeos/login/signin/signin_error_notifier_ash.cc
@@ -68,7 +68,7 @@
     const chromeos::AccountManager* const account_manager,
     const std::vector<chromeos::AccountManager::Account>& accounts) {
   for (const auto& account : accounts) {
-    if (account_manager->HasDummyGaiaToken(account.key)) {
+    if (account_manager->HasDummyGaiaTokenSync(account.key)) {
       return false;
     }
   }
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
index 182ae12e..552bd3c 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
@@ -221,33 +221,31 @@
 
 }  // namespace
 
-PasswordCheckDelegate::PasswordCheckDelegate(Profile* profile)
+PasswordCheckDelegate::PasswordCheckDelegate(
+    Profile* profile,
+    password_manager::SavedPasswordsPresenter* presenter)
     : profile_(profile),
-      profile_password_store_(PasswordStoreFactory::GetForProfile(
-          profile,
-          ServiceAccessType::EXPLICIT_ACCESS)),
-      account_password_store_(AccountPasswordStoreFactory::GetForProfile(
-          profile,
-          ServiceAccessType::EXPLICIT_ACCESS)),
-      saved_passwords_presenter_(profile_password_store_,
-                                 account_password_store_),
-      insecure_credentials_manager_(&saved_passwords_presenter_,
-                                    profile_password_store_,
-                                    account_password_store_),
+      saved_passwords_presenter_(presenter),
+      insecure_credentials_manager_(presenter,
+                                    PasswordStoreFactory::GetForProfile(
+                                        profile,
+                                        ServiceAccessType::EXPLICIT_ACCESS),
+                                    AccountPasswordStoreFactory::GetForProfile(
+                                        profile,
+                                        ServiceAccessType::EXPLICIT_ACCESS)),
       bulk_leak_check_service_adapter_(
-          &saved_passwords_presenter_,
+          presenter,
           BulkLeakCheckServiceFactory::GetForProfile(profile_),
           profile_->GetPrefs()) {
-  observed_saved_passwords_presenter_.Add(&saved_passwords_presenter_);
+  observed_saved_passwords_presenter_.Add(saved_passwords_presenter_);
   observed_insecure_credentials_manager_.Add(&insecure_credentials_manager_);
   observed_bulk_leak_check_service_.Add(
       BulkLeakCheckServiceFactory::GetForProfile(profile_));
 
-  // Instructs the presenter and provider to initialize and built their caches.
+  // Instructs the provider to initialize and build its cache.
   // This will soon after invoke OnCompromisedCredentialsChanged(). Calls to
   // GetCompromisedCredentials() that might happen until then will return an
   // empty list.
-  saved_passwords_presenter_.Init();
   insecure_credentials_manager_.Init();
 }
 
@@ -354,7 +352,7 @@
   }
 
   auto progress = base::MakeRefCounted<PasswordCheckProgress>();
-  for (const auto& password : saved_passwords_presenter_.GetSavedPasswords())
+  for (const auto& password : saved_passwords_presenter_->GetSavedPasswords())
     progress->IncrementCounts(password);
 
   password_check_progress_ = progress->GetWeakPtr();
@@ -391,7 +389,7 @@
 
   State state = bulk_leak_check_service_adapter_.GetBulkLeakCheckState();
   SavedPasswordsView saved_passwords =
-      saved_passwords_presenter_.GetSavedPasswords();
+      saved_passwords_presenter_->GetSavedPasswords();
 
   // Handle the currently running case first, only then consider errors.
   if (state == State::kRunning) {
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate.h b/chrome/browser/extensions/api/passwords_private/password_check_delegate.h
index 2d72a81..85dd651 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate.h
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate.h
@@ -24,10 +24,6 @@
 
 class Profile;
 
-namespace password_manager {
-class PasswordStore;
-}
-
 namespace extensions {
 
 extern const char kPasswordCheckDataKey[];
@@ -44,7 +40,8 @@
   using StartPasswordCheckCallback =
       PasswordsPrivateDelegate::StartPasswordCheckCallback;
 
-  explicit PasswordCheckDelegate(Profile* profile);
+  PasswordCheckDelegate(Profile* profile,
+                        password_manager::SavedPasswordsPresenter* presenter);
   PasswordCheckDelegate(const PasswordCheckDelegate&) = delete;
   PasswordCheckDelegate& operator=(const PasswordCheckDelegate&) = delete;
   ~PasswordCheckDelegate() override;
@@ -132,14 +129,10 @@
   // Raw pointer to the underlying profile. Needs to outlive this instance.
   Profile* profile_ = nullptr;
 
-  // Handles to the password stores, powering both |saved_passwords_presenter_|
-  // and |insecure_credentials_manager_|.
-  scoped_refptr<password_manager::PasswordStore> profile_password_store_;
-  scoped_refptr<password_manager::PasswordStore> account_password_store_;
-
   // Used by |insecure_credentials_manager_| to obtain the list of saved
   // passwords.
-  password_manager::SavedPasswordsPresenter saved_passwords_presenter_;
+  password_manager::SavedPasswordsPresenter* saved_passwords_presenter_ =
+      nullptr;
 
   // Used to obtain the list of insecure credentials.
   password_manager::InsecureCredentialsManager insecure_credentials_manager_;
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc b/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
index 4a65754..149aa562 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate_unittest.cc
@@ -35,6 +35,7 @@
 #include "components/password_manager/core/browser/leak_detection/leak_detection_delegate_interface.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
 #include "components/password_manager/core/browser/well_known_change_password_util.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
@@ -81,6 +82,7 @@
 using password_manager::InsecureCredentialTypeFlags;
 using password_manager::IsLeaked;
 using password_manager::LeakCheckCredential;
+using password_manager::SavedPasswordsPresenter;
 using password_manager::TestPasswordStore;
 using password_manager::prefs::kLastTimePasswordCheckCompleted;
 using signin::IdentityTestEnvironment;
@@ -230,6 +232,7 @@
  public:
   PasswordCheckDelegateTest() {
     prefs_.registry()->RegisterDoublePref(kLastTimePasswordCheckCompleted, 0.0);
+    presenter_.Init();
   }
 
   void RunUntilIdle() { task_env_.RunUntilIdle(); }
@@ -241,6 +244,7 @@
   TestingProfile& profile() { return profile_; }
   TestPasswordStore& store() { return *store_; }
   BulkLeakCheckService* service() { return bulk_leak_check_service_; }
+  SavedPasswordsPresenter& presenter() { return presenter_; }
   PasswordCheckDelegate& delegate() { return delegate_; }
 
   void EnableWellKnownChangePasswordFeatureFlag() {
@@ -263,7 +267,8 @@
   scoped_refptr<TestPasswordStore> store_ =
       CreateAndUseTestPasswordStore(&profile_);
   base::test::ScopedFeatureList scoped_feature_list_;
-  PasswordCheckDelegate delegate_{&profile_};
+  SavedPasswordsPresenter presenter_{store_};
+  PasswordCheckDelegate delegate_{&profile_, &presenter_};
 };
 
 }  // namespace
@@ -1119,7 +1124,9 @@
 
   // Use a local delegate instead of |delegate()| so that the Password Store can
   // be set-up prior to constructing the object.
-  PasswordCheckDelegate delegate(&profile());
+  SavedPasswordsPresenter new_presenter(&store());
+  PasswordCheckDelegate delegate(&profile(), &new_presenter);
+  new_presenter.Init();
   delegate.StartPasswordCheck(callback1.Get());
   delegate.StartPasswordCheck(callback2.Get());
   RunUntilIdle();
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
index 49e73e2..f6d875d0 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
@@ -15,13 +15,16 @@
 #include "build/build_config.h"
 #include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h"
 #include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h"
+#include "chrome/browser/password_manager/account_password_store_factory.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
+#include "chrome/browser/password_manager/password_store_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
 #include "chrome/common/extensions/api/passwords_private.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/keyed_service/core/service_access_type.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
 #include "components/password_manager/core/browser/password_list_sorter.h"
 #include "components/password_manager/core/browser/password_manager_features_util.h"
@@ -145,6 +148,12 @@
     : profile_(profile),
       password_manager_presenter_(
           std::make_unique<PasswordManagerPresenter>(this)),
+      saved_passwords_presenter_(PasswordStoreFactory::GetForProfile(
+                                     profile,
+                                     ServiceAccessType::EXPLICIT_ACCESS),
+                                 AccountPasswordStoreFactory::GetForProfile(
+                                     profile,
+                                     ServiceAccessType::EXPLICIT_ACCESS)),
       password_manager_porter_(std::make_unique<PasswordManagerPorter>(
           password_manager_presenter_.get(),
           base::BindRepeating(
@@ -161,13 +170,14 @@
               base::BindRepeating(&PasswordsPrivateDelegateImpl::
                                       OnAccountStorageOptInStateChanged,
                                   base::Unretained(this)))),
-      password_check_delegate_(profile),
+      password_check_delegate_(profile, &saved_passwords_presenter_),
       current_entries_initialized_(false),
       current_exceptions_initialized_(false),
       is_initialized_(false),
       web_contents_(nullptr) {
   password_manager_presenter_->Initialize();
   password_manager_presenter_->UpdatePasswordLists();
+  saved_passwords_presenter_.Init();
 }
 
 PasswordsPrivateDelegateImpl::~PasswordsPrivateDelegateImpl() {}
@@ -209,9 +219,22 @@
   const std::vector<std::string> sort_keys =
       GetSortKeys(password_id_generator_, ids);
 
-  return !ids.empty() && sort_keys.size() == ids.size() &&
-         password_manager_presenter_->ChangeSavedPassword(
-             sort_keys, new_username, new_password);
+  DCHECK(!sort_keys.empty());
+  if (ids.empty() || sort_keys.size() != ids.size())
+    return false;
+
+  std::vector<autofill::PasswordForm> forms_to_change;
+
+  for (const auto& key : sort_keys) {
+    auto forms_for_key = password_manager_presenter_->GetPasswordsForKey(key);
+    if (forms_for_key.empty())
+      return false;
+    for (const auto& form : forms_for_key)
+      forms_to_change.push_back(*form);
+  }
+
+  return saved_passwords_presenter_.EditSavedPasswords(
+      forms_to_change, new_username, new_password);
 }
 
 void PasswordsPrivateDelegateImpl::RemoveSavedPasswords(
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
index 93f8295..45feac9 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
@@ -29,6 +29,7 @@
 #include "components/password_manager/core/browser/password_account_storage_settings_watcher.h"
 #include "components/password_manager/core/browser/reauth_purpose.h"
 #include "components/password_manager/core/browser/ui/export_progress_status.h"
+#include "components/password_manager/core/browser/ui/saved_passwords_presenter.h"
 #include "extensions/browser/extension_function.h"
 
 class Profile;
@@ -146,6 +147,9 @@
   // Used to communicate with the password store.
   std::unique_ptr<PasswordManagerPresenter> password_manager_presenter_;
 
+  // Used to edit passwords and to create |password_check_delegate_|.
+  password_manager::SavedPasswordsPresenter saved_passwords_presenter_;
+
   // Used to control the export and import flows.
   std::unique_ptr<PasswordManagerPorter> password_manager_porter_;
 
diff --git a/chrome/browser/extensions/extension_service_test_base.cc b/chrome/browser/extensions/extension_service_test_base.cc
index 7d8f569..9df5dec 100644
--- a/chrome/browser/extensions/extension_service_test_base.cc
+++ b/chrome/browser/extensions/extension_service_test_base.cc
@@ -13,6 +13,7 @@
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
@@ -39,6 +40,7 @@
 #include "components/sync_preferences/pref_service_syncable.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/pref_names.h"
@@ -109,7 +111,13 @@
         default;
 
 ExtensionServiceTestBase::ExtensionServiceTestBase()
-    : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
+    : ExtensionServiceTestBase(
+          std::make_unique<content::BrowserTaskEnvironment>(
+              base::test::TaskEnvironment::MainThreadType::IO)) {}
+
+ExtensionServiceTestBase::ExtensionServiceTestBase(
+    std::unique_ptr<content::BrowserTaskEnvironment> task_environment)
+    : task_environment_(std::move(task_environment)),
       service_(nullptr),
       testing_local_state_(TestingBrowserProcess::GetGlobal()),
       registry_(nullptr),
diff --git a/chrome/browser/extensions/extension_service_test_base.h b/chrome/browser/extensions/extension_service_test_base.h
index b2b0223c..a966f10 100644
--- a/chrome/browser/extensions/extension_service_test_base.h
+++ b/chrome/browser/extensions/extension_service_test_base.h
@@ -16,7 +16,6 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
-#include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/sandboxed_unpacker.h"
@@ -33,6 +32,7 @@
 
 namespace content {
 class BrowserContext;
+class BrowserTaskEnvironment;
 }
 
 namespace sync_preferences {
@@ -74,6 +74,10 @@
 
  protected:
   ExtensionServiceTestBase();
+  // Alternatively, a subclass may pass a BrowserTaskEnvironment directly.
+  explicit ExtensionServiceTestBase(
+      std::unique_ptr<content::BrowserTaskEnvironment> task_environment);
+
   ~ExtensionServiceTestBase() override;
 
   // testing::Test implementation.
@@ -134,6 +138,9 @@
   }
   const base::FilePath& data_dir() const { return data_dir_; }
   const base::ScopedTempDir& temp_dir() const { return temp_dir_; }
+  content::BrowserTaskEnvironment* task_environment() {
+    return task_environment_.get();
+  }
 
  private:
   // Must be declared before anything that may make use of the
@@ -146,7 +153,7 @@
 
   // The MessageLoop is used by RenderViewHostTestEnabler, so this must be
   // created before it.
-  content::BrowserTaskEnvironment task_environment_;
+  std::unique_ptr<content::BrowserTaskEnvironment> task_environment_;
 
   // Enable creation of WebContents without initializing a renderer.
   content::RenderViewHostTestEnabler rvh_test_enabler_;
diff --git a/chrome/browser/extensions/extension_service_test_with_install.cc b/chrome/browser/extensions/extension_service_test_with_install.cc
index d3136ba..be36273 100644
--- a/chrome/browser/extensions/extension_service_test_with_install.cc
+++ b/chrome/browser/extensions/extension_service_test_with_install.cc
@@ -7,11 +7,13 @@
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/task_environment.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/load_error_reporter.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_creator.h"
 #include "extensions/browser/notification_types.h"
@@ -41,7 +43,14 @@
 }  // namespace
 
 ExtensionServiceTestWithInstall::ExtensionServiceTestWithInstall()
-    : installed_(nullptr),
+    : ExtensionServiceTestWithInstall(
+          std::make_unique<content::BrowserTaskEnvironment>(
+              base::test::TaskEnvironment::MainThreadType::IO)) {}
+
+ExtensionServiceTestWithInstall::ExtensionServiceTestWithInstall(
+    std::unique_ptr<content::BrowserTaskEnvironment> task_environment)
+    : ExtensionServiceTestBase(std::move(task_environment)),
+      installed_(nullptr),
       was_update_(false),
       unloaded_reason_(UnloadedExtensionReason::UNDEFINED),
       expected_extensions_count_(0),
diff --git a/chrome/browser/extensions/extension_service_test_with_install.h b/chrome/browser/extensions/extension_service_test_with_install.h
index ccb33ff..71466f1 100644
--- a/chrome/browser/extensions/extension_service_test_with_install.h
+++ b/chrome/browser/extensions/extension_service_test_with_install.h
@@ -21,6 +21,10 @@
 class FilePath;
 }
 
+namespace content {
+class BrowserTaskEnvironment;
+}
+
 namespace extensions {
 
 // An enhancement of ExtensionServiceTestBase that provides helpers to install,
@@ -29,6 +33,8 @@
                                         public ExtensionRegistryObserver {
  public:
   ExtensionServiceTestWithInstall();
+  explicit ExtensionServiceTestWithInstall(
+      std::unique_ptr<content::BrowserTaskEnvironment> task_environment);
   ~ExtensionServiceTestWithInstall() override;
 
  protected:
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 27c7f41..4855b08 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2155,9 +2155,9 @@
 const char kSmoothScrollingDescription[] =
     "Animate smoothly when scrolling page content.";
 
-const char kSmsReceiverCrossDeviceName[] = "SMS Receiver Cross Device";
-const char kSmsReceiverCrossDeviceDescription[] =
-    "Enable the SMS Receiver API to work across devices";
+const char kWebOTPCrossDeviceName[] = "WebOTP Cross Device";
+const char kWebOTPCrossDeviceDescription[] =
+    "Enable the WebOTP API to work across devices";
 
 const char kSpeculativeServiceWorkerStartOnQueryInputName[] =
     "Enable speculative start of a service worker when a search is predicted.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 9d0ba177..be6a9035 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1241,8 +1241,8 @@
 extern const char kSmoothScrollingName[];
 extern const char kSmoothScrollingDescription[];
 
-extern const char kSmsReceiverCrossDeviceName[];
-extern const char kSmsReceiverCrossDeviceDescription[];
+extern const char kWebOTPCrossDeviceName[];
+extern const char kWebOTPCrossDeviceDescription[];
 
 extern const char kSpeculativeServiceWorkerStartOnQueryInputName[];
 extern const char kSpeculativeServiceWorkerStartOnQueryInputDescription[];
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
index 5870637..67dad49 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -235,19 +235,15 @@
 
   if (first_is_lookalike &&
       ShouldBlockLookalikeUrlNavigation(first_match_type)) {
-    RecordUMAFromMatchType(first_match_type);
     return ShowInterstitial(first_suggested_url, first_url, source_id,
                             first_match_type);
   }
 
   if (last_is_lookalike && ShouldBlockLookalikeUrlNavigation(last_match_type)) {
-    RecordUMAFromMatchType(last_match_type);
     return ShowInterstitial(last_suggested_url, last_url, source_id,
                             last_match_type);
   }
 
-  RecordUMAFromMatchType(first_is_lookalike ? first_match_type
-                                            : last_match_type);
   // Interstitial normally records UKM, but still record when it's not shown.
   RecordUkmForLookalikeUrlBlockingPage(
       source_id, first_is_lookalike ? first_match_type : last_match_type,
@@ -317,6 +313,8 @@
                         &matched_domain, match_type)) {
     DCHECK(!matched_domain.empty());
 
+    RecordUMAFromMatchType(*match_type);
+
     // matched_domain can be a top domain or an engaged domain. Simply use its
     // eTLD+1 as the suggested domain.
     // 1. If matched_domain is a top domain: Top domain list already contains
@@ -346,6 +344,7 @@
   if (ShouldBlockBySpoofCheckResult(navigated_domain)) {
     *match_type = LookalikeUrlMatchType::kFailedSpoofChecks;
     *suggested_url = GURL();
+    RecordUMAFromMatchType(*match_type);
     return true;
   }
 
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
index 11d5c763..33015343 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -497,39 +497,6 @@
            LookalikeUrlMatchType::kTargetEmbedding);
 }
 
-// Same as TargetEmbedding_TopDomain_Match, but has a redirect where the first
-// and last URLs are both target embedding matches. Should only record
-// metrics for the first URL. Regression test for crbug.com/1136296.
-IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
-                       TargetEmbedding_TopDomain_Redirect_Match) {
-  const GURL kNavigatedUrl = GetLongRedirect("google.com-test.com", "site.com",
-                                             "youtube.com-test.com");
-  // UKM will record the final URL of the redirect:
-  const GURL kLastUrl = GetURL("youtube.com-test.com");
-  const GURL kExpectedSuggestedUrl = GetURLWithoutPath("google.com");
-  SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
-
-  // |TestMetricsRecordedAndInterstitialShown| assumes everything should be
-  // recorded if target embedding is not disabled. But only for target embedding
-  // checks, if TargetEmbedding is not explicitly enabled, it should be treated
-  // just like it is disabled. So we make sure an interstitial is not shown if
-  // target embedding is not enabled. And defer to
-  // |TestMetricsRecordedAndInterstitialShown| otherwise.
-  if (!target_embedding_enabled()) {
-    base::HistogramTester histograms;
-    TestInterstitialNotShown(browser(), kNavigatedUrl);
-    histograms.ExpectTotalCount(lookalikes::kHistogramName, 1);
-    histograms.ExpectBucketCount(
-        lookalikes::kHistogramName,
-        NavigationSuggestionEvent::kMatchTargetEmbedding, 1);
-  } else {
-    TestMetricsRecordedAndInterstitialShown(
-        browser(), kNavigatedUrl, kExpectedSuggestedUrl,
-        NavigationSuggestionEvent::kMatchTargetEmbedding);
-  }
-  CheckUkm({kLastUrl}, "MatchType", LookalikeUrlMatchType::kTargetEmbedding);
-}
-
 // Target embedding should not trigger on allowlisted embedder domains.
 IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
                        TargetEmbedding_EmbedderAllowlist) {
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index c2905bf..dd2f51a 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -185,6 +185,9 @@
                            is_persistent, notification, requires_attribution))];
 
   if (!notification.icon().IsEmpty()) {
+    // TODO(crbug/1138176): Resize images by adding a transparent border so that
+    // its dimensions are uniform and do not get resized once sent to the
+    // notification center
     [builder setIcon:notification.icon().ToNSImage()];
   }
 
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.h b/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.h
index 7ef7cb35..1b70c495 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.h
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.h
@@ -10,6 +10,7 @@
 
 #include "base/mac/scoped_nsobject.h"
 #include "chrome/browser/notifications/notification_common.h"
+#include "chrome/browser/notifications/notification_image_retainer.h"
 #include "chrome/browser/notifications/notification_platform_bridge.h"
 
 @class UNNotificationCenterDelegate;
@@ -60,6 +61,9 @@
   // The notification center to use for local banner notifications,
   // this can be overridden in tests.
   base::scoped_nsobject<UNUserNotificationCenter> notification_center_;
+
+  // An object that keeps temp files alive long enough for macOS to pick up.
+  NotificationImageRetainer image_retainer_;
 };
 
 #endif  // CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_PLATFORM_BRIDGE_MAC_UNNOTIFICATION_H_
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.mm b/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.mm
index e83e7d3..e3813a0 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_unnotification.mm
@@ -10,6 +10,7 @@
 #include "base/bind_helpers.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/files/file_path.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/notifications/notification_platform_bridge_mac_utils.h"
@@ -87,6 +88,15 @@
                            /*is_persistent=*/false, notification,
                            requires_attribution))];
 
+  if (!notification.icon().IsEmpty()) {
+    // TODO(crbug/1138176): Resize images by adding a transparent border so that
+    // its dimensions are uniform and do not get resized once sent to the
+    // notification center
+    base::FilePath path =
+        image_retainer_.RegisterTemporaryImage(notification.icon());
+    [builder setIconPath:base::SysUTF8ToNSString(path.value())];
+  }
+
   [builder setOrigin:base::SysUTF8ToNSString(notification.origin_url().spec())];
   [builder setNotificationId:base::SysUTF8ToNSString(notification.id())];
   [builder setProfileId:base::SysUTF8ToNSString(GetProfileId(profile))];
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java
index e8ed35e..f9c73b4b 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java
@@ -62,7 +62,7 @@
     }
 
     // TODO(crbug.com/1102025): Add call from native.
-    void onCompromisedCredentialFound(String signonRealm, GURL origin, String username,
+    void onCompromisedCredentialFound(String signonRealm, GURL associatedUrl, String username,
             String displayOrigin, String displayUsername, String password, String passwordChangeUrl,
             String associatedApp, long creationTime, boolean hasStartableScript,
             boolean hasAutoChangeButton) {
@@ -70,9 +70,10 @@
         assert displayOrigin != null;
         assert username != null;
         assert password != null;
-        mPasswordCheckObserver.onCompromisedCredentialFound(new CompromisedCredential(signonRealm,
-                origin, username, displayOrigin, displayUsername, password, passwordChangeUrl,
-                associatedApp, creationTime, true, false, hasStartableScript, hasAutoChangeButton));
+        mPasswordCheckObserver.onCompromisedCredentialFound(
+                new CompromisedCredential(signonRealm, associatedUrl, username, displayOrigin,
+                        displayUsername, password, passwordChangeUrl, associatedApp, creationTime,
+                        true, false, hasStartableScript, hasAutoChangeButton));
     }
 
     @CalledByNative
@@ -97,13 +98,13 @@
 
     @CalledByNative
     private static void insertCredential(CompromisedCredential[] credentials, int index,
-            String signonRealm, GURL origin, String username, String displayOrigin,
+            String signonRealm, GURL associatedUrl, String username, String displayOrigin,
             String displayUsername, String password, String passwordChangeUrl, String associatedApp,
             long creationTime, boolean leaked, boolean phished, boolean hasStartableScript,
             boolean hasAutoChangeButton) {
-        credentials[index] = new CompromisedCredential(signonRealm, origin, username, displayOrigin,
-                displayUsername, password, passwordChangeUrl, associatedApp, creationTime, leaked,
-                phished, hasStartableScript, hasAutoChangeButton);
+        credentials[index] = new CompromisedCredential(signonRealm, associatedUrl, username,
+                displayOrigin, displayUsername, password, passwordChangeUrl, associatedApp,
+                creationTime, leaked, phished, hasStartableScript, hasAutoChangeButton);
     }
 
     /**
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckChangePasswordHelper.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckChangePasswordHelper.java
index abce141..3c4deae 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckChangePasswordHelper.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckChangePasswordHelper.java
@@ -67,10 +67,13 @@
     /**
      * Launches a CCT with the site the given credential was used on and invokes the script that
      * fixes the compromised credential automatically.
+     *
+     * The associated URL will always contain a valid URL, never an Android app sign-on realm
+     * as scripts will only exist for websites.
      * @param credential A {@link CompromisedCredential}.
      */
     public void launchCctWithScript(CompromisedCredential credential) {
-        Intent intent = buildIntent(credential.getOrigin().getOrigin().getSpec());
+        Intent intent = buildIntent(credential.getAssociatedUrl().getOrigin().getSpec());
         populateAutofillAssistantExtras(intent, credential.getUsername());
         IntentUtils.safeStartActivity(mContext, intent);
     }
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckIconHelper.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckIconHelper.java
index fd64565..8a416bbe 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckIconHelper.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/helper/PasswordCheckIconHelper.java
@@ -99,7 +99,7 @@
         }
         if (!iconOrigin.isValid()) {
             // If neither URL is valid, try the display origin as a last, very weak hope.
-            iconOrigin = credential.getOrigin();
+            iconOrigin = credential.getAssociatedUrl();
             fallbackUrl = credential.getDisplayOrigin();
         }
         return new Pair<>(iconOrigin, fallbackUrl);
diff --git a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java
index 853be50..c3c419dd 100644
--- a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java
+++ b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java
@@ -23,7 +23,7 @@
                 @Override
                 public CompromisedCredential createFromParcel(Parcel in) {
                     final String signonRealm = in.readString();
-                    final GURL origin = GURL.deserialize(in.readString());
+                    final GURL associatedUrl = GURL.deserialize(in.readString());
                     final String username = in.readString();
                     final String displayOrigin = in.readString();
                     final String displayUsername = in.readString();
@@ -38,9 +38,10 @@
                     final boolean hasStartableScript = boolArguments[2];
                     final boolean hasAutoChangeButton = boolArguments[3];
 
-                    return new CompromisedCredential(signonRealm, origin, username, displayOrigin,
-                            displayUsername, password, passwordChangeUrl, associatedApp,
-                            creationTime, leaked, phished, hasStartableScript, hasAutoChangeButton);
+                    return new CompromisedCredential(signonRealm, associatedUrl, username,
+                            displayOrigin, displayUsername, password, passwordChangeUrl,
+                            associatedApp, creationTime, leaked, phished, hasStartableScript,
+                            hasAutoChangeButton);
                 }
 
                 @Override
@@ -50,7 +51,7 @@
             };
 
     private final String mSignonRealm;
-    private final GURL mOrigin;
+    private final GURL mAssociatedUrl;
     private final String mUsername;
     private final String mDisplayOrigin;
     private final String mDisplayUsername;
@@ -65,7 +66,8 @@
 
     /**
      * @param signonRealm The URL leading to the sign-on page.
-     * @param origin The origin used to identify this credential (may be empty).
+     * @param associatedUrl The associated URL to this credential, for android
+     *         credentials without affiliation information it will be empty.
      * @param username The name used to identify this credential (may be empty).
      * @param displayOrigin The origin displayed to the user. Not necessarily a valid URL (e.g.
      *         missing scheme).
@@ -82,11 +84,12 @@
      * @param hasAutoChangeButton True iff the button to automatically change the credential should
      *         be shown.
      */
-    public CompromisedCredential(String signonRealm, GURL origin, String username,
+    public CompromisedCredential(String signonRealm, GURL associatedUrl, String username,
             String displayOrigin, String displayUsername, String password, String passwordChangeUrl,
             String associatedApp, long creationTime, boolean leaked, boolean phished,
             boolean hasStartableScript, boolean hasAutoChangeButton) {
-        assert origin != null : "Credential origin is null! Pass an empty one instead.";
+        assert associatedUrl
+                != null : "Credential associated URL is null! Pass an empty one instead.";
         assert signonRealm != null;
         assert passwordChangeUrl != null : "Change URL may be empty but not null!";
         assert associatedApp != null : "App package name may be empty but not null!";
@@ -98,7 +101,7 @@
                 || !hasAutoChangeButton
             : "Auto change button cannot be shown without a script that can start!";
         mSignonRealm = signonRealm;
-        mOrigin = origin;
+        mAssociatedUrl = associatedUrl;
         mUsername = username;
         mDisplayOrigin = displayOrigin;
         mDisplayUsername = displayUsername;
@@ -121,8 +124,8 @@
         return mUsername;
     }
     @CalledByNative
-    public GURL getOrigin() {
-        return mOrigin;
+    public GURL getAssociatedUrl() {
+        return mAssociatedUrl;
     }
     @CalledByNative
     public String getPassword() {
@@ -161,7 +164,7 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         CompromisedCredential that = (CompromisedCredential) o;
-        return mSignonRealm.equals(that.mSignonRealm) && mOrigin.equals(that.mOrigin)
+        return mSignonRealm.equals(that.mSignonRealm) && mAssociatedUrl.equals(that.mAssociatedUrl)
                 && mUsername.equals(that.mUsername) && mDisplayOrigin.equals(that.mDisplayOrigin)
                 && mDisplayUsername.equals(that.mDisplayUsername)
                 && mPassword.equals(that.mPassword)
@@ -175,18 +178,18 @@
     @Override
     public String toString() {
         return "CompromisedCredential{"
-                + "signonRealm='" + mSignonRealm + ", origin='" + mOrigin + '\'' + '\''
-                + ", username='" + mUsername + '\'' + ", displayOrigin='" + mDisplayOrigin + '\''
-                + ", displayUsername='" + mDisplayUsername + '\'' + ", password='" + mPassword
-                + '\'' + ", passwordChangeUrl='" + mPasswordChangeUrl + '\'' + ", associatedApp='"
-                + mAssociatedApp + '\'' + ", creationTime=" + mCreationTime + ", leaked=" + mLeaked
-                + ", phished=" + mPhished + ", hasStartableScript=" + mHasStartableScript
-                + ", hasAutoChangeButton=" + mHasAutoChangeButton + '}';
+                + "signonRealm='" + mSignonRealm + ", associatedUrl='" + mAssociatedUrl + '\''
+                + '\'' + ", username='" + mUsername + '\'' + ", displayOrigin='" + mDisplayOrigin
+                + '\'' + ", displayUsername='" + mDisplayUsername + '\'' + ", password='"
+                + mPassword + '\'' + ", passwordChangeUrl='" + mPasswordChangeUrl + '\''
+                + ", associatedApp='" + mAssociatedApp + '\'' + ", creationTime=" + mCreationTime
+                + ", leaked=" + mLeaked + ", phished=" + mPhished + ", hasStartableScript="
+                + mHasStartableScript + ", hasAutoChangeButton=" + mHasAutoChangeButton + '}';
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSignonRealm, mOrigin.getPossiblyInvalidSpec(), mUsername,
+        return Objects.hash(mSignonRealm, mAssociatedUrl.getPossiblyInvalidSpec(), mUsername,
                 mDisplayOrigin, mDisplayUsername, mPassword, mPasswordChangeUrl, mAssociatedApp,
                 mCreationTime, mLeaked, mPhished, mHasStartableScript, mHasAutoChangeButton);
     }
@@ -194,7 +197,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mSignonRealm);
-        parcel.writeString(mOrigin.serialize());
+        parcel.writeString(mAssociatedUrl.serialize());
         parcel.writeString(mUsername);
         parcel.writeString(mDisplayOrigin);
         parcel.writeString(mDisplayUsername);
diff --git a/chrome/browser/password_check/android/password_check_bridge.cc b/chrome/browser/password_check/android/password_check_bridge.cc
index 1724d4ff..0511f54 100644
--- a/chrome/browser/password_check/android/password_check_bridge.cc
+++ b/chrome/browser/password_check/android/password_check_bridge.cc
@@ -24,7 +24,7 @@
       ConvertJavaStringToUTF8(
           env, Java_CompromisedCredential_getSignonRealm(env, credential)),
       *url::GURLAndroid::ToNativeGURL(
-          env, Java_CompromisedCredential_getOrigin(env, credential)),
+          env, Java_CompromisedCredential_getAssociatedUrl(env, credential)),
       ConvertJavaStringToUTF16(
           env, Java_CompromisedCredential_getUsername(env, credential)),
       ConvertJavaStringToUTF16(
diff --git a/chrome/browser/password_check/android/password_check_manager.cc b/chrome/browser/password_check/android/password_check_manager.cc
index 1afa9fc..fbe2f3d 100644
--- a/chrome/browser/password_check/android/password_check_manager.cc
+++ b/chrome/browser/password_check/android/password_check_manager.cc
@@ -229,17 +229,6 @@
   auto facet = password_manager::FacetURI::FromPotentiallyInvalidSpec(
       credential.signon_realm);
 
-  ui_credential.display_username = GetDisplayUsername(credential.username);
-  ui_credential.has_startable_script =
-      !credential.username.empty() && ShouldFetchPasswordScripts() &&
-      password_script_fetcher_->IsScriptAvailable(
-          url::Origin::Create(credential.url.GetOrigin()),
-          version_info::GetVersion());
-  ui_credential.has_auto_change_button =
-      ui_credential.has_startable_script &&
-      base::FeatureList::IsEnabled(
-          password_manager::features::kPasswordChangeInSettings);
-
   if (facet.IsValidAndroidFacetURI()) {
     const PasswordForm& android_form =
         insecure_credentials_manager_.GetSavedPasswordsFor(credential)[0];
@@ -256,6 +245,12 @@
       ui_credential.display_origin =
           base::UTF8ToUTF16(android_form.app_display_name);
     }
+    // In case no affiliated_web_realm could be obtained we should not have an
+    // associated url for android credential.
+    ui_credential.url = android_form.affiliated_web_realm.empty()
+                            ? GURL::EmptyGURL()
+                            : GURL(android_form.affiliated_web_realm);
+
   } else {
     ui_credential.display_origin = url_formatter::FormatUrl(
         credential.url.GetOrigin(),
@@ -268,6 +263,17 @@
         password_manager::CreateChangePasswordUrl(ui_credential.url).spec();
   }
 
+  ui_credential.display_username = GetDisplayUsername(credential.username);
+  ui_credential.has_startable_script =
+      !credential.username.empty() && ShouldFetchPasswordScripts() &&
+      password_script_fetcher_->IsScriptAvailable(
+          url::Origin::Create(ui_credential.url.GetOrigin()),
+          version_info::GetVersion());
+  ui_credential.has_auto_change_button =
+      ui_credential.has_startable_script &&
+      base::FeatureList::IsEnabled(
+          password_manager::features::kPasswordChangeInSettings);
+
   return ui_credential;
 }
 
diff --git a/chrome/browser/password_check/android/password_check_manager_unittest.cc b/chrome/browser/password_check/android/password_check_manager_unittest.cc
index 8a3d42d2..0b26f90 100644
--- a/chrome/browser/password_check/android/password_check_manager_unittest.cc
+++ b/chrome/browser/password_check/android/password_check_manager_unittest.cc
@@ -178,6 +178,7 @@
 auto ExpectCompromisedCredentialForUI(
     const base::string16& display_username,
     const base::string16& display_origin,
+    const GURL& url,
     const base::Optional<std::string>& package_name,
     const base::Optional<std::string>& change_password_url,
     InsecureCredentialTypeFlags insecure_type,
@@ -196,7 +197,8 @@
   return AllOf(
       Field(&CompromisedCredentialForUI::display_username, display_username),
       Field(&CompromisedCredentialForUI::display_origin, display_origin),
-      package_name_field_matcher, change_password_url_field_matcher,
+      Field(&CompromisedCredentialForUI::url, url), package_name_field_matcher,
+      change_password_url_field_matcher,
       Field(&CompromisedCredentialForUI::insecure_type, insecure_type),
       Field(&CompromisedCredentialForUI::has_startable_script,
             has_startable_script),
@@ -340,7 +342,7 @@
       manager().GetCompromisedCredentials(),
       ElementsAre(ExpectCompromisedCredentialForUI(
           base::ASCIIToUTF16(kUsername1), base::ASCIIToUTF16("example.com"),
-          base::nullopt, "https://example.com/",
+          GURL(kExampleCom), base::nullopt, "https://example.com/",
           InsecureCredentialTypeFlags::kCredentialLeaked,
           /*has_startable_script=*/false,
           /*has_auto_change_button=*/false)));
@@ -365,13 +367,14 @@
       UnorderedElementsAre(
           ExpectCompromisedCredentialForUI(
               base::ASCIIToUTF16(kUsername1),
-              base::ASCIIToUTF16("App (com.example.app)"), "com.example.app",
-              base::nullopt, InsecureCredentialTypeFlags::kCredentialLeaked,
+              base::ASCIIToUTF16("App (com.example.app)"), GURL::EmptyGURL(),
+              "com.example.app", base::nullopt,
+              InsecureCredentialTypeFlags::kCredentialLeaked,
               /*has_startable_script=*/false,
               /*has_auto_change_button=*/false),
           ExpectCompromisedCredentialForUI(
               base::ASCIIToUTF16(kUsername2), base::ASCIIToUTF16("Example App"),
-              "com.example.app", base::nullopt,
+              GURL(kExampleCom), "com.example.app", base::nullopt,
               InsecureCredentialTypeFlags::kCredentialLeaked,
               /*has_startable_script=*/false,
               /*has_auto_change_button=*/false)));
@@ -427,7 +430,7 @@
       manager().GetCompromisedCredentials(),
       ElementsAre(ExpectCompromisedCredentialForUI(
           base::ASCIIToUTF16(kUsername1), base::ASCIIToUTF16("example.com"),
-          base::nullopt, "https://example.com/",
+          GURL(kExampleCom), base::nullopt, "https://example.com/",
           InsecureCredentialTypeFlags::kCredentialLeaked,
           /*has_startable_script=*/false,
           /*has_auto_change_button=*/false)));
@@ -457,7 +460,7 @@
       manager().GetCompromisedCredentials(),
       ElementsAre(ExpectCompromisedCredentialForUI(
           base::ASCIIToUTF16(kUsername1), base::ASCIIToUTF16("example.com"),
-          base::nullopt, "https://example.com/",
+          GURL(kExampleCom), base::nullopt, "https://example.com/",
           InsecureCredentialTypeFlags::kCredentialLeaked,
           /*has_startable_script=*/true,
           /*has_auto_change_button=*/true)));
@@ -489,7 +492,7 @@
       manager().GetCompromisedCredentials(),
       ElementsAre(ExpectCompromisedCredentialForUI(
           base::ASCIIToUTF16("No username"), base::ASCIIToUTF16("example.com"),
-          base::nullopt, "https://example.com/",
+          GURL(kExampleCom), base::nullopt, "https://example.com/",
           InsecureCredentialTypeFlags::kCredentialLeaked,
           /*has_startable_script=*/false,
           /*has_auto_change_button=*/false)));
@@ -522,7 +525,7 @@
       manager().GetCompromisedCredentials(),
       ElementsAre(ExpectCompromisedCredentialForUI(
           base::ASCIIToUTF16(kUsername1), base::ASCIIToUTF16("example.com"),
-          base::nullopt, "https://example.com/",
+          GURL(kExampleCom), base::nullopt, "https://example.com/",
           InsecureCredentialTypeFlags::kCredentialLeaked,
           /*has_startable_script=*/true,
           /*has_auto_change_button=*/false)));
@@ -553,7 +556,7 @@
       manager().GetCompromisedCredentials(),
       ElementsAre(ExpectCompromisedCredentialForUI(
           base::ASCIIToUTF16(kUsername1), base::ASCIIToUTF16("example.com"),
-          base::nullopt, "https://example.com/",
+          GURL(kExampleCom), base::nullopt, "https://example.com/",
           InsecureCredentialTypeFlags::kCredentialLeaked,
           /*has_startable_script=*/false,
           /*has_auto_change_button=*/false)));
diff --git a/chrome/browser/password_manager/password_store_utils.cc b/chrome/browser/password_manager/password_store_utils.cc
index 0059869..212e87f1 100644
--- a/chrome/browser/password_manager/password_store_utils.cc
+++ b/chrome/browser/password_manager/password_store_utils.cc
@@ -16,6 +16,7 @@
 using password_manager::metrics_util::IsUsernameChanged;
 }  // namespace
 
+// TODO(crbug.com/1136815): Drop this, once Android no longer use it.
 void EditSavedPasswords(
     Profile* profile,
     const base::span<const std::unique_ptr<password_manager::PasswordForm>>
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index 48780bd..6b65833 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -3160,3 +3160,60 @@
         .replay();
   });
 });
+
+TEST_F('ChromeVoxBackgroundTest', 'FocusOnWebAreaIgnoresEvents', function() {
+  const site = `
+    <div role="application" tabindex=0 aria-label="container">
+      <select>
+        <option>apple</option>
+        <option>grape</option>
+        <option>pear</option>
+      </select>
+    </div>
+    <p>go</p>
+    <script>
+      let counter = 0;
+      document.body.getElementsByTagName('p')[0].addEventListener('click',
+          e => {
+            document.body.getElementsByTagName('select')[0].selectedIndex =
+                ++counter % 3;
+          });
+    </script>
+  `;
+  this.runWithLoadedTree(site, async function(root) {
+    const application = root.find({role: RoleType.APPLICATION});
+    const popUpButton = root.find({role: RoleType.POP_UP_BUTTON});
+    const p = root.find({role: RoleType.PARAGRAPH});
+
+    // Move focus to the select, which honors value changes through
+    // FocusAutomationHandler.
+    popUpButton.focus();
+    await TestUtils.waitForSpeech('apple');
+
+    // Clicking the paragraph programmatically changes the select value.
+    p.doDefault();
+    await TestUtils.waitForSpeech('grape');
+    assertEquals(
+        RoleType.POP_UP_BUTTON,
+        ChromeVoxState.instance.currentRange.start.node.role);
+
+    // Now, move focus to the application which is a parent of the select.
+    application.focus();
+    await TestUtils.waitForSpeech('container');
+
+    // Hook into the speak call, to see what comes next.
+    let nextSpeech;
+    ChromeVox.tts.speak = textString => {
+      nextSpeech = textString;
+    };
+
+    // Trigger another value update for the select.
+    p.doDefault();
+
+    // This comes when the select's value changes.
+    await TestUtils.waitForEvent(application, EventType.VALUE_CHANGED);
+
+    // Nothing should have been spoken.
+    assertEquals(undefined, nextSpeech);
+  });
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
index f9cc2fa..4db55d4 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/focus_automation_handler.js
@@ -36,6 +36,13 @@
    */
   onFocus(evt) {
     this.removeAllListeners();
+
+    // Events on roots and web views can be very noisy due to bubling. Ignore
+    // these.
+    if (evt.target.root == evt.target || evt.target.role == RoleType.WEB_VIEW) {
+      return;
+    }
+
     this.previousActiveDescendant_ = evt.target.activeDescendant;
     this.node_ = evt.target;
     this.addListener_(
@@ -98,6 +105,11 @@
       return;
     }
 
+    // Focus might be on a container above the popup button.
+    if (this.node_ != evt.target) {
+      return;
+    }
+
     // If it has children, that means a menu is showing.
     if (evt.target.firstChild) {
       return;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/common.js b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/common.js
index 5a42485..3e120f05 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/common.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/common.js
@@ -66,8 +66,39 @@
     keyEvent.stopPropagation = _ => {};
     return keyEvent;
   }
-}
 
+  /**
+   * Returns a promise which gets resolved when ChromeVox speaks the given
+   * string.
+   * @param {string} textStringToWaitFor
+   * @return {!Promise}
+   */
+  static waitForSpeech(textStringToWaitFor) {
+    return new Promise(resolve => {
+      ChromeVox.tts.speak = (textString) => {
+        if (textString == textStringToWaitFor) {
+          resolve();
+        }
+      };
+    });
+  }
+
+  /**
+   * Waits for the specified event on the given node.
+   * @param {!chrome.automation.AutomationNode} node
+   * @param {chrome.automation.EventType} eventType
+   * @return {!Promise}
+   */
+  static waitForEvent(node, eventType) {
+    return new Promise(resolve => {
+      const listener = () => {
+        node.removeEventListener(eventType, listener);
+        resolve();
+      };
+      node.addEventListener(eventType, listener);
+    });
+  }
+}
 
 /**
  * Similar to |TEST_F|. Generates a test for the given |testFixture|,
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.html b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.html
index 92f2fc3..9f00adb 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.html
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.html
@@ -22,7 +22,7 @@
           </div>
           <div class="flex layout horizontal center">
             <div id="ready-image-container">
-              <img id="ready-img" class="oobe-illustration"
+              <img id="ready-img" class="oobe-illustration" aria-hidden="true"
                   src="assistant_ready.svg">
             </div>
             <div class="vertical-line"></div>
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js
index d02ab06..135a4a0 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_get_more.js
@@ -123,13 +123,17 @@
       zippy.setAttribute('toggle-style', true);
       zippy.id = 'zippy-' + data['id'];
       var title = document.createElement('div');
+      title.id = 'title-' + data['id'];
       title.slot = 'title';
       title.textContent = data['title'];
+      title.setAttribute('aria-hidden', 'true');
       zippy.appendChild(title);
 
       var toggle = document.createElement('cr-toggle');
       toggle.slot = 'toggle';
       toggle.id = 'toggle-' + data['id'];
+      toggle.setAttribute('aria-labelledby', 'title-' + data['id']);
+      toggle.setAttribute('aria-describedby', 'description-' + data['id']);
       if (data['defaultEnabled']) {
         toggle.setAttribute('checked', '');
       }
@@ -139,8 +143,10 @@
       zippy.appendChild(toggle);
 
       var description = document.createElement('div');
+      description.id = 'description-' + data['id'];
       description.slot = 'content';
       description.textContent = data['description'];
+      description.setAttribute('aria-hidden', 'true');
       if (data['legalText']) {
         var legalText = document.createElement('p');
         legalText.textContent = data['legalText'];
diff --git a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
index dafce26..6d19178 100644
--- a/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
+++ b/chrome/browser/resources/chromeos/login/arc_terms_of_service.html
@@ -4,6 +4,7 @@
 
 <link rel="import" href="chrome://oobe/custom_elements.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
@@ -26,14 +27,16 @@
               class="oobe-tos-webview"></webview>
         </div>
         <div id="arcPolicyLink" class="arc-tos-content">
-          <a href="#">[[i18nDynamic(locale, 'arcPolicyLink')]]</a>
+          <a class="oobe-local-link" is="action-link">
+            [[i18nDynamic(locale, 'arcPolicyLink')]]
+          </a>
         </div>
         <div class="parameter-section arc-tos-content"
             hidden="[[isMetricsHidden]]">
           <p>
             <span>[[i18nDynamic(locale, metricsTextKey)]]</span>
-            <a href="#" id="learnMoreLinkMetrics"
-                on-click="onMetricsLearnMoreTap_">
+            <a id="learnMoreLinkMetrics" class="oobe-local-link"
+                on-click="onMetricsLearnMoreTap_" is="action-link">
               [[i18nDynamic(locale, 'arcLearnMoreText')]]
             </a>
           </p>
@@ -53,8 +56,8 @@
               disabled="[[backupRestoreManaged]]">
             <p>
               <span>[[i18nDynamic(locale, 'arcTextBackupRestore')]]</span>
-              <a href="#" id="learnMoreLinkBackupRestore"
-                on-click="onBackupRestoreLearnMoreTap_">
+              <a id="learnMoreLinkBackupRestore" class="oobe-local-link"
+                  on-click="onBackupRestoreLearnMoreTap_" is="action-link">
                 [[i18nDynamic(locale, 'arcLearnMoreText')]]
               </a>
             </p>
@@ -71,8 +74,8 @@
                 disabled="[[locationServiceManaged]]">
               <p>
                 <span>[[i18nDynamic(locale, 'arcTextLocationService')]]</span>
-                <a href="#" id="learnMoreLinkLocationService"
-                    on-click="onLocationServiceLearnMoreTap_">
+                <a id="learnMoreLinkLocationService" class="oobe-local-link"
+                    on-click="onLocationServiceLearnMoreTap_" is="action-link">
                   [[i18nDynamic(locale, 'arcLearnMoreText')]]
                 </a>
               </p>
diff --git a/chrome/browser/resources/chromeos/login/enterprise_enrollment.html b/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
index 8a502f5..ad6da245 100644
--- a/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
+++ b/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
@@ -4,6 +4,7 @@
 
 <link rel="import" href="chrome://oobe/custom_elements.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
 <dom-module id="enterprise-enrollment">
@@ -168,7 +169,7 @@
             <span id="attributePromptMessage">
               [[i18nDynamic(locale, 'oauthEnrollAttributeExplanation')]]
             </span>
-            <a href="#" id="learnMoreLink" class="oobe-local-link"
+            <a id="learnMoreLink" class="oobe-local-link" is="action-link"
                 on-click="onLearnMore_">
               [[i18nDynamic(locale, 'oauthEnrollExplainAttributeLink')]]
             </a>
diff --git a/chrome/browser/resources/chromeos/login/notification_card.html b/chrome/browser/resources/chromeos/login/notification_card.html
index 5cb9a94..7b01112 100644
--- a/chrome/browser/resources/chromeos/login/notification_card.html
+++ b/chrome/browser/resources/chromeos/login/notification_card.html
@@ -5,6 +5,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
@@ -58,7 +59,8 @@
             hidden$="[[!buttonLabel]]">
           <span>[[buttonLabel]]</span>
         </gaia-button>
-        <a href="#" on-click="linkClicked_" hidden$="{{!linkLabel}}">
+        <a on-click="linkClicked_" hidden$="{{!linkLabel}}" is="action-link"
+            class="oobe-local-link">
           <span>[[linkLabel]]</span>
         </a>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_a11y_option.html b/chrome/browser/resources/chromeos/login/oobe_a11y_option.html
index c4040004e..bac8d09 100644
--- a/chrome/browser/resources/chromeos/login/oobe_a11y_option.html
+++ b/chrome/browser/resources/chromeos/login/oobe_a11y_option.html
@@ -9,17 +9,18 @@
     <link rel="stylesheet" href="oobe_a11y_option.css">
     <div id="elementBox" class="layout horizontal">
       <div class="flex layout vertical center-justified">
-        <div id="titleContainer">
+        <div id="titleContainer" aria-hidden="true">
           <slot name="title"></slot>
         </div>
-        <div class="display-value" hidden="[[!checked]]">
+        <div class="display-value" hidden="[[!checked]]" aria-hidden="true">
           <slot name="checked-value"></slot>
         </div>
-        <div class="display-value" hidden="[[checked]]">
-          <slot name="unchecked-value"></slot>
+        <div class="display-value" hidden="[[checked]]" aria-hidden="true">
+          <slot  name="unchecked-value"></slot>
         </div>
       </div>
       <cr-toggle id="button" checked="{{checked}}"
+          aria-labeledby="titleContainer"
           aria-label$="[[labelForAria]]">
       </cr-toggle>
     </div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_adb_sideloading_screen.html b/chrome/browser/resources/chromeos/login/oobe_adb_sideloading_screen.html
index 72ecb53a..ebae966 100644
--- a/chrome/browser/resources/chromeos/login/oobe_adb_sideloading_screen.html
+++ b/chrome/browser/resources/chromeos/login/oobe_adb_sideloading_screen.html
@@ -2,6 +2,8 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
+<link rel="import" href="chrome://resources/html/action_link.html">
+
 <dom-module id="oobe-adb-sideloading-screen">
   <template>
     <link rel="stylesheet" href="chrome://resources/css/throbber.css">
@@ -22,7 +24,8 @@
       <div slot="subtitle">
         <div>
           [[i18nDynamic(locale, 'enableAdbSideloadingSetupMessage')]]
-          <a href="#" on-click="onLearnMoreTap_" class="oobe-local-link">
+          <a on-click="onLearnMoreTap_" class="oobe-local-link"
+              is="action-link">
             [[i18nDynamic(locale, 'enableAdbSideloadingLearnMore')]]
           </a>
         </div>
@@ -55,7 +58,7 @@
       </hd-iron-icon>
       <div slot="subtitle">
         [[i18nDynamic(locale, 'enableAdbSideloadingErrorMessage')]]
-        <a href="#" on-click="onLearnMoreTap_" class="oobe-local-link">
+        <a on-click="onLearnMoreTap_" class="oobe-local-link" is="action-link">
           [[i18nDynamic(locale, 'enableAdbSideloadingLearnMore')]]
         </a>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_eula.html b/chrome/browser/resources/chromeos/login/oobe_eula.html
index 34c4433..bd62a400a 100644
--- a/chrome/browser/resources/chromeos/login/oobe_eula.html
+++ b/chrome/browser/resources/chromeos/login/oobe_eula.html
@@ -1,11 +1,13 @@
 <!-- Copyright 2016 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. -->
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
 
 <dom-module id="oobe-eula-md">
   <template>
@@ -36,12 +38,11 @@
             on-contentload="onFrameLoad_">
         </webview>
         <div id="footer-more" class="layout vertical">
-          <a href="#" id="additionalTerms" on-click="onAdditionalTermsClicked_"
-              class="oobe-local-link">
+          <a id="additionalTerms" on-click="onAdditionalTermsClicked_"
+              class="oobe-local-link" is="action-link">
             [[i18nDynamic(locale, 'oobeEulaAditionalTerms')]]
           </a>
-          <a id="securitySettings" href="#"
-              class="oobe-local-link"
+          <a id="securitySettings" class="oobe-local-link" is="action-link"
               on-click="onSecuritySettingsClicked_">
             [[i18nDynamic(locale, 'eulaSystemSecuritySettings')]]
           </a>
@@ -53,9 +54,8 @@
                 <span id="usageStatsLabel">
                   [[i18nDynamic(locale, 'checkboxLogging')]]
                 </span>
-                <a id="learnMore" href="#"
-                    on-click="onUsageStatsHelpLinkClicked_"
-                    class="oobe-local-link">
+                <a id="learnMore" class="oobe-local-link" is="action-link"
+                    on-click="onUsageStatsHelpLinkClicked_">
                   [[i18nDynamic(locale, 'learnMore')]]
                 </a>
               </div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.html b/chrome/browser/resources/chromeos/login/oobe_reset.html
index a4fca4d..470ccbf 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.html
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.html
@@ -3,6 +3,7 @@
      found in the LICENSE file. -->
 
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
@@ -30,10 +31,10 @@
         </div>
         <!-- Help Link - Hidden when reverting/rolling back. -->
         <if expr="_google_chrome">
-          <a id="powerwash-help-link-md" href="#"
-              class="oobe-local-link"
-              hidden="[[inRevertState_]]"
-             on-click="onLearnMoreTap_">[[i18nDynamic(locale, 'learnMore')]]
+          <a id="powerwash-help-link-md" class="oobe-local-link"
+              hidden="[[inRevertState_]]" is="action-link"
+              on-click="onLearnMoreTap_">
+            [[i18nDynamic(locale, 'learnMore')]]
           </a>
         </if>
         <!-- Spinner - Shown when the revert process is ongoing -->
@@ -67,10 +68,10 @@
                 [[i18nDynamic(locale, 'resetTPMFirmwareUpdate')]]
               </span>
               <if expr="_google_chrome">
-                <a href="#"
-                    class="oobe-local-link"
-                    on-click="onTPMFirmwareUpdateLearnMore_"
-                    >[[i18nDynamic(locale, 'learnMore')]]</a>
+                <a class="oobe-local-link" is="action-link"
+                    on-click="onTPMFirmwareUpdateLearnMore_">
+                  [[i18nDynamic(locale, 'learnMore')]]
+                </a>
               </if>
             </div>
           </cr-checkbox>
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_enable_debugging.html b/chrome/browser/resources/chromeos/login/oobe_screen_enable_debugging.html
index 7d80e5a9..f1f3580 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_enable_debugging.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_enable_debugging.html
@@ -1,3 +1,9 @@
+<!-- Copyright 2014 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. -->
+
+<link rel="import" href="chrome://resources/html/action_link.html">
+
 <div class="step hidden" id="debugging" role="group" hidden
      i18n-values="aria-label:enableDebuggingScreenAccessibleTitle">
   <div class="step-contents" aria-live="polite" aria-atomic="true">
@@ -16,8 +22,9 @@
       <div id="enable-debugging-remove-protection-details"
           class="remove-protection-specific">
         <span i18n-content="enableDebuggingRemveRootfsMessage"></span>
-        <a id="enable-debugging-help-link" href="#"
-            i18n-content="enableDebuggingLearnMore"></a>
+        <a id="enable-debugging-help-link" class="oobe-local-link"
+            is="action-link" i18n-content="enableDebuggingLearnMore">
+        </a>
       </div>
       <div id="enable-debugging-setup-details"
           i18n-content="enableDebuggingSetupMessage"
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.html b/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.html
index b2acfbb2..2782620c 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.html
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome_dialog.html
@@ -3,6 +3,7 @@
      found in the LICENSE file. -->
 
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 
@@ -65,59 +66,59 @@
       <div slot="footer" class="welcome-illustration flex layout vertical center
           center-justified">
         <if expr="chromeos and _google_chrome">
-          <video muted hidden oobe_devices="chromebox"
+          <video muted hidden oobe_devices="chromebox" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="intro"
               class="oobe-illustration"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/chromebox_intro_2x.webm">
           </video>
-          <video muted loop hidden oobe_devices="chromebox"
+          <video muted loop hidden oobe_devices="chromebox" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="loop"
               class="oobe-illustration"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/chromebox_loop_2x.webm">
           </video>
-          <video muted hidden oobe_devices="laptop_G"
+          <video muted hidden oobe_devices="laptop_G" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="intro"
               class="oobe-illustration"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/laptop_G_intro_2x.webm">
           </video>
-          <video muted loop hidden oobe_devices="laptop_G"
+          <video muted loop hidden oobe_devices="laptop_G" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="loop"
               class="oobe-illustration"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/laptop_G_loop_2x.webm">
           </video>
-          <video muted hidden oobe_devices="laptop"
+          <video muted hidden oobe_devices="laptop" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="intro"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/laptop_intro_2x.webm">
           </video>
-          <video muted loop hidden oobe_devices="laptop"
+          <video muted loop hidden oobe_devices="laptop" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="loop"
               class="oobe-illustration"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/laptop_loop_2x.webm">
           </video>
-          <video muted hidden oobe_devices="tablet_G"
+          <video muted hidden oobe_devices="tablet_G" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="intro"
               class="oobe-illustration"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/tablet_G_intro_2x.webm">
           </video>
-          <video muted loop hidden oobe_devices="tablet_G"
+          <video muted loop hidden oobe_devices="tablet_G" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="loop"
               class="oobe-illustration"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/tablet_G_loop_2x.webm">
           </video>
-          <video muted hidden oobe_devices="tablet"
+          <video muted hidden oobe_devices="tablet" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="intro"
               class="oobe-illustration"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/tablet_intro_2x.webm">
           </video>
-          <video muted loop hidden oobe_devices="tablet"
+          <video muted loop hidden oobe_devices="tablet" aria-hidden="true"
               oobe_orientations="portrait,landscape" oobe_types="loop"
               class="oobe-illustration"
               src="../../../internal/resources/chromeos/animations/oobe-welcome/tablet_loop_2x.webm">
           </video>
         </if>
         <div id="enableDebuggingDiv" hidden="[[!debuggingLinkVisible]]">
-          <a href="#" on-click="onDebuggingLinkClicked_" id="enableDebuggingLink"
-             class="oobe-local-link">
+          <a on-click="onDebuggingLinkClicked_" id="enableDebuggingLink"
+             class="oobe-local-link" is="action-link">
             [[i18nDynamic(locale, 'debuggingFeaturesLink')]]
           </a>
         </div>
diff --git a/chrome/browser/resources/chromeos/login/screen_error_message.html b/chrome/browser/resources/chromeos/login/screen_error_message.html
index 3feaff5..95bc61e 100644
--- a/chrome/browser/resources/chromeos/login/screen_error_message.html
+++ b/chrome/browser/resources/chromeos/login/screen_error_message.html
@@ -1,4 +1,5 @@
 <link rel="import" href="chrome://oobe/custom_elements.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
 
 <div id="error-message" class="step hidden show-offline-error" hidden>
 
diff --git a/chrome/browser/resources/chromeos/login/screen_error_message.js b/chrome/browser/resources/chromeos/login/screen_error_message.js
index 572fa64..f05eec8 100644
--- a/chrome/browser/resources/chromeos/login/screen_error_message.js
+++ b/chrome/browser/resources/chromeos/login/screen_error_message.js
@@ -165,7 +165,7 @@
               loadTimeData.getString('deviceType'),
               '<b class="' + CURRENT_NETWORK_NAME_CLASS + '"></b>',
               '<a id="auto-enrollment-learn-more" class="oobe-local-link" ' +
-                  '"href="#">',
+                  '"is="action-link">',
               '</a>');
       $('auto-enrollment-learn-more').onclick = function() {
         chrome.send('launchHelpApp', [HELP_TOPIC_AUTO_ENROLLMENT]);
@@ -175,7 +175,7 @@
           'captivePortalMessage',
           '<b class="' + CURRENT_NETWORK_NAME_CLASS + '"></b>',
           '<a id="' + FIX_CAPTIVE_PORTAL_ID + '" class="oobe-local-link" ' +
-              'href="#">',
+              'is="action-link">',
           '</a>');
       $(FIX_CAPTIVE_PORTAL_ID).onclick = function() {
         self.send(
@@ -186,7 +186,7 @@
           loadTimeData.getStringF(
               'captivePortalProxyMessage',
               '<a id="' + FIX_PROXY_SETTINGS_ID +
-                  '" class="oobe-local-link" href="#">',
+                  '" class="oobe-local-link" is="action-link">',
               '</a>');
       $(FIX_PROXY_SETTINGS_ID).onclick = function() {
         chrome.send('openInternetDetailDialog');
@@ -194,17 +194,18 @@
       $('update-proxy-message-text').innerHTML = loadTimeData.getStringF(
           'updateProxyMessageText',
           '<a id="update-proxy-error-fix-proxy" class="oobe-local-link" ' +
-              'href="#">',
+              'is="action-link">',
           '</a>');
       $('update-proxy-error-fix-proxy').onclick = function() {
         chrome.send('openInternetDetailDialog');
       };
       $('signin-proxy-message-text').innerHTML = loadTimeData.getStringF(
           'signinProxyMessageText',
-          '<a id="' + RELOAD_PAGE_ID + '" class="oobe-local-link" href="#">',
+          '<a id="' + RELOAD_PAGE_ID +
+              '" class="oobe-local-link" is="action-link">',
           '</a>',
           '<a id="signin-proxy-error-fix-proxy" class="oobe-local-link" ' +
-              'href="#">',
+              'is="action-link">',
           '</a>');
       $(RELOAD_PAGE_ID).onclick = function() {
         var gaiaScreen = $(SCREEN_GAIA_SIGNIN);
@@ -217,7 +218,8 @@
 
       $('error-guest-signin').innerHTML = loadTimeData.getStringF(
           'guestSignin',
-          '<a id="error-guest-signin-link" class="oobe-local-link" href="#">',
+          '<a id="error-guest-signin-link" class="oobe-local-link" ' +
+              'is="action-link">',
           '</a>');
       $('error-guest-signin-link')
           .addEventListener('click', this.launchGuestSession_.bind(this));
@@ -225,14 +227,15 @@
       $('error-guest-signin-fix-network').innerHTML = loadTimeData.getStringF(
           'guestSigninFixNetwork',
           '<a id="error-guest-fix-network-signin-link" ' +
-              'class="oobe-local-link" href="#">',
+              'class="oobe-local-link" is="action-link">',
           '</a>');
       $('error-guest-fix-network-signin-link')
           .addEventListener('click', this.launchGuestSession_.bind(this));
 
       $('error-offline-login').innerHTML = loadTimeData.getStringF(
           'offlineLogin',
-          '<a id="error-offline-login-link" class="oobe-local-link" href="#">',
+          '<a id="error-offline-login-link" class="oobe-local-link" ' +
+              'is="action-link">',
           '</a>');
       $('error-offline-login-link').onclick = function() {
         chrome.send('offlineLogin');
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
index 75ef3f70..943d7d6 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.html
@@ -4,6 +4,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
 
 <dom-module id="gaia-signin">
   <template>
@@ -84,9 +85,9 @@
         [[i18nDynamic(locale, 'samlInterstitialMessage',
                       samlInterstitialDomain_)]]
         <p>
-          <a href="#" class="oobe-local-link" on-click="onSamlPageChangeAccount_"
-              id="interstitial-change-account">
-              [[i18nDynamic(locale, 'samlInterstitialChangeAccountLink')]]
+          <a class="oobe-local-link" on-click="onSamlPageChangeAccount_"
+              id="interstitial-change-account" is="action-link">
+            [[i18nDynamic(locale, 'samlInterstitialChangeAccountLink')]]
           </a>
         </p>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/user_creation.html b/chrome/browser/resources/chromeos/login/user_creation.html
index bb1705327..c29afed4 100644
--- a/chrome/browser/resources/chromeos/login/user_creation.html
+++ b/chrome/browser/resources/chromeos/login/user_creation.html
@@ -4,6 +4,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_card_radio_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_group/cr_radio_group.html">
+<link rel="import" href="chrome://resources/html/action_link.html">
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <dom-module id="user-creation">
@@ -116,7 +117,8 @@
       <div slot="subtitle">
         <div>
           [[i18nDynamic(locale, 'childSignInSubtitle')]]
-          <a href="#" class="oobe-local-link" on-click="onLearnMoreClicked_">
+          <a class="oobe-local-link" on-click="onLearnMoreClicked_"
+              is="action-link">
             [[i18nDynamic(locale, 'childSignInLearnMore')]]
           </a>
         </div>
diff --git a/chrome/browser/sharing/sharing_device_registration.cc b/chrome/browser/sharing/sharing_device_registration.cc
index 3a46b94..e687749d 100644
--- a/chrome/browser/sharing/sharing_device_registration.cc
+++ b/chrome/browser/sharing/sharing_device_registration.cc
@@ -318,7 +318,7 @@
 
 bool SharingDeviceRegistration::IsSmsFetcherSupported() const {
 #if defined(OS_ANDROID)
-  return base::FeatureList::IsEnabled(kSmsReceiverCrossDevice);
+  return base::FeatureList::IsEnabled(kWebOTPCrossDevice);
 #endif
 
   return false;
diff --git a/chrome/browser/sharing/sms/sms_fetch_request_handler.h b/chrome/browser/sharing/sms/sms_fetch_request_handler.h
index ab45ddf..e8cca6d 100644
--- a/chrome/browser/sharing/sms/sms_fetch_request_handler.h
+++ b/chrome/browser/sharing/sms/sms_fetch_request_handler.h
@@ -29,7 +29,7 @@
                  SharingMessageHandler::DoneCallback done_callback) override;
 
  private:
-  // Request represents an incoming request from a remote SmsService.
+  // Request represents an incoming request from a remote WebOTPService.
   // It manages subscribing and unsubscribing for SMSes in SmsFetcher and
   // responding to the callback.
   // It also lets SmsFetchRequestHandler know when the request is fulfilled
diff --git a/chrome/browser/sharing/sms/sms_flags.cc b/chrome/browser/sharing/sms/sms_flags.cc
index 13d03b6c..f884266 100644
--- a/chrome/browser/sharing/sms/sms_flags.cc
+++ b/chrome/browser/sharing/sms/sms_flags.cc
@@ -4,5 +4,5 @@
 
 #include "chrome/browser/sharing/sms/sms_flags.h"
 
-const base::Feature kSmsReceiverCrossDevice{"SmsReceiverCrossDevice",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kWebOTPCrossDevice{"WebOTPCrossDevice",
+                                       base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/sharing/sms/sms_flags.h b/chrome/browser/sharing/sms/sms_flags.h
index d540c882..076e037a 100644
--- a/chrome/browser/sharing/sms/sms_flags.h
+++ b/chrome/browser/sharing/sms/sms_flags.h
@@ -9,6 +9,6 @@
 
 // Feature to allow devices to send/receive the sms fetch request from/to a
 // remote device through the SharingService.
-extern const base::Feature kSmsReceiverCrossDevice;
+extern const base::Feature kWebOTPCrossDevice;
 
 #endif  // CHROME_BROWSER_SHARING_SMS_SMS_FLAGS_H_
diff --git a/chrome/browser/sharing/sms/sms_remote_fetcher.cc b/chrome/browser/sharing/sms/sms_remote_fetcher.cc
index 13bb550..de57443 100644
--- a/chrome/browser/sharing/sms/sms_remote_fetcher.cc
+++ b/chrome/browser/sharing/sms/sms_remote_fetcher.cc
@@ -21,7 +21,7 @@
     content::BrowserContext* context,
     const url::Origin& origin,
     base::OnceCallback<void(base::Optional<std::string>)> callback) {
-  if (!base::FeatureList::IsEnabled(kSmsReceiverCrossDevice)) {
+  if (!base::FeatureList::IsEnabled(kWebOTPCrossDevice)) {
     std::move(callback).Run(base::nullopt);
     return;
   }
diff --git a/chrome/browser/sharing/sms/sms_remote_fetcher_unittest.cc b/chrome/browser/sharing/sms/sms_remote_fetcher_unittest.cc
index 3945b3d9..bcc46f2 100644
--- a/chrome/browser/sharing/sms/sms_remote_fetcher_unittest.cc
+++ b/chrome/browser/sharing/sms/sms_remote_fetcher_unittest.cc
@@ -65,7 +65,7 @@
   content::BrowserTaskEnvironment task_environment;
   TestingProfile profile;
 
-  flags.InitAndEnableFeature(kSmsReceiverCrossDevice);
+  flags.InitAndEnableFeature(kWebOTPCrossDevice);
 
   MockSharingService* service = CreateSharingService(&profile);
 
@@ -90,7 +90,7 @@
   content::BrowserTaskEnvironment task_environment;
   TestingProfile profile;
 
-  flags.InitAndEnableFeature(kSmsReceiverCrossDevice);
+  flags.InitAndEnableFeature(kWebOTPCrossDevice);
 
   MockSharingService* service = CreateSharingService(&profile);
 
@@ -129,7 +129,7 @@
   content::BrowserTaskEnvironment task_environment;
   TestingProfile profile;
 
-  flags.InitAndEnableFeature(kSmsReceiverCrossDevice);
+  flags.InitAndEnableFeature(kWebOTPCrossDevice);
 
   MockSharingService* service = CreateSharingService(&profile);
 
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 76f71fb26..29c5257 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -1487,8 +1487,6 @@
 IN_PROC_BROWSER_TEST_F(
     SingleClientBookmarksSyncTestWithEnabledReuploadRemoteBookmarks,
     ShouldReuploadFullTitleAfterRestartOnIncrementalChange) {
-  ASSERT_TRUE(SetupClients());
-
   // Check that the full title was not uploaded to the server yet.
   ASSERT_EQ(
       1u,
@@ -1500,6 +1498,7 @@
                    .bookmark()
                    .has_full_title());
 
+  ASSERT_TRUE(SetupClients());
 #if defined(OS_CHROMEOS)
   // signin::SetRefreshTokenForPrimaryAccount() is needed on ChromeOS in order
   // to get a non-empty refresh token on startup.
diff --git a/chrome/browser/ui/cocoa/notifications/notification_builder_base.h b/chrome/browser/ui/cocoa/notifications/notification_builder_base.h
index f16868b..558ea73 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_builder_base.h
+++ b/chrome/browser/ui/cocoa/notifications/notification_builder_base.h
@@ -29,7 +29,6 @@
 - (void)setTitle:(NSString*)title;
 - (void)setSubTitle:(NSString*)subTitle;
 - (void)setContextMessage:(NSString*)contextMessage;
-- (void)setIcon:(NSImage*)icon;
 - (void)setButtons:(NSString*)primaryButton
     secondaryButton:(NSString*)secondaryButton;
 - (void)setTag:(NSString*)tag;
diff --git a/chrome/browser/ui/cocoa/notifications/notification_builder_base.mm b/chrome/browser/ui/cocoa/notifications/notification_builder_base.mm
index a0211c7..4d24b29d 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_builder_base.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_builder_base.mm
@@ -44,18 +44,6 @@
   }
 }
 
-- (void)setIcon:(NSImage*)icon {
-  if (icon) {
-    if ([icon conformsToProtocol:@protocol(NSSecureCoding)]) {
-      [_notificationData setObject:icon
-                            forKey:notification_constants::kNotificationImage];
-    } else {  // NSImage only conforms to NSSecureCoding from 10.10 onwards.
-      [_notificationData setObject:[icon TIFFRepresentation]
-                            forKey:notification_constants::kNotificationImage];
-    }
-  }
-}
-
 - (void)setButtons:(NSString*)primaryButton
     secondaryButton:(NSString*)secondaryButton {
   DCHECK(primaryButton.length);
diff --git a/chrome/browser/ui/cocoa/notifications/notification_builder_mac.h b/chrome/browser/ui/cocoa/notifications/notification_builder_mac.h
index 11a1ad8..a672946 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_builder_mac.h
+++ b/chrome/browser/ui/cocoa/notifications/notification_builder_mac.h
@@ -39,6 +39,8 @@
                       optionsLabel:(NSString*)optionsLabel
                      settingsLabel:(NSString*)settingsLabel;
 
+// Sets the icon that is displayed in the notification if present
+- (void)setIcon:(NSImage*)icon;
 
 // Returns a notification ready to be displayed out of the provided
 // |notificationData|.
diff --git a/chrome/browser/ui/cocoa/notifications/notification_builder_mac.mm b/chrome/browser/ui/cocoa/notifications/notification_builder_mac.mm
index b746509..8d1abd2 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_builder_mac.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_builder_mac.mm
@@ -30,6 +30,19 @@
   return self;
 }
 
+- (void)setIcon:(NSImage*)icon {
+  if (!icon)
+    return;
+
+  if ([icon conformsToProtocol:@protocol(NSSecureCoding)]) {
+    [_notificationData setObject:icon
+                          forKey:notification_constants::kNotificationIcon];
+  } else {  // NSImage only conforms to NSSecureCoding from 10.10 onwards.
+    [_notificationData setObject:[icon TIFFRepresentation]
+                          forKey:notification_constants::kNotificationIcon];
+  }
+}
+
 - (NSUserNotification*)buildUserNotification {
   base::scoped_nsobject<NSUserNotification> toast(
       [[NSUserNotification alloc] init]);
@@ -44,16 +57,15 @@
 
   // Icon
   if ([_notificationData
-          objectForKey:notification_constants::kNotificationImage]) {
+          objectForKey:notification_constants::kNotificationIcon]) {
     if ([[NSImage class] conformsToProtocol:@protocol(NSSecureCoding)]) {
       NSImage* image = [_notificationData
-          objectForKey:notification_constants::kNotificationImage];
+          objectForKey:notification_constants::kNotificationIcon];
       [toast setContentImage:image];
     } else {  // NSImage only conforms to NSSecureCoding from 10.10 onwards.
       base::scoped_nsobject<NSImage> image([[NSImage alloc]
-          initWithData:
-              [_notificationData
-                  objectForKey:notification_constants::kNotificationImage]]);
+          initWithData:[_notificationData objectForKey:notification_constants::
+                                                           kNotificationIcon]]);
       [toast setContentImage:image];
     }
   }
diff --git a/chrome/browser/ui/cocoa/notifications/notification_constants_mac.h b/chrome/browser/ui/cocoa/notifications/notification_constants_mac.h
index 3b58fa9e..8bb63ff 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_constants_mac.h
+++ b/chrome/browser/ui/cocoa/notifications/notification_constants_mac.h
@@ -12,7 +12,8 @@
 extern NSString* const kNotificationTitle;
 extern NSString* const kNotificationSubTitle;
 extern NSString* const kNotificationInformativeText;
-extern NSString* const kNotificationImage;
+extern NSString* const kNotificationIcon;
+extern NSString* const kNotificationIconPath;
 extern NSString* const kNotificationButtonOne;
 extern NSString* const kNotificationButtonTwo;
 extern NSString* const kNotificationTag;
diff --git a/chrome/browser/ui/cocoa/notifications/notification_constants_mac.mm b/chrome/browser/ui/cocoa/notifications/notification_constants_mac.mm
index 45d63ff..b1fc5dd 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_constants_mac.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_constants_mac.mm
@@ -11,7 +11,8 @@
 NSString* const kNotificationTitle = @"title";
 NSString* const kNotificationSubTitle = @"subtitle";
 NSString* const kNotificationInformativeText = @"informativeText";
-NSString* const kNotificationImage = @"icon";
+NSString* const kNotificationIcon = @"icon";
+NSString* const kNotificationIconPath = @"iconPath";
 NSString* const kNotificationButtonOne = @"buttonOne";
 NSString* const kNotificationButtonTwo = @"buttonTwo";
 NSString* const kNotificationTag = @"tag";
diff --git a/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.h b/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.h
index 9fcec19..d166f32 100644
--- a/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.h
+++ b/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.h
@@ -33,6 +33,9 @@
 API_AVAILABLE(macosx(10.14))
 @interface UNNotificationBuilder : NotificationBuilderBase
 
+// Sets the icon path that is used to display it in the notification if present
+- (void)setIconPath:(NSString*)iconPath;
+
 // Returns a notification ready to be displayed out of the provided
 // |notificationData|.
 - (UNMutableNotificationContent*)buildUserNotification;
diff --git a/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.mm b/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.mm
index 0b2f4715b..7e7f9bc 100644
--- a/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.mm
+++ b/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.mm
@@ -14,6 +14,11 @@
 
 @implementation UNNotificationBuilder
 
+- (void)setIconPath:(NSString*)iconPath {
+  [_notificationData setObject:iconPath
+                        forKey:notification_constants::kNotificationIconPath];
+}
+
 - (UNMutableNotificationContent*)buildUserNotification {
   base::scoped_nsobject<UNMutableNotificationContent> toast(
       [[UNMutableNotificationContent alloc] init]);
@@ -66,6 +71,30 @@
   NSNumber* incognito = [_notificationData
       objectForKey:notification_constants::kNotificationIncognito];
 
+  // Icon
+  if ([_notificationData
+          objectForKey:notification_constants::kNotificationIconPath]) {
+    NSURL* url = [NSURL
+        fileURLWithPath:
+            [_notificationData
+                objectForKey:notification_constants::kNotificationIconPath]];
+    // When the files are saved using NotificationImageRetainer, they're saved
+    // without the .png extension. So |options| here is used to tell the system
+    // that the file is of type PNG, as NotificationImageRetainer converts files
+    // to PNG before writing them.
+    UNNotificationAttachment* attachment = [UNNotificationAttachment
+        attachmentWithIdentifier:notificationId
+                             URL:url
+                         options:@{
+                           UNNotificationAttachmentOptionsTypeHintKey :
+                               (NSString*)kUTTypePNG
+                         }
+                           error:nil];
+
+    if (attachment != nil)
+      [toast setAttachments:@[ attachment ]];
+  }
+
   [toast setUserInfo:@{
     notification_constants::kNotificationOrigin : origin,
     notification_constants::kNotificationId : notificationId,
diff --git a/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac_unittest.mm b/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac_unittest.mm
index 52b2194..b138757 100644
--- a/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac_unittest.mm
+++ b/chrome/browser/ui/cocoa/notifications/unnotification_builder_mac_unittest.mm
@@ -4,12 +4,20 @@
 
 #import <UserNotifications/UserNotifications.h>
 
+#include "base/files/file_util.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_path_override.h"
+#include "base/test/task_environment.h"
 #include "chrome/browser/notifications/notification_handler.h"
+#include "chrome/browser/notifications/notification_image_retainer.h"
 #include "chrome/browser/ui/cocoa/notifications/notification_constants_mac.h"
 #import "chrome/browser/ui/cocoa/notifications/unnotification_builder_mac.h"
+#include "chrome/common/chrome_paths.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/image/image.h"
 
 namespace {
 
@@ -40,6 +48,7 @@
     EXPECT_EQ("hey there", base::SysNSStringToUTF8([content body]));
     EXPECT_EQ("https://www.moe.example.com",
               base::SysNSStringToUTF8([content subtitle]));
+    EXPECT_EQ(0ul, [[content attachments] count]);
   }
 }
 
@@ -193,3 +202,44 @@
         boolValue]);
   }
 }
+
+TEST(UNNotificationBuilderMacTest, TestIcon) {
+  if (@available(macOS 10.14, *)) {
+    base::scoped_nsobject<UNNotificationBuilder> builder =
+        NewTestBuilder(NotificationHandler::Type::WEB_PERSISTENT);
+
+    base::test::TaskEnvironment task_environment(
+        base::test::TaskEnvironment::TimeSource::MOCK_TIME);
+    base::ScopedPathOverride user_data_dir_override(chrome::DIR_USER_DATA);
+
+    auto image_retainer = std::make_unique<NotificationImageRetainer>(
+        task_environment.GetMainThreadTaskRunner(),
+        task_environment.GetMockTickClock());
+
+    SkBitmap icon;
+    icon.allocN32Pixels(64, 64);
+    icon.eraseARGB(255, 100, 150, 200);
+    gfx::Image image = gfx::Image::CreateFrom1xBitmap(icon);
+
+    base::FilePath temp_file = image_retainer->RegisterTemporaryImage(image);
+    ASSERT_FALSE(temp_file.empty());
+    ASSERT_TRUE(base::PathExists(temp_file));
+
+    [builder setIconPath:base::SysUTF8ToNSString(temp_file.value())];
+
+    UNMutableNotificationContent* content = [builder buildUserNotification];
+
+    EXPECT_EQ(1ul, [[content attachments] count]);
+  }
+}
+
+TEST(UNNotificationBuilderMacTest, TestIconWrongPath) {
+  if (@available(macOS 10.14, *)) {
+    base::scoped_nsobject<UNNotificationBuilder> builder =
+        NewTestBuilder(NotificationHandler::Type::WEB_PERSISTENT);
+    [builder setIconPath:@"wrong-path"];
+    UNMutableNotificationContent* content = [builder buildUserNotification];
+
+    EXPECT_EQ(0ul, [[content attachments] count]);
+  }
+}
diff --git a/chrome/browser/ui/passwords/settings/password_manager_presenter.cc b/chrome/browser/ui/passwords/settings/password_manager_presenter.cc
index 1948833..1f4bb8c2 100644
--- a/chrome/browser/ui/passwords/settings/password_manager_presenter.cc
+++ b/chrome/browser/ui/passwords/settings/password_manager_presenter.cc
@@ -295,6 +295,15 @@
   return TryGetPasswordForms(password_map_, index);
 }
 
+base::span<const std::unique_ptr<autofill::PasswordForm>>
+PasswordManagerPresenter::GetPasswordsForKey(
+    const std::string& sort_key) const {
+  auto it = password_map_.find(sort_key);
+  if (it != password_map_.end())
+    return it->second;
+  return {};
+}
+
 std::vector<base::string16> PasswordManagerPresenter::GetUsernamesForRealm(
     size_t index) {
   const password_manager::PasswordForm* current_form =
@@ -325,57 +334,6 @@
   return TryGetPasswordForm(exception_map_, index);
 }
 
-bool PasswordManagerPresenter::ChangeSavedPassword(
-    const std::vector<std::string>& sort_keys,
-    const base::string16& new_username,
-    const base::string16& new_password) {
-  // Make sure new_password is not empty.
-  if (new_password.empty()) {
-    DLOG(ERROR) << "The password is empty.";
-    return false;
-  }
-  DCHECK(!sort_keys.empty());
-
-  std::vector<base::span<const FormVector::value_type>> old_forms_for_sort_keys;
-
-  for (const auto& sort_key : sort_keys) {
-    // Find the equivalence class that needs to be updated.
-    auto it = password_map_.find(sort_key);
-    if (it == password_map_.end())
-      return false;
-
-    const FormVector& old_forms = it->second;
-    DCHECK(!old_forms.empty());
-
-    // In case the username changed, make sure that there exists no other
-    // credential with the same signon_realm and username.
-    auto has_conflicting_username = [&old_forms,
-                                     &new_username](const auto& form) {
-      return new_username == form->username_value &&
-             base::ranges::any_of(old_forms, [&form](const auto& old_form) {
-               return form->signon_realm == old_form->signon_realm &&
-                      form->IsUsingAccountStore() ==
-                          old_form->IsUsingAccountStore();
-             });
-    };
-
-    const base::string16& old_username = old_forms[0]->username_value;
-
-    if (old_username != new_username &&
-        base::ranges::any_of(GetAllPasswords(), has_conflicting_username)) {
-      return false;
-    }
-    old_forms_for_sort_keys.push_back(old_forms);
-  }
-
-  for (const auto& old_forms : old_forms_for_sort_keys) {
-    EditSavedPasswords(password_view_->GetProfile(), old_forms, new_username,
-                       new_password);
-  }
-
-  return true;
-}
-
 void PasswordManagerPresenter::RemoveSavedPassword(size_t index) {
   if (TryRemovePasswordEntries(&password_map_, index)) {
     base::RecordAction(
diff --git a/chrome/browser/ui/passwords/settings/password_manager_presenter.h b/chrome/browser/ui/passwords/settings/password_manager_presenter.h
index c42aa99..ee7d1801 100644
--- a/chrome/browser/ui/passwords/settings/password_manager_presenter.h
+++ b/chrome/browser/ui/passwords/settings/password_manager_presenter.h
@@ -57,6 +57,10 @@
   // Gets the password entry at |index|.
   const password_manager::PasswordForm* GetPassword(size_t index) const;
 
+  // Gets the password entries corresponding to |sort_key|.
+  base::span<const std::unique_ptr<autofill::PasswordForm>> GetPasswordsForKey(
+      const std::string& sort_key) const;
+
   // Gets the vector of password entries with the same credentials and from the
   // same site as the one stored at |index|.
   base::span<const std::unique_ptr<password_manager::PasswordForm>>
@@ -74,11 +78,6 @@
   const password_manager::PasswordForm* GetPasswordException(
       size_t index) const;
 
-  // Changes the password corresponding to |sort_keys|.
-  bool ChangeSavedPassword(const std::vector<std::string>& sort_keys,
-                           const base::string16& new_username,
-                           const base::string16& new_password);
-
   // Removes the saved password entries at |index|, or corresponding to
   // |sort_key|, respectively.
   // TODO(https://crbug.com/778146): Unify these methods and the implementation
diff --git a/chrome/browser/ui/passwords/settings/password_manager_presenter_unittest.cc b/chrome/browser/ui/passwords/settings/password_manager_presenter_unittest.cc
index 8b52a22..a92d7aa9 100644
--- a/chrome/browser/ui/passwords/settings/password_manager_presenter_unittest.cc
+++ b/chrome/browser/ui/passwords/settings/password_manager_presenter_unittest.cc
@@ -59,8 +59,6 @@
 
 constexpr char kExampleCom[] = "https://example.com/";
 constexpr char kExampleOrg[] = "https://example.org/";
-constexpr char kNewPass[] = "new_pass";
-constexpr char kNewUser[] = "new_user";
 constexpr char kPassword[] = "pass";
 constexpr char kPassword2[] = "pass2";
 constexpr char kUsername[] = "user";
@@ -236,29 +234,6 @@
     return form;
   }
 
-  bool ChangeSavedPasswordBySortKey(base::StringPiece url,
-                                    base::StringPiece old_username,
-                                    base::StringPiece old_password,
-                                    base::StringPiece new_username,
-                                    base::StringPiece new_password) {
-    password_manager::PasswordForm temp_form;
-    temp_form.url = GURL(url);
-    temp_form.signon_realm = temp_form.url.GetOrigin().spec();
-    temp_form.username_element = base::ASCIIToUTF16("username");
-    temp_form.password_element = base::ASCIIToUTF16("password");
-    temp_form.username_value = base::ASCIIToUTF16(old_username);
-    temp_form.password_value = base::ASCIIToUTF16(old_password);
-
-    bool result =
-        mock_controller_.GetPasswordManagerPresenter()->ChangeSavedPassword(
-            {password_manager::CreateSortKey(temp_form)},
-            base::ASCIIToUTF16(new_username), base::ASCIIToUTF16(new_password));
-    // The password store posts mutation tasks to a background thread, thus we
-    // need to spin the message loop here.
-    task_environment_.RunUntilIdle();
-    return result;
-  }
-
   void UpdatePasswordLists() {
     mock_controller_.GetPasswordManagerPresenter()->UpdatePasswordLists();
     task_environment_.RunUntilIdle();
@@ -291,189 +266,6 @@
 
 namespace {
 
-TEST_F(PasswordManagerPresenterTest,
-       ChangeSavedPasswordBySortKey_RejectEmptyPassword) {
-  AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
-  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1)));
-  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
-  UpdatePasswordLists();
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kUsername, kPassword)));
-  testing::Mock::VerifyAndClearExpectations(&GetUIController());
-
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser, "");
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kUsername, kPassword)));
-}
-
-TEST_F(PasswordManagerPresenterTest,
-       ChangeSavedPasswordBySortKey_ChangeUsername) {
-  AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
-  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1)));
-  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
-  UpdatePasswordLists();
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kUsername, kPassword)));
-  testing::Mock::VerifyAndClearExpectations(&GetUIController());
-
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser,
-                               kPassword);
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kNewUser, kPassword)));
-}
-
-TEST_F(PasswordManagerPresenterTest,
-       ChangeSavedPasswordBySortKey_ChangeUsernameAndPassword) {
-  AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
-  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1)));
-  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
-  UpdatePasswordLists();
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kUsername, kPassword)));
-  testing::Mock::VerifyAndClearExpectations(&GetUIController());
-
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser,
-                               kNewPass);
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kNewUser, kNewPass)));
-}
-
-TEST_F(PasswordManagerPresenterTest,
-       ChangeSavedPasswordBySortKey_ChangeUsernameAndPasswordForAllEntities) {
-  char kMobileExampleCom[] = "https://m.example.com/";
-  AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
-  AddPasswordEntry(GURL(kMobileExampleCom), kUsername, kPassword);
-  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1)));
-  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
-  UpdatePasswordLists();
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kUsername, kPassword)));
-  testing::Mock::VerifyAndClearExpectations(&GetUIController());
-
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser,
-                               kNewPass);
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kNewUser, kNewPass)));
-  EXPECT_THAT(
-      GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kMobileExampleCom)),
-      ElementsAre(Pair(kNewUser, kNewPass)));
-}
-
-TEST_F(PasswordManagerPresenterTest,
-       ChangeSavedPasswordBySortKey_RejectSameUsernameForSameRealm) {
-  AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
-  AddPasswordEntry(GURL(kExampleCom), kUsername2, kPassword2);
-  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(2)));
-  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
-  UpdatePasswordLists();
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              UnorderedElementsAre(Pair(kUsername, kPassword),
-                                   Pair(kUsername2, kPassword2)));
-  testing::Mock::VerifyAndClearExpectations(&GetUIController());
-
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kUsername2,
-                               kPassword);
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              UnorderedElementsAre(Pair(kUsername, kPassword),
-                                   Pair(kUsername2, kPassword2)));
-}
-
-TEST_F(PasswordManagerPresenterTest,
-       ChangeSavedPasswordBySortKey_DontRejectSameUsernameForDifferentRealm) {
-  AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
-  AddPasswordEntry(GURL(kExampleOrg), kUsername2, kPassword2);
-  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(2)));
-  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
-  UpdatePasswordLists();
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kUsername, kPassword)));
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)),
-              ElementsAre(Pair(kUsername2, kPassword2)));
-  testing::Mock::VerifyAndClearExpectations(&GetUIController());
-
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kUsername2,
-                               kPassword);
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              ElementsAre(Pair(kUsername2, kPassword)));
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)),
-              ElementsAre(Pair(kUsername2, kPassword2)));
-}
-
-TEST_F(PasswordManagerPresenterTest,
-       ChangeSavedPasswordBySortKey_UpdateDuplicates) {
-  AddPasswordEntry(GURL(std::string(kExampleCom) + "pathA"), kUsername,
-                   kPassword);
-  AddPasswordEntry(GURL(std::string(kExampleCom) + "pathB"), kUsername,
-                   kPassword);
-  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(1)));
-  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
-  UpdatePasswordLists();
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              UnorderedElementsAre(Pair(kUsername, kPassword),
-                                   Pair(kUsername, kPassword)));
-  testing::Mock::VerifyAndClearExpectations(&GetUIController());
-
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser,
-                               kNewPass);
-  EXPECT_THAT(
-      GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-      UnorderedElementsAre(Pair(kNewUser, kNewPass), Pair(kNewUser, kNewPass)));
-}
-
-TEST_F(PasswordManagerPresenterTest,
-       ChangeSavedPasswordBySortKey_EditUsernameForTheRightCredential) {
-  AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
-  AddPasswordEntry(GURL(kExampleCom), kUsername2, kPassword);
-  AddPasswordEntry(GURL(kExampleOrg), kUsername, kPassword);
-  AddPasswordEntry(GURL(kExampleOrg), kUsername2, kPassword);
-  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(4)));
-  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
-  UpdatePasswordLists();
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              UnorderedElementsAre(Pair(kUsername, kPassword),
-                                   Pair(kUsername2, kPassword)));
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)),
-              UnorderedElementsAre(Pair(kUsername, kPassword),
-                                   Pair(kUsername2, kPassword)));
-  testing::Mock::VerifyAndClearExpectations(&GetUIController());
-
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kNewUser,
-                               kPassword);
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              UnorderedElementsAre(Pair(kNewUser, kPassword),
-                                   Pair(kUsername2, kPassword)));
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)),
-              UnorderedElementsAre(Pair(kUsername, kPassword),
-                                   Pair(kUsername2, kPassword)));
-}
-
-TEST_F(PasswordManagerPresenterTest,
-       ChangeSavedPasswordBySortKey_EditPasswordForTheRightCredential) {
-  AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
-  AddPasswordEntry(GURL(kExampleCom), kUsername2, kPassword);
-  AddPasswordEntry(GURL(kExampleOrg), kUsername, kPassword);
-  AddPasswordEntry(GURL(kExampleOrg), kUsername2, kPassword);
-  EXPECT_CALL(GetUIController(), SetPasswordList(SizeIs(4)));
-  EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
-  UpdatePasswordLists();
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              UnorderedElementsAre(Pair(kUsername, kPassword),
-                                   Pair(kUsername2, kPassword)));
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)),
-              UnorderedElementsAre(Pair(kUsername, kPassword),
-                                   Pair(kUsername2, kPassword)));
-  testing::Mock::VerifyAndClearExpectations(&GetUIController());
-
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kUsername,
-                               kNewPass);
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleCom)),
-              UnorderedElementsAre(Pair(kUsername, kNewPass),
-                                   Pair(kUsername2, kPassword)));
-  EXPECT_THAT(GetUsernamesAndPasswords(GetStoredPasswordsForRealm(kExampleOrg)),
-              UnorderedElementsAre(Pair(kUsername, kPassword),
-                                   Pair(kUsername2, kPassword)));
-}
-
 TEST_F(PasswordManagerPresenterTest, UIControllerIsCalled) {
   EXPECT_CALL(GetUIController(), SetPasswordList(IsEmpty()));
   EXPECT_CALL(GetUIController(), SetPasswordExceptionList(IsEmpty()));
@@ -498,6 +290,21 @@
   UpdatePasswordLists();
 }
 
+TEST_F(PasswordManagerPresenterTest, SavedPasswordsReturnedCorrectly) {
+  autofill::PasswordForm password1 =
+      AddPasswordEntry(GURL(kExampleCom), kUsername, kPassword);
+  AddPasswordEntry(GURL(kExampleOrg), kUsername, kPassword);
+  UpdatePasswordLists();
+
+  base::span<const std::unique_ptr<autofill::PasswordForm>> passwords =
+      GetUIController().GetPasswordManagerPresenter()->GetPasswordsForKey(
+          password_manager::CreateSortKey(password1));
+
+  ASSERT_EQ(1u, passwords.size());
+  EXPECT_THAT(GetUsernamesAndPasswords({*passwords[0]}),
+              ElementsAre(Pair(kUsername, kPassword)));
+}
+
 // Check that only stored passwords, not blocklisted entries, are provided for
 // exporting.
 TEST_F(PasswordManagerPresenterTest, BlocklistedPasswordsNotExported) {
@@ -739,25 +546,4 @@
       1);
 }
 
-// This test changes the username of a credentials stored in the profile store
-// to be equal to a username of a credential stored in the account store for the
-// same domain.
-TEST_F(PasswordManagerPresenterTestWithAccountStore,
-       ChangeSavedPasswordBySortKey_EditUsername) {
-  AddPasswordToStore(profile_store(), GURL(kExampleCom), kUsername, kPassword);
-  AddPasswordToStore(account_store(), GURL(kExampleCom), kUsername2, kPassword);
-
-  UpdatePasswordLists();
-
-  EXPECT_THAT(GetUsernamesAndPasswords(
-                  GetPasswordsInStoreForRealm(*profile_store(), kExampleCom)),
-              ElementsAre(Pair(kUsername, kPassword)));
-  ChangeSavedPasswordBySortKey(kExampleCom, kUsername, kPassword, kUsername2,
-                               kPassword);
-
-  EXPECT_THAT(GetUsernamesAndPasswords(
-                  GetPasswordsInStoreForRealm(*profile_store(), kExampleCom)),
-              ElementsAre(Pair(kUsername2, kPassword)));
-}
-
 }  // namespace
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index c2a8d15..f07d400f5 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -1046,7 +1046,7 @@
 gfx::Rect OverlayWindowViews::GetWorkAreaForWindow() const {
   return display::Screen::GetScreen()
       ->GetDisplayNearestWindow(
-          native_widget()
+          native_widget() && IsVisible()
               ? GetNativeWindow()
               : controller_->GetWebContents()->GetTopLevelNativeWindow())
       .work_area();
@@ -1059,6 +1059,9 @@
   if (!native_widget())
     return window_size;
 
+  // native_widget() is required for OnSizeConstraintsChanged.
+  OnSizeConstraintsChanged();
+
   if (window_size.width() <= max_size_.width() &&
       window_size.height() <= max_size_.height()) {
     return window_size;
diff --git a/chrome/browser/ui/webui/profile_helper_browsertest.cc b/chrome/browser/ui/webui/profile_helper_browsertest.cc
index c490603..c4d9753 100644
--- a/chrome/browser/ui/webui/profile_helper_browsertest.cc
+++ b/chrome/browser/ui/webui/profile_helper_browsertest.cc
@@ -175,12 +175,6 @@
   Browser* new_browser = added_observer.Wait();
 
   EXPECT_EQ(1u, browser_list->size());
-  // On Lacros, the pointer to the new browser may be the same as the old one.
-  // https://crbug.com/1130131
-#if !defined(OS_CHROMEOS) || !defined(OS_LINUX)
-  EXPECT_FALSE(base::Contains(*browser_list, original_browser));
-  EXPECT_NE(new_browser, original_browser);
-#endif
   EXPECT_NE(original_browser_profile_path, new_browser->profile()->GetPath());
   EXPECT_EQ(1u, storage.GetNumberOfProfiles());
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
index 8073584..b9acb8d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
@@ -320,7 +320,7 @@
         // Secondary accounts in child user session cannot be unmigrated. If
         // such account has dummy gaia token, it was invalidated.
         .SetUnmigrated(!is_child_user &&
-                       account_manager_->HasDummyGaiaToken(account_key))
+                       account_manager_->HasDummyGaiaTokenSync(account_key))
         .SetIsSignedIn(!identity_manager_
                             ->HasAccountWithRefreshTokenInPersistentErrorState(
                                 maybe_account_info->account_id));
diff --git a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc
index 6fb4c06..f09f763 100644
--- a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler_browsertest.cc
@@ -336,7 +336,7 @@
         user_manager::UserType::USER_TYPE_CHILD) {
       EXPECT_FALSE(account.FindBoolKey("unmigrated").value());
     } else {
-      EXPECT_EQ(account_manager()->HasDummyGaiaToken(expected_account.key),
+      EXPECT_EQ(account_manager()->HasDummyGaiaTokenSync(expected_account.key),
                 account.FindBoolKey("unmigrated").value());
     }
     EXPECT_EQ(expected_account.key.account_type,
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 102993e..94bd3e9 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1602611974-f7262751d1dc906200528d84bf62f89d4908c5e2.profdata
+chrome-linux-master-1602654939-1f5462f983274209ab06546353a58e9ab57d2346.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 40ccec81..4b48f7b 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1602611974-a7265b2715cbc4cd86dbab6cb07ce46a3b6949bc.profdata
+chrome-mac-master-1602654939-6aaab1e7299ba115ccc1d7beffbea8c42a27a599.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index c7f13412..1b355e9 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1602567235-326c7abc194f22112a72664d7e7a2c66cfd57197.profdata
+chrome-win32-master-1602633476-69602bad93ac5850796e454db1a842456d0dd6b1.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index e5d641d..f081ab4 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1602590170-40f871c7cd5f95ac04c91a2fc42fd5651e0714b3.profdata
+chrome-win64-master-1602633476-7a781163f6d564d4e8e8516c828c3e90624a360b.profdata
diff --git a/chrome/tools/build/linux/FILES.cfg b/chrome/tools/build/linux/FILES.cfg
index d4daa5a..4a7367a4 100644
--- a/chrome/tools/build/linux/FILES.cfg
+++ b/chrome/tools/build/linux/FILES.cfg
@@ -345,7 +345,7 @@
   },
   # MojoJS support data (credential-management):
   {
-    'filename': 'gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js',
+    'filename': 'gen/third_party/blink/public/mojom/sms/webotp_service.mojom-lite.js',
     'buildtype': ['dev', 'official'],
     'archive': 'mojojs.zip',
   },
diff --git a/chromeos/components/account_manager/account_manager.cc b/chromeos/components/account_manager/account_manager.cc
index 2679073..5158530 100644
--- a/chromeos/components/account_manager/account_manager.cc
+++ b/chromeos/components/account_manager/account_manager.cc
@@ -635,7 +635,8 @@
          it->second.token != kActiveDirectoryDummyToken;
 }
 
-bool AccountManager::HasDummyGaiaToken(const AccountKey& account_key) const {
+bool AccountManager::HasDummyGaiaTokenSync(
+    const AccountKey& account_key) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(init_state_, InitializationState::kInitialized);
 
@@ -643,6 +644,19 @@
   return it != accounts_.end() && it->second.token == kInvalidToken;
 }
 
+void AccountManager::HasDummyGaiaToken(
+    const AccountKey& account_key,
+    base::OnceCallback<void(bool)> callback) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // TODO(https://crbug.com/1135980): Remove this DCHECK and call
+  // |RunOnInitialization| instead.
+  DCHECK_EQ(init_state_, InitializationState::kInitialized);
+
+  auto it = accounts_.find(account_key);
+  std::move(callback).Run(it != accounts_.end() &&
+                          it->second.token == kInvalidToken);
+}
+
 void AccountManager::MaybeRevokeTokenOnServer(const AccountKey& account_key,
                                               const std::string& old_token) {
   if ((account_key.account_type ==
diff --git a/chromeos/components/account_manager/account_manager.h b/chromeos/components/account_manager/account_manager.h
index 3c1f5a2e7..6651a7f 100644
--- a/chromeos/components/account_manager/account_manager.h
+++ b/chromeos/components/account_manager/account_manager.h
@@ -242,10 +242,13 @@
   bool IsTokenAvailable(const AccountKey& account_key) const;
 
   // Returns true if the token stored against |account_key| is a dummy Gaia
-  // token. This is meant to be used only by
-  // |ProfileOAuth2TokenServiceDelegateChromeOS| to pre-emptively reject access
-  // token requests for |account_key|.
-  bool HasDummyGaiaToken(const AccountKey& account_key) const;
+  // token.
+  bool HasDummyGaiaTokenSync(const AccountKey& account_key) const;
+
+  // Calls the |callback| with true if the token stored against |account_key| is
+  // a dummy Gaia token.
+  void HasDummyGaiaToken(const AccountKey& account_key,
+                         base::OnceCallback<void(bool)> callback) const;
 
  private:
   enum InitializationState {
diff --git a/chromeos/components/account_manager/account_manager_unittest.cc b/chromeos/components/account_manager/account_manager_unittest.cc
index 80058179..7b10899 100644
--- a/chromeos/components/account_manager/account_manager_unittest.cc
+++ b/chromeos/components/account_manager/account_manager_unittest.cc
@@ -116,6 +116,23 @@
     return raw_email;
   }
 
+  bool HasDummyGaiaTokenBlocking(
+      const AccountManager::AccountKey& account_key) {
+    bool has_dummy_token_result = false;
+
+    base::RunLoop run_loop;
+    account_manager_->HasDummyGaiaToken(
+        account_key,
+        base::BindLambdaForTesting(
+            [&has_dummy_token_result, &run_loop](bool has_dummy_token) {
+              has_dummy_token_result = has_dummy_token;
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+
+    return has_dummy_token_result;
+  }
+
   // Helper method to reset and initialize |account_manager_| with default
   // parameters.
   void ResetAndInitializeAccountManager() {
@@ -617,4 +634,19 @@
   EXPECT_TRUE(IsAccountKeyPresent(GetAccountsBlocking(), kGaiaAccountKey));
 }
 
+TEST_F(AccountManagerTest, HasDummyGaiaTokenReturnsTrueForInvalidTokens) {
+  EXPECT_FALSE(account_manager()->IsTokenAvailable(kGaiaAccountKey));
+  account_manager()->UpsertAccount(kGaiaAccountKey, kRawUserEmail,
+                                   AccountManager::kInvalidToken);
+  RunAllPendingTasks();
+  EXPECT_TRUE(HasDummyGaiaTokenBlocking(kGaiaAccountKey));
+}
+
+TEST_F(AccountManagerTest, HasDummyGaiaTokenReturnsFalseForValidTokens) {
+  EXPECT_FALSE(account_manager()->IsTokenAvailable(kGaiaAccountKey));
+  account_manager()->UpsertAccount(kGaiaAccountKey, kRawUserEmail, kGaiaToken);
+  RunAllPendingTasks();
+  EXPECT_FALSE(HasDummyGaiaTokenBlocking(kGaiaAccountKey));
+}
+
 }  // namespace chromeos
diff --git a/chromeos/components/help_app_ui/BUILD.gn b/chromeos/components/help_app_ui/BUILD.gn
index 33335e7..e04fc02e 100644
--- a/chromeos/components/help_app_ui/BUILD.gn
+++ b/chromeos/components/help_app_ui/BUILD.gn
@@ -50,7 +50,8 @@
 group("closure_compile") {
   testonly = true
   deps = [
-    ":closure_compile_tests",
+    ":closure_compile_browsertests",
+    ":closure_compile_test_lib",
     "resources:closure_compile",
     "resources/mock:closure_compile",
   ]
@@ -92,7 +93,7 @@
   ]
 }
 
-js_type_check("closure_compile_tests") {
+js_type_check("closure_compile_test_lib") {
   testonly = true
   closure_flags = system_app_closure_flags_strict
   deps = [
@@ -102,6 +103,17 @@
   ]
 }
 
+# Use relaxed flags for the browsertest files themselves. This removes null
+# checks and "could not determine type" errors which don't add a lot of value.
+js_type_check("closure_compile_browsertests") {
+  testonly = true
+  closure_flags = system_app_closure_flags
+  deps = [
+    ":test_help_app_guest_ui_browsertest_js",
+    ":test_help_app_ui_browsertest_js",
+  ]
+}
+
 js_library("test_driver_api_js") {
   testonly = true
   sources = [ "test/driver_api.js" ]
@@ -126,3 +138,27 @@
     "//chromeos/components/system_apps/public/js:message_pipe",
   ]
 }
+
+js_library("test_help_app_ui_browsertest_js") {
+  testonly = true
+  sources = [ "test/help_app_ui_browsertest.js" ]
+  externs_list =
+      [ "//chromeos/components/web_applications/js2gtest_support.externs.js" ]
+  deps = [
+    ":test_driver_js",
+    "//chromeos/components/help_app_ui/resources:browser_proxy",
+    "//chromeos/components/system_apps/public/js:message_pipe",
+  ]
+}
+
+js_library("test_help_app_guest_ui_browsertest_js") {
+  testonly = true
+  sources = [ "test/help_app_guest_ui_browsertest.js" ]
+  externs_list =
+      [ "//chromeos/components/web_applications/js2gtest_support.externs.js" ]
+  deps = [
+    ":test_driver_js",
+    "//chromeos/components/help_app_ui/resources:receiver",
+    "//chromeos/components/system_apps/public/js:message_pipe",
+  ]
+}
diff --git a/chromeos/components/help_app_ui/resources/browser_proxy.js b/chromeos/components/help_app_ui/resources/browser_proxy.js
index aa9743bb..8189652 100644
--- a/chromeos/components/help_app_ui/resources/browser_proxy.js
+++ b/chromeos/components/help_app_ui/resources/browser_proxy.js
@@ -36,6 +36,7 @@
 const TITLE_ID = 'title';
 const BODY_ID = 'body';
 const CATEGORY_ID = 'main-category';
+const SUBHEADING_ID = 'subheading';
 
 /**
  * A pipe through which we can send messages to the guest frame.
@@ -64,6 +65,7 @@
       const data_from_app =
           /** @type {!Array<!helpApp.SearchableItem>} */ (message);
       const data_to_send = data_from_app.map(searchable_item => {
+        /** @type {!Array<!chromeos.localSearchService.mojom.Content>} */
         const contents = [
           {
             id: TITLE_ID,
@@ -71,16 +73,28 @@
             weight: 1.0,
           },
           {
-            id: BODY_ID,
-            content: toString16(searchable_item.body),
-            weight: 0.2,
-          },
-          {
             id: CATEGORY_ID,
             content: toString16(searchable_item.mainCategoryName),
             weight: 0.1,
           },
         ];
+        // If there are subheadings, use those instead of the body.
+        if (searchable_item.subheadings
+            && searchable_item.subheadings.length > 0) {
+          for (let i = 0; i < searchable_item.subheadings.length; ++i) {
+            contents.push({
+              id: SUBHEADING_ID + i,
+              content: toString16(searchable_item.subheadings[i]),
+              weight: 0.4,
+            });
+          }
+        } else if (searchable_item.body) {
+          contents.push({
+            id: BODY_ID,
+            content: toString16(searchable_item.body),
+            weight: 0.2,
+          });
+        }
         return {
           id: searchable_item.id,
           contents,
@@ -121,6 +135,16 @@
         const titlePositions = [];
         /** @type {!Array<!helpApp.Position>} */
         const bodyPositions = [];
+        // Id of the best subheading that appears in positions. We consider
+        // the subheading containing the most match positions to be the best.
+        // "" means no subheading positions found.
+        let bestSubheadingId = "";
+        /**
+         * Counts how many positions there are for each subheading id.
+         * @type {!Object<string, number>}
+         */
+        const subheadingPosCounts = {};
+        // Note: result.positions is not sorted.
         for (const position of result.positions) {
           if (position.contentId === TITLE_ID) {
             titlePositions.push(
@@ -128,16 +152,56 @@
           } else if (position.contentId === BODY_ID) {
             bodyPositions.push(
                 {length: position.length, start: position.start});
+          } else if (position.contentId.startsWith(SUBHEADING_ID)) {
+            // Update the subheadings's position count and check if it's the new
+            // best subheading.
+            const newCount = (subheadingPosCounts[position.contentId] || 0) + 1;
+            subheadingPosCounts[position.contentId] = newCount;
+            if (!bestSubheadingId
+                || newCount > subheadingPosCounts[bestSubheadingId]) {
+              bestSubheadingId = position.contentId;
+            }
           }
         }
+        // Use only the positions of the best subheading.
+        /** @type {!Array<!helpApp.Position>} */
+        const subheadingPositions = [];
+        if (bestSubheadingId) {
+          for (const position of result.positions) {
+            if (position.contentId === bestSubheadingId) {
+              subheadingPositions.push({
+                start: position.start,
+                length: position.length,
+              });
+            }
+          }
+          subheadingPositions.sort(compareByStart);
+        }
+
         // Sort positions by start index.
-        titlePositions.sort((a, b) => a.start - b.start);
-        bodyPositions.sort((a, b) => a.start - b.start);
+        titlePositions.sort(compareByStart);
+        bodyPositions.sort(compareByStart);
         return {
           id: result.id,
           titlePositions,
           bodyPositions,
+          subheadingIndex: bestSubheadingId
+              ? Number(bestSubheadingId.substring(SUBHEADING_ID.length))
+              : null,
+          subheadingPositions: bestSubheadingId
+              ? subheadingPositions
+              : null,
         };
       });
       return {results};
     });
+
+/**
+ * Compare two positions by their start index. Use for sorting.
+ *
+ * @param {!helpApp.Position} a
+ * @param {!helpApp.Position} b
+ */
+function compareByStart(a, b) {
+  return a.start - b.start;
+}
diff --git a/chromeos/components/help_app_ui/test/help_app_guest_ui_browsertest.js b/chromeos/components/help_app_ui/test/help_app_guest_ui_browsertest.js
index 4741898..52b741d 100644
--- a/chromeos/components/help_app_ui/test/help_app_guest_ui_browsertest.js
+++ b/chromeos/components/help_app_ui/test/help_app_guest_ui_browsertest.js
@@ -8,3 +8,69 @@
 GUEST_TEST('GuestHasLang', () => {
   assertEquals(document.documentElement.lang, 'en-US');
 });
+
+// Test that search works when called from the guest frame, and it works for
+// searchable items with and without subheadings.
+GUEST_TEST('GuestCanSearchWithHeadings', async () => {
+  const delegate = /** @type {!helpApp.ClientApi} */ (
+      document.querySelector('showoff-app')).getDelegate();
+  await delegate.addOrUpdateSearchIndex([{
+    // Title match. No subheadings.
+    id: 'test-id-1',
+    title: 'Title with verycomplicatedsearchtoken',
+    body: 'Body text',
+    mainCategoryName: 'Help',
+    locale: 'en-US',
+  },{
+    // Subheading match.
+    id: 'test-id-2',
+    title: 'Title 2',
+    subheadings: [
+      'Subheading 1',
+      'verycomplicatedsearchtoken in subheading. Verycomplicatedsearchtoken',
+      'Another subheading with verycomplicatedsearchtoken',
+    ],
+    body: 'Body text',
+    mainCategoryName: 'Help',
+    locale: 'en-US',
+  },{
+    // Should not appear in the results.
+    id: 'test-id-3',
+    title: 'Title of irrelevant article',
+    body: 'Body text',
+    mainCategoryName: 'Help',
+    locale: 'en-US',
+  }]);
+
+  // Keep polling until the index finishes updating or too much time has passed.
+  /** @type {?helpApp.FindResponse} */
+  let response = null;
+  for (let numTries = 0; numTries < 100; numTries++) {
+    response = await delegate.findInSearchIndex('verycomplicatedsearchtoken');
+    if (response && response.results) break;
+    await new Promise(resolve => {setTimeout(resolve, 50)});
+  }
+
+  assertDeepEquals(response.results, [
+    // The first result only matches on the title.
+    {
+      id: 'test-id-1',
+      titlePositions: [{start: 11, length: 26}],
+      subheadingIndex: null,
+      subheadingPositions: null,
+      bodyPositions: [],
+    },
+    // The second result only matches on the second and third subheadings, and
+    // it uses the subheading with the most matches in the snippet.
+    {
+      id: 'test-id-2',
+      titlePositions: [],
+      subheadingIndex: 1,
+      subheadingPositions: [
+        {start: 0, length: 26},
+        {start: 42, length: 26},
+      ],
+      bodyPositions: [],
+    },
+  ]);
+});
diff --git a/chromeos/components/help_app_ui/test/help_app_ui_browsertest.js b/chromeos/components/help_app_ui/test/help_app_ui_browsertest.js
index 27188f1a..55c29ce 100644
--- a/chromeos/components/help_app_ui/test/help_app_ui_browsertest.js
+++ b/chromeos/components/help_app_ui/test/help_app_ui_browsertest.js
@@ -34,6 +34,15 @@
   }
 
   /** @override */
+  get featureList() {
+    return {
+      enabled: [
+        'chromeos::features::kHelpAppSearchServiceIntegration',
+      ]
+    };
+  }
+
+  /** @override */
   get typedefCppFixture() {
     return 'HelpAppUiBrowserTest';
   }
@@ -46,7 +55,8 @@
 
 // Tests that chrome://help-app goes somewhere instead of 404ing or crashing.
 TEST_F('HelpAppUIBrowserTest', 'HasChromeSchemeURL', () => {
-  const guest = document.querySelector('iframe');
+  const guest = /** @type {!HTMLIFrameElement} */ (
+      document.querySelector('iframe'));
 
   assertEquals(document.location.origin, HOST_ORIGIN);
   assertEquals(guest.src, GUEST_ORIGIN + '/');
@@ -79,3 +89,8 @@
   await runTestInGuest('GuestHasLang');
   testDone();
 });
+
+TEST_F('HelpAppUIBrowserTest', 'GuestCanSearchWithSubheadings', async () => {
+  await runTestInGuest('GuestCanSearchWithHeadings');
+  testDone();
+});
diff --git a/chromeos/components/phonehub/fake_notification_manager.cc b/chromeos/components/phonehub/fake_notification_manager.cc
index d4787aa..ad1513b 100644
--- a/chromeos/components/phonehub/fake_notification_manager.cc
+++ b/chromeos/components/phonehub/fake_notification_manager.cc
@@ -25,62 +25,10 @@
   SetNotificationsInternal(base::flat_set<Notification>{notification});
 }
 
-void FakeNotificationManager::SetNotificationsInternal(
-    const base::flat_set<Notification>& notifications) {
-  base::flat_set<int64_t> added_ids;
-  base::flat_set<int64_t> updated_ids;
-
-  for (const Notification& notification : notifications) {
-    int64_t id = notification.id();
-    auto it = id_to_notification_map_.find(id);
-
-    if (it == id_to_notification_map_.end()) {
-      id_to_notification_map_.emplace(id, notification);
-      added_ids.emplace(id);
-      continue;
-    }
-
-    it->second = notification;
-    updated_ids.emplace(id);
-  }
-
-  NotifyNotificationsAdded(added_ids);
-  NotifyNotificationsUpdated(updated_ids);
-}
-
 void FakeNotificationManager::RemoveNotification(int64_t id) {
   RemoveNotificationsInternal(base::flat_set<int64_t>{id});
 }
 
-void FakeNotificationManager::RemoveNotificationsInternal(
-    const base::flat_set<int64_t>& ids) {
-  for (int64_t id : ids) {
-    auto it = id_to_notification_map_.find(id);
-    DCHECK(it != id_to_notification_map_.end());
-    id_to_notification_map_.erase(it);
-  }
-
-  NotifyNotificationsRemoved(ids);
-}
-
-void FakeNotificationManager::ClearNotificationsInternal() {
-  base::flat_set<int64_t> removed_ids;
-  for (const auto& pair : id_to_notification_map_) {
-    removed_ids.emplace(pair.first);
-  }
-
-  id_to_notification_map_.clear();
-  NotifyNotificationsRemoved(removed_ids);
-}
-
-const Notification* FakeNotificationManager::GetNotification(
-    int64_t notification_id) const {
-  auto it = id_to_notification_map_.find(notification_id);
-  if (it == id_to_notification_map_.end())
-    return nullptr;
-  return &it->second;
-}
-
 void FakeNotificationManager::DismissNotification(int64_t notification_id) {
   DCHECK(base::Contains(id_to_notification_map_, notification_id));
   dismissed_notification_ids_.push_back(notification_id);
diff --git a/chromeos/components/phonehub/fake_notification_manager.h b/chromeos/components/phonehub/fake_notification_manager.h
index c383cc54..d09247c 100644
--- a/chromeos/components/phonehub/fake_notification_manager.h
+++ b/chromeos/components/phonehub/fake_notification_manager.h
@@ -20,14 +20,15 @@
   FakeNotificationManager();
   ~FakeNotificationManager() override;
 
+  using NotificationManager::SetNotificationsInternal;
+
+  using NotificationManager::RemoveNotificationsInternal;
+
+  using NotificationManager::ClearNotificationsInternal;
+
   void SetNotification(const Notification& notification);
-  void SetNotificationsInternal(
-      const base::flat_set<Notification>& notifications) override;
 
   void RemoveNotification(int64_t id);
-  void RemoveNotificationsInternal(const base::flat_set<int64_t>& ids) override;
-
-  void ClearNotificationsInternal() override;
 
   const std::vector<int64_t>& dismissed_notification_ids() const {
     return dismissed_notification_ids_;
@@ -50,12 +51,10 @@
 
  private:
   // NotificationManager:
-  const Notification* GetNotification(int64_t notification_id) const override;
   void DismissNotification(int64_t notification_id) override;
   void SendInlineReply(int64_t notification_id,
                        const base::string16& inline_reply_text) override;
 
-  std::unordered_map<int64_t, Notification> id_to_notification_map_;
   std::vector<int64_t> dismissed_notification_ids_;
   std::vector<InlineReplyMetadata> inline_replies_;
 };
diff --git a/chromeos/components/phonehub/notification_manager.cc b/chromeos/components/phonehub/notification_manager.cc
index 7173b153..1613fa3 100644
--- a/chromeos/components/phonehub/notification_manager.cc
+++ b/chromeos/components/phonehub/notification_manager.cc
@@ -4,6 +4,8 @@
 
 #include "chromeos/components/phonehub/notification_manager.h"
 
+#include "chromeos/components/multidevice/logging/logging.h"
+
 namespace chromeos {
 namespace phonehub {
 
@@ -37,5 +39,63 @@
     observer.OnNotificationsRemoved(notification_ids);
 }
 
+void NotificationManager::SetNotificationsInternal(
+    const base::flat_set<Notification>& notifications) {
+  PA_LOG(INFO) << "Setting notifications internally.";
+
+  base::flat_set<int64_t> added_ids;
+  base::flat_set<int64_t> updated_ids;
+
+  for (const Notification& notification : notifications) {
+    int64_t id = notification.id();
+    auto it = id_to_notification_map_.find(id);
+
+    if (it == id_to_notification_map_.end()) {
+      id_to_notification_map_.emplace(id, notification);
+      added_ids.emplace(id);
+      continue;
+    }
+
+    it->second = notification;
+    updated_ids.emplace(id);
+  }
+
+  NotifyNotificationsAdded(added_ids);
+  NotifyNotificationsUpdated(updated_ids);
+}
+
+void NotificationManager::RemoveNotificationsInternal(
+    const base::flat_set<int64_t>& notification_ids) {
+  PA_LOG(INFO) << "Removing notifications internally.";
+
+  for (int64_t id : notification_ids) {
+    auto it = id_to_notification_map_.find(id);
+    DCHECK(it != id_to_notification_map_.end());
+    id_to_notification_map_.erase(it);
+  }
+
+  NotifyNotificationsRemoved(notification_ids);
+}
+
+void NotificationManager::ClearNotificationsInternal() {
+  PA_LOG(INFO) << "Clearing notification internally.";
+
+  base::flat_set<int64_t> removed_ids;
+  for (const auto& pair : id_to_notification_map_) {
+    removed_ids.emplace(pair.first);
+  }
+
+  id_to_notification_map_.clear();
+  NotifyNotificationsRemoved(removed_ids);
+}
+
+const Notification* NotificationManager::GetNotification(
+    int64_t notification_id) const {
+  auto it = id_to_notification_map_.find(notification_id);
+  if (it == id_to_notification_map_.end())
+    return nullptr;
+  return &it->second;
+}
+
 }  // namespace phonehub
 }  // namespace chromeos
diff --git a/chromeos/components/phonehub/notification_manager.h b/chromeos/components/phonehub/notification_manager.h
index 85ba3403..7b7ecbf 100644
--- a/chromeos/components/phonehub/notification_manager.h
+++ b/chromeos/components/phonehub/notification_manager.h
@@ -6,6 +6,7 @@
 #define CHROMEOS_COMPONENTS_PHONEHUB_NOTIFICATION_MANAGER_H_
 
 #include <stdint.h>
+#include <unordered_map>
 
 #include "base/containers/flat_set.h"
 #include "base/observer_list.h"
@@ -45,8 +46,7 @@
   // Returns null if no notification exists with the given ID. Pointers returned
   // by this function should not be cached, since the underlying Notification
   // object may be deleted by a future update.
-  virtual const Notification* GetNotification(
-      int64_t notification_id) const = 0;
+  const Notification* GetNotification(int64_t notification_id) const;
 
   // Dismisses the notification with the given ID; if no notification exists
   // with this ID, this function is a no-op.
@@ -62,23 +62,24 @@
 
  protected:
   friend class PhoneStatusProcessor;
+  friend class NotificationManagerImplTest;
 
   NotificationManager();
 
   // Sets the internal collection of notifications. This does not send any
   // requests to the remote phone device.
-  virtual void SetNotificationsInternal(
-      const base::flat_set<Notification>& notifications) = 0;
+  void SetNotificationsInternal(
+      const base::flat_set<Notification>& notifications);
 
   // Removes the dismissed notifications from the internal collection of
   // notifications. Does not send a request to remove notifications to the
   // remote device.
-  virtual void RemoveNotificationsInternal(
-      const base::flat_set<int64_t>& notification_ids) = 0;
+  void RemoveNotificationsInternal(
+      const base::flat_set<int64_t>& notification_ids);
 
   // Clears the underlying internal collection of notifications. This does not
   // send any requests to clear the phone's notifications.
-  virtual void ClearNotificationsInternal() = 0;
+  void ClearNotificationsInternal();
 
   void NotifyNotificationsAdded(
       const base::flat_set<int64_t>& notification_ids);
@@ -87,6 +88,8 @@
   void NotifyNotificationsRemoved(
       const base::flat_set<int64_t>& notification_ids);
 
+  std::unordered_map<int64_t, Notification> id_to_notification_map_;
+
  private:
   base::ObserverList<Observer> observer_list_;
 };
diff --git a/chromeos/components/phonehub/notification_manager_impl.cc b/chromeos/components/phonehub/notification_manager_impl.cc
index 11b3020..f079bc3 100644
--- a/chromeos/components/phonehub/notification_manager_impl.cc
+++ b/chromeos/components/phonehub/notification_manager_impl.cc
@@ -4,47 +4,41 @@
 
 #include "chromeos/components/phonehub/notification_manager_impl.h"
 
+#include "base/containers/flat_set.h"
 #include "chromeos/components/multidevice/logging/logging.h"
+#include "chromeos/components/phonehub/message_sender.h"
 #include "chromeos/components/phonehub/notification.h"
 
 namespace chromeos {
 namespace phonehub {
 
-NotificationManagerImpl::NotificationManagerImpl() = default;
+NotificationManagerImpl::NotificationManagerImpl(MessageSender* message_sender)
+    : message_sender_(message_sender) {
+  DCHECK(message_sender_);
+}
 
 NotificationManagerImpl::~NotificationManagerImpl() = default;
 
-const Notification* NotificationManagerImpl::GetNotification(
-    int64_t notification_id) const {
-  return nullptr;
-}
-
-void NotificationManagerImpl::SetNotificationsInternal(
-    const base::flat_set<Notification>& notifications) {
-  PA_LOG(INFO) << "Setting notifications internally.";
-  // TODO(jimmyxong): Implement this stub function.
-}
-
-void NotificationManagerImpl::RemoveNotificationsInternal(
-    const base::flat_set<int64_t>& notification_ids) {
-  PA_LOG(INFO) << "Removing notifications internally.";
-  // TODO(jimmyxgong): Implement this stub function.
-}
-
 void NotificationManagerImpl::DismissNotification(int64_t notification_id) {
   PA_LOG(INFO) << "Dismissing notification with ID " << notification_id << ".";
-}
 
-void NotificationManagerImpl::ClearNotificationsInternal() {
-  PA_LOG(INFO) << "Clearing notification internally.";
-  // TODO(jimmyxgong): Implement this stub function.
+  RemoveNotificationsInternal(base::flat_set<int64_t>{notification_id});
+  message_sender_->SendDismissNotificationRequest(notification_id);
 }
 
 void NotificationManagerImpl::SendInlineReply(
     int64_t notification_id,
     const base::string16& inline_reply_text) {
+  if (!GetNotification(notification_id)) {
+    PA_LOG(INFO) << "Could not send inline reply for notification with ID "
+                 << notification_id << ".";
+    return;
+  }
+
   PA_LOG(INFO) << "Sending inline reply for notification with ID "
                << notification_id << ".";
+  message_sender_->SendNotificationInlineReplyRequest(notification_id,
+                                                      inline_reply_text);
 }
 
 }  // namespace phonehub
diff --git a/chromeos/components/phonehub/notification_manager_impl.h b/chromeos/components/phonehub/notification_manager_impl.h
index fcd0bf0..c05a235 100644
--- a/chromeos/components/phonehub/notification_manager_impl.h
+++ b/chromeos/components/phonehub/notification_manager_impl.h
@@ -5,30 +5,28 @@
 #ifndef CHROMEOS_COMPONENTS_PHONEHUB_NOTIFICATION_MANAGER_IMPL_H_
 #define CHROMEOS_COMPONENTS_PHONEHUB_NOTIFICATION_MANAGER_IMPL_H_
 
-#include "base/containers/flat_set.h"
+#include <unordered_map>
+
 #include "chromeos/components/phonehub/notification.h"
 #include "chromeos/components/phonehub/notification_manager.h"
 
 namespace chromeos {
 namespace phonehub {
 
-// TODO(https://crbug.com/1106937): Add real implementation.
+class MessageSender;
+
 class NotificationManagerImpl : public NotificationManager {
  public:
-  NotificationManagerImpl();
+  NotificationManagerImpl(MessageSender* message_sender);
   ~NotificationManagerImpl() override;
 
  private:
   // NotificationManager:
-  const Notification* GetNotification(int64_t notification_id) const override;
-  void SetNotificationsInternal(
-      const base::flat_set<Notification>& notifications) override;
-  void RemoveNotificationsInternal(
-      const base::flat_set<int64_t>& notification_ids) override;
-  void ClearNotificationsInternal() override;
   void DismissNotification(int64_t notification_id) override;
   void SendInlineReply(int64_t notification_id,
                        const base::string16& inline_reply_text) override;
+
+  MessageSender* message_sender_;
 };
 
 }  // namespace phonehub
diff --git a/chromeos/components/phonehub/notification_manager_impl_unittest.cc b/chromeos/components/phonehub/notification_manager_impl_unittest.cc
index 7efccde..296a91db 100644
--- a/chromeos/components/phonehub/notification_manager_impl_unittest.cc
+++ b/chromeos/components/phonehub/notification_manager_impl_unittest.cc
@@ -8,14 +8,33 @@
 
 #include "base/containers/flat_map.h"
 #include "base/optional.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chromeos/components/phonehub/fake_message_sender.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
 namespace phonehub {
 namespace {
 
+const char kAppName[] = "Test App";
+const char kPackageName[] = "com.google.testapp";
+
+const char kTitle[] = "Test notification";
+const char kTextContent[] = "This is a test notification";
+
 enum class NotificationState { kAdded, kUpdated, kRemoved };
 
+Notification CreateNotification(int64_t id) {
+  return chromeos::phonehub::Notification(
+      id,
+      chromeos::phonehub::Notification::AppMetadata(base::UTF8ToUTF16(kAppName),
+                                                    kPackageName,
+                                                    /*icon=*/gfx::Image()),
+      base::Time::Now(), Notification::Importance::kDefault,
+      /*inline_reply_id=*/0, base::UTF8ToUTF16(kTitle),
+      base::UTF8ToUTF16(kTextContent));
+}
+
 class FakeObserver : public NotificationManager::Observer {
  public:
   FakeObserver() = default;
@@ -63,23 +82,151 @@
 
   // testing::Test:
   void SetUp() override {
-    manager_ = std::make_unique<NotificationManagerImpl>();
+    fake_message_sender_ = std::make_unique<FakeMessageSender>();
+    manager_ =
+        std::make_unique<NotificationManagerImpl>(fake_message_sender_.get());
     manager_->AddObserver(&fake_observer_);
   }
 
   void TearDown() override { manager_->RemoveObserver(&fake_observer_); }
 
   NotificationManager& manager() { return *manager_; }
+  FakeMessageSender& fake_message_sender() { return *fake_message_sender_; }
+
+  void SetNotificationsInternal(
+      const base::flat_set<Notification>& notifications) {
+    manager_->SetNotificationsInternal(notifications);
+  }
+
+  void ClearNotificationsInternal() { manager_->ClearNotificationsInternal(); }
+
+  size_t GetNumNotifications() {
+    return manager_->id_to_notification_map_.size();
+  }
+
+  base::Optional<NotificationState> GetNotificationState(
+      int64_t notification_id) {
+    return fake_observer_.GetState(notification_id);
+  }
 
  private:
   FakeObserver fake_observer_;
+  std::unique_ptr<FakeMessageSender> fake_message_sender_;
   std::unique_ptr<NotificationManager> manager_;
 };
 
-// TODO(khorimoto): Remove this test once we have real functionality to test.
-TEST_F(NotificationManagerImplTest, Initialize) {
-  EXPECT_FALSE(manager().GetNotification(/*notification_id=*/0));
+TEST_F(NotificationManagerImplTest, SetAndClearNotificationsInternal) {
+  EXPECT_EQ(0u, GetNumNotifications());
+  const int64_t expected_id1 = 0;
+  const int64_t expected_id2 = 1;
+
+  SetNotificationsInternal(base::flat_set<Notification>{
+      CreateNotification(expected_id1), CreateNotification(expected_id2)});
+  EXPECT_EQ(2u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id1));
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id2));
+
+  ClearNotificationsInternal();
+  EXPECT_EQ(0u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kRemoved, GetNotificationState(expected_id1));
+  EXPECT_EQ(NotificationState::kRemoved, GetNotificationState(expected_id2));
 }
 
+TEST_F(NotificationManagerImplTest, GetNotification) {
+  EXPECT_EQ(0u, GetNumNotifications());
+  const int64_t expected_id1 = 0;
+
+  SetNotificationsInternal(
+      base::flat_set<Notification>{CreateNotification(expected_id1)});
+  EXPECT_EQ(1u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id1));
+
+  // Call GetNotification() on an existent notification id. Expect a non-null
+  // pointer.
+  EXPECT_TRUE(manager().GetNotification(expected_id1));
+
+  // Call GetNotification() on a non-existent notification id. Expect a null
+  // pointer.
+  EXPECT_FALSE(manager().GetNotification(/*notification_id=*/4));
+
+  // Remove |expected_id1| and expect that GetNotification() returns a null
+  // pointer.
+  manager().DismissNotification(expected_id1);
+  EXPECT_EQ(1u, fake_message_sender().GetDismissNotificationRequestCallCount());
+  EXPECT_EQ(expected_id1,
+            fake_message_sender().GetRecentDismissNotificationRequest());
+  EXPECT_FALSE(manager().GetNotification(expected_id1));
+}
+
+TEST_F(NotificationManagerImplTest, DismissNotifications) {
+  EXPECT_EQ(0u, GetNumNotifications());
+  const int64_t expected_id1 = 0;
+  const int64_t expected_id2 = 1;
+
+  SetNotificationsInternal(base::flat_set<Notification>{
+      CreateNotification(expected_id1), CreateNotification(expected_id2)});
+  EXPECT_EQ(2u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id1));
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id2));
+
+  manager().DismissNotification(expected_id2);
+  EXPECT_EQ(1u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id1));
+  EXPECT_EQ(NotificationState::kRemoved, GetNotificationState(expected_id2));
+  EXPECT_EQ(1u, fake_message_sender().GetDismissNotificationRequestCallCount());
+  EXPECT_EQ(expected_id2,
+            fake_message_sender().GetRecentDismissNotificationRequest());
+}
+
+TEST_F(NotificationManagerImplTest, UpdatedNotification) {
+  EXPECT_EQ(0u, GetNumNotifications());
+  const int64_t expected_id1 = 0;
+  const int64_t expected_id2 = 1;
+
+  SetNotificationsInternal(base::flat_set<Notification>{
+      CreateNotification(expected_id1), CreateNotification(expected_id2)});
+  EXPECT_EQ(2u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id1));
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id2));
+
+  // Simulate updating a notification.
+  SetNotificationsInternal(
+      base::flat_set<Notification>{CreateNotification(expected_id1)});
+  EXPECT_EQ(2u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kUpdated, GetNotificationState(expected_id1));
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id2));
+}
+
+TEST_F(NotificationManagerImplTest, SendInlineReply) {
+  EXPECT_EQ(0u, GetNumNotifications());
+  const int64_t expected_id1 = 0;
+
+  SetNotificationsInternal(
+      base::flat_set<Notification>{CreateNotification(expected_id1)});
+  EXPECT_EQ(1u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id1));
+
+  // Simulate sending an inline reply to a notification.
+  const base::string16& expected_reply(base::UTF8ToUTF16("test reply"));
+  manager().SendInlineReply(expected_id1, expected_reply);
+  EXPECT_EQ(1u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id1));
+  EXPECT_EQ(1u,
+            fake_message_sender().GetNotificationInlineReplyRequestCallCount());
+  std::pair<int64_t, base::string16> pair =
+      fake_message_sender().GetRecentNotificationInlineReplyRequest();
+  EXPECT_EQ(expected_id1, pair.first);
+  EXPECT_EQ(expected_reply, pair.second);
+
+  // Simulate sending an inline reply to a non-existent notification. Expect
+  // that no new reply calls were called and that the most recent reply is the
+  // same as the previous inline reply call.
+  manager().SendInlineReply(/*notification_id=*/5, /*reply=*/base::string16());
+  EXPECT_EQ(1u,
+            fake_message_sender().GetNotificationInlineReplyRequestCallCount());
+  pair = fake_message_sender().GetRecentNotificationInlineReplyRequest();
+  EXPECT_EQ(expected_id1, pair.first);
+  EXPECT_EQ(expected_reply, pair.second);
+}
 }  // namespace phonehub
 }  // namespace chromeos
diff --git a/chromeos/components/phonehub/phone_hub_manager_impl.cc b/chromeos/components/phonehub/phone_hub_manager_impl.cc
index b33c368..9e84451 100644
--- a/chromeos/components/phonehub/phone_hub_manager_impl.cc
+++ b/chromeos/components/phonehub/phone_hub_manager_impl.cc
@@ -63,7 +63,8 @@
               feature_status_provider_.get(),
               message_sender_.get(),
               connection_scheduler_.get())),
-      notification_manager_(std::make_unique<NotificationManagerImpl>()),
+      notification_manager_(
+          std::make_unique<NotificationManagerImpl>(message_sender_.get())),
       onboarding_ui_tracker_(std::make_unique<OnboardingUiTrackerImpl>(
           pref_service,
           feature_status_provider_.get(),
diff --git a/chromeos/components/phonehub/phone_status_processor.cc b/chromeos/components/phonehub/phone_status_processor.cc
index afdfdd3..1da6a9c 100644
--- a/chromeos/components/phonehub/phone_status_processor.cc
+++ b/chromeos/components/phonehub/phone_status_processor.cc
@@ -255,11 +255,14 @@
   SetReceivedNotifications(phone_status_update.updated_notifications());
   SetReceivedPhoneStatusModelStates(phone_status_update.properties());
 
-  base::flat_set<int64_t> removed_notification_ids;
-  for (auto& id : phone_status_update.removed_notification_ids()) {
-    removed_notification_ids.emplace(id);
+  if (!phone_status_update.removed_notification_ids().empty()) {
+    base::flat_set<int64_t> removed_notification_ids;
+    for (auto& id : phone_status_update.removed_notification_ids()) {
+      removed_notification_ids.emplace(id);
+    }
+    notification_manager_->RemoveNotificationsInternal(
+        removed_notification_ids);
   }
-  notification_manager_->RemoveNotificationsInternal(removed_notification_ids);
 }
 
 void PhoneStatusProcessor::OnHostStatusChanged(
diff --git a/chromeos/dbus/concierge_client.cc b/chromeos/dbus/concierge_client.cc
index b3de50f6..2c706871 100644
--- a/chromeos/dbus/concierge_client.cc
+++ b/chromeos/dbus/concierge_client.cc
@@ -428,13 +428,13 @@
   dbus::ObjectProxy* concierge_proxy_ = nullptr;
 
   base::ObserverList<Observer> observer_list_{
-      base::ObserverListPolicy::EXISTING_ONLY};
+      ConciergeClient::kObserverListPolicy};
   base::ObserverList<VmObserver>::Unchecked vm_observer_list_{
-      base::ObserverListPolicy::EXISTING_ONLY};
+      ConciergeClient::kObserverListPolicy};
   base::ObserverList<ContainerObserver>::Unchecked container_observer_list_{
-      base::ObserverListPolicy::EXISTING_ONLY};
+      ConciergeClient::kObserverListPolicy};
   base::ObserverList<DiskImageObserver>::Unchecked disk_image_observer_list_{
-      base::ObserverListPolicy::EXISTING_ONLY};
+      ConciergeClient::kObserverListPolicy};
 
   bool is_vm_started_signal_connected_ = false;
   bool is_vm_stopped_signal_connected_ = false;
diff --git a/chromeos/dbus/concierge_client.h b/chromeos/dbus/concierge_client.h
index ca3d823..ec3c943 100644
--- a/chromeos/dbus/concierge_client.h
+++ b/chromeos/dbus/concierge_client.h
@@ -21,6 +21,9 @@
 // start and stop VMs, as well as for disk image management.
 class COMPONENT_EXPORT(CHROMEOS_DBUS) ConciergeClient : public DBusClient {
  public:
+  static constexpr base::ObserverListPolicy kObserverListPolicy =
+      base::ObserverListPolicy::EXISTING_ONLY;
+
   // Used for observing Concierge service itself.
   class Observer : public base::CheckedObserver {
    public:
diff --git a/chromeos/dbus/fake_concierge_client.h b/chromeos/dbus/fake_concierge_client.h
index 28d85757..1014c7e 100644
--- a/chromeos/dbus/fake_concierge_client.h
+++ b/chromeos/dbus/fake_concierge_client.h
@@ -332,13 +332,17 @@
   std::vector<vm_tools::concierge::DiskImageStatusResponse>
       disk_image_status_signals_;
 
-  base::ObserverList<Observer> observer_list_;
+  base::ObserverList<Observer> observer_list_{
+      ConciergeClient::kObserverListPolicy};
 
-  base::ObserverList<VmObserver>::Unchecked vm_observer_list_;
+  base::ObserverList<VmObserver>::Unchecked vm_observer_list_{
+      ConciergeClient::kObserverListPolicy};
 
-  base::ObserverList<ContainerObserver>::Unchecked container_observer_list_;
+  base::ObserverList<ContainerObserver>::Unchecked container_observer_list_{
+      ConciergeClient::kObserverListPolicy};
 
-  base::ObserverList<DiskImageObserver>::Unchecked disk_image_observer_list_;
+  base::ObserverList<DiskImageObserver>::Unchecked disk_image_observer_list_{
+      ConciergeClient::kObserverListPolicy};
 
   // Note: This should remain the last member so it'll be destroyed and
   // invalidate its weak pointers before any other members are destroyed.
diff --git a/chromeos/dbus/session_manager/fake_session_manager_client.h b/chromeos/dbus/session_manager/fake_session_manager_client.h
index b5339940..222b5b6 100644
--- a/chromeos/dbus/session_manager/fake_session_manager_client.h
+++ b/chromeos/dbus/session_manager/fake_session_manager_client.h
@@ -288,7 +288,8 @@
   // requested restarted arguments.
   base::Optional<std::vector<std::string>> restart_job_argv_;
 
-  base::ObserverList<Observer>::Unchecked observers_;
+  base::ObserverList<Observer>::Unchecked observers_{
+      SessionManagerClient::kObserverListPolicy};
   SessionManagerClient::ActiveSessionsMap user_sessions_;
   std::vector<std::string> server_backed_state_keys_;
 
diff --git a/chromeos/dbus/session_manager/session_manager_client.cc b/chromeos/dbus/session_manager/session_manager_client.cc
index d319ec7..2d04a3dc 100644
--- a/chromeos/dbus/session_manager/session_manager_client.cc
+++ b/chromeos/dbus/session_manager/session_manager_client.cc
@@ -1062,7 +1062,7 @@
   dbus::ObjectProxy* session_manager_proxy_ = nullptr;
   std::unique_ptr<BlockingMethodCaller> blocking_method_caller_;
   base::ObserverList<Observer>::Unchecked observers_{
-      base::ObserverListPolicy::EXISTING_ONLY};
+      SessionManagerClient::kObserverListPolicy};
 
   // Most recent screen-lock state received from session_manager.
   bool screen_is_locked_ = false;
diff --git a/chromeos/dbus/session_manager/session_manager_client.h b/chromeos/dbus/session_manager/session_manager_client.h
index 6f68781..b94d8e9 100644
--- a/chromeos/dbus/session_manager/session_manager_client.h
+++ b/chromeos/dbus/session_manager/session_manager_client.h
@@ -41,6 +41,9 @@
 // SessionManagerClient is used to communicate with the session manager.
 class COMPONENT_EXPORT(SESSION_MANAGER) SessionManagerClient {
  public:
+  static constexpr base::ObserverListPolicy kObserverListPolicy =
+      base::ObserverListPolicy::EXISTING_ONLY;
+
   // The result type received from session manager on request to retrieve the
   // policy. Used to define the buckets for an enumerated UMA histogram.
   // Hence,
diff --git a/components/arc/enterprise/arc_data_snapshotd_manager.cc b/components/arc/enterprise/arc_data_snapshotd_manager.cc
index c20c173d..0a331788 100644
--- a/components/arc/enterprise/arc_data_snapshotd_manager.cc
+++ b/components/arc/enterprise/arc_data_snapshotd_manager.cc
@@ -11,12 +11,15 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/dbus/upstart/upstart_client.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/enterprise/arc_data_snapshotd_bridge.h"
 #include "components/prefs/pref_service.h"
+#include "components/session_manager/core/session_manager.h"
+#include "components/user_manager/user_manager.h"
 #include "ui/ozone/public/ozone_switches.h"
 
 namespace arc {
@@ -34,7 +37,7 @@
 constexpr char kPrevious[] = "previous";
 constexpr char kLast[] = "last";
 constexpr char kBlockedUiReboot[] = "blocked_ui_reboot";
-constexpr char kStartedDate[] = "started_date";
+constexpr char kStarted[] = "started";
 
 bool IsSnapshotEnabled() {
   // TODO(pbond): implement policy processing.
@@ -59,6 +62,9 @@
 
 bool ArcDataSnapshotdManager::is_snapshot_enabled_for_testing_ = false;
 
+// This class is owned by ChromeBrowserMainPartsChromeos.
+static ArcDataSnapshotdManager* g_arc_data_snapshotd_manager = nullptr;
+
 ArcDataSnapshotdManager::SnapshotInfo::SnapshotInfo(const base::Value* value,
                                                     bool last)
     : is_last_(last) {
@@ -153,11 +159,11 @@
 ArcDataSnapshotdManager::Snapshot::CreateForTesting(
     PrefService* local_state,
     bool blocked_ui_mode,
-    const std::string& started_date,
+    bool started,
     std::unique_ptr<SnapshotInfo> last,
     std::unique_ptr<SnapshotInfo> previous) {
   return base::WrapUnique(new ArcDataSnapshotdManager::Snapshot(
-      local_state, blocked_ui_mode, started_date, std::move(last),
+      local_state, blocked_ui_mode, started, std::move(last),
       std::move(previous)));
 }
 
@@ -182,9 +188,9 @@
       blocked_ui_mode_ = found.value();
   }
   {
-    auto* found = dict->FindStringPath(kStartedDate);
-    if (found)
-      started_date_ = *found;
+    auto found = dict->FindBoolPath(kStarted);
+    if (found.has_value())
+      started_ = found.value();
   }
 }
 
@@ -195,7 +201,7 @@
   if (last_)
     last_->Sync(&dict);
   dict.SetBoolKey(kBlockedUiReboot, blocked_ui_mode_);
-  dict.SetStringKey(kStartedDate, started_date_);
+  dict.SetBoolKey(kStarted, started_);
   local_state_->Set(arc::prefs::kArcSnapshotInfo, std::move(dict));
 }
 
@@ -205,23 +211,42 @@
   Sync();
 }
 
+void ArcDataSnapshotdManager::Snapshot::StartNewSnapshot() {
+  previous_ = std::move(last_);
+  last_ = nullptr;
+
+  started_ = true;
+  Sync();
+}
+
 ArcDataSnapshotdManager::Snapshot::Snapshot(
     PrefService* local_state,
     bool blocked_ui_mode,
-    const std::string& started_date,
+    bool started,
     std::unique_ptr<SnapshotInfo> last,
     std::unique_ptr<SnapshotInfo> previous)
     : local_state_(local_state),
       blocked_ui_mode_(blocked_ui_mode),
-      started_date_(started_date),
+      started_(started),
       last_(std::move(last)),
       previous_(std::move(previous)) {
   DCHECK(local_state_);
 }
 
-ArcDataSnapshotdManager::ArcDataSnapshotdManager(PrefService* local_state)
-    : snapshot_{local_state} {
+// static
+ArcDataSnapshotdManager* ArcDataSnapshotdManager::Get() {
+  return g_arc_data_snapshotd_manager;
+}
+
+ArcDataSnapshotdManager::ArcDataSnapshotdManager(
+    PrefService* local_state,
+    base::OnceClosure attempt_user_exit_callback)
+    : snapshot_{local_state},
+      attempt_user_exit_callback_(std::move(attempt_user_exit_callback)) {
+  DCHECK(!g_arc_data_snapshotd_manager);
   DCHECK(local_state);
+  g_arc_data_snapshotd_manager = this;
+
   snapshot_.Parse();
 
   if (IsRestoredSession()) {
@@ -237,6 +262,11 @@
 }
 
 ArcDataSnapshotdManager::~ArcDataSnapshotdManager() {
+  DCHECK(g_arc_data_snapshotd_manager);
+  g_arc_data_snapshotd_manager = nullptr;
+
+  session_manager::SessionManager::Get()->RemoveObserver(this);
+
   snapshot_.Sync();
   EnsureDaemonStopped(base::DoNothing());
 }
@@ -261,6 +291,37 @@
   StopDaemon(std::move(callback));
 }
 
+bool ArcDataSnapshotdManager::IsAutoLoginConfigured() {
+  switch (state_) {
+    case ArcDataSnapshotdManager::State::kBlockedUi:
+    case ArcDataSnapshotdManager::State::kMgsToLaunch:
+    case ArcDataSnapshotdManager::State::kMgsLaunched:
+      return true;
+    case ArcDataSnapshotdManager::State::kNone:
+    case ArcDataSnapshotdManager::State::kRestored:
+      return false;
+  }
+}
+
+bool ArcDataSnapshotdManager::IsAutoLoginAllowed() {
+  switch (state_) {
+    case ArcDataSnapshotdManager::State::kBlockedUi:
+      return false;
+    case ArcDataSnapshotdManager::State::kNone:
+    case ArcDataSnapshotdManager::State::kRestored:
+    case ArcDataSnapshotdManager::State::kMgsLaunched:
+    case ArcDataSnapshotdManager::State::kMgsToLaunch:
+      return true;
+  }
+}
+
+void ArcDataSnapshotdManager::OnSessionStateChanged() {
+  if (state_ != State::kMgsToLaunch)
+    return;
+  if (user_manager::UserManager::Get()->IsLoggedInAsPublicAccount())
+    state_ = State::kMgsLaunched;
+}
+
 void ArcDataSnapshotdManager::StopDaemon(base::OnceClosure callback) {
   VLOG(1) << "Stopping arc-data-snapshotd";
   daemon_weak_ptr_factory_.InvalidateWeakPtrs();
@@ -331,10 +392,22 @@
 
 void ArcDataSnapshotdManager::OnKeyPairGenerated(bool success) {
   if (success) {
+    VLOG(1) << "Managed Guest Session is ready to be started with blocked UI.";
     state_ = State::kMgsToLaunch;
+    session_manager::SessionManager::Get()->AddObserver(this);
+    // Move last to previous snapshot:
+    snapshot_.StartNewSnapshot();
+
+    if (!reset_autologin_callback_.is_null())
+      std::move(reset_autologin_callback_).Run();
   } else {
-    // TODO(pbond): restart browser to normal.
     LOG(ERROR) << "Key pair generation failed. Abort snapshot creation.";
+
+    snapshot_.set_blocked_ui_mode(false);
+    snapshot_.Sync();
+
+    DCHECK(!attempt_user_exit_callback_.is_null());
+    EnsureDaemonStopped(std::move(attempt_user_exit_callback_));
   }
 }
 
diff --git a/components/arc/enterprise/arc_data_snapshotd_manager.h b/components/arc/enterprise/arc_data_snapshotd_manager.h
index 4dec658..aba4e15a 100644
--- a/components/arc/enterprise/arc_data_snapshotd_manager.h
+++ b/components/arc/enterprise/arc_data_snapshotd_manager.h
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
+#include "components/session_manager/core/session_manager_observer.h"
 
 class PrefService;
 
@@ -26,7 +27,8 @@
 
 // This class manages ARC data/ directory snapshots and controls the lifetime of
 // the arc-data-snapshotd daemon.
-class ArcDataSnapshotdManager final {
+class ArcDataSnapshotdManager final
+    : public session_manager::SessionManagerObserver {
  public:
   // State of the flow.
   enum class State {
@@ -109,7 +111,7 @@
     static std::unique_ptr<Snapshot> CreateForTesting(
         PrefService* local_state,
         bool blocked_ui_mode,
-        const std::string& started_date,
+        bool started,
         std::unique_ptr<SnapshotInfo> last,
         std::unique_ptr<SnapshotInfo> previous);
 
@@ -121,14 +123,22 @@
     // if |last| is true or previous otherwise.
     void ClearSnapshot(bool last);
 
+    // Moves last snapshot to previous and updates a |start_date| to the current
+    // date.
+    void StartNewSnapshot();
+
+    void set_blocked_ui_mode(bool blocked_ui_mode) {
+      blocked_ui_mode_ = blocked_ui_mode;
+    }
     bool is_blocked_ui_mode() const { return blocked_ui_mode_; }
+    bool started() const { return started_; }
     SnapshotInfo* last() { return last_.get(); }
     SnapshotInfo* previous() { return previous_.get(); }
 
    private:
     Snapshot(PrefService* local_state,
              bool blocked_ui_mode,
-             const std::string& started_date,
+             bool started,
              std::unique_ptr<SnapshotInfo> last,
              std::unique_ptr<SnapshotInfo> previous);
 
@@ -138,32 +148,49 @@
     // Values should be kept in sync with values stored in arc.snapshot
     // preference.
     bool blocked_ui_mode_ = false;
-    std::string started_date_;
+    bool started_;
     std::unique_ptr<SnapshotInfo> last_;
     std::unique_ptr<SnapshotInfo> previous_;
   };
 
-  explicit ArcDataSnapshotdManager(PrefService* local_state);
+  ArcDataSnapshotdManager(PrefService* local_state,
+                          base::OnceClosure attempt_user_exit_callback);
   ArcDataSnapshotdManager(const ArcDataSnapshotdManager&) = delete;
   ArcDataSnapshotdManager& operator=(const ArcDataSnapshotdManager&) = delete;
-  ~ArcDataSnapshotdManager();
+  ~ArcDataSnapshotdManager() override;
+
+  static ArcDataSnapshotdManager* Get();
 
   // Starts arc-data-snapshotd.
   void EnsureDaemonStarted(base::OnceClosure callback);
   // Stops arc-data-snapshotd.
   void EnsureDaemonStopped(base::OnceClosure callback);
 
+  // Returns true if autologin to public account should be performed.
+  bool IsAutoLoginConfigured();
+  // Returns true if autologin is allowed to be performed and manager is not
+  // waiting for the response from arc-data-snapshotd daemon.
+  bool IsAutoLoginAllowed();
+
+  // session_manager::SessionManagerObserver:
+  void OnSessionStateChanged() override;
+
   // Get |bridge_| for testing.
   ArcDataSnapshotdBridge* bridge() { return bridge_.get(); }
 
   State state() const { return state_; }
 
+  void set_reset_autologin_callback(base::OnceClosure callback) {
+    reset_autologin_callback_ = std::move(callback);
+  }
+
   static void set_snapshot_enabled_for_testing(bool enabled) {
     is_snapshot_enabled_for_testing_ = enabled;
   }
   static bool is_snapshot_enabled_for_testing() {
     return is_snapshot_enabled_for_testing_;
   }
+  void set_state_for_testing(State state) { state_ = state; }
 
  private:
   // Attempts to arc-data-snapshotd daemon regardless of state of the class.
@@ -201,6 +228,11 @@
 
   std::unique_ptr<ArcDataSnapshotdBridge> bridge_;
 
+  base::OnceClosure attempt_user_exit_callback_;
+
+  // Callback to reset an autologin timer once userless MGS is ready to start.
+  base::OnceClosure reset_autologin_callback_;
+
   // Used for cancelling previously posted tasks to daemon.
   base::WeakPtrFactory<ArcDataSnapshotdManager> daemon_weak_ptr_factory_{this};
   // WeakPtrFactory to use for callbacks.
diff --git a/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc b/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
index d645161..ca770132 100644
--- a/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
+++ b/components/arc/enterprise/arc_data_snapshotd_manager_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/test/bind_test_util.h"
 #include "base/test/task_environment.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/dbus/arc/fake_arc_data_snapshotd_client.h"
@@ -17,6 +18,7 @@
 #include "components/arc/arc_prefs.h"
 #include "components/arc/enterprise/arc_data_snapshotd_bridge.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/session_manager/core/session_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/ozone/public/ozone_switches.h"
@@ -54,12 +56,14 @@
     chromeos::DBusThreadManager::Initialize();
     EXPECT_TRUE(chromeos::DBusThreadManager::Get()->IsUsingFakes());
 
-    arc::prefs::RegisterLocalStatePrefs(local_state_.registry());
-
     upstart_client_ = std::make_unique<TestUpstartClient>();
   }
 
-  void SetUp() override { SetDBusClientAvailability(true /* is_available */); }
+  void SetUp() override {
+    SetDBusClientAvailability(true /* is_available */);
+
+    arc::prefs::RegisterLocalStatePrefs(local_state_.registry());
+  }
 
   ~ArcDataSnapshotdManagerBasicTest() override {
     chromeos::DBusThreadManager::Shutdown();
@@ -99,10 +103,11 @@
  protected:
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  TestingPrefServiceSimple local_state_;
 
  private:
-  TestingPrefServiceSimple local_state_;
   std::unique_ptr<TestUpstartClient> upstart_client_;
+  session_manager::SessionManager session_manager_;
 };
 
 // Tests flows in ArcDataSnapshotdManager:
@@ -114,6 +119,7 @@
  public:
   void SetUp() override {
     SetDBusClientAvailability(is_dbus_client_available());
+    arc::prefs::RegisterLocalStatePrefs(local_state_.registry());
   }
 
   bool is_dbus_client_available() { return GetParam(); }
@@ -125,7 +131,9 @@
   }
 
   // Check number of snapshots in local_state.
-  void CheckSnapshotsNumber(int expected_number) {
+  void CheckSnapshots(int expected_snapshots_number,
+                      bool expected_blocked_ui = true,
+                      bool expected_snapshot_started = false) {
     ArcDataSnapshotdManager::Snapshot snapshot(local_state());
     snapshot.Parse();
     int actual_number = 0;
@@ -135,12 +143,14 @@
     if (snapshot.last()) {
       actual_number++;
     }
-    EXPECT_EQ(expected_number, actual_number);
+    EXPECT_EQ(expected_snapshots_number, actual_number);
+    EXPECT_EQ(expected_blocked_ui, snapshot.is_blocked_ui_mode());
+    EXPECT_EQ(expected_snapshot_started, snapshot.started());
   }
 
   // Set up local_state with info for previous and last snapshots and blocked ui
   // mode.
-  void SetupLocalState() {
+  void SetupLocalState(bool blocked_ui_mode) {
     auto last = ArcDataSnapshotdManager::SnapshotInfo::CreateForTesting(
         "" /* os_version */, "" /* creation_date */, false /* verified */,
         false /* updated */, true /* last */);
@@ -148,8 +158,8 @@
         "" /* os_version */, "" /* creation_date */, false /* verified */,
         false /* updated */, false /* last */);
     auto snapshot = ArcDataSnapshotdManager::Snapshot::CreateForTesting(
-        local_state(), true /* blocked_ui_mode */, "" /* started_date */,
-        std::move(last), std::move(previous));
+        local_state(), blocked_ui_mode, false /* started */, std::move(last),
+        std::move(previous));
     snapshot->Sync();
   }
 
@@ -170,7 +180,9 @@
 
 // Test basic scenario: start / stop arc-data-snapshotd.
 TEST_F(ArcDataSnapshotdManagerBasicTest, Basic) {
-  ArcDataSnapshotdManager manager(local_state());
+  // Daemon stopped in ctor, since no need to be running.
+  ExpectStopDaemon(false /* success */);
+  ArcDataSnapshotdManager manager(local_state(), base::DoNothing());
   EXPECT_EQ(manager.state(), ArcDataSnapshotdManager::State::kNone);
   EXPECT_FALSE(manager.bridge());
 
@@ -186,7 +198,9 @@
 // Test a double start scenario: start arc-data-snapshotd twice.
 // Upstart job returns "false" if the job is already running.
 TEST_F(ArcDataSnapshotdManagerBasicTest, DoubleStart) {
-  ArcDataSnapshotdManager manager(local_state());
+  // Daemon stopped in ctor, since no need to be running.
+  ExpectStopDaemon(false /* success */);
+  ArcDataSnapshotdManager manager(local_state(), base::DoNothing());
   EXPECT_EQ(manager.state(), ArcDataSnapshotdManager::State::kNone);
   EXPECT_FALSE(manager.bridge());
 
@@ -208,7 +222,11 @@
 // Test that arc-data-snapshotd daemon is already stopped when |manager| tries
 // to stop it.
 TEST_F(ArcDataSnapshotdManagerBasicTest, UpstartFailures) {
-  ArcDataSnapshotdManager manager(local_state());
+  // Daemon stopped in ctor, since no need to be running.
+  ExpectStopDaemon(false /* success */);
+
+  ArcDataSnapshotdManager manager(local_state(), base::DoNothing());
+
   EXPECT_EQ(manager.state(), ArcDataSnapshotdManager::State::kNone);
   EXPECT_FALSE(manager.bridge());
 
@@ -225,8 +243,11 @@
   SetUpRestoredSessionCommandLine();
   // The attempt to stop the daemon, started before crash.
   ExpectStopDaemon(true /*success */);
-  ArcDataSnapshotdManager manager(local_state());
+  ArcDataSnapshotdManager manager(local_state(), base::DoNothing());
   EXPECT_EQ(manager.state(), ArcDataSnapshotdManager::State::kRestored);
+  EXPECT_FALSE(manager.IsAutoLoginConfigured());
+  EXPECT_TRUE(manager.IsAutoLoginAllowed());
+
   EXPECT_FALSE(manager.bridge());
 
   ExpectStartDaemon(true /*success */);
@@ -239,8 +260,9 @@
 // Test clear snapshots flow.
 TEST_P(ArcDataSnapshotdManagerFlowTest, ClearSnapshotsBasic) {
   // Set up two snapshots (previous and last) in local_state.
-  SetupLocalState();
-  CheckSnapshotsNumber(2 /* expected_number */);
+  SetupLocalState(false /* blocked_ui_mode */);
+  CheckSnapshots(2 /* expected_snapshots_number */,
+                 false /* expected_blocked_ui_mode */);
 
   // Once |manager| is created, it tries to clear both snapshots, because the
   // mechanism is disabled by default, and stop the daemon.
@@ -248,12 +270,15 @@
   ExpectStartDaemon(true /*success */);
   // Stop once finished clearing.
   ExpectStopDaemon(true /*success */);
-  ArcDataSnapshotdManager manager(local_state());
+  ArcDataSnapshotdManager manager(local_state(), base::DoNothing());
   RunUntilIdle();
 
   // No snapshots in local_state either.
   EXPECT_EQ(manager.state(), ArcDataSnapshotdManager::State::kNone);
-  CheckSnapshotsNumber(0 /* expected_number */);
+  EXPECT_FALSE(manager.IsAutoLoginConfigured());
+  EXPECT_TRUE(manager.IsAutoLoginAllowed());
+  CheckSnapshots(0 /* expected_snapshots_number */,
+                 false /* expected_blocked_ui_mode */);
 
   EXPECT_FALSE(manager.bridge());
 }
@@ -261,8 +286,8 @@
 // Test blocked UI mode flow.
 TEST_P(ArcDataSnapshotdManagerFlowTest, BlockedUiBasic) {
   // Set up two snapshots (previous and last) in local_state.
-  SetupLocalState();
-  CheckSnapshotsNumber(2 /* expected_number */);
+  SetupLocalState(true /* blocked_ui_mode */);
+  CheckSnapshots(2 /* expected_snapshots_number */);
   // Enable snapshotting mechanism for testing.
   ArcDataSnapshotdManager::set_snapshot_enabled_for_testing(true /* enabled */);
 
@@ -272,21 +297,38 @@
   ExpectStartDaemon(true /*success */);
   // Stop once finished clearing.
   ExpectStopDaemon(true /*success */);
-  ArcDataSnapshotdManager manager(local_state());
+  bool is_attempt_user_exit_called = false;
+  ArcDataSnapshotdManager manager(
+      local_state(),
+      base::BindLambdaForTesting([&is_attempt_user_exit_called]() {
+        is_attempt_user_exit_called = true;
+      }));
   CheckHeadlessMode();
   EXPECT_EQ(manager.state(), ArcDataSnapshotdManager::State::kBlockedUi);
+  EXPECT_TRUE(manager.IsAutoLoginConfigured());
+  EXPECT_FALSE(manager.IsAutoLoginAllowed());
+
   RunUntilIdle();
 
-  // Snapshots are valid, no need to clear.
-  CheckSnapshotsNumber(2 /* expected_number */);
+  if (is_dbus_client_available()) {
+    EXPECT_FALSE(is_attempt_user_exit_called);
+    EXPECT_EQ(manager.state(), ArcDataSnapshotdManager::State::kMgsToLaunch);
+    EXPECT_TRUE(manager.IsAutoLoginConfigured());
+    EXPECT_TRUE(manager.IsAutoLoginAllowed());
 
-  auto expected_state = is_dbus_client_available()
-                            ? ArcDataSnapshotdManager::State::kMgsToLaunch
-                            : ArcDataSnapshotdManager::State::kBlockedUi;
-
-  // The communication is established and MGS can be launched.
-  EXPECT_EQ(manager.state(), expected_state);
-  EXPECT_TRUE(manager.bridge());
+    EXPECT_TRUE(manager.bridge());
+    // Starts a last snapshot creation. last became previous.
+    CheckSnapshots(1 /* expected_snapshots_number */,
+                   true /*expected_blocked_ui */,
+                   true /* expected_snapshot_started */);
+  } else {
+    EXPECT_TRUE(is_attempt_user_exit_called);
+    EXPECT_EQ(manager.state(), ArcDataSnapshotdManager::State::kBlockedUi);
+    EXPECT_FALSE(manager.bridge());
+    // Snapshots are valid. No need to clear.
+    CheckSnapshots(2 /* expected_snapshots_number */,
+                   false /* expected_blocked_ui */);
+  }
 }
 
 INSTANTIATE_TEST_SUITE_P(ArcDataSnapshotdManagerFlowTest,
diff --git a/components/autofill/core/browser/payments/autofill_offer_manager.cc b/components/autofill/core/browser/payments/autofill_offer_manager.cc
index c418886..0dbee44 100644
--- a/components/autofill/core/browser/payments/autofill_offer_manager.cc
+++ b/components/autofill/core/browser/payments/autofill_offer_manager.cc
@@ -62,14 +62,8 @@
   for (auto& suggestion : suggestions) {
     std::string id = suggestion.backend_id;
     if (eligible_offers_map.count(id)) {
-      base::string16 reward_amount =
-          base::UTF8ToUTF16(eligible_offers_map[id]->offer_reward_amount);
-
-      auto string_id = (reward_amount.find('%') == std::string::npos)
-                           ? IDS_AUTOFILL_OFFERS_DISCOUNT
-                           : IDS_AUTOFILL_OFFERS_CASHBACK;
       suggestion.offer_label =
-          l10n_util::GetStringFUTF16(string_id, reward_amount);
+          l10n_util::GetStringUTF16(IDS_AUTOFILL_OFFERS_CASHBACK);
     }
   }
 }
diff --git a/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc b/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
index 802136d..85f7277 100644
--- a/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/autofill_offer_manager_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/autofill_clock.h"
+#include "components/strings/grit/components_strings.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -87,18 +88,6 @@
   std::unique_ptr<AutofillOfferManager> autofill_offer_manager_ = nullptr;
 };
 
-TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_EligibleDiscount) {
-  CreditCard card = CreateCreditCard(kTestGuid);
-  CreateCreditCardOfferForCard(card, "$4");
-
-  std::vector<Suggestion> suggestions = {Suggestion()};
-  suggestions[0].backend_id = kTestGuid;
-  autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
-                                                       suggestions);
-
-  EXPECT_EQ(suggestions[0].offer_label, base::UTF8ToUTF16("$4 Off"));
-}
-
 TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_EligibleCashback) {
   CreditCard card = CreateCreditCard(kTestGuid);
   CreateCreditCardOfferForCard(card, "5%");
@@ -108,7 +97,8 @@
   autofill_offer_manager_->UpdateSuggestionsWithOffers(GURL(kTestUrlWithParam),
                                                        suggestions);
 
-  EXPECT_EQ(suggestions[0].offer_label, base::UTF8ToUTF16("5% Cash Back"));
+  EXPECT_EQ(suggestions[0].offer_label,
+            l10n_util::GetStringUTF16(IDS_AUTOFILL_OFFERS_CASHBACK));
 }
 
 TEST_F(AutofillOfferManagerTest, UpdateSuggestionsWithOffers_ExpiredOffer) {
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index f1ad220e8..3ae5f77c 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -507,10 +507,7 @@
 
   <!-- Credit card offers and rewards related strings -->
   <message name="IDS_AUTOFILL_OFFERS_CASHBACK" desc="Displays that a cashback offer will be rewarded if credit card is used on current page. Part of Autofill suggestions popup.">
-    <ph name="PERCENT">$1<ex>5%</ex></ph> Cash Back
-  </message>
-  <message name="IDS_AUTOFILL_OFFERS_DISCOUNT" desc="Displays a discount that will be rewarded if credit card is used on current page. Part of Autofill suggestions popup.">
-    <ph name="DISCOUNT">$1<ex>$$3</ex></ph> Off
+    Cashback linked
   </message>
 
 </grit-part>
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_CASHBACK.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_CASHBACK.png.sha1
index ef49ef3..e27c712 100644
--- a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_CASHBACK.png.sha1
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_CASHBACK.png.sha1
@@ -1 +1 @@
-378244426ac8f525d17518614e3d7b1508061cf9
\ No newline at end of file
+130304f80bbc14548f470c8363d9dda1fffc280b
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_DISCOUNT.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_DISCOUNT.png.sha1
deleted file mode 100644
index 0f0c9a19..0000000
--- a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_OFFERS_DISCOUNT.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a09ae4c895b292cf8ce625cf9843a477d86dfc35
\ No newline at end of file
diff --git a/components/bookmarks/browser/model_loader.cc b/components/bookmarks/browser/model_loader.cc
index 28d04dcb..da66510 100644
--- a/components/bookmarks/browser/model_loader.cc
+++ b/components/bookmarks/browser/model_loader.cc
@@ -96,7 +96,6 @@
       int64_t max_node_id = 0;
       std::string sync_metadata_str;
       BookmarkCodec codec;
-      base::TimeTicks start_time = base::TimeTicks::Now();
       codec.Decode(*root, details->bb_node(), details->other_folder_node(),
                    details->mobile_folder_node(), &max_node_id,
                    &sync_metadata_str);
@@ -107,8 +106,6 @@
       details->set_ids_reassigned(codec.ids_reassigned());
       details->set_guids_reassigned(codec.guids_reassigned());
       details->set_model_meta_info_map(codec.model_meta_info_map());
-      base::UmaHistogramTimes("Bookmarks.DecodeTime",
-                              base::TimeTicks::Now() - start_time);
 
       load_index = true;
     }
@@ -120,10 +117,7 @@
   // Load any extra root nodes now, after the IDs have been potentially
   // reassigned.
   if (load_index) {
-    base::TimeTicks start_time = base::TimeTicks::Now();
     AddBookmarksToIndex(details, details->root_node());
-    base::UmaHistogramTimes("Bookmarks.CreateBookmarkIndexTime",
-                            base::TimeTicks::Now() - start_time);
   }
 
   details->CreateUrlIndex();
@@ -133,16 +127,11 @@
       base::saturated_cast<int>(details->url_index()->UrlCount()));
 
   if (emit_experimental_uma && details->root_node()) {
-    base::TimeTicks start_time = base::TimeTicks::Now();
-
-    int num_duplicate_urls = GetNumDuplicateUrls(details->root_node());
+    const int num_duplicate_urls = GetNumDuplicateUrls(details->root_node());
     if (num_duplicate_urls > 0) {
       base::UmaHistogramCounts10000(
           "Bookmarks.Count.OnProfileLoad.DuplicateUrl", num_duplicate_urls);
     }
-
-    base::UmaHistogramTimes("Bookmarks.DuplicateAndEmptyTitleDetectionTime",
-                            base::TimeTicks::Now() - start_time);
   }
 }
 
diff --git a/components/browser_ui/sms/android/BUILD.gn b/components/browser_ui/sms/android/BUILD.gn
index edb5d07..a4acd7f 100644
--- a/components/browser_ui/sms/android/BUILD.gn
+++ b/components/browser_ui/sms/android/BUILD.gn
@@ -27,15 +27,15 @@
 
 generate_jni("jni_headers") {
   sources = [
-    "java/src/org/chromium/components/browser_ui/sms/SmsReceiverInfoBar.java",
+    "java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java",
   ]
 }
 
 android_library("java") {
   resources_package = "org.chromium.components.browser_ui.sms"
   sources = [
-    "java/src/org/chromium/components/browser_ui/sms/SmsReceiverInfoBar.java",
-    "java/src/org/chromium/components/browser_ui/sms/SmsReceiverUma.java",
+    "java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java",
+    "java/src/org/chromium/components/browser_ui/sms/WebOTPServiceUma.java",
   ]
 
   deps = [
diff --git a/components/browser_ui/sms/android/README.md b/components/browser_ui/sms/android/README.md
index 1d78b97..72c95099 100644
--- a/components/browser_ui/sms/android/README.md
+++ b/components/browser_ui/sms/android/README.md
@@ -1,3 +1,3 @@
-# SMS Receiver API
+# WebOTP Service API
 
-This directory contains the android specific implementation of the SMS Receiver API user interface. For more details, refer to [this README file](https://cs.chromium.org/chromium/src/content/browser/sms/README.md).
+This directory contains the android specific implementation of the WebOTP Service API user interface. For more details, refer to [this README file](https://cs.chromium.org/chromium/src/content/browser/sms/README.md).
diff --git a/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/SmsReceiverInfoBar.java b/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java
similarity index 77%
rename from components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/SmsReceiverInfoBar.java
rename to components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java
index cc08acf..69f9fe9 100644
--- a/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/SmsReceiverInfoBar.java
+++ b/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java
@@ -22,8 +22,8 @@
 /**
  * An InfoBar that asks for the user's permission to share the SMS with the page.
  */
-public class SmsReceiverInfoBar extends ConfirmInfoBar {
-    private static final String TAG = "SmsReceiverInfoBar";
+public class WebOTPServiceInfoBar extends ConfirmInfoBar {
+    private static final String TAG = "WebOTPServiceInfoBar";
     private static final boolean DEBUG = false;
     private String mMessage;
     private WindowAndroid mWindowAndroid;
@@ -31,13 +31,13 @@
 
     @VisibleForTesting
     @CalledByNative
-    public static SmsReceiverInfoBar create(WindowAndroid windowAndroid, int iconId, String title,
+    public static WebOTPServiceInfoBar create(WindowAndroid windowAndroid, int iconId, String title,
             String message, String okButtonLabel) {
-        if (DEBUG) Log.d(TAG, "SmsReceiverInfoBar.create()");
-        return new SmsReceiverInfoBar(windowAndroid, iconId, title, message, okButtonLabel);
+        if (DEBUG) Log.d(TAG, "WebOTPServiceInfoBar.create()");
+        return new WebOTPServiceInfoBar(windowAndroid, iconId, title, message, okButtonLabel);
     }
 
-    private SmsReceiverInfoBar(WindowAndroid windowAndroid, int iconId, String title,
+    private WebOTPServiceInfoBar(WindowAndroid windowAndroid, int iconId, String title,
             String message, String okButtonLabel) {
         super(iconId, R.color.infobar_icon_drawable_color,
                 /*iconBitmap=*/null, /*message=*/title, /*linkText=*/null, okButtonLabel,
@@ -54,7 +54,7 @@
     @Override
     public void createContent(InfoBarLayout layout) {
         super.createContent(layout);
-        SmsReceiverUma.recordInfobarAction(SmsReceiverUma.InfobarAction.SHOWN);
+        WebOTPServiceUma.recordInfobarAction(WebOTPServiceUma.InfobarAction.SHOWN);
 
         Activity activity = mWindowAndroid.getActivity().get();
         if (activity != null) {
@@ -64,7 +64,8 @@
             if (focusedView != null
                     && keyboardVisibilityDelegate.isKeyboardShowing(activity, focusedView)) {
                 keyboardVisibilityDelegate.hideKeyboard(focusedView);
-                SmsReceiverUma.recordInfobarAction(SmsReceiverUma.InfobarAction.KEYBOARD_DISMISSED);
+                WebOTPServiceUma.recordInfobarAction(
+                        WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED);
                 mKeyboardDismissedTime = SystemClock.uptimeMillis();
             }
         }
@@ -79,7 +80,7 @@
         super.onCloseButtonClicked();
 
         if (mKeyboardDismissedTime != null) {
-            SmsReceiverUma.recordCancelTimeAfterKeyboardDismissal(
+            WebOTPServiceUma.recordCancelTimeAfterKeyboardDismissal(
                     SystemClock.uptimeMillis() - mKeyboardDismissedTime);
         }
     }
diff --git a/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/SmsReceiverUma.java b/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceUma.java
similarity index 86%
rename from components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/SmsReceiverUma.java
rename to components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceUma.java
index 347f8ab..47e7210 100644
--- a/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/SmsReceiverUma.java
+++ b/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceUma.java
@@ -12,10 +12,10 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * Helper Class for Sms Receiver UMA Collection.
+ * Helper Class for WebOTP Service UMA Collection.
  */
-public final class SmsReceiverUma {
-    // Note: these values must match the SMSReceiverInfobar enum in enums.xml.
+public final class WebOTPServiceUma {
+    // Note: these values must match the WebOTPServiceInfobar enum in enums.xml.
     // Only add new values at the end, right before NUM_ENTRIES.
     @IntDef({InfobarAction.SHOWN, InfobarAction.KEYBOARD_DISMISSED})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/components/browser_ui/sms/android/sms_infobar.cc b/components/browser_ui/sms/android/sms_infobar.cc
index baa9ef9..8c9b6f46 100644
--- a/components/browser_ui/sms/android/sms_infobar.cc
+++ b/components/browser_ui/sms/android/sms_infobar.cc
@@ -5,7 +5,7 @@
 #include "components/browser_ui/sms/android/sms_infobar.h"
 
 #include "base/android/jni_string.h"
-#include "components/browser_ui/sms/android/jni_headers/SmsReceiverInfoBar_jni.h"
+#include "components/browser_ui/sms/android/jni_headers/WebOTPServiceInfoBar_jni.h"
 #include "components/browser_ui/sms/android/sms_infobar_delegate.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/android/window_android.h"
@@ -52,8 +52,8 @@
   base::android::ScopedJavaLocalRef<jobject> window_android =
       web_contents_->GetNativeView()->GetWindowAndroid()->GetJavaObject();
 
-  return Java_SmsReceiverInfoBar_create(env, window_android, GetJavaIconId(),
-                                        title, message, button);
+  return Java_WebOTPServiceInfoBar_create(env, window_android, GetJavaIconId(),
+                                          title, message, button);
 }
 
 }  // namespace sms
diff --git a/components/browser_ui/sms/android/sms_infobar_delegate.cc b/components/browser_ui/sms/android/sms_infobar_delegate.cc
index d023211..59f1bd3 100644
--- a/components/browser_ui/sms/android/sms_infobar_delegate.cc
+++ b/components/browser_ui/sms/android/sms_infobar_delegate.cc
@@ -31,7 +31,7 @@
 
 infobars::InfoBarDelegate::InfoBarIdentifier SmsInfoBarDelegate::GetIdentifier()
     const {
-  return SMS_RECEIVER_INFOBAR_DELEGATE;
+  return WEBOTP_SERVICE_INFOBAR_DELEGATE;
 }
 
 int SmsInfoBarDelegate::GetIconId() const {
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h
index 58682c1..824b68c 100644
--- a/components/infobars/core/infobar_delegate.h
+++ b/components/infobars/core/infobar_delegate.h
@@ -162,7 +162,7 @@
     SEND_TAB_TO_SELF_INFOBAR_DELEGATE = 92,
     TAB_SHARING_INFOBAR_DELEGATE = 93,
     SAFETY_TIP_INFOBAR_DELEGATE = 94,
-    SMS_RECEIVER_INFOBAR_DELEGATE = 95,
+    WEBOTP_SERVICE_INFOBAR_DELEGATE = 95,
     KNOWN_INTERCEPTION_DISCLOSURE_INFOBAR_DELEGATE = 96,
     SYNC_ERROR_INFOBAR_DELEGATE_ANDROID = 97,
     MIXED_CONTENT_DOWNLOAD_INFOBAR_DELEGATE_ANDROID = 98,
diff --git a/components/lookalikes/core/lookalike_url_util.cc b/components/lookalikes/core/lookalike_url_util.cc
index c2a0cbe6..15d92ed 100644
--- a/components/lookalikes/core/lookalike_url_util.cc
+++ b/components/lookalikes/core/lookalike_url_util.cc
@@ -34,7 +34,7 @@
 
 namespace lookalikes {
 
-const char kHistogramName[] = "NavigationSuggestion.Event2";
+const char kHistogramName[] = "NavigationSuggestion.Event";
 
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterListPref(prefs::kLookalikeWarningAllowlistDomains);
diff --git a/components/password_manager/core/browser/ui/saved_passwords_presenter.cc b/components/password_manager/core/browser/ui/saved_passwords_presenter.cc
index a54776b..cc91f8cd 100644
--- a/components/password_manager/core/browser/ui/saved_passwords_presenter.cc
+++ b/components/password_manager/core/browser/ui/saved_passwords_presenter.cc
@@ -13,6 +13,31 @@
 #include "base/stl_util.h"
 #include "base/strings/string16.h"
 #include "components/password_manager/core/browser/password_form.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
+
+namespace {
+using password_manager::metrics_util::IsPasswordChanged;
+using password_manager::metrics_util::IsUsernameChanged;
+using SavedPasswordsView =
+    password_manager::SavedPasswordsPresenter::SavedPasswordsView;
+
+bool IsUsernameAlreadyUsed(SavedPasswordsView all_forms,
+                           SavedPasswordsView forms_to_check,
+                           const base::string16& new_username) {
+  // In case the username changed, make sure that there exists no other
+  // credential with the same signon_realm and username in the same store.
+  auto has_conflicting_username = [&forms_to_check,
+                                   &new_username](const auto& form) {
+    return new_username == form.username_value &&
+           base::ranges::any_of(forms_to_check, [&form](const auto& old_form) {
+             return form.signon_realm == old_form.signon_realm &&
+                    form.IsUsingAccountStore() ==
+                        old_form.IsUsingAccountStore();
+           });
+  };
+  return base::ranges::any_of(all_forms, has_conflicting_username);
+}
+}  // namespace
 
 namespace password_manager {
 
@@ -56,6 +81,47 @@
   return true;
 }
 
+bool SavedPasswordsPresenter::EditSavedPasswords(
+    const SavedPasswordsView forms,
+    const base::string16& new_username,
+    const base::string16& new_password) {
+  IsUsernameChanged username_changed(new_username != forms[0].username_value);
+  IsPasswordChanged password_changed(new_password != forms[0].password_value);
+
+  if (new_password.empty())
+    return false;
+  if (username_changed &&
+      IsUsernameAlreadyUsed(passwords_, forms, new_username))
+    return false;
+
+  // An updated username implies a change in the primary key, thus we need to
+  // make sure to call the right API. Update every entry in the equivalence
+  // class.
+  if (username_changed || password_changed) {
+    for (const auto& old_form : forms) {
+      PasswordStore& store =
+          old_form.IsUsingAccountStore() ? *account_store_ : *profile_store_;
+
+      autofill::PasswordForm new_form = old_form;
+      new_form.username_value = new_username;
+      new_form.password_value = new_password;
+
+      if (username_changed) {
+        // Changing username requires deleting old form and adding new one. So
+        // the different API should be called.
+        store.UpdateLoginWithPrimaryKey(new_form, old_form);
+      } else {
+        store.UpdateLogin(new_form);
+      }
+      NotifyEdited(new_form);
+    }
+  }
+
+  password_manager::metrics_util::LogPasswordEditResult(username_changed,
+                                                        password_changed);
+  return true;
+}
+
 SavedPasswordsPresenter::SavedPasswordsView
 SavedPasswordsPresenter::GetSavedPasswords() const {
   return passwords_;
diff --git a/components/password_manager/core/browser/ui/saved_passwords_presenter.h b/components/password_manager/core/browser/ui/saved_passwords_presenter.h
index e1e7c128..d0588f2 100644
--- a/components/password_manager/core/browser/ui/saved_passwords_presenter.h
+++ b/components/password_manager/core/browser/ui/saved_passwords_presenter.h
@@ -69,6 +69,13 @@
   // in |passwords_|.
   bool EditPassword(const PasswordForm& form, base::string16 new_password);
 
+  // Modifies provided password forms, with |new_username| and |new_password|.
+  // |forms| must represent single credential, with its duplicates, or the
+  // same form saved on another store type.
+  bool EditSavedPasswords(const SavedPasswordsView forms,
+                          const base::string16& new_username,
+                          const base::string16& new_password);
+
   // Returns a list of the currently saved credentials.
   SavedPasswordsView GetSavedPasswords() const;
 
diff --git a/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc b/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc
index e13810e..02c736ac 100644
--- a/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc
+++ b/components/password_manager/core/browser/ui/saved_passwords_presenter_unittest.cc
@@ -7,8 +7,10 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/scoped_observer.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/password_manager/core/browser/password_form.h"
+#include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/test_password_store.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -151,6 +153,191 @@
   presenter().RemoveObserver(&observer);
 }
 
+TEST_F(SavedPasswordsPresenterTest, EditOnlyUsername) {
+  PasswordForm form;
+  form.signon_realm = "https://example.com";
+  form.username_value = base::ASCIIToUTF16("test@gmail.com");
+  form.password_value = base::ASCIIToUTF16("password");
+  form.in_store = PasswordForm::Store::kProfileStore;
+
+  StrictMockSavedPasswordsPresenterObserver observer;
+  presenter().AddObserver(&observer);
+
+  EXPECT_CALL(observer, OnSavedPasswordsChanged);
+  store().AddLogin(form);
+  RunUntilIdle();
+  EXPECT_FALSE(store().IsEmpty());
+
+  std::vector<PasswordForm> forms = {form};
+
+  const base::string16 new_username = base::ASCIIToUTF16("new_username");
+  PasswordForm updated_username = form;
+  updated_username.username_value = new_username;
+
+  // Verify that editing a username triggers the right notifications.
+  base::HistogramTester histogram_tester;
+
+  EXPECT_CALL(observer, OnEdited(updated_username));
+  EXPECT_CALL(observer, OnSavedPasswordsChanged(ElementsAre(updated_username)));
+  EXPECT_TRUE(
+      presenter().EditSavedPasswords(forms, new_username, form.password_value));
+  RunUntilIdle();
+  EXPECT_THAT(
+      store().stored_passwords(),
+      ElementsAre(Pair(form.signon_realm, ElementsAre(updated_username))));
+
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.PasswordEditUpdatedValues",
+      metrics_util::PasswordEditUpdatedValues::kUsername, 1);
+
+  presenter().RemoveObserver(&observer);
+}
+
+TEST_F(SavedPasswordsPresenterTest, EditOnlyPassword) {
+  PasswordForm form;
+  form.signon_realm = "https://example.com";
+  form.username_value = base::ASCIIToUTF16("test@gmail.com");
+  form.password_value = base::ASCIIToUTF16("password");
+  form.in_store = PasswordForm::Store::kProfileStore;
+
+  StrictMockSavedPasswordsPresenterObserver observer;
+  presenter().AddObserver(&observer);
+
+  EXPECT_CALL(observer, OnSavedPasswordsChanged);
+  store().AddLogin(form);
+  RunUntilIdle();
+  EXPECT_FALSE(store().IsEmpty());
+
+  std::vector<PasswordForm> forms = {form};
+
+  const base::string16 new_password = base::ASCIIToUTF16("new_password");
+  PasswordForm updated_password = form;
+  updated_password.password_value = new_password;
+
+  base::HistogramTester histogram_tester;
+  // Verify that editing a password triggers the right notifications.
+  EXPECT_CALL(observer, OnEdited(updated_password));
+  EXPECT_CALL(observer, OnSavedPasswordsChanged(ElementsAre(updated_password)));
+  EXPECT_TRUE(
+      presenter().EditSavedPasswords(forms, form.username_value, new_password));
+  RunUntilIdle();
+  EXPECT_THAT(
+      store().stored_passwords(),
+      ElementsAre(Pair(form.signon_realm, ElementsAre(updated_password))));
+  histogram_tester.ExpectUniqueSample(
+      "PasswordManager.PasswordEditUpdatedValues",
+      metrics_util::PasswordEditUpdatedValues::kPassword, 1);
+
+  presenter().RemoveObserver(&observer);
+}
+
+TEST_F(SavedPasswordsPresenterTest, EditUsernameAndPassword) {
+  PasswordForm form;
+  form.signon_realm = "https://example.com";
+  form.username_value = base::ASCIIToUTF16("test@gmail.com");
+  form.password_value = base::ASCIIToUTF16("password");
+  form.in_store = PasswordForm::Store::kProfileStore;
+
+  StrictMockSavedPasswordsPresenterObserver observer;
+  presenter().AddObserver(&observer);
+
+  EXPECT_CALL(observer, OnSavedPasswordsChanged);
+  store().AddLogin(form);
+  RunUntilIdle();
+  EXPECT_FALSE(store().IsEmpty());
+
+  std::vector<PasswordForm> forms = {form};
+
+  const base::string16 new_username = base::ASCIIToUTF16("new_username");
+  const base::string16 new_password = base::ASCIIToUTF16("new_password");
+
+  PasswordForm updated_both = form;
+  updated_both.username_value = new_username;
+  updated_both.password_value = new_password;
+
+  base::HistogramTester histogram_tester;
+  // Verify that editing username and password triggers the right notifications.
+  EXPECT_CALL(observer, OnEdited(updated_both));
+  EXPECT_CALL(observer, OnSavedPasswordsChanged(ElementsAre(updated_both)));
+  EXPECT_TRUE(
+      presenter().EditSavedPasswords(forms, new_username, new_password));
+  RunUntilIdle();
+  EXPECT_THAT(store().stored_passwords(),
+              ElementsAre(Pair(form.signon_realm, ElementsAre(updated_both))));
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.PasswordEditUpdatedValues",
+      metrics_util::PasswordEditUpdatedValues::kBoth, 1);
+
+  presenter().RemoveObserver(&observer);
+}
+
+TEST_F(SavedPasswordsPresenterTest, EditPasswordFails) {
+  PasswordForm form1;
+  form1.signon_realm = "https://example.com";
+  form1.username_value = base::ASCIIToUTF16("test1@gmail.com");
+  form1.password_value = base::ASCIIToUTF16("password");
+  form1.in_store = PasswordForm::Store::kProfileStore;
+
+  PasswordForm form2;
+  form2.signon_realm = "https://example.com";
+  form2.username_value = base::ASCIIToUTF16("test2@gmail.com");
+  form2.password_value = base::ASCIIToUTF16("password");
+  form2.in_store = PasswordForm::Store::kProfileStore;
+
+  store().AddLogin(form1);
+  store().AddLogin(form2);
+  RunUntilIdle();
+  EXPECT_FALSE(store().IsEmpty());
+
+  std::vector<PasswordForm> forms{form1};
+
+  // Updating the form with the username which is already used for same website
+  // fails.
+  const base::string16 new_username = base::ASCIIToUTF16("test2@gmail.com");
+  EXPECT_FALSE(presenter().EditSavedPasswords(forms, new_username,
+                                              form1.password_value));
+  RunUntilIdle();
+  EXPECT_THAT(store().stored_passwords(),
+              ElementsAre(Pair(form1.signon_realm, ElementsAre(form1, form2))));
+
+  // Updating the form with the empty password fails.
+  EXPECT_FALSE(presenter().EditSavedPasswords(forms, form1.username_value,
+                                              base::string16()));
+  RunUntilIdle();
+  EXPECT_THAT(store().stored_passwords(),
+              ElementsAre(Pair(form1.signon_realm, ElementsAre(form1, form2))));
+}
+
+TEST_F(SavedPasswordsPresenterTest, EditPasswordWithoutChanges) {
+  PasswordForm form;
+  form.signon_realm = "https://example.com";
+  form.username_value = base::ASCIIToUTF16("test1@gmail.com");
+  form.password_value = base::ASCIIToUTF16("password");
+  form.in_store = PasswordForm::Store::kProfileStore;
+
+  store().AddLogin(form);
+
+  RunUntilIdle();
+  StrictMockSavedPasswordsPresenterObserver observer;
+  presenter().AddObserver(&observer);
+
+  EXPECT_FALSE(store().IsEmpty());
+  // Verify that editing a form without changing the username or password does
+  // not triggers notifications.
+  base::HistogramTester histogram_tester;
+  EXPECT_CALL(observer, OnEdited).Times(0);
+  EXPECT_CALL(observer, OnSavedPasswordsChanged).Times(0);
+  std::vector<PasswordForm> forms = {form};
+  EXPECT_TRUE(presenter().EditSavedPasswords(forms, form.username_value,
+                                             form.password_value));
+  RunUntilIdle();
+  histogram_tester.ExpectBucketCount(
+      "PasswordManager.PasswordEditUpdatedValues",
+      metrics_util::PasswordEditUpdatedValues::kNone, 1);
+
+  presenter().RemoveObserver(&observer);
+}
+
 namespace {
 
 class SavedPasswordsPresenterWithTwoStoresTest : public ::testing::Test {
@@ -232,4 +419,37 @@
   presenter().RemoveObserver(&observer);
 }
 
+// This tests changing the username of a credentials stored in the profile store
+// to be equal to a username of a credential stored in the account store for the
+// same domain.
+TEST_F(SavedPasswordsPresenterWithTwoStoresTest, EditUsername) {
+  PasswordForm profile_store_form;
+  profile_store_form.username_value = base::ASCIIToUTF16("profile@gmail.com");
+  profile_store_form.password_value = base::ASCIIToUTF16("profile_pass");
+  profile_store_form.in_store = PasswordForm::Store::kProfileStore;
+
+  PasswordForm account_store_form;
+  account_store_form.username_value = base::ASCIIToUTF16("account@gmail.com");
+  account_store_form.password_value = base::ASCIIToUTF16("account_pass");
+  account_store_form.in_store = PasswordForm::Store::kAccountStore;
+
+  profile_store().AddLogin(profile_store_form);
+  account_store().AddLogin(account_store_form);
+  RunUntilIdle();
+
+  EXPECT_THAT(profile_store().stored_passwords(),
+              ElementsAre(Pair(profile_store_form.signon_realm,
+                               ElementsAre(profile_store_form))));
+
+  auto new_username = account_store_form.username_value;
+  std::vector<PasswordForm> forms_to_edit{profile_store_form};
+  EXPECT_TRUE(presenter().EditSavedPasswords(
+      forms_to_edit, new_username, profile_store_form.password_value));
+  RunUntilIdle();
+  profile_store_form.username_value = new_username;
+  EXPECT_THAT(profile_store().stored_passwords(),
+              ElementsAre(Pair(profile_store_form.signon_realm,
+                               ElementsAre(profile_store_form))));
+}
+
 }  // namespace password_manager
diff --git a/components/performance_manager/performance_manager.cc b/components/performance_manager/performance_manager.cc
index e8178ed..ea18b346 100644
--- a/components/performance_manager/performance_manager.cc
+++ b/components/performance_manager/performance_manager.cc
@@ -13,6 +13,7 @@
 #include "components/performance_manager/performance_manager_registry_impl.h"
 #include "components/performance_manager/performance_manager_tab_helper.h"
 #include "components/performance_manager/public/performance_manager_owned.h"
+#include "content/public/browser/render_process_host.h"
 
 namespace performance_manager {
 
@@ -102,6 +103,17 @@
 }
 
 // static
+base::WeakPtr<ProcessNode>
+PerformanceManager::GetProcessNodeForRenderProcessHostId(
+    RenderProcessHostId id) {
+  DCHECK(id);
+  auto* rph = content::RenderProcessHost::FromID(id.value());
+  if (!rph)
+    return nullptr;
+  return GetProcessNodeForRenderProcessHost(rph);
+}
+
+// static
 void PerformanceManager::AddObserver(
     PerformanceManagerMainThreadObserver* observer) {
   PerformanceManagerRegistryImpl::GetInstance()->AddObserver(observer);
diff --git a/components/performance_manager/public/performance_manager.h b/components/performance_manager/public/performance_manager.h
index a22013fe..c147cbe 100644
--- a/components/performance_manager/public/performance_manager.h
+++ b/components/performance_manager/public/performance_manager.h
@@ -12,6 +12,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
+#include "components/performance_manager/public/render_process_host_id.h"
 
 namespace content {
 class RenderFrameHost;
@@ -96,6 +97,16 @@
   static base::WeakPtr<ProcessNode> GetProcessNodeForRenderProcessHost(
       content::RenderProcessHost* rph);
 
+  // Returns a WeakPtr to the ProcessNode associated with a given
+  // RenderProcessHostId (which must be valid), or a null WeakPtr if there's no
+  // ProcessNode for this ID. (There may be no RenderProcessHost for this ID,
+  // or it may be during a brief window after the RPH is created but before the
+  // ProcessNode is added.) Valid to call from the main thread only, the
+  // returned WeakPtr should only be dereferenced on the PM sequence (e.g. it
+  // can be used in a CallOnGraph callback).
+  static base::WeakPtr<ProcessNode> GetProcessNodeForRenderProcessHostId(
+      RenderProcessHostId id);
+
   // Adds / removes an observer that is notified of PerformanceManager events
   // that happen on the main thread. Can only be called on the main thread.
   static void AddObserver(PerformanceManagerMainThreadObserver* observer);
diff --git a/components/performance_manager/public/v8_memory/v8_detailed_memory.h b/components/performance_manager/public/v8_memory/v8_detailed_memory.h
index b1a85c2..76fa601 100644
--- a/components/performance_manager/public/v8_memory/v8_detailed_memory.h
+++ b/components/performance_manager/public/v8_memory/v8_detailed_memory.h
@@ -11,6 +11,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/time/time.h"
@@ -191,6 +192,7 @@
 class V8DetailedMemoryRequest;
 class V8DetailedMemoryRequestAnySeq;
 class V8DetailedMemoryRequestOneShot;
+class V8DetailedMemoryRequestOneShotAnySeq;
 
 class V8DetailedMemoryDecorator
     : public GraphOwned,
@@ -232,8 +234,8 @@
   // Implementation details below this point.
 
   // V8DetailedMemoryRequest objects register themselves with the decorator.
-  // If |process_node| is null, the request will be sent to every process,
-  // otherwise it will be sent only to |process_node|.
+  // If |process_node| is null, the request will be sent to every renderer
+  // process, otherwise it will be sent only to |process_node|.
   void AddMeasurementRequest(util::PassKey<V8DetailedMemoryRequest>,
                              V8DetailedMemoryRequest* request,
                              const ProcessNode* process_node = nullptr);
@@ -411,11 +413,14 @@
   // Private constructor for V8DetailedMemoryRequestAnySeq. Saves
   // |off_sequence_request| as a pointer to the off-sequence object that
   // triggered the request and starts measurements with frequency
-  // |min_time_between_requests|.
+  // |min_time_between_requests|. If |process_to_measure| is nullopt, the
+  // request will be sent to every renderer process, otherwise it will be sent
+  // only to |process_to_measure|.
   V8DetailedMemoryRequest(
       util::PassKey<V8DetailedMemoryRequestAnySeq>,
       const base::TimeDelta& min_time_between_requests,
       MeasurementMode mode,
+      base::Optional<base::WeakPtr<ProcessNode>> process_to_measure,
       base::WeakPtr<V8DetailedMemoryRequestAnySeq> off_sequence_request);
 
   // Private constructor for V8DetailedMemoryRequestOneShot. Sets
@@ -437,6 +442,9 @@
       const ProcessNode* process_node) const;
 
  private:
+  void StartMeasurementFromOffSequence(
+      base::Optional<base::WeakPtr<ProcessNode>> process_to_measure,
+      Graph* graph);
   void StartMeasurementImpl(Graph* graph, const ProcessNode* process_node);
 
   base::TimeDelta min_time_between_requests_;
@@ -489,7 +497,20 @@
       const ProcessNode* process_node,
       const V8DetailedMemoryProcessData* process_data) final;
 
+  // Implementation details below this point.
+
+  // Private constructor for V8DetailedMemoryRequestOneShotAnySeq. Will be
+  // called from off-sequence.
+  V8DetailedMemoryRequestOneShot(
+      util::PassKey<V8DetailedMemoryRequestOneShotAnySeq>,
+      base::WeakPtr<ProcessNode> process,
+      MeasurementCallback callback,
+      MeasurementMode mode = MeasurementMode::kDefault);
+
  private:
+  void InitializeRequest(const ProcessNode* process, MeasurementMode mode);
+  void InitializeRequestFromOffSequence(base::WeakPtr<ProcessNode> process,
+                                        MeasurementMode mode);
   void DeleteRequest();
 
 #if DCHECK_IS_ON()
@@ -534,9 +555,16 @@
  public:
   using MeasurementMode = V8DetailedMemoryRequest::MeasurementMode;
 
+  // Creates a memory measurement request that will be sent repeatedly with at
+  // least |min_time_between_requests| between each measurement. The request
+  // will be sent to the process with ID |process_to_measure|, which must be a
+  // renderer process, or to all renderer processes if |process_to_measure| is
+  // nullopt. The process will perform the measurement during a GC as determined
+  // by |mode|.
   explicit V8DetailedMemoryRequestAnySeq(
       const base::TimeDelta& min_time_between_requests,
-      MeasurementMode mode = MeasurementMode::kDefault);
+      MeasurementMode mode = MeasurementMode::kDefault,
+      base::Optional<RenderProcessHostId> process_to_measure = base::nullopt);
   ~V8DetailedMemoryRequestAnySeq();
 
   V8DetailedMemoryRequestAnySeq(const V8DetailedMemoryRequestAnySeq&) = delete;
@@ -564,6 +592,11 @@
       const V8DetailedMemoryObserverAnySeq::FrameDataMap& frame_data) const;
 
  private:
+  void InitializeWrappedRequest(
+      const base::TimeDelta& min_time_between_requests,
+      MeasurementMode mode,
+      base::Optional<base::WeakPtr<ProcessNode>> process_to_measure);
+
   std::unique_ptr<V8DetailedMemoryRequest> request_;
   base::ObserverList<V8DetailedMemoryObserverAnySeq, /*check_empty=*/true>
       observers_;
@@ -575,6 +608,63 @@
   base::WeakPtrFactory<V8DetailedMemoryRequestAnySeq> weak_factory_{this};
 };
 
+// Wrapper that can instantiate a V8DetailedMemoryRequestOneShot from any
+// sequence.
+class V8DetailedMemoryRequestOneShotAnySeq {
+ public:
+  using MeasurementMode = V8DetailedMemoryRequest::MeasurementMode;
+
+  using FrameDataMap = V8DetailedMemoryObserverAnySeq::FrameDataMap;
+
+  // A callback that will be called on the request's sequence with the results
+  // of the measurement. |process_id| will always match the value passed to
+  // the V8DetailedMemoryRequestOneShotAnySeq constructor.
+  using MeasurementCallback =
+      base::OnceCallback<void(RenderProcessHostId process_id,
+                              const V8DetailedMemoryProcessData& process_data,
+                              const FrameDataMap& frame_data)>;
+
+  V8DetailedMemoryRequestOneShotAnySeq(
+      RenderProcessHostId process_id,
+      MeasurementCallback callback,
+      MeasurementMode mode = MeasurementMode::kDefault);
+
+  ~V8DetailedMemoryRequestOneShotAnySeq();
+
+  V8DetailedMemoryRequestOneShotAnySeq(
+      const V8DetailedMemoryRequestOneShotAnySeq&) = delete;
+  V8DetailedMemoryRequestOneShotAnySeq& operator=(
+      const V8DetailedMemoryRequestOneShotAnySeq&) = delete;
+
+ private:
+  void InitializeWrappedRequest(MeasurementMode mode,
+                                base::WeakPtr<ProcessNode>);
+
+  // Called on the PM sequence when a measurement is available. It will call
+  // request->InvokeWrappedCallback on |task_runner|.
+  static void OnMeasurementAvailable(
+      scoped_refptr<base::SequencedTaskRunner> task_runner,
+      base::WeakPtr<V8DetailedMemoryRequestOneShotAnySeq> request,
+      const ProcessNode* process_node,
+      const V8DetailedMemoryProcessData* process_data);
+
+  void InvokeWrappedCallback(RenderProcessHostId process_id,
+                             const V8DetailedMemoryProcessData& process_data,
+                             const FrameDataMap& frame_data);
+
+  MeasurementCallback wrapped_callback_;
+
+  // The wrapped request. Must only be accessed from the PM sequence.
+  std::unique_ptr<V8DetailedMemoryRequestOneShot> request_;
+
+  // This object can live on any sequence but all methods and the destructor
+  // must be called from that sequence.
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<V8DetailedMemoryRequestOneShotAnySeq> weak_factory_{
+      this};
+};
+
 //////////////////////////////////////////////////////////////////////////////
 // The following internal functions are exposed in the header for testing.
 
diff --git a/components/performance_manager/v8_memory/v8_detailed_memory.cc b/components/performance_manager/v8_memory/v8_detailed_memory.cc
index 1075e69..701cdc6 100644
--- a/components/performance_manager/v8_memory/v8_detailed_memory.cc
+++ b/components/performance_manager/v8_memory/v8_detailed_memory.cc
@@ -592,15 +592,18 @@
     util::PassKey<V8DetailedMemoryRequestAnySeq>,
     const base::TimeDelta& min_time_between_requests,
     MeasurementMode mode,
+    base::Optional<base::WeakPtr<ProcessNode>> process_to_measure,
     base::WeakPtr<V8DetailedMemoryRequestAnySeq> off_sequence_request)
     : V8DetailedMemoryRequest(min_time_between_requests, mode) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
   off_sequence_request_ = std::move(off_sequence_request);
   off_sequence_request_sequence_ = base::SequencedTaskRunnerHandle::Get();
-  // Unretained is safe since |this| will be destroyed on the graph sequence.
+  // Unretained is safe since |this| will be destroyed on the graph sequence
+  // from an async task posted after this.
   PerformanceManager::CallOnGraph(
-      FROM_HERE, base::BindOnce(&V8DetailedMemoryRequest::StartMeasurement,
-                                base::Unretained(this)));
+      FROM_HERE,
+      base::BindOnce(&V8DetailedMemoryRequest::StartMeasurementFromOffSequence,
+                     base::Unretained(this), std::move(process_to_measure)));
 }
 
 V8DetailedMemoryRequest::V8DetailedMemoryRequest(
@@ -700,6 +703,22 @@
     observer.OnV8MemoryMeasurementAvailable(process_node, process_data);
 }
 
+void V8DetailedMemoryRequest::StartMeasurementFromOffSequence(
+    base::Optional<base::WeakPtr<ProcessNode>> process_to_measure,
+    Graph* graph) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!process_to_measure) {
+    // No process was given so measure all renderers in the graph.
+    StartMeasurement(graph);
+  } else if (!process_to_measure.value()) {
+    // V8DetailedMemoryRequestAnySeq was called with a process ID that wasn't
+    // found in the graph, or has already been destroyed. Do nothing.
+  } else {
+    DCHECK_EQ(graph, process_to_measure.value()->GetGraph());
+    StartMeasurementForProcess(process_to_measure.value().get());
+  }
+}
+
 void V8DetailedMemoryRequest::StartMeasurementImpl(
     Graph* graph,
     const ProcessNode* process_node) {
@@ -726,16 +745,7 @@
     MeasurementCallback callback,
     MeasurementMode mode)
     : callback_(std::move(callback)), mode_(mode) {
-  DCHECK(process);
-  DCHECK_EQ(process->GetProcessType(), content::PROCESS_TYPE_RENDERER);
-  request_ = std::make_unique<V8DetailedMemoryRequest>(
-      util::PassKey<V8DetailedMemoryRequestOneShot>(), mode);
-  request_->AddObserver(this);
-  request_->StartMeasurementForProcess(process);
-
-#if DCHECK_IS_ON()
-  process_ = process;
-#endif
+  InitializeRequest(process, mode);
 }
 
 V8DetailedMemoryRequestOneShot::~V8DetailedMemoryRequestOneShot() {
@@ -758,6 +768,48 @@
   std::move(callback_).Run(process_node, process_data);
 }
 
+// This constructor is called from the V8DetailedMemoryRequestOneShotAnySeq's
+// sequence.
+V8DetailedMemoryRequestOneShot::V8DetailedMemoryRequestOneShot(
+    util::PassKey<V8DetailedMemoryRequestOneShotAnySeq>,
+    base::WeakPtr<ProcessNode> process,
+    MeasurementCallback callback,
+    MeasurementMode mode)
+    : callback_(std::move(callback)), mode_(mode) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+  // Unretained is safe since |this| will be destroyed on the graph sequence
+  // from an async task posted after this.
+  PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          &V8DetailedMemoryRequestOneShot::InitializeRequestFromOffSequence,
+          base::Unretained(this), std::move(process), mode));
+}
+
+void V8DetailedMemoryRequestOneShot::InitializeRequest(
+    const ProcessNode* process,
+    MeasurementMode mode) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(process);
+  DCHECK_EQ(process->GetProcessType(), content::PROCESS_TYPE_RENDERER);
+  request_ = std::make_unique<V8DetailedMemoryRequest>(
+      util::PassKey<V8DetailedMemoryRequestOneShot>(), mode);
+  request_->AddObserver(this);
+  request_->StartMeasurementForProcess(process);
+
+#if DCHECK_IS_ON()
+  process_ = process;
+#endif
+}
+
+void V8DetailedMemoryRequestOneShot::InitializeRequestFromOffSequence(
+    base::WeakPtr<ProcessNode> process,
+    MeasurementMode mode) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (process)
+    InitializeRequest(process.get(), mode);
+}
+
 void V8DetailedMemoryRequestOneShot::DeleteRequest() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (request_)
@@ -1069,16 +1121,28 @@
 
 V8DetailedMemoryRequestAnySeq::V8DetailedMemoryRequestAnySeq(
     const base::TimeDelta& min_time_between_requests,
-    MeasurementMode mode) {
-  // |request_| must be initialized in the constructor body so that
-  // |weak_factory_| is completely constructed.
-  //
-  // Can't use make_unique since this calls the private any-sequence
-  // constructor. After construction the V8DetailedMemoryRequest must only be
-  // accessed on the graph sequence.
-  request_ = base::WrapUnique(new V8DetailedMemoryRequest(
-      util::PassKey<V8DetailedMemoryRequestAnySeq>(), min_time_between_requests,
-      mode, weak_factory_.GetWeakPtr()));
+    MeasurementMode mode,
+    base::Optional<RenderProcessHostId> process_to_measure) {
+  base::Optional<base::WeakPtr<ProcessNode>> process_node;
+  if (process_to_measure) {
+    // GetProcessNodeForRenderProcessHostId must be called from the UI thread.
+    auto ui_task_runner = content::GetUIThreadTaskRunner({});
+    if (!ui_task_runner->RunsTasksInCurrentSequence()) {
+      ui_task_runner->PostTaskAndReplyWithResult(
+          FROM_HERE,
+          base::BindOnce(
+              &PerformanceManager::GetProcessNodeForRenderProcessHostId,
+              process_to_measure.value()),
+          base::BindOnce(
+              &V8DetailedMemoryRequestAnySeq::InitializeWrappedRequest,
+              weak_factory_.GetWeakPtr(), min_time_between_requests, mode));
+      return;
+    }
+    process_node = PerformanceManager::GetProcessNodeForRenderProcessHostId(
+        process_to_measure.value());
+  }
+  InitializeWrappedRequest(min_time_between_requests, mode,
+                           std::move(process_node));
 }
 
 V8DetailedMemoryRequestAnySeq::~V8DetailedMemoryRequestAnySeq() {
@@ -1121,6 +1185,114 @@
                                             process_data, frame_data);
 }
 
+void V8DetailedMemoryRequestAnySeq::InitializeWrappedRequest(
+    const base::TimeDelta& min_time_between_requests,
+    MeasurementMode mode,
+    base::Optional<base::WeakPtr<ProcessNode>> process_to_measure) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Can't use make_unique since this calls the private any-sequence
+  // constructor. After construction the V8DetailedMemoryRequest must only be
+  // accessed on the graph sequence.
+  request_ = base::WrapUnique(new V8DetailedMemoryRequest(
+      util::PassKey<V8DetailedMemoryRequestAnySeq>(), min_time_between_requests,
+      mode, std::move(process_to_measure), weak_factory_.GetWeakPtr()));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// V8DetailedMemoryRequestOneShotAnySeq
+
+V8DetailedMemoryRequestOneShotAnySeq::V8DetailedMemoryRequestOneShotAnySeq(
+    RenderProcessHostId process_id,
+    MeasurementCallback callback,
+    MeasurementMode mode)
+    : wrapped_callback_(std::move(callback)) {
+  // GetProcessNodeForRenderProcessHostId must be called from the UI thread.
+  auto ui_task_runner = content::GetUIThreadTaskRunner({});
+  if (ui_task_runner->RunsTasksInCurrentSequence()) {
+    InitializeWrappedRequest(
+        mode,
+        PerformanceManager::GetProcessNodeForRenderProcessHostId(process_id));
+  } else {
+    ui_task_runner->PostTaskAndReplyWithResult(
+        FROM_HERE,
+        base::BindOnce(
+            &PerformanceManager::GetProcessNodeForRenderProcessHostId,
+            process_id),
+        base::BindOnce(
+            &V8DetailedMemoryRequestOneShotAnySeq::InitializeWrappedRequest,
+            weak_factory_.GetWeakPtr(), mode));
+  }
+}
+
+V8DetailedMemoryRequestOneShotAnySeq::~V8DetailedMemoryRequestOneShotAnySeq() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          [](std::unique_ptr<V8DetailedMemoryRequestOneShot> request) {
+            request.reset();
+          },
+          std::move(request_)));
+}
+
+void V8DetailedMemoryRequestOneShotAnySeq::InitializeWrappedRequest(
+    MeasurementMode mode,
+    base::WeakPtr<ProcessNode> process_node) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Can't use make_unique since this calls the private any-sequence
+  // constructor. After construction the V8DetailedMemoryRequestOneShot must
+  // only be accessed on the graph sequence.
+  request_ = base::WrapUnique(new V8DetailedMemoryRequestOneShot(
+      util::PassKey<V8DetailedMemoryRequestOneShotAnySeq>(),
+      std::move(process_node),
+      base::BindOnce(
+          &V8DetailedMemoryRequestOneShotAnySeq::OnMeasurementAvailable,
+          base::SequencedTaskRunnerHandle::Get(), weak_factory_.GetWeakPtr()),
+      mode));
+}
+
+// static
+void V8DetailedMemoryRequestOneShotAnySeq::OnMeasurementAvailable(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    base::WeakPtr<V8DetailedMemoryRequestOneShotAnySeq> request,
+    const ProcessNode* process_node,
+    const V8DetailedMemoryProcessData* process_data) {
+  DCHECK(process_node);
+  DCHECK_ON_GRAPH_SEQUENCE(process_node->GetGraph());
+
+  using FrameAndData =
+      std::pair<content::GlobalFrameRoutingId, V8DetailedMemoryFrameData>;
+  std::vector<FrameAndData> all_frame_data;
+  process_node->VisitFrameNodes(base::BindRepeating(
+      [](std::vector<FrameAndData>* all_frame_data,
+         const FrameNode* frame_node) {
+        const auto* frame_data =
+            V8DetailedMemoryFrameData::ForFrameNode(frame_node);
+        if (frame_data) {
+          all_frame_data->push_back(std::make_pair(
+              frame_node->GetRenderFrameHostProxy().global_frame_routing_id(),
+              *frame_data));
+        }
+        return true;
+      },
+      base::Unretained(&all_frame_data)));
+
+  task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &V8DetailedMemoryRequestOneShotAnySeq::InvokeWrappedCallback,
+          std::move(request), process_node->GetRenderProcessHostId(),
+          *process_data, std::move(all_frame_data)));
+}
+
+void V8DetailedMemoryRequestOneShotAnySeq::InvokeWrappedCallback(
+    RenderProcessHostId process_id,
+    const V8DetailedMemoryProcessData& process_data,
+    const FrameDataMap& frame_data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::move(wrapped_callback_).Run(process_id, process_data, frame_data);
+}
+
 }  // namespace v8_memory
 
 }  // namespace performance_manager
diff --git a/components/performance_manager/v8_memory/v8_detailed_memory_unittest.cc b/components/performance_manager/v8_memory/v8_detailed_memory_unittest.cc
index 4e4377c..59bf58a 100644
--- a/components/performance_manager/v8_memory/v8_detailed_memory_unittest.cc
+++ b/components/performance_manager/v8_memory/v8_detailed_memory_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
@@ -28,6 +29,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
@@ -312,6 +314,23 @@
     : public PerformanceManagerTestHarness,
       public V8DetailedMemoryDecoratorTestBase {
  public:
+  void SetUp() override {
+    PerformanceManagerTestHarness::SetUp();
+
+    // Precondition: CallOnGraph must run on a different sequence. Note that
+    // all tasks passed to CallOnGraph will only run when run_loop.Run() is
+    // called.
+    ASSERT_TRUE(GetMainThreadTaskRunner()->RunsTasksInCurrentSequence());
+    base::RunLoop run_loop;
+    PerformanceManager::CallOnGraph(
+        FROM_HERE, base::BindLambdaForTesting([&] {
+          EXPECT_FALSE(
+              this->GetMainThreadTaskRunner()->RunsTasksInCurrentSequence());
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+  }
+
   scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner()
       override {
     return task_environment()->GetMainThreadTaskRunner();
@@ -1626,16 +1645,6 @@
 }
 
 TEST_F(V8DetailedMemoryRequestAnySeqTest, RequestIsSequenceSafe) {
-  // Precondition: CallOnGraph must run on a different sequence.  Note that all
-  // tasks passed to CallOnGraph will only run when run_loop.Run() is called
-  // below.
-  ASSERT_TRUE(GetMainThreadTaskRunner()->RunsTasksInCurrentSequence());
-  PerformanceManager::CallOnGraph(
-      FROM_HERE, base::BindLambdaForTesting([this] {
-        EXPECT_FALSE(
-            this->GetMainThreadTaskRunner()->RunsTasksInCurrentSequence());
-      }));
-
   // Set the active contents and simulate a navigation, which adds nodes to the
   // graph.
   SetContents(CreateTestWebContents());
@@ -1696,7 +1705,7 @@
   EXPECT_CALL(observer,
               OnV8MemoryMeasurementAvailable(process_id, expected_process_data,
                                              expected_frame_data))
-      .WillOnce([this, &run_loop, &process_id, &expected_frame_data]() {
+      .WillOnce([&]() {
         run_loop.Quit();
         ASSERT_TRUE(
             this->GetMainThreadTaskRunner()->RunsTasksInCurrentSequence())
@@ -1747,6 +1756,179 @@
   run_loop2.Run();
 }
 
+TEST_F(V8DetailedMemoryRequestAnySeqTest, SingleProcessRequest) {
+  content::IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+  SetContents(CreateTestWebContents());
+
+  // Set up a page at a.com with a subframe at b.com. These should be in
+  // different processes.
+  const GURL kUrlA("http://a.com/");
+  const GURL kUrlB("http://b.com/");
+  content::RenderFrameHost* main_frame =
+      content::NavigationSimulator::NavigateAndCommitFromBrowser(
+          web_contents(), GURL("http://a.com"));
+  content::RenderFrameHost* child_frame =
+      content::RenderFrameHostTester::For(main_frame)->AppendChild("frame1");
+  child_frame = content::NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("http://b.com"), child_frame);
+
+  const RenderProcessHostId process_id1(main_frame->GetProcess()->GetID());
+  const RenderProcessHostId process_id2(child_frame->GetProcess()->GetID());
+  ASSERT_NE(process_id1, process_id2);
+
+  V8DetailedMemoryProcessData expected_process_data1;
+  expected_process_data1.set_unassociated_v8_bytes_used(1U);
+  V8DetailedMemoryProcessData expected_process_data2;
+  expected_process_data2.set_unassociated_v8_bytes_used(2U);
+
+  MockV8DetailedMemoryReporter mock_reporter1;
+  MockV8DetailedMemoryReporter mock_reporter2;
+  {
+    auto data = NewPerProcessV8MemoryUsage(1);
+    data->isolates[0]->unassociated_bytes_used = 1U;
+    ExpectBindAndRespondToQuery(&mock_reporter1, std::move(data), process_id1);
+
+    data = NewPerProcessV8MemoryUsage(1);
+    data->isolates[0]->unassociated_bytes_used = 2U;
+    ExpectBindAndRespondToQuery(&mock_reporter2, std::move(data), process_id2);
+  }
+
+  // Create one request that measures both processes, and one request that
+  // measures only one.
+  V8DetailedMemoryRequestAnySeq all_process_request(
+      V8DetailedMemoryDecoratorTest::kMinTimeBetweenRequests);
+  MockV8DetailedMemoryObserverAnySeq all_process_observer;
+  all_process_request.AddObserver(&all_process_observer);
+
+  V8DetailedMemoryRequestAnySeq single_process_request(
+      V8DetailedMemoryDecoratorTest::kMinTimeBetweenRequests,
+      MeasurementMode::kBounded, process_id1);
+  MockV8DetailedMemoryObserverAnySeq single_process_observer;
+  single_process_request.AddObserver(&single_process_observer);
+
+  // When a measurement is available the all process observer should be invoked
+  // for both processes, and the single process observer only for process 1.
+  EXPECT_CALL(
+      all_process_observer,
+      OnV8MemoryMeasurementAvailable(process_id1, expected_process_data1, _));
+  EXPECT_CALL(
+      all_process_observer,
+      OnV8MemoryMeasurementAvailable(process_id2, expected_process_data2, _));
+  EXPECT_CALL(
+      single_process_observer,
+      OnV8MemoryMeasurementAvailable(process_id1, expected_process_data1, _));
+
+  // Now execute all the above tasks.
+  task_environment()->RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&mock_reporter1);
+  Mock::VerifyAndClearExpectations(&mock_reporter2);
+  Mock::VerifyAndClearExpectations(&all_process_observer);
+  Mock::VerifyAndClearExpectations(&single_process_observer);
+
+  // Must remove the observer before destroying the request to avoid a DCHECK
+  // from ObserverList.
+  all_process_request.RemoveObserver(&all_process_observer);
+  single_process_request.RemoveObserver(&single_process_observer);
+}
+
+TEST_F(V8DetailedMemoryRequestAnySeqTest, OneShot) {
+  content::IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+  SetContents(CreateTestWebContents());
+
+  // Set up a page at a.com with a subframe at b.com. These should be in
+  // different processes. We will create one request that measures both
+  // processes, and a one-shot request that measures only one.
+  const GURL kUrlA("http://a.com/");
+  const GURL kUrlB("http://b.com/");
+  content::RenderFrameHost* main_frame =
+      content::NavigationSimulator::NavigateAndCommitFromBrowser(
+          web_contents(), GURL("http://a.com"));
+  content::RenderFrameHost* child_frame =
+      content::RenderFrameHostTester::For(main_frame)->AppendChild("frame1");
+  child_frame = content::NavigationSimulator::NavigateAndCommitFromDocument(
+      GURL("http://b.com"), child_frame);
+
+  const RenderProcessHostId process_id1(main_frame->GetProcess()->GetID());
+  const RenderProcessHostId process_id2(child_frame->GetProcess()->GetID());
+  ASSERT_NE(process_id1, process_id2);
+
+  // Set the all process request to only send once within the test.
+  V8DetailedMemoryRequestAnySeq all_process_request(
+      V8DetailedMemoryDecoratorTest::kMinTimeBetweenRequests * 100);
+
+  // Create a mock reporter for each process and expect a query and reply on
+  // each.
+  MockV8DetailedMemoryReporter mock_reporter1;
+
+  {
+    auto data = NewPerProcessV8MemoryUsage(1);
+    data->isolates[0]->unassociated_bytes_used = 1ULL;
+    ExpectBindAndRespondToQuery(&mock_reporter1, std::move(data), process_id1);
+  }
+
+  MockV8DetailedMemoryReporter mock_reporter2;
+  {
+    auto data = NewPerProcessV8MemoryUsage(1);
+    data->isolates[0]->unassociated_bytes_used = 2ULL;
+    ExpectBindAndRespondToQuery(&mock_reporter2, std::move(data), process_id2);
+  }
+
+  task_environment()->RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&mock_reporter1);
+  Mock::VerifyAndClearExpectations(&mock_reporter2);
+
+  // Create a one-shot request for process1 and expect the callback to be
+  // called only for that process.
+  {
+    auto data = NewPerProcessV8MemoryUsage(1);
+    data->isolates[0]->unassociated_bytes_used = 3ULL;
+    ExpectQueryAndReply(&mock_reporter1, std::move(data));
+  }
+
+  uint64_t unassociated_v8_bytes_used = 0;
+  V8DetailedMemoryRequestOneShotAnySeq process1_request(
+      process_id1,
+      base::BindLambdaForTesting(
+          [&](RenderProcessHostId process_id,
+              const V8DetailedMemoryProcessData& process_data,
+              const V8DetailedMemoryRequestOneShotAnySeq::FrameDataMap&
+                  frame_data) {
+            EXPECT_EQ(process_id, process_id1);
+            unassociated_v8_bytes_used =
+                process_data.unassociated_v8_bytes_used();
+          }));
+  task_environment()->RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&mock_reporter1);
+  Mock::VerifyAndClearExpectations(&mock_reporter2);
+  EXPECT_EQ(unassociated_v8_bytes_used, 3ULL);
+
+  // Create another request, but delete it before the result arrives.
+  {
+    auto data = NewPerProcessV8MemoryUsage(1);
+    data->isolates[0]->unassociated_bytes_used = 4ULL;
+    ExpectQueryAndDelayReply(&mock_reporter1, base::TimeDelta::FromSeconds(10),
+                             std::move(data));
+  }
+
+  auto doomed_request = std::make_unique<V8DetailedMemoryRequestOneShotAnySeq>(
+      process_id1,
+      base::BindOnce(
+          [](RenderProcessHostId process_id,
+             const V8DetailedMemoryProcessData& process_data,
+             const V8DetailedMemoryRequestOneShotAnySeq::FrameDataMap&
+                 frame_data) {
+            FAIL() << "Callback called after request deleted.";
+          }));
+
+  // Verify that requests are sent but reply is not received.
+  task_environment()->RunUntilIdle();
+  Mock::VerifyAndClearExpectations(&mock_reporter1);
+  Mock::VerifyAndClearExpectations(&mock_reporter2);
+
+  doomed_request.reset();
+  task_environment()->RunUntilIdle();
+}
+
 }  // namespace v8_memory
 
 }  // namespace performance_manager
diff --git a/components/prerender/browser/prerender_link_manager.cc b/components/prerender/browser/prerender_link_manager.cc
index 6a8c2b5..34326d4 100644
--- a/components/prerender/browser/prerender_link_manager.cc
+++ b/components/prerender/browser/prerender_link_manager.cc
@@ -125,9 +125,8 @@
       std::move(attributes), initiator_origin, std::move(processor_client),
       manager_->GetCurrentTimeTicks(), prerender_contents);
 
-  // Observe disconnect of the client and treat as equivalent to explicit
-  // abandonment. Similar to above, the raw pointer to |this| is safe because
-  // |prerender| is owned by |this|.
+  // Observe disconnect of the client to abandon the running prerender. The raw
+  // pointer to |this| is safe because |prerender| is owned by |this|.
   prerender->remote_processor_client.set_disconnect_handler(
       base::BindOnce(&PrerenderLinkManager::OnAbandonPrerender,
                      base::Unretained(this), prerender->prerender_id));
diff --git a/components/prerender/browser/prerender_processor_impl.cc b/components/prerender/browser/prerender_processor_impl.cc
index 1068f665..9017cf96 100644
--- a/components/prerender/browser/prerender_processor_impl.cc
+++ b/components/prerender/browser/prerender_processor_impl.cc
@@ -79,14 +79,6 @@
     link_manager->OnCancelPrerender(*prerender_id_);
 }
 
-void PrerenderProcessorImpl::Abandon() {
-  if (!prerender_id_)
-    return;
-  auto* link_manager = GetPrerenderLinkManager();
-  if (link_manager)
-    link_manager->OnAbandonPrerender(*prerender_id_);
-}
-
 PrerenderLinkManager* PrerenderProcessorImpl::GetPrerenderLinkManager() {
   auto* render_frame_host =
       content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
diff --git a/components/prerender/browser/prerender_processor_impl.h b/components/prerender/browser/prerender_processor_impl.h
index 8866a50d..60f39be 100644
--- a/components/prerender/browser/prerender_processor_impl.h
+++ b/components/prerender/browser/prerender_processor_impl.h
@@ -34,7 +34,6 @@
              mojo::PendingRemote<blink::mojom::PrerenderProcessorClient> client)
       override;
   void Cancel() override;
-  void Abandon() override;
 
  private:
   PrerenderLinkManager* GetPrerenderLinkManager();
diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc
index a616251..399cc20 100644
--- a/components/signin/core/browser/account_reconcilor.cc
+++ b/components/signin/core/browser/account_reconcilor.cc
@@ -128,12 +128,10 @@
 
 // TODO(https://crbug.com/1122551): Move this code and
 // |RevokeAllSecondaryTokens| to |DiceAccountReconcilorDelegate|.
-signin::RevokeTokenAction RevokeTokensNotInCookies(
+void RevokeTokensNotInCookies(
     signin::IdentityManager* identity_manager,
     const CoreAccountId& primary_account,
     const std::vector<gaia::ListedAccount>& gaia_accounts) {
-  bool invalidated_primary_account_token = false;
-  bool revoked_token_for_secondary_account = false;
   signin_metrics::SourceForRefreshTokenOperation source =
       signin_metrics::SourceForRefreshTokenOperation::
           kAccountReconcilor_RevokeTokensNotInCookies;
@@ -146,28 +144,11 @@
 
     auto* accounts_mutator = identity_manager->GetAccountsMutator();
     if (account == primary_account) {
-      invalidated_primary_account_token = true;
       accounts_mutator->InvalidateRefreshTokenForPrimaryAccount(source);
     } else {
-      revoked_token_for_secondary_account = true;
       accounts_mutator->RemoveAccount(account, source);
     }
   }
-
-  signin::RevokeTokenAction revoke_token_action =
-      signin::RevokeTokenAction::kNone;
-  if (invalidated_primary_account_token &&
-      revoked_token_for_secondary_account) {
-    revoke_token_action =
-        signin::RevokeTokenAction::kRevokeTokensForPrimaryAndSecondaryAccounts;
-  } else if (invalidated_primary_account_token) {
-    revoke_token_action =
-        signin::RevokeTokenAction::kInvalidatePrimaryAccountToken;
-  } else if (revoked_token_for_secondary_account) {
-    revoke_token_action =
-        signin::RevokeTokenAction::kRevokeSecondaryAccountsTokens;
-  }
-  return revoke_token_action;
 }
 
 // Pick the account will become first after this reconcile is finished.
@@ -652,9 +633,9 @@
     // thus use sync account.
     // TODO(https://crbug.com/1122551): Move to |DiceAccountReconcilorDelegate|.
     DCHECK_EQ(consent_level, ConsentLevel::kSync);
-    signin::RevokeTokenAction revoke_token_action = RevokeTokensNotInCookies(
-        identity_manager_, primary_account, verified_gaia_accounts);
-    delegate_->OnRevokeTokensNotInCookiesCompleted(revoke_token_action);
+    RevokeTokensNotInCookies(identity_manager_, primary_account,
+                             verified_gaia_accounts);
+    delegate_->OnRevokeTokensNotInCookiesCompleted();
   }
 
   // Revoking tokens for secondary accounts causes the AccountTracker to
diff --git a/components/signin/core/browser/account_reconcilor_delegate.h b/components/signin/core/browser/account_reconcilor_delegate.h
index 3b3888c..e4cd238 100644
--- a/components/signin/core/browser/account_reconcilor_delegate.h
+++ b/components/signin/core/browser/account_reconcilor_delegate.h
@@ -19,15 +19,6 @@
 
 namespace signin {
 
-// Possible revoke token actions taken by the AccountReconcilor.
-enum class RevokeTokenAction {
-  kNone,
-  kInvalidatePrimaryAccountToken,
-  kRevokeSecondaryAccountsTokens,
-  kRevokeTokensForPrimaryAndSecondaryAccounts,
-  kMaxValue = kRevokeTokensForPrimaryAndSecondaryAccounts
-};
-
 // Base class for AccountReconcilorDelegate.
 class AccountReconcilorDelegate {
  public:
@@ -110,8 +101,7 @@
   virtual bool ShouldRevokeTokensNotInCookies() const;
 
   // Called when |RevokeTokensNotInCookies| is finished.
-  virtual void OnRevokeTokensNotInCookiesCompleted(
-      RevokeTokenAction revoke_token_action) {}
+  virtual void OnRevokeTokensNotInCookiesCompleted() {}
 
   // Returns whether tokens should be revoked when the Gaia cookie has been
   // explicitly deleted by the user.
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc
index 9cbc468..85720336 100644
--- a/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -49,7 +49,6 @@
 #include "components/signin/core/browser/active_directory_account_reconcilor_delegate.h"
 #endif
 
-using signin::RevokeTokenAction;
 using signin_metrics::AccountReconcilorState;
 
 namespace {
@@ -465,7 +464,6 @@
   const char* gaia_api_calls;
   const char* tokens_after_reconcile;
   const char* cookies_after_reconcile;
-  RevokeTokenAction revoke_token_action;
 };
 
 // Pretty prints a AccountReconcilorTestTableParam. Used by gtest.
@@ -1045,8 +1043,6 @@
   // nothing on the second call.
   CheckReconcileIdempotent(kDiceParams, GetParam(), /*multilogin=*/false);
   RunReconcile();
-  histogram_tester()->ExpectTotalCount("ForceDiceMigration.RevokeTokenAction",
-                                       0);
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -1072,34 +1068,34 @@
 
 // clang-format off
 const std::vector<ForceDiceMigrationTestTableParam> kForceDiceParams = {
-    {"*A",   "AB",   "XA", "*A",    "A"   , RevokeTokenAction::kNone},
-    {"*AxB", "AB",   "XA", "*A",    "A"   , RevokeTokenAction::kNone},
-    {"AxB",  "AB",   "XA", "A",     "A"   , RevokeTokenAction::kNone},
-    {"xAxB", "AB",   "X",  "",      ""    , RevokeTokenAction::kNone},
-    {"*A",   "",     "",   "*xA",   ""    , RevokeTokenAction::kInvalidatePrimaryAccountToken},
-    {"*A",   "B",    "X",  "*xA",   ""    , RevokeTokenAction::kInvalidatePrimaryAccountToken},
-    {"*AB",  "B",    "",   "*xAB",  "B"   , RevokeTokenAction::kInvalidatePrimaryAccountToken},
-    {"*AxB", "B",    "X",  "*xA",   ""    , RevokeTokenAction::kInvalidatePrimaryAccountToken},
-    {"*ABC", "CB",   "",   "*xABC", "CB"  , RevokeTokenAction::kInvalidatePrimaryAccountToken},
-    {"*AB",  "A",    "",   "*A",    "A"   , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"AB",   "A",    "",   "A",     "A"   , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"AB",   "",     "",   "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"xAB",  "",     "",   "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"xAB",  "A",    "X",  "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"xAB",  "xA",   "",   "",      "xA"  , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"xAB",  "B",    "",   "B",     "B"   , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"AxB",  "B",    "X",  "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"AxB",  "",     "",   "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"xAxB", "",     "",   "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"B",    "xA",   "",   "",      "xA"  , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"AB",   "xAB",  "",   "B",     "xAB" , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"xAB",  "xAC",  "X",  "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"xAB",  "AxC",  "X",  "",      ""    , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"AB",   "BC",   "XB", "B",     "B"   , RevokeTokenAction::kRevokeSecondaryAccountsTokens},
-    {"*AB",  "",     "",   "*xA",   ""    , RevokeTokenAction::kRevokeTokensForPrimaryAndSecondaryAccounts},
-    {"*xAB", "",     "",   "*xA",   ""    , RevokeTokenAction::kRevokeTokensForPrimaryAndSecondaryAccounts},
-    {"*AxB", "",     "",   "*xA",   ""    , RevokeTokenAction::kRevokeTokensForPrimaryAndSecondaryAccounts},
-    {"*AB",  "xBxA", "",   "*xA",   "xBxA", RevokeTokenAction::kRevokeTokensForPrimaryAndSecondaryAccounts}
+    {"*A",   "AB",   "XA", "*A",    "A"   },
+    {"*AxB", "AB",   "XA", "*A",    "A"   },
+    {"AxB",  "AB",   "XA", "A",     "A"   },
+    {"xAxB", "AB",   "X",  "",      ""    },
+    {"*A",   "",     "",   "*xA",   ""    },
+    {"*A",   "B",    "X",  "*xA",   ""    },
+    {"*AB",  "B",    "",   "*xAB",  "B"   },
+    {"*AxB", "B",    "X",  "*xA",   ""    },
+    {"*ABC", "CB",   "",   "*xABC", "CB"  },
+    {"*AB",  "A",    "",   "*A",    "A"   },
+    {"AB",   "A",    "",   "A",     "A"   },
+    {"AB",   "",     "",   "",      ""    },
+    {"xAB",  "",     "",   "",      ""    },
+    {"xAB",  "A",    "X",  "",      ""    },
+    {"xAB",  "xA",   "",   "",      "xA"  },
+    {"xAB",  "B",    "",   "B",     "B"   },
+    {"AxB",  "B",    "X",  "",      ""    },
+    {"AxB",  "",     "",   "",      ""    },
+    {"xAxB", "",     "",   "",      ""    },
+    {"B",    "xA",   "",   "",      "xA"  },
+    {"AB",   "xAB",  "",   "B",     "xAB" },
+    {"xAB",  "xAC",  "X",  "",      ""    },
+    {"xAB",  "AxC",  "X",  "",      ""    },
+    {"AB",   "BC",   "XB", "B",     "B"   },
+    {"*AB",  "",     "",   "*xA",   ""    },
+    {"*xAB", "",     "",   "*xA",   ""    },
+    {"*AxB", "",     "",   "*xA",   ""    },
+    {"*AB",  "xBxA", "",   "*xA",   "xBxA"}
   };
 // clang-format on
 
@@ -1112,8 +1108,6 @@
   EXPECT_TRUE(test_signin_client()->is_dice_migration_completed());
   EXPECT_FALSE(
       GetMockReconcilor()->delegate_->ShouldRevokeTokensNotInCookies());
-  histogram_tester()->ExpectUniqueSample("ForceDiceMigration.RevokeTokenAction",
-                                         GetParam().revoke_token_action, 1);
 }
 
 // Check that the result state of the reconcile is in a final state (reconcile
diff --git a/components/signin/core/browser/dice_account_reconcilor_delegate.cc b/components/signin/core/browser/dice_account_reconcilor_delegate.cc
index b008cc3..2da4c58 100644
--- a/components/signin/core/browser/dice_account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/dice_account_reconcilor_delegate.cc
@@ -305,12 +305,9 @@
   return !migration_completed_;
 }
 
-void DiceAccountReconcilorDelegate::OnRevokeTokensNotInCookiesCompleted(
-    RevokeTokenAction revoke_token_action) {
+void DiceAccountReconcilorDelegate::OnRevokeTokensNotInCookiesCompleted() {
   migration_completed_ = true;
   signin_client_->SetDiceMigrationCompleted();
-  UMA_HISTOGRAM_ENUMERATION("ForceDiceMigration.RevokeTokenAction",
-                            revoke_token_action);
 }
 
 bool DiceAccountReconcilorDelegate::ShouldRevokeTokensOnCookieDeleted() {
diff --git a/components/signin/core/browser/dice_account_reconcilor_delegate.h b/components/signin/core/browser/dice_account_reconcilor_delegate.h
index ad62529..3deee4d2 100644
--- a/components/signin/core/browser/dice_account_reconcilor_delegate.h
+++ b/components/signin/core/browser/dice_account_reconcilor_delegate.h
@@ -41,8 +41,7 @@
   // Returns true if in force migration to dice state.
   bool ShouldRevokeTokensNotInCookies() const override;
   // Disables force dice migration and sets dice migration as completed.
-  void OnRevokeTokensNotInCookiesCompleted(
-      RevokeTokenAction revoke_token_action) override;
+  void OnRevokeTokensNotInCookiesCompleted() override;
   void OnReconcileFinished(const CoreAccountId& first_account) override;
   bool ShouldRevokeTokensOnCookieDeleted() override;
   bool ShouldRevokeTokensBeforeMultilogin(
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc
index fdda983..c75044db 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.cc
@@ -308,7 +308,7 @@
   // However, if we know that |account_key| has a dummy token, store a
   // persistent error against it, so that we can pre-emptively reject access
   // token requests for it.
-  if (account_manager_->HasDummyGaiaToken(account.key)) {
+  if (account_manager_->HasDummyGaiaTokenSync(account.key)) {
     error = GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
         GoogleServiceAuthError::InvalidGaiaCredentialsReason::
             CREDENTIALS_REJECTED_BY_CLIENT);
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index c5d19c6a..bd843970 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -34,14 +34,10 @@
     "engine/commit_and_get_updates_types.h",
     "engine/commit_queue.h",
     "engine/configure_reason.h",
-    "engine/cycle/commit_counters.cc",
-    "engine/cycle/commit_counters.h",
     "engine/cycle/model_neutral_state.cc",
     "engine/cycle/model_neutral_state.h",
     "engine/cycle/sync_cycle_snapshot.cc",
     "engine/cycle/sync_cycle_snapshot.h",
-    "engine/cycle/update_counters.cc",
-    "engine/cycle/update_counters.h",
     "engine/data_type_activation_response.cc",
     "engine/data_type_activation_response.h",
     "engine/data_type_debug_info_listener.cc",
diff --git a/components/sync/base/syncer_error.cc b/components/sync/base/syncer_error.cc
index 15e11bc..51d7c57 100644
--- a/components/sync/base/syncer_error.cc
+++ b/components/sync/base/syncer_error.cc
@@ -26,7 +26,6 @@
     ENUM_CASE(NETWORK_IO_ERROR);
     ENUM_CASE(SYNC_SERVER_ERROR);
     ENUM_CASE(SYNC_AUTH_ERROR);
-    ENUM_CASE(SERVER_RETURN_INVALID_CREDENTIAL);
     ENUM_CASE(SERVER_RETURN_UNKNOWN_ERROR);
     ENUM_CASE(SERVER_RETURN_THROTTLED);
     ENUM_CASE(SERVER_RETURN_TRANSIENT_ERROR);
@@ -36,7 +35,6 @@
     ENUM_CASE(SERVER_RETURN_CONFLICT);
     ENUM_CASE(SERVER_RESPONSE_VALIDATION_FAILED);
     ENUM_CASE(SERVER_RETURN_DISABLED_BY_ADMIN);
-    ENUM_CASE(SERVER_RETURN_USER_ROLLBACK);
     ENUM_CASE(SERVER_RETURN_PARTIAL_FAILURE);
     ENUM_CASE(SERVER_RETURN_CLIENT_DATA_OBSOLETE);
     ENUM_CASE(SERVER_RETURN_ENCRYPTION_OBSOLETE);
diff --git a/components/sync/base/syncer_error.h b/components/sync/base/syncer_error.h
index 312d98a..f7456de 100644
--- a/components/sync/base/syncer_error.h
+++ b/components/sync/base/syncer_error.h
@@ -7,7 +7,6 @@
 
 #include <string>
 
-
 namespace syncer {
 
 // This class describes all the possible results of a sync cycle. It should be
@@ -27,8 +26,7 @@
     SYNC_AUTH_ERROR = 5,                 // HTTP auth error.
 
     // Based on values returned by server.  Most are defined in sync.proto.
-    // TODO(crbug.com/951350): Unused, remove.
-    SERVER_RETURN_INVALID_CREDENTIAL = 6,
+    // Deprecated: SERVER_RETURN_INVALID_CREDENTIAL = 6,
     SERVER_RETURN_UNKNOWN_ERROR = 7,
     SERVER_RETURN_THROTTLED = 8,
     SERVER_RETURN_TRANSIENT_ERROR = 9,
@@ -38,8 +36,7 @@
     SERVER_RETURN_CONFLICT = 13,
     SERVER_RESPONSE_VALIDATION_FAILED = 14,
     SERVER_RETURN_DISABLED_BY_ADMIN = 15,
-    // TODO(crbug.com/951350): Unused, remove.
-    SERVER_RETURN_USER_ROLLBACK = 16,
+    // Deprecated: SERVER_RETURN_USER_ROLLBACK = 16,
     SERVER_RETURN_PARTIAL_FAILURE = 17,
     SERVER_RETURN_CLIENT_DATA_OBSOLETE = 18,
     SERVER_RETURN_ENCRYPTION_OBSOLETE = 19,
@@ -54,7 +51,7 @@
     kMaxValue = SYNCER_OK,
   };
 
-  constexpr SyncerError() {}
+  constexpr SyncerError() = default;
   // Note: NETWORK_CONNECTION_UNAVAILABLE, SYNC_SERVER_ERROR, and
   // SYNC_AUTH_ERROR are *not* valid inputs for this constructor. These types
   // of errors must be created via the factory functions below.
diff --git a/components/sync/engine/cycle/commit_counters.cc b/components/sync/engine/cycle/commit_counters.cc
deleted file mode 100644
index 8ca4eab..0000000
--- a/components/sync/engine/cycle/commit_counters.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/engine/cycle/commit_counters.h"
-
-#include "base/json/json_string_value_serializer.h"
-
-namespace syncer {
-
-CommitCounters::CommitCounters()
-    : num_creation_commits_attempted(0),
-      num_deletion_commits_attempted(0),
-      num_update_commits_attempted(0),
-      num_commits_success(0),
-      num_commits_conflict(0),
-      num_commits_error(0) {}
-
-CommitCounters::~CommitCounters() {}
-
-std::unique_ptr<base::DictionaryValue> CommitCounters::ToValue() const {
-  std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue());
-  value->SetInteger("numCreationCommitsAttempted",
-                    num_creation_commits_attempted);
-  value->SetInteger("numDeletionCommitsAttempted",
-                    num_deletion_commits_attempted);
-  value->SetInteger("numUpdateCommitsAttempted", num_update_commits_attempted);
-  value->SetInteger("numCommitsAttempted", num_creation_commits_attempted +
-                                               num_deletion_commits_attempted +
-                                               num_update_commits_attempted);
-  value->SetInteger("numCommitsSuccess", num_commits_success);
-  value->SetInteger("numCommitsConflict", num_commits_conflict);
-  value->SetInteger("numCommitsError", num_commits_error);
-  return value;
-}
-
-std::string CommitCounters::ToString() const {
-  std::string result;
-  std::unique_ptr<base::DictionaryValue> value = ToValue();
-  JSONStringValueSerializer serializer(&result);
-  serializer.Serialize(*value);
-  return result;
-}
-
-}  // namespace syncer
diff --git a/components/sync/engine/cycle/commit_counters.h b/components/sync/engine/cycle/commit_counters.h
deleted file mode 100644
index f7cb353..0000000
--- a/components/sync/engine/cycle/commit_counters.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_ENGINE_CYCLE_COMMIT_COUNTERS_H_
-#define COMPONENTS_SYNC_ENGINE_CYCLE_COMMIT_COUNTERS_H_
-
-#include <memory>
-#include <string>
-
-#include "base/values.h"
-
-namespace syncer {
-
-// A class to maintain counts related to sync commit requests and responses.
-struct CommitCounters {
-  CommitCounters();
-  ~CommitCounters();
-
-  std::unique_ptr<base::DictionaryValue> ToValue() const;
-  std::string ToString() const;
-
-  // Counters updated before sending a commit message to the server.
-  int num_creation_commits_attempted;
-  int num_deletion_commits_attempted;
-  int num_update_commits_attempted;
-
-  // Counters updated based on the response from the server.
-  int num_commits_success;
-  int num_commits_conflict;
-  int num_commits_error;
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_ENGINE_CYCLE_COMMIT_COUNTERS_H_
diff --git a/components/sync/engine/cycle/update_counters.cc b/components/sync/engine/cycle/update_counters.cc
deleted file mode 100644
index 9288b080..0000000
--- a/components/sync/engine/cycle/update_counters.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/engine/cycle/update_counters.h"
-
-#include "base/json/json_string_value_serializer.h"
-
-namespace syncer {
-
-UpdateCounters::UpdateCounters()
-    : num_initial_updates_received(0),
-      num_non_initial_updates_received(0),
-      num_non_initial_reflected_updates_received(0),
-      num_non_initial_tombstone_updates_received(0),
-      num_updates_applied(0),
-      num_hierarchy_conflict_application_failures(0),
-      num_encryption_conflict_application_failures(0),
-      num_server_overwrites(0),
-      num_local_overwrites(0) {}
-
-UpdateCounters::~UpdateCounters() {}
-
-std::unique_ptr<base::DictionaryValue> UpdateCounters::ToValue() const {
-  std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue());
-
-  value->SetInteger("numInitialUpdatesReceived", num_initial_updates_received);
-  value->SetInteger("numUpdatesReceived", num_non_initial_updates_received);
-  value->SetInteger("numReflectedUpdatesReceived",
-                    num_non_initial_reflected_updates_received);
-  value->SetInteger("numTombstoneUpdatesReceived",
-                    num_non_initial_tombstone_updates_received);
-
-  value->SetInteger("numUpdatesApplied", num_updates_applied);
-  value->SetInteger("numHierarchyConflictApplicationFailures",
-                    num_hierarchy_conflict_application_failures);
-  value->SetInteger("numEncryptionConflictApplicationFailures",
-                    num_encryption_conflict_application_failures);
-
-  value->SetInteger("numServerOverwrites", num_server_overwrites);
-  value->SetInteger("numLocalOverwrites", num_local_overwrites);
-
-  return value;
-}
-
-std::string UpdateCounters::ToString() const {
-  std::string result;
-  std::unique_ptr<base::DictionaryValue> value = ToValue();
-  JSONStringValueSerializer serializer(&result);
-  serializer.Serialize(*value);
-  return result;
-}
-
-}  // namespace syncer
diff --git a/components/sync/engine/cycle/update_counters.h b/components/sync/engine/cycle/update_counters.h
deleted file mode 100644
index c4a3fbe..0000000
--- a/components/sync/engine/cycle/update_counters.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_ENGINE_CYCLE_UPDATE_COUNTERS_H_
-#define COMPONENTS_SYNC_ENGINE_CYCLE_UPDATE_COUNTERS_H_
-
-#include <memory>
-#include <string>
-
-#include "base/values.h"
-
-namespace syncer {
-
-// A class to maintain counts related to the update requests and responses for
-// a particular sync type.
-struct UpdateCounters {
-  UpdateCounters();
-  ~UpdateCounters();
-
-  std::unique_ptr<base::DictionaryValue> ToValue() const;
-  std::string ToString() const;
-
-  int num_initial_updates_received;
-  int num_non_initial_updates_received;
-  int num_non_initial_reflected_updates_received;
-  int num_non_initial_tombstone_updates_received;
-
-  int num_updates_applied;
-  int num_hierarchy_conflict_application_failures;
-  int num_encryption_conflict_application_failures;
-
-  int num_server_overwrites;
-  int num_local_overwrites;
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_ENGINE_CYCLE_UPDATE_COUNTERS_H_
diff --git a/components/sync/engine_impl/backoff_delay_provider_unittest.cc b/components/sync/engine_impl/backoff_delay_provider_unittest.cc
index 49104887..fbf640b3 100644
--- a/components/sync/engine_impl/backoff_delay_provider_unittest.cc
+++ b/components/sync/engine_impl/backoff_delay_provider_unittest.cc
@@ -72,14 +72,6 @@
             delay->GetInitialDelay(state).InSeconds());
 
   state.last_download_updates_result = SyncerError(SyncerError::SYNCER_OK);
-  // Note that updating credentials triggers a canary job, trumping
-  // the initial delay, but in theory we still expect this function to treat
-  // it like any other error in the system (except migration).
-  state.commit_result =
-      SyncerError(SyncerError::SERVER_RETURN_INVALID_CREDENTIAL);
-  EXPECT_EQ(kInitialBackoffRetrySeconds,
-            delay->GetInitialDelay(state).InSeconds());
-
   state.commit_result = SyncerError(SyncerError::SERVER_RETURN_MIGRATION_DONE);
   EXPECT_EQ(kInitialBackoffImmediateRetrySeconds,
             delay->GetInitialDelay(state).InSeconds());
@@ -125,14 +117,6 @@
             delay->GetInitialDelay(state).InSeconds());
 
   state.last_download_updates_result = SyncerError(SyncerError::SYNCER_OK);
-  // Note that updating credentials triggers a canary job, trumping
-  // the initial delay, but in theory we still expect this function to treat
-  // it like any other error in the system (except migration).
-  state.commit_result =
-      SyncerError(SyncerError::SERVER_RETURN_INVALID_CREDENTIAL);
-  EXPECT_EQ(kInitialBackoffShortRetrySeconds,
-            delay->GetInitialDelay(state).InSeconds());
-
   state.commit_result = SyncerError(SyncerError::SERVER_RETURN_MIGRATION_DONE);
   EXPECT_EQ(kInitialBackoffImmediateRetrySeconds,
             delay->GetInitialDelay(state).InSeconds());
diff --git a/components/sync/engine_impl/commit.cc b/components/sync/engine_impl/commit.cc
index 6feea43..8a4ac8b 100644
--- a/components/sync/engine_impl/commit.cc
+++ b/components/sync/engine_impl/commit.cc
@@ -51,7 +51,6 @@
     case SyncerError::NETWORK_IO_ERROR:
       return SyncCommitError::kNetworkError;
     case SyncerError::SYNC_AUTH_ERROR:
-    case SyncerError::SERVER_RETURN_INVALID_CREDENTIAL:
       return SyncCommitError::kAuthError;
     case SyncerError::SYNC_SERVER_ERROR:
     case SyncerError::SERVER_RETURN_UNKNOWN_ERROR:
@@ -61,7 +60,6 @@
     case SyncerError::SERVER_RETURN_CLEAR_PENDING:
     case SyncerError::SERVER_RETURN_NOT_MY_BIRTHDAY:
     case SyncerError::SERVER_RETURN_CONFLICT:
-    case SyncerError::SERVER_RETURN_USER_ROLLBACK:
     case SyncerError::SERVER_RETURN_PARTIAL_FAILURE:
     case SyncerError::SERVER_RETURN_CLIENT_DATA_OBSOLETE:
     case SyncerError::SERVER_RETURN_ENCRYPTION_OBSOLETE:
diff --git a/components/sync/engine_impl/commit_contribution_impl.cc b/components/sync/engine_impl/commit_contribution_impl.cc
index 188ddd4..ae2c04c 100644
--- a/components/sync/engine_impl/commit_contribution_impl.cc
+++ b/components/sync/engine_impl/commit_contribution_impl.cc
@@ -161,11 +161,6 @@
     }
   }
 
-  CommitCounters* counters = debug_info_emitter_->GetMutableCommitCounters();
-  counters->num_commits_success += successes;
-  counters->num_commits_conflict += transient_error_commits;
-  counters->num_commits_error += transient_error_commits;
-
   // Send whatever successful and failed responses we did get back to our
   // parent. It's the schedulers job to handle the failures, but parent may
   // react to them as well.
diff --git a/components/sync/engine_impl/cycle/data_type_debug_info_emitter.h b/components/sync/engine_impl/cycle/data_type_debug_info_emitter.h
index 06f414a..c9774e4f 100644
--- a/components/sync/engine_impl/cycle/data_type_debug_info_emitter.h
+++ b/components/sync/engine_impl/cycle/data_type_debug_info_emitter.h
@@ -9,8 +9,6 @@
 
 #include "base/macros.h"
 #include "components/sync/base/model_type.h"
-#include "components/sync/engine/cycle/commit_counters.h"
-#include "components/sync/engine/cycle/update_counters.h"
 
 namespace base {
 class HistogramBase;
@@ -18,6 +16,28 @@
 
 namespace syncer {
 
+// A class to maintain counts related to sync commit requests and responses.
+struct CommitCounters {
+  CommitCounters() = default;
+  ~CommitCounters() = default;
+
+  // Counters updated before sending a commit message to the server.
+  int num_creation_commits_attempted = 0;
+  int num_deletion_commits_attempted = 0;
+  int num_update_commits_attempted = 0;
+};
+
+// A class to maintain counts related to the update requests and responses for
+// a particular sync type.
+struct UpdateCounters {
+  UpdateCounters() = default;
+  ~UpdateCounters() = default;
+
+  int num_initial_updates_received = 0;
+  int num_non_initial_updates_received = 0;
+  int num_non_initial_tombstone_updates_received = 0;
+};
+
 // Supports various kinds of debugging requests for a certain directory type.
 //
 // TODO(crbug.com/1102849): Rename Emit*() methods to mention UMA, and update
diff --git a/components/sync/engine_impl/model_type_worker.cc b/components/sync/engine_impl/model_type_worker.cc
index b743957..1ea367c 100644
--- a/components/sync/engine_impl/model_type_worker.cc
+++ b/components/sync/engine_impl/model_type_worker.cc
@@ -370,14 +370,9 @@
   DeduplicatePendingUpdatesBasedOnClientTagHash();
   DeduplicatePendingUpdatesBasedOnOriginatorClientItemId();
 
-  int num_updates_applied = pending_updates_.size();
   model_type_processor_->OnUpdateReceived(model_type_state_,
                                           std::move(pending_updates_));
 
-  UpdateCounters* counters = debug_info_emitter_->GetMutableUpdateCounters();
-  counters->num_updates_applied += num_updates_applied;
-  debug_info_emitter_->EmitUpdateCountersUpdate();
-
   pending_updates_.clear();
 }
 
diff --git a/components/sync/engine_impl/model_type_worker_unittest.cc b/components/sync/engine_impl/model_type_worker_unittest.cc
index c6e21902..3c9b354 100644
--- a/components/sync/engine_impl/model_type_worker_unittest.cc
+++ b/components/sync/engine_impl/model_type_worker_unittest.cc
@@ -97,8 +97,6 @@
             emitter->GetCommitCounters().num_creation_commits_attempted);
   EXPECT_EQ(expected_deletion_count,
             emitter->GetCommitCounters().num_deletion_commits_attempted);
-  EXPECT_EQ(expected_creation_count + expected_deletion_count,
-            emitter->GetCommitCounters().num_commits_success);
 }
 
 }  // namespace
@@ -689,7 +687,6 @@
   NormalInitialize();
 
   EXPECT_EQ(0, emitter()->GetUpdateCounters().num_non_initial_updates_received);
-  EXPECT_EQ(0, emitter()->GetUpdateCounters().num_updates_applied);
 
   const ClientTagHash tag_hash = GenerateTagHash(kTag1);
 
@@ -715,7 +712,6 @@
   EXPECT_EQ(kValue1, entity.specifics.preference().value());
 
   EXPECT_EQ(1, emitter()->GetUpdateCounters().num_non_initial_updates_received);
-  EXPECT_EQ(1, emitter()->GetUpdateCounters().num_updates_applied);
 }
 
 TEST_F(ModelTypeWorkerTest, ReceiveUpdates_NoDuplicateHash) {
diff --git a/components/sync/engine_impl/net/server_connection_manager.cc b/components/sync/engine_impl/net/server_connection_manager.cc
index 7b74540..62a4fd1 100644
--- a/components/sync/engine_impl/net/server_connection_manager.cc
+++ b/components/sync/engine_impl/net/server_connection_manager.cc
@@ -73,7 +73,14 @@
 }
 
 // static
-HttpResponse HttpResponse::ForHttpError(int http_status_code) {
+HttpResponse HttpResponse::ForUnspecifiedError() {
+  HttpResponse response;
+  response.server_status = CONNECTION_UNAVAILABLE;
+  return response;
+}
+
+// static
+HttpResponse HttpResponse::ForHttpStatusCode(int http_status_code) {
   HttpResponse response;
   if (http_status_code == net::HTTP_OK) {
     response.server_status = SERVER_CONNECTION_OK;
@@ -115,7 +122,7 @@
   // second request. Need to notify sync frontend again to request new token,
   // otherwise backend will stay in SYNC_AUTH_ERROR state while frontend thinks
   // everything is fine and takes no actions.
-  SetServerResponse(HttpResponse::ForHttpError(net::HTTP_UNAUTHORIZED));
+  SetServerResponse(HttpResponse::ForHttpStatusCode(net::HTTP_UNAUTHORIZED));
   return false;
 }
 
diff --git a/components/sync/engine_impl/net/server_connection_manager.h b/components/sync/engine_impl/net/server_connection_manager.h
index 70acc88..590ff88a 100644
--- a/components/sync/engine_impl/net/server_connection_manager.h
+++ b/components/sync/engine_impl/net/server_connection_manager.h
@@ -38,8 +38,6 @@
 
     // SYNC_AUTH_ERROR is returned when the HTTP status code indicates that an
     // auth error has occurred (i.e. a 401).
-    // TODO(crbug.com/842096, crbug.com/951350): Remove this and instead use
-    // SYNC_SERVER_ERROR plus |http_status_code| == 401.
     SYNC_AUTH_ERROR,
 
     // SERVER_CONNECTION_OK is returned when request was handled correctly.
@@ -64,8 +62,8 @@
   static HttpResponse Uninitialized();
   static HttpResponse ForNetError(int net_error_code);
   static HttpResponse ForIoError();
-  // TODO(crbug.com/951350): Rename to ForHttpStatusCode.
-  static HttpResponse ForHttpError(int http_status_code);
+  static HttpResponse ForUnspecifiedError();
+  static HttpResponse ForHttpStatusCode(int http_status_code);
   static HttpResponse ForSuccess();
 
  private:
@@ -85,7 +83,7 @@
   virtual void OnServerConnectionEvent(const ServerConnectionEvent& event) = 0;
 
  protected:
-  virtual ~ServerConnectionEventListener() {}
+  virtual ~ServerConnectionEventListener() = default;
 };
 
 // Use this class to interact with the sync server.
diff --git a/components/sync/engine_impl/net/sync_server_connection_manager.cc b/components/sync/engine_impl/net/sync_server_connection_manager.cc
index 9933a72..8204c8c 100644
--- a/components/sync/engine_impl/net/sync_server_connection_manager.cc
+++ b/components/sync/engine_impl/net/sync_server_connection_manager.cc
@@ -108,8 +108,7 @@
   // Issue the POST, blocking until it finishes.
   if (!cancelation_signal_->TryRegisterHandler(this)) {
     // Return early because cancelation signal was signaled.
-    // TODO(crbug.com/951350): Introduce an extra status code for canceled?
-    return HttpResponse::ForNetError(0);
+    return HttpResponse::ForUnspecifiedError();
   }
   base::ScopedClosureRunner auto_unregister(base::BindOnce(
       &CancelationSignal::UnregisterHandler,
@@ -125,7 +124,7 @@
   }
 
   // We got a server response, copy over response codes and content.
-  HttpResponse response = HttpResponse::ForHttpError(http_status_code);
+  HttpResponse response = HttpResponse::ForHttpStatusCode(http_status_code);
   response.content_length =
       static_cast<int64_t>(post_provider_->GetResponseContentLength());
   response.payload_length =
@@ -210,11 +209,11 @@
     // Print a log to distinguish this "known failure" from others.
     DVLOG(1) << "ServerConnectionManager forcing SYNC_AUTH_ERROR due to missing"
                 " access token";
-    return HttpResponse::ForHttpError(net::HTTP_UNAUTHORIZED);
+    return HttpResponse::ForHttpStatusCode(net::HTTP_UNAUTHORIZED);
   }
 
   if (cancelation_signal_->IsSignalled()) {
-    return HttpResponse::ForNetError(0);
+    return HttpResponse::ForUnspecifiedError();
   }
 
   auto connection = std::make_unique<Connection>(post_provider_factory_.get(),
@@ -228,13 +227,11 @@
 
   if (http_response.server_status == HttpResponse::SYNC_AUTH_ERROR) {
     ClearAccessToken();
+  } else if (http_response.server_status ==
+             HttpResponse::SERVER_CONNECTION_OK) {
+    connection->ReadBufferResponse(buffer_out, &http_response);
   }
 
-  if (http_response.server_status != HttpResponse::SERVER_CONNECTION_OK) {
-    return http_response;
-  }
-
-  connection->ReadBufferResponse(buffer_out, &http_response);
   return http_response;
 }
 
diff --git a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
index 8c2b9e2..e4acf24 100644
--- a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
@@ -1664,7 +1664,7 @@
                 RecordSyncShareMultiple(&times, kMinNumSamples, true)));
 
   connection()->SetServerResponse(
-      HttpResponse::ForHttpError(net::HTTP_UNAUTHORIZED));
+      HttpResponse::ForHttpStatusCode(net::HTTP_UNAUTHORIZED));
   StartSyncScheduler(base::Time());
 
   // Run to wait for polling.
diff --git a/components/sync/test/engine/mock_connection_manager.cc b/components/sync/test/engine/mock_connection_manager.cc
index 6b5a53f..4f7efe2 100644
--- a/components/sync/test/engine/mock_connection_manager.cc
+++ b/components/sync/test/engine/mock_connection_manager.cc
@@ -108,7 +108,7 @@
 
   if (--countdown_to_postbuffer_fail_ == 0) {
     // Fail as countdown hits zero.
-    return HttpResponse::ForHttpError(net::HTTP_BAD_REQUEST);
+    return HttpResponse::ForHttpStatusCode(net::HTTP_BAD_REQUEST);
   }
 
   if (!server_reachable_) {
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 88312d56..04f3d64 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1691,10 +1691,10 @@
     "sms/sms_provider.h",
     "sms/sms_queue.cc",
     "sms/sms_queue.h",
-    "sms/sms_service.cc",
-    "sms/sms_service.h",
     "sms/user_consent_handler.cc",
     "sms/user_consent_handler.h",
+    "sms/webotp_service.cc",
+    "sms/webotp_service.h",
     "speech/speech_recognition_dispatcher_host.cc",
     "speech/speech_recognition_dispatcher_host.h",
     "speech/speech_recognition_manager_impl.cc",
diff --git a/content/browser/DEPS b/content/browser/DEPS
index a0177bb..9e3aa27 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -104,7 +104,6 @@
   "+third_party/blink/public/common",
   "+third_party/blink/public/mojom",
   "+third_party/blink/public/platform/resource_request_blocked_reason.h",
-  "+third_party/blink/public/platform/viewport_intersection_state.h",
   "+third_party/blink/public/platform/web_client_hints_type.h",
   "+third_party/blink/public/platform/web_content_security_policy.h",
   "+third_party/blink/public/common/page/drag_operation.h",
@@ -114,7 +113,7 @@
   "+third_party/blink/public/platform/web_text_input_type.h",
   "+third_party/blink/public/platform/mac/web_scrollbar_theme.h",
   "+third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h",
-  "+third_party/blink/public/platform/modules/sms/sms_receiver.mojom.h",
+  "+third_party/blink/public/platform/modules/sms/webotp_service.mojom.h",
   "+third_party/blink/public/public_buildflags.h",
   "+third_party/blink/public/strings/grit/blink_strings.h",
   "+third_party/blink/public/web/web_ax_enums.h",
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 3eceddd92..33afdfd0 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -730,7 +730,8 @@
   ui::AXActionData action_data;
   action_data.action = ax::mojom::Action::kFocus;
   action_data.target_node_id = node.GetId();
-  delegate_->AccessibilityViewSetFocus();
+  if (!delegate_->AccessibilityViewHasFocus())
+    delegate_->AccessibilityViewSetFocus();
   delegate_->AccessibilityPerformAction(action_data);
 }
 
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
index b95c41e..0cef96e 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.cc
@@ -90,6 +90,11 @@
       is_expanded);
 }
 
+void BrowserAccessibilityManagerAuraLinux::FireInvalidStatusChangedEvent(
+    BrowserAccessibility* node) {
+  ToBrowserAccessibilityAuraLinux(node)->GetNode()->OnInvalidStatusChanged();
+}
+
 void BrowserAccessibilityManagerAuraLinux::FireEvent(BrowserAccessibility* node,
                                                      ax::mojom::Event event) {
   ToBrowserAccessibilityAuraLinux(node)->GetNode()->NotifyAccessibilityEvent(
@@ -206,7 +211,7 @@
       FireDescriptionChangedEvent(node);
       break;
     case ui::AXEventGenerator::Event::INVALID_STATUS_CHANGED:
-      FireEvent(node, ax::mojom::Event::kInvalidStatusChanged);
+      FireInvalidStatusChangedEvent(node);
       break;
     case ui::AXEventGenerator::Event::PARENT_CHANGED:
       FireParentChangedEvent(node);
diff --git a/content/browser/accessibility/browser_accessibility_manager_auralinux.h b/content/browser/accessibility/browser_accessibility_manager_auralinux.h
index 4550abc..a4dc4e7 100644
--- a/content/browser/accessibility/browser_accessibility_manager_auralinux.h
+++ b/content/browser/accessibility/browser_accessibility_manager_auralinux.h
@@ -37,6 +37,7 @@
   void FireSelectedEvent(BrowserAccessibility* node);
   void FireEnabledChangedEvent(BrowserAccessibility* node);
   void FireExpandedEvent(BrowserAccessibility* node, bool is_expanded);
+  void FireInvalidStatusChangedEvent(BrowserAccessibility* node);
   void FireLoadingEvent(BrowserAccessibility* node, bool is_loading);
   void FireNameChangedEvent(BrowserAccessibility* node);
   void FireDescriptionChangedEvent(BrowserAccessibility* node);
diff --git a/content/browser/android/web_contents_observer_proxy.cc b/content/browser/android/web_contents_observer_proxy.cc
index 566982b..58d3d40f3 100644
--- a/content/browser/android/web_contents_observer_proxy.cc
+++ b/content/browser/android/web_contents_observer_proxy.cc
@@ -76,6 +76,14 @@
       render_frame_host->GetRoutingID());
 }
 
+void WebContentsObserverProxy::RenderFrameDeleted(
+    RenderFrameHost* render_frame_host) {
+  JNIEnv* env = AttachCurrentThread();
+  Java_WebContentsObserverProxy_renderFrameDeleted(
+      env, java_observer_, render_frame_host->GetProcess()->GetID(),
+      render_frame_host->GetRoutingID());
+}
+
 void WebContentsObserverProxy::RenderViewReady() {
   JNIEnv* env = AttachCurrentThread();
   Java_WebContentsObserverProxy_renderViewReady(env, java_observer_);
diff --git a/content/browser/android/web_contents_observer_proxy.h b/content/browser/android/web_contents_observer_proxy.h
index a33a64e3..80874d0 100644
--- a/content/browser/android/web_contents_observer_proxy.h
+++ b/content/browser/android/web_contents_observer_proxy.h
@@ -33,6 +33,7 @@
 
  private:
   void RenderFrameCreated(RenderFrameHost* render_frame_host) override;
+  void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
   void RenderViewReady() override;
   void RenderProcessGone(base::TerminationStatus termination_status) override;
   void DidStartLoading() override;
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 67a7d8b..036b4a0 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -2285,7 +2285,7 @@
   // all not restored reasons. As a result, we only check for the blocklist
   // reason.
   ExpectBlocklistedFeature(
-      blink::scheduler::WebSchedulerTrackedFeature::kSmsService, FROM_HERE);
+      blink::scheduler::WebSchedulerTrackedFeature::kWebOTPService, FROM_HERE);
 }
 
 // crbug.com/1090223
@@ -4049,8 +4049,7 @@
 }
 
 class BackForwardCacheBrowserTestWithServiceWorkerEnabled
-    : public BackForwardCacheBrowserTest,
-      public testing::WithParamInterface<bool> {
+    : public BackForwardCacheBrowserTest {
  public:
   BackForwardCacheBrowserTestWithServiceWorkerEnabled() {}
   ~BackForwardCacheBrowserTestWithServiceWorkerEnabled() override {}
@@ -4059,16 +4058,11 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     EnableFeatureAndSetParams(features::kBackForwardCache,
                               "service_worker_supported", "true");
-    if (GetParam())
-      EnableFeatureAndSetParams(features::kServiceWorkerOnUI, "", "");
-    else
-      DisableFeature(features::kServiceWorkerOnUI);
-
     BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
   }
 };
 
-IN_PROC_BROWSER_TEST_P(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
                        CachedPagesWithServiceWorkers) {
   CreateHttpsServer();
   SetupCrossSiteRedirector(https_server());
@@ -4099,7 +4093,7 @@
   EXPECT_EQ(rfh_a, current_frame_host());
 }
 
-IN_PROC_BROWSER_TEST_P(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
                        EvictIfCacheBlocksServiceWorkerVersionActivation) {
   CreateHttpsServer();
   https_server()->RegisterRequestHandler(
@@ -4143,7 +4137,7 @@
       FROM_HERE);
 }
 
-IN_PROC_BROWSER_TEST_P(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
                        EvictWithPostMessageToCachedClient) {
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
   https_server.RegisterRequestHandler(
@@ -4207,7 +4201,7 @@
       FROM_HERE);
 }
 
-IN_PROC_BROWSER_TEST_P(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestWithServiceWorkerEnabled,
                        EvictOnServiceWorkerClaim) {
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
   https_server.RegisterRequestHandler(
@@ -4260,10 +4254,6 @@
       FROM_HERE);
 }
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         BackForwardCacheBrowserTestWithServiceWorkerEnabled,
-                         testing::Bool());
-
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CachePagesWithBeacon) {
   constexpr char kKeepalivePath[] = "/keepalive";
 
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index ba25194..a861ccf 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -111,7 +111,7 @@
 #include "third_party/blink/public/mojom/prerender/prerender.mojom.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 #include "third_party/blink/public/mojom/quota/quota_manager_host.mojom.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom.h"
 #include "third_party/blink/public/mojom/speech/speech_recognizer.mojom.h"
 #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
 #include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
@@ -592,9 +592,10 @@
       base::BindRepeating(&RenderFrameHostImpl::BindScreenEnumerationReceiver,
                           base::Unretained(host)));
 
-  if (base::FeatureList::IsEnabled(features::kSmsReceiver)) {
-    map->Add<blink::mojom::SmsReceiver>(base::BindRepeating(
-        &RenderFrameHostImpl::BindSmsReceiverReceiver, base::Unretained(host)));
+  if (base::FeatureList::IsEnabled(features::kWebOTP)) {
+    map->Add<blink::mojom::WebOTPService>(
+        base::BindRepeating(&RenderFrameHostImpl::BindWebOTPServiceReceiver,
+                            base::Unretained(host)));
   }
 
   map->Add<blink::mojom::WebUsbService>(base::BindRepeating(
@@ -875,9 +876,10 @@
   map->Add<blink::mojom::DedicatedWorkerHostFactory>(
       base::BindRepeating(&DedicatedWorkerHost::CreateNestedDedicatedWorker,
                           base::Unretained(host)));
-  if (base::FeatureList::IsEnabled(features::kSmsReceiver)) {
-    map->Add<blink::mojom::SmsReceiver>(base::BindRepeating(
-        &DedicatedWorkerHost::BindSmsReceiverReceiver, base::Unretained(host)));
+  if (base::FeatureList::IsEnabled(features::kWebOTP)) {
+    map->Add<blink::mojom::WebOTPService>(
+        base::BindRepeating(&DedicatedWorkerHost::BindWebOTPServiceReceiver,
+                            base::Unretained(host)));
   }
   map->Add<blink::mojom::WebUsbService>(base::BindRepeating(
       &DedicatedWorkerHost::CreateWebUsbService, base::Unretained(host)));
diff --git a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 41ae7c8..21d0844 100644
--- a/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/content/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -570,6 +570,9 @@
   CaptureScreenshotAndCompareTo(expected_bitmap, ENCODING_JPEG, false);
 }
 
+// ChromeOS does not support software compositing.
+#if !defined(OS_CHROMEOS)
+
 class NoGPUCaptureScreenshotTest : public CaptureScreenshotTest {
   void SetUpCommandLine(base::CommandLine* command_line) override {
     CaptureScreenshotTest::SetUpCommandLine(command_line);
@@ -585,7 +588,12 @@
   // TODO(eseckler): Reenable with error limit if necessary.
   if (base::SysInfo::IsLowEndDevice())
     return;
-
+  // If disabling software compositing is disabled by the test caller,
+  // we're out of luck.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableSoftwareCompositingFallback)) {
+    return;
+  }
   shell()->LoadURL(
       GURL("data:text/html,"
            "<style>body,html { padding: 0; margin: 0; }</style>"
@@ -618,6 +626,8 @@
   EXPECT_GT(static_cast<int>(SkColorGetB(bottom_left)), 128);
 }
 
+#endif  // !defined(OS_CHROMEOS)
+
 // Setting frame size (through RWHV) is not supported on Android.
 // This test seems to be very flaky on windows: https://crbug.com/801173
 #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \
diff --git a/content/browser/devtools/protocol/emulation_handler.cc b/content/browser/devtools/protocol/emulation_handler.cc
index 7f53939..4c7b28a 100644
--- a/content/browser/devtools/protocol/emulation_handler.cc
+++ b/content/browser/devtools/protocol/emulation_handler.cc
@@ -306,13 +306,13 @@
   params.screen_type = mobile ? blink::mojom::EmulatedScreenType::kMobile
                               : blink::mojom::EmulatedScreenType::kDesktop;
   params.screen_size =
-      blink::WebSize(screen_width.fromMaybe(0), screen_height.fromMaybe(0));
+      gfx::Size(screen_width.fromMaybe(0), screen_height.fromMaybe(0));
   if (position_x.isJust() && position_y.isJust()) {
     params.view_position =
         gfx::Point(position_x.fromMaybe(0), position_y.fromMaybe(0));
   }
   params.device_scale_factor = device_scale_factor;
-  params.view_size = blink::WebSize(width, height);
+  params.view_size = gfx::Size(width, height);
   params.scale = scale.fromMaybe(1);
   params.screen_orientation_type = orientationType;
   params.screen_orientation_angle = orientationAngle;
diff --git a/content/browser/renderer_host/back_forward_cache_impl.cc b/content/browser/renderer_host/back_forward_cache_impl.cc
index 44d350a..cbe310133 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.cc
+++ b/content/browser/renderer_host/back_forward_cache_impl.cc
@@ -142,7 +142,7 @@
       FeatureToBit(
           WebSchedulerTrackedFeature::kRequestedVideoCapturePermission) |
       FeatureToBit(WebSchedulerTrackedFeature::kSharedWorker) |
-      FeatureToBit(WebSchedulerTrackedFeature::kSmsService) |
+      FeatureToBit(WebSchedulerTrackedFeature::kWebOTPService) |
       FeatureToBit(WebSchedulerTrackedFeature::kSpeechRecognizer) |
       FeatureToBit(WebSchedulerTrackedFeature::kSpeechSynthesis) |
       FeatureToBit(WebSchedulerTrackedFeature::kWakeLock) |
diff --git a/content/browser/renderer_host/cross_process_frame_connector.cc b/content/browser/renderer_host/cross_process_frame_connector.cc
index ec792f0..3af7cf0 100644
--- a/content/browser/renderer_host/cross_process_frame_connector.cc
+++ b/content/browser/renderer_host/cross_process_frame_connector.cc
@@ -78,8 +78,6 @@
   IPC_BEGIN_MESSAGE_MAP(CrossProcessFrameConnector, msg)
     IPC_MESSAGE_HANDLER(FrameHostMsg_SynchronizeVisualProperties,
                         OnSynchronizeVisualProperties)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateViewportIntersection,
-                        OnUpdateViewportIntersection)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -317,11 +315,11 @@
   SynchronizeVisualProperties(visual_properties);
 }
 
-void CrossProcessFrameConnector::OnUpdateViewportIntersection(
-    const blink::ViewportIntersectionState& intersection_state) {
+void CrossProcessFrameConnector::UpdateViewportIntersection(
+    const blink::mojom::ViewportIntersectionState& intersection_state) {
   intersection_state_ = intersection_state;
   if (view_)
-    view_->UpdateViewportIntersection(intersection_state);
+    view_->UpdateViewportIntersection(intersection_state_);
 
   if (IsVisible()) {
     // Record metrics if a crashed subframe became visible as a result of this
diff --git a/content/browser/renderer_host/cross_process_frame_connector.h b/content/browser/renderer_host/cross_process_frame_connector.h
index e4425fc8..85f9da8 100644
--- a/content/browser/renderer_host/cross_process_frame_connector.h
+++ b/content/browser/renderer_host/cross_process_frame_connector.h
@@ -139,6 +139,8 @@
   }
 
   void UpdateRenderThrottlingStatus(bool is_throttled, bool subtree_throttled);
+  void UpdateViewportIntersection(
+      const blink::mojom::ViewportIntersectionState& intersection_state);
 
   // These enums back crashed frame histograms - see MaybeLogCrash() and
   // MaybeLogShownCrash() below.  Please do not modify or remove existing enum
@@ -190,8 +192,6 @@
   // Handlers for messages received from the parent frame.
   void OnSynchronizeVisualProperties(
       const blink::FrameVisualProperties& visual_properties);
-  void OnUpdateViewportIntersection(
-      const blink::ViewportIntersectionState& viewport_intersection);
 
   // Gets the current RenderFrameHost for the
   // |frame_proxy_in_parent_renderer_|'s (i.e., the child frame's)
diff --git a/content/browser/renderer_host/frame_connector_delegate.cc b/content/browser/renderer_host/frame_connector_delegate.cc
index 91f2a5e..990b055 100644
--- a/content/browser/renderer_host/frame_connector_delegate.cc
+++ b/content/browser/renderer_host/frame_connector_delegate.cc
@@ -152,4 +152,6 @@
     bool use_zoom_for_device_scale_factor)
     : use_zoom_for_device_scale_factor_(use_zoom_for_device_scale_factor) {}
 
+FrameConnectorDelegate::~FrameConnectorDelegate() = default;
+
 }  // namespace content
diff --git a/content/browser/renderer_host/frame_connector_delegate.h b/content/browser/renderer_host/frame_connector_delegate.h
index 512d061..e7f41bb 100644
--- a/content/browser/renderer_host/frame_connector_delegate.h
+++ b/content/browser/renderer_host/frame_connector_delegate.h
@@ -14,9 +14,9 @@
 #include "content/common/content_export.h"
 #include "third_party/blink/public/common/widget/screen_info.h"
 #include "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom-forward.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom.h"
 #include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
 #include "third_party/blink/public/mojom/input/pointer_lock_result.mojom-shared.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
 #include "ui/gfx/geometry/rect.h"
 
 namespace blink {
@@ -164,7 +164,7 @@
   virtual void UnlockMouse() {}
 
   // Returns the state of the frame's intersection with the top-level viewport.
-  const blink::ViewportIntersectionState& intersection_state() const {
+  const blink::mojom::ViewportIntersectionState& intersection_state() const {
     return intersection_state_;
   }
 
@@ -237,14 +237,14 @@
  protected:
   explicit FrameConnectorDelegate(bool use_zoom_for_device_scale_factor);
 
-  virtual ~FrameConnectorDelegate() {}
+  virtual ~FrameConnectorDelegate();
 
   // The RenderWidgetHostView for the frame. Initially NULL.
   RenderWidgetHostViewChildFrame* view_ = nullptr;
 
   // This is here rather than in the implementation class so that
   // intersection_state() can return a reference.
-  blink::ViewportIntersectionState intersection_state_;
+  blink::mojom::ViewportIntersectionState intersection_state_;
 
   blink::ScreenInfo screen_info_;
   gfx::Size local_frame_size_in_dip_;
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 9a166929..a7459337 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -113,7 +113,7 @@
 #include "content/browser/service_worker/service_worker_container_host.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_object_host.h"
-#include "content/browser/sms/sms_service.h"
+#include "content/browser/sms/webotp_service.h"
 #include "content/browser/speech/speech_synthesis_impl.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/url_loader_factory_params_helper.h"
@@ -235,7 +235,7 @@
 #include "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom.h"
 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom.h"
 #include "third_party/blink/public/mojom/timing/resource_timing.mojom.h"
 #include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
 #include "third_party/blink/public/mojom/webauthn/virtual_authenticator.mojom.h"
@@ -1872,19 +1872,9 @@
   if (IsInactiveAndDisallowReactivation())
     return;
 
-  // Ensure focus is transferred to the guest web contents that this
-  // RenderFrameHostImpl is a part of. This is similar to what happens for
-  // pointer input, and ensures the focus state in both browser and renderer
-  // processes matches expectations in order for focus actions to take their
-  // intended effect.
-  RenderWidgetHostImpl* host = GetRenderWidgetHost();
-  host->delegate()->FocusOwningWebContents(host);
-
-  if (!AccessibilityViewHasFocus()) {
-    RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
-    if (view)
-      view->Focus();
-  }
+  RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
+  if (view)
+    view->Focus();
 }
 
 gfx::Rect RenderFrameHostImpl::AccessibilityGetViewBounds() {
@@ -7858,15 +7848,15 @@
                             std::move(receiver));
 }
 
-void RenderFrameHostImpl::BindSmsReceiverReceiver(
-    mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver) {
+void RenderFrameHostImpl::BindWebOTPServiceReceiver(
+    mojo::PendingReceiver<blink::mojom::WebOTPService> receiver) {
   if (GetParent() && !GetMainFrame()->GetLastCommittedOrigin().IsSameOriginWith(
                          GetLastCommittedOrigin())) {
     mojo::ReportBadMessage("Must have the same origin as the top-level frame.");
     return;
   }
   auto* fetcher = SmsFetcher::Get(GetProcess()->GetBrowserContext());
-  SmsService::Create(fetcher, this, std::move(receiver));
+  WebOTPService::Create(fetcher, this, std::move(receiver));
   document_used_web_otp_ = true;
 }
 
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index eb17bb3..4f25b7f 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -133,7 +133,7 @@
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom.h"
 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-forward.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-forward.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom-forward.h"
 #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom-forward.h"
 #include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
 #include "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom-forward.h"
@@ -1405,8 +1405,8 @@
   void BindInputInjectorReceiver(
       mojo::PendingReceiver<mojom::InputInjector> receiver);
 
-  void BindSmsReceiverReceiver(
-      mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver);
+  void BindWebOTPServiceReceiver(
+      mojo::PendingReceiver<blink::mojom::WebOTPService> receiver);
 
   void BindRestrictedCookieManager(
       mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver);
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 6d11988..54ddc4bd 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -216,7 +216,7 @@
         switches::kJavaScriptFlags, "--expose_gc");
 
     base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-        switches::kEnableBlinkFeatures, "SmsReceiver");
+        switches::kEnableBlinkFeatures, "WebOTP");
   }
   net::EmbeddedTestServer* https_server() { return &https_server_; }
 
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index 7aacc0e..1dee3929 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -679,6 +679,12 @@
       params->user_gesture, params->impression);
 }
 
+void RenderFrameProxyHost::UpdateViewportIntersection(
+    blink::mojom::ViewportIntersectionStatePtr intersection_state) {
+  cross_process_frame_connector_->UpdateViewportIntersection(
+      *intersection_state);
+}
+
 void RenderFrameProxyHost::DidChangeOpener(
     const base::Optional<base::UnguessableToken>& opener_frame_token) {
   frame_tree_node_->render_manager()->DidChangeOpener(
diff --git a/content/browser/renderer_host/render_frame_proxy_host.h b/content/browser/renderer_host/render_frame_proxy_host.h
index 0605662..f42d058f 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.h
+++ b/content/browser/renderer_host/render_frame_proxy_host.h
@@ -177,6 +177,8 @@
   void PrintCrossProcessSubframe(const gfx::Rect& rect,
                                  int document_cookie) override;
   void Detach() override;
+  void UpdateViewportIntersection(
+      blink::mojom::ViewportIntersectionStatePtr intersection_state) override;
 
   // blink::mojom::RemoteMainFrameHost overrides:
   void FocusPage() override;
@@ -211,6 +213,7 @@
   // The interceptor needs access to frame_host_receiver_for_testing().
   friend class RouteMessageEventInterceptor;
   friend class OpenURLInterceptor;
+  friend class UpdateViewportIntersectionMessageFilter;
 
   // Helper to retrieve the |AgentSchedulingGroup| this proxy host is associated
   // with.
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.cc b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
index c1051f7c..0a6a54a 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.cc
@@ -36,6 +36,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "third_party/blink/public/common/input/web_touch_event.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom.h"
 #include "ui/base/ime/mojom/text_input_state.mojom.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/size_conversions.h"
@@ -423,12 +424,12 @@
 }
 
 void RenderWidgetHostViewChildFrame::UpdateViewportIntersection(
-    const blink::ViewportIntersectionState& intersection_state) {
+    const blink::mojom::ViewportIntersectionState& intersection_state) {
   if (host()) {
     host()->SetIntersectsViewport(
         !intersection_state.viewport_intersection.IsEmpty());
-    host()->Send(new WidgetMsg_SetViewportIntersection(host()->GetRoutingID(),
-                                                       intersection_state));
+    host()->GetAssociatedFrameWidget()->SetViewportIntersection(
+        intersection_state.Clone());
   }
 }
 
@@ -626,7 +627,7 @@
        ToRoundedSize(last_stable_screen_rect_.size())) ||
       (std::abs(last_stable_screen_rect_.x() - screen_rect.x()) +
            std::abs(last_stable_screen_rect_.y() - screen_rect.y()) >
-       blink::kMaxChildFrameScreenRectMovement)) {
+       blink::mojom::kMaxChildFrameScreenRectMovement)) {
     last_stable_screen_rect_ = screen_rect;
     screen_rect_stable_since_ = base::TimeTicks::Now();
   }
@@ -634,8 +635,8 @@
 
 bool RenderWidgetHostViewChildFrame::ScreenRectIsUnstableFor(
     const blink::WebInputEvent& event) {
-  if (event.TimeStamp() -
-          base::TimeDelta::FromMilliseconds(blink::kMinScreenRectStableTimeMs) <
+  if (event.TimeStamp() - base::TimeDelta::FromMilliseconds(
+                              blink::mojom::kMinScreenRectStableTimeMs) <
       screen_rect_stable_since_) {
     return true;
   }
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame.h b/content/browser/renderer_host/render_widget_host_view_child_frame.h
index dd2f976..1fd98c7 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame.h
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame.h
@@ -28,8 +28,8 @@
 #include "content/public/browser/touch_selection_controller_client_manager.h"
 #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h"
 #include "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom-forward.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-forward.h"
 #include "third_party/blink/public/mojom/input/input_event_result.mojom-shared.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -185,7 +185,7 @@
   void UnregisterFrameSinkId();
 
   void UpdateViewportIntersection(
-      const blink::ViewportIntersectionState& intersection_state);
+      const blink::mojom::ViewportIntersectionState& intersection_state);
 
   // TODO(sunxd): Rename SetIsInert to UpdateIsInert.
   void SetIsInert();
diff --git a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
index 7333a39..2dad841 100644
--- a/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_child_frame_unittest.cc
@@ -29,6 +29,7 @@
 #include "content/common/widget_messages.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/fake_frame_widget.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/test/mock_render_widget_host_delegate.h"
@@ -40,7 +41,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/frame/frame_visual_properties.h"
 #include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/compositor/compositor.h"
 
@@ -63,10 +63,11 @@
     last_surface_info_ = surface_info;
   }
 
-  void SetViewportIntersection(const blink::WebRect& viewport_intersection,
-                               const blink::WebRect& main_frame_intersection,
-                               const blink::WebRect& compositor_visible_rect,
-                               blink::FrameOcclusionState occlusion_state) {
+  void SetViewportIntersection(
+      const blink::WebRect& viewport_intersection,
+      const blink::WebRect& main_frame_intersection,
+      const blink::WebRect& compositor_visible_rect,
+      blink::mojom::FrameOcclusionState occlusion_state) {
     intersection_state_.viewport_intersection = viewport_intersection;
     intersection_state_.main_frame_intersection = main_frame_intersection;
     intersection_state_.compositor_visible_rect = compositor_visible_rect;
@@ -214,8 +215,9 @@
 TEST_F(RenderWidgetHostViewChildFrameTest, ViewportIntersectionUpdated) {
   blink::WebRect intersection_rect(5, 5, 100, 80);
   blink::WebRect main_frame_intersection(5, 10, 200, 200);
-  blink::FrameOcclusionState occlusion_state =
-      blink::FrameOcclusionState::kPossiblyOccluded;
+  blink::mojom::FrameOcclusionState occlusion_state =
+      blink::mojom::FrameOcclusionState::kPossiblyOccluded;
+
   test_frame_connector_->SetViewportIntersection(
       intersection_rect, main_frame_intersection, intersection_rect,
       occlusion_state);
@@ -224,23 +226,29 @@
       static_cast<MockRenderProcessHost*>(widget_host_->GetProcess());
   process->Init();
 
+  mojo::AssociatedRemote<blink::mojom::FrameWidgetHost> blink_frame_widget_host;
+  auto blink_frame_widget_host_receiver =
+      blink_frame_widget_host.BindNewEndpointAndPassDedicatedReceiver();
+  mojo::AssociatedRemote<blink::mojom::FrameWidget> blink_frame_widget;
+  auto blink_frame_widget_receiver =
+      blink_frame_widget.BindNewEndpointAndPassDedicatedReceiver();
+  widget_host_->BindFrameWidgetInterfaces(
+      std::move(blink_frame_widget_host_receiver), blink_frame_widget.Unbind());
+  FakeFrameWidget fake_frame_widget(std::move(blink_frame_widget_receiver));
+
   widget_host_->Init();
 
-  const IPC::Message* intersection_update =
-      process->sink().GetUniqueMessageMatching(
-          WidgetMsg_SetViewportIntersection::ID);
-  ASSERT_TRUE(intersection_update);
-  std::tuple<blink::ViewportIntersectionState> intersection_state;
+  base::RunLoop().RunUntilIdle();
 
-  WidgetMsg_SetViewportIntersection::Read(intersection_update,
-                                          &intersection_state);
-  EXPECT_EQ(intersection_rect,
-            std::get<0>(intersection_state).viewport_intersection);
-  EXPECT_EQ(main_frame_intersection,
-            std::get<0>(intersection_state).main_frame_intersection);
-  EXPECT_EQ(intersection_rect,
-            std::get<0>(intersection_state).compositor_visible_rect);
-  EXPECT_EQ(occlusion_state, std::get<0>(intersection_state).occlusion_state);
+  auto& intersection_state = fake_frame_widget.GetIntersectionState();
+  EXPECT_EQ(gfx::Rect(intersection_rect),
+            intersection_state->viewport_intersection);
+  EXPECT_EQ(gfx::Rect(main_frame_intersection),
+            intersection_state->main_frame_intersection);
+  EXPECT_EQ(gfx::Rect(intersection_rect),
+            intersection_state->compositor_visible_rect);
+  EXPECT_EQ(static_cast<blink::mojom::FrameOcclusionState>(occlusion_state),
+            intersection_state->occlusion_state);
 }
 
 class RenderWidgetHostViewChildFrameZoomForDSFTest
diff --git a/content/browser/service_worker/service_worker_container_host_unittest.cc b/content/browser/service_worker/service_worker_container_host_unittest.cc
index 2087dca..3cb6a22 100644
--- a/content/browser/service_worker/service_worker_container_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_container_host_unittest.cc
@@ -1119,8 +1119,7 @@
  public:
   ServiceWorkerContainerHostTestWithBackForwardCache() {
     scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{features::kBackForwardCache, {GetFeatureParams()}},
-         {features::kServiceWorkerOnUI, {}}},
+        {{features::kBackForwardCache, {GetFeatureParams()}}},
         /*disabled_features=*/{});
   }
 
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index b4b10d6..976abee5 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -328,7 +328,7 @@
 
 // static
 bool ServiceWorkerContext::IsServiceWorkerOnUIEnabled() {
-  return base::FeatureList::IsEnabled(features::kServiceWorkerOnUI);
+  return true;
 }
 
 // static
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 5ca119e..d40e817 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -77,6 +77,7 @@
 #include "content/common/content_navigation_policy.h"
 #include "content/common/frame.mojom-test-utils.h"
 #include "content/common/frame_messages.h"
+#include "content/common/frame_proxy.mojom-test-utils.h"
 #include "content/common/input/actions_parser.h"
 #include "content/common/input/synthetic_pinch_gesture_params.h"
 #include "content/common/input_messages.h"
@@ -630,37 +631,42 @@
   rwh->ForwardGestureEvent(gesture_tap_down);
 }
 
-// Class to monitor incoming FrameHostMsg_UpdateViewportIntersection messages.
-class UpdateViewportIntersectionMessageFilter
-    : public content::BrowserMessageFilter {
- public:
-  // If no routing_id is specified, filter will match all routing id's.
-  explicit UpdateViewportIntersectionMessageFilter(
-      int routing_id = MSG_ROUTING_NONE)
-      : content::BrowserMessageFilter(FrameMsgStart),
-        routing_id_(routing_id),
-        msg_received_(false) {}
+}  // namespace
 
-  bool OnMessageReceived(const IPC::Message& message) override {
-    if (routing_id_ == MSG_ROUTING_NONE ||
-        message.routing_id() == routing_id_) {
-      IPC_BEGIN_MESSAGE_MAP(UpdateViewportIntersectionMessageFilter, message)
-        IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateViewportIntersection,
-                            OnUpdateViewportIntersection)
-      IPC_END_MESSAGE_MAP()
-    }
-    return false;
+// Class to monitor incoming UpdateViewportIntersection messages.
+class UpdateViewportIntersectionMessageFilter
+    : public blink::mojom::RemoteFrameHostInterceptorForTesting {
+ public:
+  explicit UpdateViewportIntersectionMessageFilter(
+      content::RenderFrameProxyHost* rfph)
+      : intersection_state_(blink::mojom::ViewportIntersectionState::New()),
+        render_frame_proxy_host_(rfph) {
+    render_frame_proxy_host_->frame_host_receiver_for_testing()
+        .SwapImplForTesting(this);
   }
 
-  const blink::ViewportIntersectionState& GetIntersectionState() const {
+  const blink::mojom::ViewportIntersectionStatePtr& GetIntersectionState()
+      const {
     return intersection_state_;
   }
 
+  RenderFrameProxyHost* GetForwardingInterface() override {
+    return render_frame_proxy_host_;
+  }
+
+  void UpdateViewportIntersection(
+      blink::mojom::ViewportIntersectionStatePtr intersection_state) override {
+    intersection_state_ = std::move(intersection_state);
+    msg_received_ = true;
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
   bool MessageReceived() const { return msg_received_; }
 
   void Clear() {
     msg_received_ = false;
-    intersection_state_ = blink::ViewportIntersectionState();
+    intersection_state_ = blink::mojom::ViewportIntersectionState::New();
   }
 
   void Wait() {
@@ -679,24 +685,12 @@
   void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
 
  private:
-  ~UpdateViewportIntersectionMessageFilter() override {}
-
-  void OnUpdateViewportIntersection(
-      const blink::ViewportIntersectionState& intersection_state) {
-    intersection_state_ = intersection_state;
-    msg_received_ = true;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-  const int routing_id_;
   base::RunLoop* run_loop_ = nullptr;
   bool msg_received_;
-  blink::ViewportIntersectionState intersection_state_;
-  DISALLOW_COPY_AND_ASSIGN(UpdateViewportIntersectionMessageFilter);
+  blink::mojom::ViewportIntersectionStatePtr intersection_state_;
+  content::RenderFrameProxyHost* render_frame_proxy_host_;
 };
 
-}  // namespace
-
 //
 // SitePerProcessBrowserTestBase
 //
@@ -12220,10 +12214,10 @@
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
 
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
-  scoped_refptr<UpdateViewportIntersectionMessageFilter> child2_filter =
-      new UpdateViewportIntersectionMessageFilter();
-  root->child_at(2)->current_frame_host()->GetProcess()->AddFilter(
-      child2_filter.get());
+  RenderFrameProxyHost* child2_proxy =
+      root->child_at(2)->render_manager()->GetProxyToParent();
+  auto child2_filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child2_proxy);
 
   // Force lifecycle update in root and child2 to make sure child2 has sent
   // viewport intersection into to grand child before child2 becomes throttled.
@@ -12267,10 +12261,10 @@
 
   // This will catch b sending viewport intersection information to c.
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
-  scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
-      new UpdateViewportIntersectionMessageFilter();
-  root->child_at(0)->current_frame_host()->GetProcess()->AddFilter(
-      filter.get());
+  RenderFrameProxyHost* iframe_c_proxy =
+      root->child_at(0)->child_at(0)->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(iframe_c_proxy);
 
   // Use EvalJsAfterLifecycleUpdate to force animation frames in `a` and `b` to
   // ensure that the viewport intersection for initial layout state has been
@@ -12314,7 +12308,7 @@
       &screen_info);
   // Convert from CSS to physical pixels
   expected.Scale(screen_info.device_scale_factor);
-  gfx::Transform actual = filter->GetIntersectionState().main_frame_transform;
+  gfx::Transform actual = filter->GetIntersectionState()->main_frame_transform;
   gfx::Point viewport_offset;
   EXPECT_TRUE(actual.TransformPointReverse(&viewport_offset));
   viewport_offset = gfx::Point(-viewport_offset.x(), -viewport_offset.y());
@@ -12465,9 +12459,11 @@
                             ->GetFrameTree()
                             ->root();
 
-  scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
-      new UpdateViewportIntersectionMessageFilter();
-  root->current_frame_host()->GetProcess()->AddFilter(filter.get());
+  FrameTreeNode* child = root->child_at(0);
+  RenderFrameProxyHost* child_proxy =
+      child->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy);
 
   // Force a lifecycle update and wait for it to finish; by the time this call
   // returns, the viewport intersection IPC should already have been received
@@ -12479,7 +12475,7 @@
   ASSERT_TRUE(eval_result.error.empty());
   int div_offset_top = eval_result.ExtractInt();
   gfx::Rect compositing_rect =
-      filter->GetIntersectionState().compositor_visible_rect;
+      filter->GetIntersectionState()->compositor_visible_rect;
 
   float device_scale_factor = 1.0f;
   if (IsUseZoomForDSFEnabled())
@@ -12538,10 +12534,10 @@
   FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
                             ->GetFrameTree()
                             ->root();
-  FrameTreeNode* child = root->child_at(0);
+  FrameTreeNode* child_b = root->child_at(0);
 
   NavigateFrameToURL(
-      child,
+      child_b,
       embedded_test_server()->GetURL(
           "bar.com", "/frame_tree/page_with_large_scrollable_frame.html"));
 
@@ -12556,9 +12552,11 @@
 
   // This adds the filter to the immediate child iframe. It verifies that the
   // child sets the nested iframe's compositing rect correctly.
-  scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
-      new UpdateViewportIntersectionMessageFilter();
-  child->current_frame_host()->GetProcess()->AddFilter(filter.get());
+  FrameTreeNode* child_c = child_b->child_at(0);
+  RenderFrameProxyHost* child_c_proxy =
+      child_c->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_c_proxy);
 
   // Scroll the child frame so that it is partially clipped. This will cause the
   // top 10 pixels of the child frame to be clipped. Applying the scale factor
@@ -12575,14 +12573,14 @@
   // and handled by the filter. Extract the page offset of the leaf iframe
   // within the middle document.
   EvalJsResult child_eval_result = EvalJsAfterLifecycleUpdate(
-      child->current_frame_host(),
+      child_b->current_frame_host(),
       "document.getElementsByTagName('div')[0].scrollTo(0, 5000);",
       "document.getElementsByTagName('div')[0].getBoundingClientRect().top;");
   ASSERT_TRUE(child_eval_result.error.empty());
   int child_div_offset_top = child_eval_result.ExtractInt();
 
   gfx::Rect compositing_rect =
-      filter->GetIntersectionState().compositor_visible_rect;
+      filter->GetIntersectionState()->compositor_visible_rect;
 
   float scale_factor = 1.0f;
   if (IsUseZoomForDSFEnabled())
@@ -12594,7 +12592,7 @@
   // main frame. The containing frame is clipped by 50 pixels at the top, due
   // to the scroll offset of the main frame, so we subtract that from the full
   // height of the containing frame.
-  int view_height = (child->current_frame_host()
+  int view_height = (child_b->current_frame_host()
                          ->GetRenderWidgetHost()
                          ->GetView()
                          ->GetViewBounds()
@@ -12636,9 +12634,11 @@
                             ->GetFrameTree()
                             ->root();
 
-  scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
-      new UpdateViewportIntersectionMessageFilter();
-  root->current_frame_host()->GetProcess()->AddFilter(filter.get());
+  FrameTreeNode* child = root->child_at(0);
+  RenderFrameProxyHost* child_proxy =
+      child->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy);
 
   // Force a lifecycle update and wait for it to finish. Changing the width of
   // the iframe should cause the parent renderer to propagate a new
@@ -12650,7 +12650,7 @@
       "document.querySelector('iframe').style.width = '250px'", "");
   ASSERT_TRUE(filter->MessageReceived());
   gfx::Rect compositing_rect =
-      filter->GetIntersectionState().compositor_visible_rect;
+      filter->GetIntersectionState()->compositor_visible_rect;
 
   float scale_factor = 1.0f;
   if (IsUseZoomForDSFEnabled())
@@ -12683,9 +12683,9 @@
       "baz.com", "/site_isolation/page-with-select.html"));
   NavigateFrameToURL(child_node, site_url);
 
-  scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
-      new UpdateViewportIntersectionMessageFilter();
-  root->current_frame_host()->GetProcess()->AddFilter(filter.get());
+  RenderFrameProxyHost* root_proxy = root->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(root_proxy);
 
   // Position the select element so that it is out of the viewport, then scroll
   // it into view.
@@ -12853,9 +12853,10 @@
 
   // This will intercept messages sent from B to C, describing C's viewport
   // intersection.
-  scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
-      new UpdateViewportIntersectionMessageFilter();
-  child_node->current_frame_host()->GetProcess()->AddFilter(filter.get());
+  RenderFrameProxyHost* child_proxy =
+      child_node->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy);
 
   // Run requestAnimationFrame in A and B to make sure initial layout has
   // completed and initial IPCs sent.
@@ -12869,12 +12870,12 @@
   // Scroll the child frame out of view, causing it to become throttled.
   ASSERT_TRUE(ExecJs(root->current_frame_host(), "window.scrollTo(0, 5000)"));
   filter->Wait();
-  EXPECT_TRUE(filter->GetIntersectionState().viewport_intersection.IsEmpty());
+  EXPECT_TRUE(filter->GetIntersectionState()->viewport_intersection.IsEmpty());
 
   // Scroll the frame back into view.
   ASSERT_TRUE(ExecJs(root->current_frame_host(), "window.scrollTo(0, 0)"));
   filter->Wait();
-  EXPECT_FALSE(filter->GetIntersectionState().viewport_intersection.IsEmpty());
+  EXPECT_FALSE(filter->GetIntersectionState()->viewport_intersection.IsEmpty());
 }
 
 // Test to verify that the main frame document intersection
@@ -12887,10 +12888,10 @@
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
 
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
-  FrameTreeNode* child_node = root->child_at(0);
+  FrameTreeNode* child_node_b = root->child_at(0);
   GURL site_url(embedded_test_server()->GetURL(
       "bar.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
-  NavigateFrameToURL(child_node, site_url);
+  NavigateFrameToURL(child_node_b, site_url);
 
   EXPECT_EQ(
       " Site A ------------ proxies for B C\n"
@@ -12901,31 +12902,34 @@
       "      C = http://baz.com/",
       DepictFrameTree(root));
 
-  scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
-      new UpdateViewportIntersectionMessageFilter();
-  child_node->current_frame_host()->GetProcess()->AddFilter(filter.get());
+  FrameTreeNode* child_node_c = child_node_b->child_at(0);
+  RenderFrameProxyHost* child_proxy_c =
+      child_node_c->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy_c);
 
   // Run requestAnimationFrame in A and B to make sure initial layout has
   // completed and initial IPC's sent.
   ASSERT_TRUE(EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", "")
                   .error.empty());
   ASSERT_TRUE(
-      EvalJsAfterLifecycleUpdate(child_node->current_frame_host(), "", "")
+      EvalJsAfterLifecycleUpdate(child_node_b->current_frame_host(), "", "")
           .error.empty());
   filter->Clear();
 
   // Scroll the child frame out of view, causing it to become throttled.
   ASSERT_TRUE(
-      ExecJs(child_node->current_frame_host(), "window.scrollTo(0, 5000)"));
+      ExecJs(child_node_b->current_frame_host(), "window.scrollTo(0, 5000)"));
   filter->Wait();
-  EXPECT_TRUE(filter->GetIntersectionState().main_frame_intersection.IsEmpty());
+  EXPECT_TRUE(
+      filter->GetIntersectionState()->main_frame_intersection.IsEmpty());
 
   // Scroll the frame back into view.
   ASSERT_TRUE(
-      ExecJs(child_node->current_frame_host(), "window.scrollTo(0, 0)"));
+      ExecJs(child_node_b->current_frame_host(), "window.scrollTo(0, 0)"));
   filter->Wait();
   EXPECT_FALSE(
-      filter->GetIntersectionState().main_frame_intersection.IsEmpty());
+      filter->GetIntersectionState()->main_frame_intersection.IsEmpty());
 }
 
 namespace {
@@ -15713,19 +15717,17 @@
 
   // This will intercept messages sent from B1 to C1, describing C1's viewport
   // intersection.
+  RenderFrameProxyHost* c1_proxy =
+      c1_node->render_manager()->GetProxyToParent();
   auto b1_to_c1_message_filter =
-      base::MakeRefCounted<UpdateViewportIntersectionMessageFilter>(
-          c1_node->render_manager()->GetProxyToParent()->GetRoutingID());
-  b1_node->current_frame_host()->GetProcess()->AddFilter(
-      b1_to_c1_message_filter.get());
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(c1_proxy);
 
   // This will intercept messages sent from B2 to C2, describing C2's viewport
   // intersection.
+  RenderFrameProxyHost* c2_proxy =
+      c2_node->render_manager()->GetProxyToParent();
   auto b2_to_c2_message_filter =
-      base::MakeRefCounted<UpdateViewportIntersectionMessageFilter>(
-          c2_node->render_manager()->GetProxyToParent()->GetRoutingID());
-  b2_node->current_frame_host()->GetProcess()->AddFilter(
-      b2_to_c2_message_filter.get());
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(c2_proxy);
 
   // Running requestAnimationFrame will ensure that any pending IPC's have been
   // sent by the renderer and received by the browser.
@@ -15745,41 +15747,39 @@
   // main_frame_scroll_offset, and then verify that each of them propagates
   // their own value of main_frame_scroll_offset to C1 and C2, respectively.
   // The IPC code mimics messages that A would send to B1 and B2.
-  blink::ViewportIntersectionState b1_intersection_state =
-      b1_node->render_manager()
-          ->GetProxyToParent()
-          ->cross_process_frame_connector()
-          ->intersection_state();
+  auto b1_intersection_state = b1_node->render_manager()
+                                   ->GetProxyToParent()
+                                   ->cross_process_frame_connector()
+                                   ->intersection_state();
+
   b1_intersection_state.main_frame_scroll_offset.Offset(10, 0);
   // A change in main_frame_scroll_offset by itself will not cause B1 to be
   // marked dirty, so we also modify viewport_intersection.
-  b1_intersection_state.viewport_intersection.y += 7;
-  b1_intersection_state.viewport_intersection.height -= 7;
-  {
-    FrameHostMsg_UpdateViewportIntersection message(MSG_ROUTING_NONE,
-                                                    b1_intersection_state);
-    b1_node->render_manager()
-        ->GetProxyToParent()
-        ->cross_process_frame_connector()
-        ->OnMessageReceived(message);
-  }
+  b1_intersection_state.viewport_intersection.set_y(
+      b1_intersection_state.viewport_intersection.y() + 7);
+  b1_intersection_state.viewport_intersection.set_height(
+      b1_intersection_state.viewport_intersection.height() - 7);
 
-  blink::ViewportIntersectionState b2_intersection_state =
-      b2_node->render_manager()
-          ->GetProxyToParent()
-          ->cross_process_frame_connector()
-          ->intersection_state();
+  b1_node->render_manager()
+      ->GetProxyToParent()
+      ->cross_process_frame_connector()
+      ->UpdateViewportIntersection(b1_intersection_state);
+
+  auto b2_intersection_state = b2_node->render_manager()
+                                   ->GetProxyToParent()
+                                   ->cross_process_frame_connector()
+                                   ->intersection_state();
+
   b2_intersection_state.main_frame_scroll_offset.Offset(20, 0);
-  b2_intersection_state.viewport_intersection.y += 7;
-  b2_intersection_state.viewport_intersection.height -= 7;
-  {
-    FrameHostMsg_UpdateViewportIntersection message(MSG_ROUTING_NONE,
-                                                    b2_intersection_state);
-    b2_node->render_manager()
-        ->GetProxyToParent()
-        ->cross_process_frame_connector()
-        ->OnMessageReceived(message);
-  }
+  b2_intersection_state.viewport_intersection.set_y(
+      b2_intersection_state.viewport_intersection.y() + 7);
+  b2_intersection_state.viewport_intersection.set_height(
+      b2_intersection_state.viewport_intersection.height() - 7);
+
+  b2_node->render_manager()
+      ->GetProxyToParent()
+      ->cross_process_frame_connector()
+      ->UpdateViewportIntersection(b2_intersection_state);
 
   // Once IPC's have been flushed to the C frames, we should see conflicting
   // values for main_frame_scroll_offset.
@@ -15788,10 +15788,10 @@
   ASSERT_TRUE(b1_to_c1_message_filter->MessageReceived());
   ASSERT_TRUE(b2_to_c2_message_filter->MessageReceived());
   EXPECT_EQ(
-      b1_to_c1_message_filter->GetIntersectionState().main_frame_scroll_offset,
+      b1_to_c1_message_filter->GetIntersectionState()->main_frame_scroll_offset,
       gfx::Point(10, 0));
   EXPECT_EQ(
-      b2_to_c2_message_filter->GetIntersectionState().main_frame_scroll_offset,
+      b2_to_c2_message_filter->GetIntersectionState()->main_frame_scroll_offset,
       gfx::Point(20, 0));
   b1_to_c1_message_filter->Clear();
   b2_to_c2_message_filter->Clear();
@@ -15812,10 +15812,10 @@
       &screen_info);
   float expected_y = screen_info.device_scale_factor * 5.0;
   EXPECT_NEAR(b1_to_c1_message_filter->GetIntersectionState()
-                  .main_frame_scroll_offset.y(),
+                  ->main_frame_scroll_offset.y(),
               expected_y, 1.f);
   EXPECT_NEAR(b2_to_c2_message_filter->GetIntersectionState()
-                  .main_frame_scroll_offset.y(),
+                  ->main_frame_scroll_offset.y(),
               expected_y, 1.f);
 }
 
diff --git a/content/browser/sms/README.md b/content/browser/sms/README.md
index f5d30d4..f22c712b 100644
--- a/content/browser/sms/README.md
+++ b/content/browser/sms/README.md
@@ -1,6 +1,6 @@
 # Web OTP API
 
-Android has [automatic and one-tap SMS verification](https://developers.google.com/identity/sms-retriever). We would like to cover the gap on web platform and implement the SMS Receiver API for web developers.
+Android has [automatic and one-tap SMS verification](https://developers.google.com/identity/sms-retriever). We would like to cover the gap on web platform and implement the WebOTP Service API for web developers.
 
 ## Web-exposed Interfaces
 
@@ -12,7 +12,7 @@
 
 ## Testing
 
-* Unit tests are located in [content/browser/sms/sms_service_unittest.cc](https://cs.chromium.org/chromium/src/content/browser/sms/sms_service_unittest.cc).
+* Unit tests are located in [content/browser/sms/webotp_service_unittest.cc](https://cs.chromium.org/chromium/src/content/browser/sms/webotp_service_unittest.cc).
 * Browser tests are located in [content/browser/sms/sms_browsertest.cc](https://cs.chromium.org/chromium/src/content/browser/sms/sms_browsertest.cc).
 * The Android related tests are located in [chrome/android/javatests/src/org/chromium/chrome/browser/sms/](https://cs.chromium.org/chromium/src/chrome/android/javatests/src/org/chromium/chrome/browser/sms/).
 * Web platform tests are located in [third_party/blink/web_tests/http/tests/credentialmanager/](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/http/tests/credentialmanager/)
diff --git a/content/browser/sms/sms_browsertest.cc b/content/browser/sms/sms_browsertest.cc
index 8214bd9..ec2e231 100644
--- a/content/browser/sms/sms_browsertest.cc
+++ b/content/browser/sms/sms_browsertest.cc
@@ -10,9 +10,9 @@
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/sms/sms_fetcher_impl.h"
-#include "content/browser/sms/sms_service.h"
 #include "content/browser/sms/test/mock_sms_provider.h"
 #include "content/browser/sms/test/mock_sms_web_contents_delegate.h"
+#include "content/browser/sms/webotp_service.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
@@ -25,7 +25,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gmock/include/gmock/gmock.h"
-#include "third_party/blink/public/common/sms/sms_receiver_outcome.h"
+#include "third_party/blink/public/common/sms/webotp_service_outcome.h"
 
 using blink::mojom::SmsStatus;
 using ::testing::_;
@@ -47,7 +47,7 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     ContentBrowserTest::SetUpCommandLine(command_line);
     command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
-                                    "SmsReceiver");
+                                    "WebOTPService");
     command_line->AppendSwitch(
         switches::kEnableExperimentalWebPlatformFeatures);
     command_line->AppendSwitchASCII(switches::kWebOtpBackend,
@@ -55,11 +55,11 @@
     cert_verifier_.SetUpCommandLine(command_line);
   }
 
-  void ExpectOutcomeUKM(const GURL& url, blink::SMSReceiverOutcome outcome) {
+  void ExpectOutcomeUKM(const GURL& url, blink::WebOTPServiceOutcome outcome) {
     auto entries = ukm_recorder()->GetEntriesByName(Entry::kEntryName);
 
     if (entries.empty())
-      FAIL() << "No SMSReceiverOutcome was recorded";
+      FAIL() << "No WebOTPServiceOutcome was recorded";
 
     for (const auto* const entry : entries) {
       const int64_t* metric = ukm_recorder()->GetEntryMetric(entry, "Outcome");
@@ -68,7 +68,7 @@
         return;
       }
     }
-    FAIL() << "Expected SMSReceiverOutcome was not recorded";
+    FAIL() << "Expected WebOTPServiceOutcome was not recorded";
   }
 
   void ExpectTimingUKM(const std::string& metric_name) {
@@ -211,7 +211,7 @@
   ASSERT_FALSE(GetSmsFetcher()->HasSubscribers());
 
   content::FetchHistogramsFromChildProcesses();
-  ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
+  ExpectOutcomeUKM(url, blink::WebOTPServiceOutcome::kSuccess);
   ExpectTimingUKM("TimeSmsReceiveMs");
   ExpectTimingUKM("TimeSuccessMs");
   histogram_tester.ExpectTotalCount("Blink.Sms.Receive.TimeSuccess", 1);
@@ -262,7 +262,7 @@
 
   ASSERT_FALSE(GetSmsFetcher()->HasSubscribers());
 
-  ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
+  ExpectOutcomeUKM(url, blink::WebOTPServiceOutcome::kSuccess);
 }
 
 // Disabled test: https://crbug.com/1052385
@@ -336,7 +336,7 @@
 
   ASSERT_TRUE(GetSmsFetcher()->HasSubscribers());
 
-  ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
+  ExpectOutcomeUKM(url, blink::WebOTPServiceOutcome::kSuccess);
 
   ukm_recorder()->Purge();
 
@@ -360,7 +360,7 @@
 
   ASSERT_FALSE(GetSmsFetcher()->HasSubscribers());
 
-  ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
+  ExpectOutcomeUKM(url, blink::WebOTPServiceOutcome::kSuccess);
 }
 
 IN_PROC_BROWSER_TEST_F(SmsBrowserTest, Reload) {
@@ -482,7 +482,7 @@
 
   ASSERT_TRUE(mock_provider_ptr->HasObservers());
 
-  ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
+  ExpectOutcomeUKM(url, blink::WebOTPServiceOutcome::kSuccess);
 
   ukm_recorder()->Purge();
 
@@ -506,7 +506,7 @@
 
   ASSERT_FALSE(GetSmsFetcher()->HasSubscribers());
 
-  ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kSuccess);
+  ExpectOutcomeUKM(url, blink::WebOTPServiceOutcome::kSuccess);
 }
 
 // Disabled test: https://crbug.com/1052385
@@ -579,8 +579,8 @@
 
   ASSERT_FALSE(GetSmsFetcher()->HasSubscribers());
 
-  ExpectOutcomeUKM(url1, blink::SMSReceiverOutcome::kSuccess);
-  ExpectOutcomeUKM(url2, blink::SMSReceiverOutcome::kSuccess);
+  ExpectOutcomeUKM(url1, blink::WebOTPServiceOutcome::kSuccess);
+  ExpectOutcomeUKM(url2, blink::WebOTPServiceOutcome::kSuccess);
 }
 
 IN_PROC_BROWSER_TEST_F(SmsBrowserTest, SmsReceivedAfterTabIsClosed) {
@@ -648,7 +648,7 @@
   EXPECT_EQ("AbortError", EvalJs(shell(), "error"));
 
   content::FetchHistogramsFromChildProcesses();
-  ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kCancelled);
+  ExpectOutcomeUKM(url, blink::WebOTPServiceOutcome::kCancelled);
   histogram_tester.ExpectTotalCount("Blink.Sms.Receive.TimeCancel", 1);
 }
 
@@ -692,7 +692,7 @@
 
   EXPECT_EQ("AbortError", EvalJs(shell(), "request"));
 
-  ExpectOutcomeUKM(url, blink::SMSReceiverOutcome::kAborted);
+  ExpectOutcomeUKM(url, blink::WebOTPServiceOutcome::kAborted);
 }
 
 IN_PROC_BROWSER_TEST_F(SmsBrowserTest, SmsFetcherUAF) {
@@ -710,14 +710,14 @@
   auto* fetcher = SmsFetcher::Get(shell()->web_contents()->GetBrowserContext());
   auto* fetcher2 =
       SmsFetcher::Get(shell()->web_contents()->GetBrowserContext());
-  mojo::Remote<blink::mojom::SmsReceiver> service;
-  mojo::Remote<blink::mojom::SmsReceiver> service2;
+  mojo::Remote<blink::mojom::WebOTPService> service;
+  mojo::Remote<blink::mojom::WebOTPService> service2;
 
   RenderFrameHost* render_frame_host = shell()->web_contents()->GetMainFrame();
-  SmsService::Create(fetcher, render_frame_host,
-                     service.BindNewPipeAndPassReceiver());
-  SmsService::Create(fetcher2, render_frame_host,
-                     service2.BindNewPipeAndPassReceiver());
+  WebOTPService::Create(fetcher, render_frame_host,
+                        service.BindNewPipeAndPassReceiver());
+  WebOTPService::Create(fetcher2, render_frame_host,
+                        service2.BindNewPipeAndPassReceiver());
 
   base::RunLoop navigate;
 
@@ -795,7 +795,7 @@
 
   RenderFrameHost* render_frame_host = shell()->web_contents()->GetMainFrame();
   EXPECT_FALSE(render_frame_host->DocumentUsedWebOTP());
-  // navigator.credentials.get() creates an SmsService which will notify the
+  // navigator.credentials.get() creates an WebOTPService which will notify the
   // RenderFrameHost that WebOTP has been used.
   std::string script = R"(
     (async () => {
diff --git a/content/browser/sms/sms_fetcher_impl.h b/content/browser/sms/sms_fetcher_impl.h
index 606c135..e0b3c98 100644
--- a/content/browser/sms/sms_fetcher_impl.h
+++ b/content/browser/sms/sms_fetcher_impl.h
@@ -35,8 +35,8 @@
   // Called by devices that do not have telephony capabilities and exclusively
   // listen for SMSes received on other devices.
   void Subscribe(const url::Origin& origin, Subscriber* subscriber) override;
-  // Called by |SmsService| to fetch SMSes retrieved by the SmsProvider from the
-  // requested device.
+  // Called by |WebOTPService| to fetch SMSes retrieved by the SmsProvider from
+  // the requested device.
   void Subscribe(const url::Origin& origin,
                  Subscriber* subscriber,
                  RenderFrameHost* rfh) override;
diff --git a/content/browser/sms/sms_metrics.cc b/content/browser/sms/sms_metrics.cc
index 259c2fd..3ed8ccc 100644
--- a/content/browser/sms/sms_metrics.cc
+++ b/content/browser/sms/sms_metrics.cc
@@ -28,7 +28,7 @@
                              duration);
 }
 
-void RecordDestroyedReason(blink::SmsReceiverDestroyedReason reason) {
+void RecordDestroyedReason(blink::WebOTPServiceDestroyedReason reason) {
   UMA_HISTOGRAM_ENUMERATION("Blink.Sms.Receive.DestroyedReason", reason);
 }
 
diff --git a/content/browser/sms/sms_metrics.h b/content/browser/sms/sms_metrics.h
index 3772ca8..a71da517 100644
--- a/content/browser/sms/sms_metrics.h
+++ b/content/browser/sms/sms_metrics.h
@@ -7,7 +7,7 @@
 
 #include "content/browser/sms/sms_parser.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
-#include "third_party/blink/public/common/sms/sms_receiver_destroyed_reason.h"
+#include "third_party/blink/public/common/sms/webotp_service_destroyed_reason.h"
 
 namespace base {
 class TimeDelta;
@@ -29,7 +29,7 @@
 // presses the Continue button.
 void RecordContinueOnSuccessTime(base::TimeDelta duration);
 
-void RecordDestroyedReason(blink::SmsReceiverDestroyedReason reason);
+void RecordDestroyedReason(blink::WebOTPServiceDestroyedReason reason);
 
 // Records the status of parsing an incoming SMS when using the WebOTP API.
 void RecordSmsParsingStatus(SmsParsingStatus status, ukm::SourceId source_id);
diff --git a/content/browser/sms/sms_provider_gms_user_consent.cc b/content/browser/sms/sms_provider_gms_user_consent.cc
index 4187ad93..9896fe8 100644
--- a/content/browser/sms/sms_provider_gms_user_consent.cc
+++ b/content/browser/sms/sms_provider_gms_user_consent.cc
@@ -55,7 +55,7 @@
 void SmsProviderGmsUserConsent::OnTimeout(JNIEnv* env) {}
 
 base::android::ScopedJavaGlobalRef<jobject>
-SmsProviderGmsUserConsent::GetSmsReceiverForTesting() const {
+SmsProviderGmsUserConsent::GetWebOTPServiceForTesting() const {
   return j_sms_receiver_;
 }
 
diff --git a/content/browser/sms/sms_provider_gms_user_consent.h b/content/browser/sms/sms_provider_gms_user_consent.h
index 309fa76..89c6a56b4 100644
--- a/content/browser/sms/sms_provider_gms_user_consent.h
+++ b/content/browser/sms/sms_provider_gms_user_consent.h
@@ -28,7 +28,8 @@
   // Implements JNI method SmsUserConsentReceiver.Natives.onTimeout().
   void OnTimeout(JNIEnv* env);
 
-  base::android::ScopedJavaGlobalRef<jobject> GetSmsReceiverForTesting() const;
+  base::android::ScopedJavaGlobalRef<jobject> GetWebOTPServiceForTesting()
+      const;
 
  private:
   base::android::ScopedJavaGlobalRef<jobject> j_sms_receiver_;
diff --git a/content/browser/sms/sms_provider_gms_user_consent_unittest.cc b/content/browser/sms/sms_provider_gms_user_consent_unittest.cc
index 62ef935..0ae57d6 100644
--- a/content/browser/sms/sms_provider_gms_user_consent_unittest.cc
+++ b/content/browser/sms/sms_provider_gms_user_consent_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/sms/sms_service.h"
+#include "content/browser/sms/webotp_service.h"
 
 #include <string>
 
@@ -52,7 +52,7 @@
     j_fake_sms_retriever_client_.Reset(
         Java_FakeSmsUserConsentRetrieverClient_create(AttachCurrentThread()));
     Java_SmsUserConsentFakes_setUserConsentClientForTesting(
-        AttachCurrentThread(), provider_->GetSmsReceiverForTesting(),
+        AttachCurrentThread(), provider_->GetWebOTPServiceForTesting(),
         j_fake_sms_retriever_client_,
         ui::WindowAndroid::CreateForTesting()->GetJavaObject());
     provider_->AddObserver(&observer_);
diff --git a/content/browser/sms/sms_provider_gms_verification.cc b/content/browser/sms/sms_provider_gms_verification.cc
index bf75527..082be769 100644
--- a/content/browser/sms/sms_provider_gms_verification.cc
+++ b/content/browser/sms/sms_provider_gms_verification.cc
@@ -46,7 +46,7 @@
 void SmsProviderGmsVerification::OnTimeout(JNIEnv* env) {}
 
 base::android::ScopedJavaGlobalRef<jobject>
-SmsProviderGmsVerification::GetSmsReceiverForTesting() const {
+SmsProviderGmsVerification::GetWebOTPServiceForTesting() const {
   return j_sms_receiver_;
 }
 
diff --git a/content/browser/sms/sms_provider_gms_verification.h b/content/browser/sms/sms_provider_gms_verification.h
index 443931a8..a757c5a8 100644
--- a/content/browser/sms/sms_provider_gms_verification.h
+++ b/content/browser/sms/sms_provider_gms_verification.h
@@ -30,7 +30,8 @@
   // Implements JNI method SmsVerificationReceiver.Natives.onTimeout().
   void OnTimeout(JNIEnv* env);
 
-  base::android::ScopedJavaGlobalRef<jobject> GetSmsReceiverForTesting() const;
+  base::android::ScopedJavaGlobalRef<jobject> GetWebOTPServiceForTesting()
+      const;
 
  private:
   base::android::ScopedJavaGlobalRef<jobject> j_sms_receiver_;
diff --git a/content/browser/sms/sms_provider_gms_verification_unittest.cc b/content/browser/sms/sms_provider_gms_verification_unittest.cc
index c18723b..56fef1e 100644
--- a/content/browser/sms/sms_provider_gms_verification_unittest.cc
+++ b/content/browser/sms/sms_provider_gms_verification_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/sms/sms_service.h"
+#include "content/browser/sms/webotp_service.h"
 
 #include <string>
 
@@ -51,7 +51,7 @@
     j_fake_sms_retriever_client_.Reset(
         Java_FakeSmsRetrieverClient_create(AttachCurrentThread()));
     Java_SmsVerificationFakes_setClientForTesting(
-        AttachCurrentThread(), provider_->GetSmsReceiverForTesting(),
+        AttachCurrentThread(), provider_->GetWebOTPServiceForTesting(),
         j_fake_sms_retriever_client_);
     provider_->AddObserver(&observer_);
   }
diff --git a/content/browser/sms/user_consent_handler.cc b/content/browser/sms/user_consent_handler.cc
index 1f67a6cc..c6654a03 100644
--- a/content/browser/sms/user_consent_handler.cc
+++ b/content/browser/sms/user_consent_handler.cc
@@ -4,7 +4,7 @@
 
 #include "content/browser/sms/user_consent_handler.h"
 #include "base/callback.h"
-#include "content/browser/sms/sms_service.h"
+#include "content/browser/sms/webotp_service.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 
diff --git a/content/browser/sms/user_consent_handler.h b/content/browser/sms/user_consent_handler.h
index d2b3e90..bd0445fd 100644
--- a/content/browser/sms/user_consent_handler.h
+++ b/content/browser/sms/user_consent_handler.h
@@ -7,7 +7,7 @@
 
 #include "base/callback_forward.h"
 #include "content/common/content_export.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-shared.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom-shared.h"
 #include "url/origin.h"
 
 namespace content {
diff --git a/content/browser/sms/sms_service.cc b/content/browser/sms/webotp_service.cc
similarity index 81%
rename from content/browser/sms/sms_service.cc
rename to content/browser/sms/webotp_service.cc
index cde54af..1b2fa61b 100644
--- a/content/browser/sms/sms_service.cc
+++ b/content/browser/sms/webotp_service.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/sms/sms_service.h"
+#include "content/browser/sms/webotp_service.h"
 
 #include <iterator>
 #include <memory>
@@ -27,19 +27,19 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-shared.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom-shared.h"
 
-using blink::SmsReceiverDestroyedReason;
+using blink::WebOTPServiceDestroyedReason;
 using blink::mojom::SmsStatus;
 
 namespace content {
 
-SmsService::SmsService(
+WebOTPService::WebOTPService(
     SmsFetcher* fetcher,
     std::unique_ptr<UserConsentHandler> consent_handler,
     const url::Origin& origin,
     RenderFrameHost* host,
-    mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver)
+    mojo::PendingReceiver<blink::mojom::WebOTPService> receiver)
     : FrameServiceBase(host, std::move(receiver)),
       fetcher_(fetcher),
       consent_handler_(std::move(consent_handler)),
@@ -47,15 +47,15 @@
   DCHECK(fetcher_);
 }
 
-SmsService::SmsService(
+WebOTPService::WebOTPService(
     SmsFetcher* fetcher,
     RenderFrameHost* host,
-    mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver)
-    : SmsService(fetcher,
-                 nullptr,
-                 host->GetLastCommittedOrigin(),
-                 host,
-                 std::move(receiver)) {
+    mojo::PendingReceiver<blink::mojom::WebOTPService> receiver)
+    : WebOTPService(fetcher,
+                    nullptr,
+                    host->GetLastCommittedOrigin(),
+                    host,
+                    std::move(receiver)) {
   bool needs_user_prompt =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kWebOtpBackend) == switches::kWebOtpBackendSmsVerification;
@@ -68,28 +68,28 @@
   }
 }
 
-SmsService::~SmsService() {
+WebOTPService::~WebOTPService() {
   if (callback_)
     CompleteRequest(SmsStatus::kUnhandledRequest);
   DCHECK(!callback_);
 }
 
 // static
-void SmsService::Create(
+void WebOTPService::Create(
     SmsFetcher* fetcher,
     RenderFrameHost* host,
-    mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver) {
+    mojo::PendingReceiver<blink::mojom::WebOTPService> receiver) {
   DCHECK(host);
 
-  // SmsService owns itself. It will self-destruct when a mojo interface
+  // WebOTPService owns itself. It will self-destruct when a mojo interface
   // error occurs, the render frame host is deleted, or the render frame host
   // navigates to a new document.
-  new SmsService(fetcher, host, std::move(receiver));
+  new WebOTPService(fetcher, host, std::move(receiver));
   static_cast<RenderFrameHostImpl*>(host)->OnSchedulerTrackedFeatureUsed(
-      blink::scheduler::WebSchedulerTrackedFeature::kSmsService);
+      blink::scheduler::WebSchedulerTrackedFeature::kWebOTPService);
 }
 
-void SmsService::Receive(ReceiveCallback callback) {
+void WebOTPService::Receive(ReceiveCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // TODO(majidvp): The comment below seems incorrect. This flow is used for
@@ -127,7 +127,7 @@
   fetcher_->Subscribe(origin_, this, render_frame_host());
 }
 
-void SmsService::OnReceive(const std::string& one_time_code) {
+void WebOTPService::OnReceive(const std::string& one_time_code) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!one_time_code_);
   DCHECK(!start_time_.is_null());
@@ -141,11 +141,11 @@
   one_time_code_ = one_time_code;
 
   consent_handler_->RequestUserConsent(
-      one_time_code, base::BindOnce(&SmsService::CompleteRequest,
+      one_time_code, base::BindOnce(&WebOTPService::CompleteRequest,
                                     weak_ptr_factory_.GetWeakPtr()));
 }
 
-void SmsService::OnFailure(FailureType failure_type) {
+void WebOTPService::OnFailure(FailureType failure_type) {
   SmsParser::SmsParsingStatus status = SmsParsingStatus::kParsed;
   switch (failure_type) {
     case FailureType::kSmsNotParsed_OTPFormatRegexNotMatch:
@@ -167,22 +167,23 @@
   RecordSmsParsingStatus(status, render_frame_host()->GetPageUkmSourceId());
 }
 
-void SmsService::Abort() {
+void WebOTPService::Abort() {
   DCHECK(callback_);
   CompleteRequest(SmsStatus::kAborted);
 }
 
-void SmsService::NavigationEntryCommitted(
+void WebOTPService::NavigationEntryCommitted(
     const content::LoadCommittedDetails& load_details) {
   switch (load_details.type) {
     case NavigationType::NAVIGATION_TYPE_NEW_PAGE:
-      RecordDestroyedReason(SmsReceiverDestroyedReason::kNavigateNewPage);
+      RecordDestroyedReason(WebOTPServiceDestroyedReason::kNavigateNewPage);
       break;
     case NavigationType::NAVIGATION_TYPE_EXISTING_PAGE:
-      RecordDestroyedReason(SmsReceiverDestroyedReason::kNavigateExistingPage);
+      RecordDestroyedReason(
+          WebOTPServiceDestroyedReason::kNavigateExistingPage);
       break;
     case NavigationType::NAVIGATION_TYPE_SAME_PAGE:
-      RecordDestroyedReason(SmsReceiverDestroyedReason::kNavigateSamePage);
+      RecordDestroyedReason(WebOTPServiceDestroyedReason::kNavigateSamePage);
       break;
     default:
       // Ignore cases we don't care about.
@@ -190,7 +191,7 @@
   }
 }
 
-void SmsService::CompleteRequest(blink::mojom::SmsStatus status) {
+void WebOTPService::CompleteRequest(blink::mojom::SmsStatus status) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   base::Optional<std::string> code = base::nullopt;
@@ -218,7 +219,7 @@
   CleanUp();
 }
 
-void SmsService::CleanUp() {
+void WebOTPService::CleanUp() {
   // Skip resetting |one_time_code_|, |sms| and |receive_time_| while prompt is
   // still open in case it needs to be returned to the next incoming request
   // upon prompt confirmation.
diff --git a/content/browser/sms/sms_service.h b/content/browser/sms/webotp_service.h
similarity index 60%
rename from content/browser/sms/sms_service.h
rename to content/browser/sms/webotp_service.h
index 8b341af0..4929891 100644
--- a/content/browser/sms/sms_service.h
+++ b/content/browser/sms/webotp_service.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_SMS_SMS_SERVICE_H_
-#define CONTENT_BROWSER_SMS_SMS_SERVICE_H_
+#ifndef CONTENT_BROWSER_SMS_WEBOTP_SERVICE_H_
+#define CONTENT_BROWSER_SMS_WEBOTP_SERVICE_H_
 
 #include <memory>
 #include <string>
@@ -17,7 +17,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/frame_service_base.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom.h"
 #include "url/origin.h"
 
 namespace content {
@@ -26,35 +26,34 @@
 class SmsFetcher;
 struct LoadCommittedDetails;
 
-// SmsService handles mojo connections from the renderer, observing the incoming
-// SMS messages from an SmsFetcher.
-// In practice, it is owned and managed by a RenderFrameHost. It accomplishes
-// that via subclassing FrameServiceBase, which observes the lifecycle of a
-// RenderFrameHost and manages it own memory.
-// Create() creates a self-managed instance of SmsService and binds it to the
-// request.
-class CONTENT_EXPORT SmsService
-    : public FrameServiceBase<blink::mojom::SmsReceiver>,
+// WebOTPService handles mojo connections from the renderer, observing the
+// incoming SMS messages from an SmsFetcher. In practice, it is owned and
+// managed by a RenderFrameHost. It accomplishes that via subclassing
+// FrameServiceBase, which observes the lifecycle of a RenderFrameHost and
+// manages it own memory. Create() creates a self-managed instance of
+// WebOTPService and binds it to the request.
+class CONTENT_EXPORT WebOTPService
+    : public FrameServiceBase<blink::mojom::WebOTPService>,
       public SmsFetcher::Subscriber {
  public:
   static void Create(SmsFetcher*,
                      RenderFrameHost*,
-                     mojo::PendingReceiver<blink::mojom::SmsReceiver>);
+                     mojo::PendingReceiver<blink::mojom::WebOTPService>);
 
-  SmsService(SmsFetcher*,
-             RenderFrameHost*,
-             mojo::PendingReceiver<blink::mojom::SmsReceiver>);
-  SmsService(SmsFetcher*,
-             std::unique_ptr<UserConsentHandler> consent_handler,
-             const url::Origin&,
-             RenderFrameHost*,
-             mojo::PendingReceiver<blink::mojom::SmsReceiver>);
-  ~SmsService() override;
+  WebOTPService(SmsFetcher*,
+                RenderFrameHost*,
+                mojo::PendingReceiver<blink::mojom::WebOTPService>);
+  WebOTPService(SmsFetcher*,
+                std::unique_ptr<UserConsentHandler> consent_handler,
+                const url::Origin&,
+                RenderFrameHost*,
+                mojo::PendingReceiver<blink::mojom::WebOTPService>);
+  ~WebOTPService() override;
 
   using FailureType = SmsFetcher::FailureType;
   using SmsParsingStatus = SmsParser::SmsParsingStatus;
 
-  // blink::mojom::SmsReceiver:
+  // blink::mojom::WebOTPService:
   void Receive(ReceiveCallback) override;
   void Abort() override;
 
@@ -75,7 +74,6 @@
  private:
   void CleanUp();
 
-
   // |fetcher_| is safe because all instances of SmsFetcher are owned
   // by the browser context, which transitively (through RenderFrameHost) owns
   // and outlives this class.
@@ -90,11 +88,11 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  base::WeakPtrFactory<SmsService> weak_ptr_factory_{this};
+  base::WeakPtrFactory<WebOTPService> weak_ptr_factory_{this};
 
-  DISALLOW_COPY_AND_ASSIGN(SmsService);
+  DISALLOW_COPY_AND_ASSIGN(WebOTPService);
 };
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_SMS_SMS_SERVICE_H_
+#endif  // CONTENT_BROWSER_SMS_WEBOTP_SERVICE_H_
diff --git a/content/browser/sms/sms_service_unittest.cc b/content/browser/sms/webotp_service_unittest.cc
similarity index 87%
rename from content/browser/sms/sms_service_unittest.cc
rename to content/browser/sms/webotp_service_unittest.cc
index 79071de2..fed70b3 100644
--- a/content/browser/sms/sms_service_unittest.cc
+++ b/content/browser/sms/webotp_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 "content/browser/sms/sms_service.h"
+#include "content/browser/sms/webotp_service.h"
 
 #include <memory>
 #include <string>
@@ -34,15 +34,15 @@
 #include "services/service_manager/public/cpp/bind_source_info.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/sms/sms_receiver_destroyed_reason.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-shared.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom.h"
+#include "third_party/blink/public/common/sms/webotp_service_destroyed_reason.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom-shared.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom.h"
 
 using base::BindLambdaForTesting;
 using base::Optional;
-using blink::SmsReceiverDestroyedReason;
-using blink::mojom::SmsReceiver;
+using blink::WebOTPServiceDestroyedReason;
 using blink::mojom::SmsStatus;
+using blink::mojom::WebOTPService;
 using std::string;
 using ::testing::_;
 using ::testing::ByMove;
@@ -62,9 +62,9 @@
 
 class StubWebContentsDelegate : public WebContentsDelegate {};
 
-// Service encapsulates a SmsService endpoint, with all of its dependencies
+// Service encapsulates a WebOTPService endpoint, with all of its dependencies
 // mocked out (and the common plumbing needed to inject them), and a
-// mojo::Remote<SmsReceiver> endpoint that tests can use to make requests.
+// mojo::Remote<WebOTPService> endpoint that tests can use to make requests.
 // It exposes some common methods, like MakeRequest and NotifyReceive, but it
 // also exposes the low level mocks that enables tests to set expectations and
 // control the testing environment.
@@ -79,7 +79,7 @@
     // cancels requests early if one does not exist.
     web_contents->SetDelegate(&contents_delegate_);
 
-    service_ = std::make_unique<SmsService>(
+    service_ = std::make_unique<WebOTPService>(
         &fetcher_, std::move(user_consent_handler), origin,
         web_contents->GetMainFrame(),
         service_remote_.BindNewPipeAndPassReceiver());
@@ -96,7 +96,7 @@
   SmsFetcher* fetcher() { return &fetcher_; }
   UserConsentHandler* consent_handler() { return consent_handler_; }
 
-  void MakeRequest(SmsReceiver::ReceiveCallback callback) {
+  void MakeRequest(WebOTPService::ReceiveCallback callback) {
     service_remote_->Receive(std::move(callback));
   }
 
@@ -111,16 +111,16 @@
   NiceMock<MockSmsProvider> provider_;
   SmsFetcherImpl fetcher_;
   UserConsentHandler* consent_handler_;
-  mojo::Remote<blink::mojom::SmsReceiver> service_remote_;
-  std::unique_ptr<SmsService> service_;
+  mojo::Remote<blink::mojom::WebOTPService> service_remote_;
+  std::unique_ptr<WebOTPService> service_;
 };
 
-class SmsServiceTest : public RenderViewHostTestHarness {
+class WebOTPServiceTest : public RenderViewHostTestHarness {
  protected:
-  SmsServiceTest() = default;
-  ~SmsServiceTest() override = default;
+  WebOTPServiceTest() = default;
+  ~WebOTPServiceTest() override = default;
 
-  void ExpectDestroyedReasonCount(SmsReceiverDestroyedReason bucket,
+  void ExpectDestroyedReasonCount(WebOTPServiceDestroyedReason bucket,
                                   int32_t count) {
     histogram_tester_.ExpectBucketCount("Blink.Sms.Receive.DestroyedReason",
                                         bucket, count);
@@ -133,19 +133,18 @@
  private:
   base::HistogramTester histogram_tester_;
 
-  DISALLOW_COPY_AND_ASSIGN(SmsServiceTest);
+  DISALLOW_COPY_AND_ASSIGN(WebOTPServiceTest);
 };
 
 }  // namespace
 
-TEST_F(SmsServiceTest, Basic) {
+TEST_F(WebOTPServiceTest, Basic) {
   NavigateAndCommit(GURL(kTestUrl));
 
   Service service(web_contents());
 
   base::RunLoop loop;
 
-
   EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
     service.NotifyReceive(GURL(kTestUrl), "hi");
   }));
@@ -162,7 +161,7 @@
   ASSERT_FALSE(service.fetcher()->HasSubscribers());
 }
 
-TEST_F(SmsServiceTest, HandlesMultipleCalls) {
+TEST_F(WebOTPServiceTest, HandlesMultipleCalls) {
   NavigateAndCommit(GURL(kTestUrl));
 
   Service service(web_contents());
@@ -202,7 +201,7 @@
   }
 }
 
-TEST_F(SmsServiceTest, IgnoreFromOtherOrigins) {
+TEST_F(WebOTPServiceTest, IgnoreFromOtherOrigins) {
   NavigateAndCommit(GURL(kTestUrl));
 
   Service service(web_contents());
@@ -233,7 +232,7 @@
   EXPECT_EQ(SmsStatus::kSuccess, sms_status);
 }
 
-TEST_F(SmsServiceTest, ExpectOneReceiveTwo) {
+TEST_F(WebOTPServiceTest, ExpectOneReceiveTwo) {
   NavigateAndCommit(GURL(kTestUrl));
 
   Service service(web_contents());
@@ -243,7 +242,6 @@
 
   base::RunLoop sms_loop;
 
-
   EXPECT_CALL(*service.provider(), Retrieve(_)).WillOnce(Invoke([&service]() {
     // Delivers two SMSes for the same origin, even if only one was being
     // expected.
@@ -267,7 +265,7 @@
   EXPECT_EQ(SmsStatus::kSuccess, sms_status);
 }
 
-TEST_F(SmsServiceTest, AtMostOneSmsRequestPerOrigin) {
+TEST_F(WebOTPServiceTest, AtMostOneSmsRequestPerOrigin) {
   NavigateAndCommit(GURL(kTestUrl));
 
   Service service(web_contents());
@@ -281,9 +279,8 @@
 
   EXPECT_CALL(*service.provider(), Retrieve(_))
       .WillOnce(Return())
-      .WillOnce(Invoke([&service]() {
-        service.NotifyReceive(GURL(kTestUrl), "second");
-      }));
+      .WillOnce(Invoke(
+          [&service]() { service.NotifyReceive(GURL(kTestUrl), "second"); }));
 
   service.MakeRequest(
       BindLambdaForTesting([&sms_status1, &response1, &sms1_loop](
@@ -313,7 +310,7 @@
   EXPECT_EQ(SmsStatus::kSuccess, sms_status2);
 }
 
-TEST_F(SmsServiceTest, CleansUp) {
+TEST_F(WebOTPServiceTest, CleansUp) {
   NavigateAndCommit(GURL(kTestUrl));
 
   NiceMock<MockSmsWebContentsDelegate> delegate;
@@ -323,9 +320,9 @@
 
   NiceMock<MockSmsProvider> provider;
   SmsFetcherImpl fetcher(web_contents()->GetBrowserContext(), &provider);
-  mojo::Remote<blink::mojom::SmsReceiver> service;
-  SmsService::Create(&fetcher, main_rfh(),
-                     service.BindNewPipeAndPassReceiver());
+  mojo::Remote<blink::mojom::WebOTPService> service;
+  WebOTPService::Create(&fetcher, main_rfh(),
+                        service.BindNewPipeAndPassReceiver());
 
   base::RunLoop navigate;
 
@@ -353,14 +350,14 @@
   ASSERT_FALSE(fetcher.HasSubscribers());
 }
 
-TEST_F(SmsServiceTest, CancelForNoDelegate) {
+TEST_F(WebOTPServiceTest, CancelForNoDelegate) {
   NavigateAndCommit(GURL(kTestUrl));
 
   NiceMock<MockSmsProvider> provider;
   SmsFetcherImpl fetcher(web_contents()->GetBrowserContext(), &provider);
-  mojo::Remote<blink::mojom::SmsReceiver> service;
-  SmsService::Create(&fetcher, main_rfh(),
-                     service.BindNewPipeAndPassReceiver());
+  mojo::Remote<blink::mojom::WebOTPService> service;
+  WebOTPService::Create(&fetcher, main_rfh(),
+                        service.BindNewPipeAndPassReceiver());
 
   base::RunLoop loop;
 
@@ -376,7 +373,7 @@
   ASSERT_FALSE(fetcher.HasSubscribers());
 }
 
-TEST_F(SmsServiceTest, Abort) {
+TEST_F(WebOTPServiceTest, Abort) {
   NavigateAndCommit(GURL(kTestUrl));
 
   Service service(web_contents());
@@ -397,7 +394,7 @@
   ASSERT_FALSE(service.fetcher()->HasSubscribers());
 }
 
-TEST_F(SmsServiceTest, RecordMetricsForNewPage) {
+TEST_F(WebOTPServiceTest, RecordMetricsForNewPage) {
   // This test depends on the page being destroyed on navigation.
   web_contents()->GetController().GetBackForwardCache().DisableForTesting(
       content::BackForwardCache::TEST_ASSUMES_NO_CACHING);
@@ -409,9 +406,9 @@
 
   NiceMock<MockSmsProvider> provider;
   SmsFetcherImpl fetcher(web_contents()->GetBrowserContext(), &provider);
-  mojo::Remote<blink::mojom::SmsReceiver> service;
-  SmsService::Create(&fetcher, main_rfh(),
-                     service.BindNewPipeAndPassReceiver());
+  mojo::Remote<blink::mojom::WebOTPService> service;
+  WebOTPService::Create(&fetcher, main_rfh(),
+                        service.BindNewPipeAndPassReceiver());
 
   base::RunLoop navigate;
 
@@ -435,10 +432,10 @@
 
   reload.Run();
 
-  ExpectDestroyedReasonCount(SmsReceiverDestroyedReason::kNavigateNewPage, 1);
+  ExpectDestroyedReasonCount(WebOTPServiceDestroyedReason::kNavigateNewPage, 1);
 }
 
-TEST_F(SmsServiceTest, RecordMetricsForSamePage) {
+TEST_F(WebOTPServiceTest, RecordMetricsForSamePage) {
   NavigateAndCommit(GURL(kTestUrl));
   NiceMock<MockSmsWebContentsDelegate> delegate;
   WebContentsImpl* web_contents_impl =
@@ -447,9 +444,9 @@
 
   NiceMock<MockSmsProvider> provider;
   SmsFetcherImpl fetcher(web_contents()->GetBrowserContext(), &provider);
-  mojo::Remote<blink::mojom::SmsReceiver> service;
-  SmsService::Create(&fetcher, main_rfh(),
-                     service.BindNewPipeAndPassReceiver());
+  mojo::Remote<blink::mojom::WebOTPService> service;
+  WebOTPService::Create(&fetcher, main_rfh(),
+                        service.BindNewPipeAndPassReceiver());
 
   base::RunLoop navigate;
 
@@ -473,7 +470,8 @@
 
   reload.Run();
 
-  ExpectDestroyedReasonCount(SmsReceiverDestroyedReason::kNavigateSamePage, 1);
+  ExpectDestroyedReasonCount(WebOTPServiceDestroyedReason::kNavigateSamePage,
+                             1);
 }
 
 // Following tests exercise parts of sms service logic that depend on user
@@ -524,14 +522,14 @@
   bool IsPromptOpen() const { return !on_complete_callback_.is_null(); }
 
  private:
-  // The actual consent handler is owned by SmsService but we keep a ptr to
+  // The actual consent handler is owned by WebOTPService but we keep a ptr to
   // it so it can be used to set expectations for it. It is safe since the
   // sms service lifetime is the same as this object.
   NiceMock<MockUserConsentHandler>* mock_handler_;
   CompletionCallback on_complete_callback_;
 };
 
-TEST_F(SmsServiceTest, SecondRequestDuringPrompt) {
+TEST_F(WebOTPServiceTest, SecondRequestDuringPrompt) {
   NavigateAndCommit(GURL(kTestUrl));
 
   ServiceWithPrompt service(web_contents());
@@ -577,7 +575,7 @@
   EXPECT_EQ(SmsStatus::kSuccess, sms_status2);
 }
 
-TEST_F(SmsServiceTest, AbortWhilePrompt) {
+TEST_F(WebOTPServiceTest, AbortWhilePrompt) {
   NavigateAndCommit(GURL(kTestUrl));
 
   ServiceWithPrompt service(web_contents());
@@ -606,7 +604,7 @@
   service.ConfirmPrompt();
 }
 
-TEST_F(SmsServiceTest, RequestAfterAbortWhilePrompt) {
+TEST_F(WebOTPServiceTest, RequestAfterAbortWhilePrompt) {
   NavigateAndCommit(GURL(kTestUrl));
 
   ServiceWithPrompt service(web_contents());
@@ -660,7 +658,7 @@
   }
 }
 
-TEST_F(SmsServiceTest, SecondRequestWhilePrompt) {
+TEST_F(WebOTPServiceTest, SecondRequestWhilePrompt) {
   NavigateAndCommit(GURL(kTestUrl));
 
   ServiceWithPrompt service(web_contents());
@@ -704,7 +702,7 @@
   ASSERT_FALSE(service.fetcher()->HasSubscribers());
 }
 
-TEST_F(SmsServiceTest, RecordTimeMetricsForContinueOnSuccess) {
+TEST_F(WebOTPServiceTest, RecordTimeMetricsForContinueOnSuccess) {
   NavigateAndCommit(GURL(kTestUrl));
 
   ServiceWithPrompt service(web_contents());
@@ -728,7 +726,7 @@
   histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeSmsReceive", 1);
 }
 
-TEST_F(SmsServiceTest, RecordMetricsForCancelOnSuccess) {
+TEST_F(WebOTPServiceTest, RecordMetricsForCancelOnSuccess) {
   NavigateAndCommit(GURL(kTestUrl));
 
   ServiceWithPrompt service(web_contents());
@@ -753,7 +751,7 @@
   histogram_tester().ExpectTotalCount("Blink.Sms.Receive.TimeSmsReceive", 1);
 }
 
-TEST_F(SmsServiceTest, RecordMetricsForExistingPage) {
+TEST_F(WebOTPServiceTest, RecordMetricsForExistingPage) {
   // This test depends on the page being destroyed on navigation.
   web_contents()->GetController().GetBackForwardCache().DisableForTesting(
       content::BackForwardCache::TEST_ASSUMES_NO_CACHING);
@@ -767,9 +765,9 @@
 
   NiceMock<MockSmsProvider> provider;
   SmsFetcherImpl fetcher(web_contents()->GetBrowserContext(), &provider);
-  mojo::Remote<blink::mojom::SmsReceiver> service;
-  SmsService::Create(&fetcher, main_rfh(),
-                     service.BindNewPipeAndPassReceiver());
+  mojo::Remote<blink::mojom::WebOTPService> service;
+  WebOTPService::Create(&fetcher, main_rfh(),
+                        service.BindNewPipeAndPassReceiver());
 
   base::RunLoop navigate;
 
@@ -793,8 +791,8 @@
 
   reload.Run();
 
-  ExpectDestroyedReasonCount(SmsReceiverDestroyedReason::kNavigateExistingPage,
-                             1);
+  ExpectDestroyedReasonCount(
+      WebOTPServiceDestroyedReason::kNavigateExistingPage, 1);
 }
 
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_observer_browsertest.cc b/content/browser/web_contents/web_contents_observer_browsertest.cc
index 40b03def..cce3295 100644
--- a/content/browser/web_contents/web_contents_observer_browsertest.cc
+++ b/content/browser/web_contents/web_contents_observer_browsertest.cc
@@ -135,15 +135,7 @@
 
 }  // namespace
 
-class WebContentsObserverWithSWonUIBrowserTest
-    : public WebContentsObserverBrowserTest {
- public:
-  WebContentsObserverWithSWonUIBrowserTest() {
-    feature_list_.InitAndEnableFeature(features::kServiceWorkerOnUI);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(WebContentsObserverWithSWonUIBrowserTest,
+IN_PROC_BROWSER_TEST_F(WebContentsObserverBrowserTest,
                        OnServiceWorkerAccessed_ContentClientBlocked) {
   GURL service_worker_scope =
       embedded_test_server()->GetURL("/service_worker/");
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index ae127006d..8128d61f51 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -436,8 +436,8 @@
   ancestor_render_frame_host->BindIdleManager(std::move(receiver));
 }
 
-void DedicatedWorkerHost::BindSmsReceiverReceiver(
-    mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver) {
+void DedicatedWorkerHost::BindWebOTPServiceReceiver(
+    mojo::PendingReceiver<blink::mojom::WebOTPService> receiver) {
   RenderFrameHostImpl* ancestor_render_frame_host =
       RenderFrameHostImpl::FromID(ancestor_render_frame_host_id_);
   if (!ancestor_render_frame_host) {
@@ -446,7 +446,7 @@
     return;
   }
 
-  ancestor_render_frame_host->BindSmsReceiverReceiver(std::move(receiver));
+  ancestor_render_frame_host->BindWebOTPServiceReceiver(std::move(receiver));
 }
 
 #if !defined(OS_ANDROID)
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
index fb5e88f..0dd58d79 100644
--- a/content/browser/worker_host/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -22,7 +22,7 @@
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/idle/idle_manager.mojom-forward.h"
 #include "third_party/blink/public/mojom/loader/content_security_notifier.mojom.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-forward.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom-forward.h"
 #include "third_party/blink/public/mojom/usb/web_usb_service.mojom-forward.h"
 #include "third_party/blink/public/mojom/wake_lock/wake_lock.mojom-forward.h"
 #include "third_party/blink/public/mojom/websockets/websocket_connector.mojom-forward.h"
@@ -73,8 +73,8 @@
       mojo::PendingReceiver<blink::mojom::IdleManager> receiver);
   void CreateNestedDedicatedWorker(
       mojo::PendingReceiver<blink::mojom::DedicatedWorkerHostFactory> receiver);
-  void BindSmsReceiverReceiver(
-      mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver);
+  void BindWebOTPServiceReceiver(
+      mojo::PendingReceiver<blink::mojom::WebOTPService> receiver);
   void CreateWebUsbService(
       mojo::PendingReceiver<blink::mojom::WebUsbService> receiver);
   void CreateWebSocketConnector(
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 9d93c4f..6cc4621 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -272,7 +272,7 @@
      features::kSignedExchangeSubresourcePrefetch},
     {wf::EnableIdleDetection, features::kIdleDetection, kSetOnlyIfOverridden},
     {wf::EnableSkipTouchEventFilter, blink::features::kSkipTouchEventFilter},
-    {wf::EnableSmsReceiver, features::kSmsReceiver, kSetOnlyIfOverridden},
+    {wf::EnableWebOTP, features::kWebOTP, kSetOnlyIfOverridden},
     {wf::EnableClickPointerEvent, features::kClickPointerEvent},
     {wf::EnableConsolidatedMovementXY, features::kConsolidatedMovementXY},
     {wf::EnableCooperativeScheduling, features::kCooperativeScheduling},
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 238f40e..62f1df4d 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -65,7 +65,6 @@
 #include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom.h"
 #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
 #include "third_party/blink/public/web/web_frame_owner_properties.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -114,9 +113,6 @@
                           blink::mojom::PolicyDisposition::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::FrameVisibility,
                           blink::mojom::FrameVisibility::kMaxValue)
-IPC_ENUM_TRAITS_MIN_MAX_VALUE(blink::FrameOcclusionState,
-                              blink::FrameOcclusionState::kUnknown,
-                              blink::FrameOcclusionState::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::WebFeature,
                           blink::mojom::WebFeature::kMaxValue)
 IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::InsecureRequestPolicy,
@@ -211,16 +207,6 @@
   IPC_STRUCT_TRAITS_MEMBER(disallow_document_access)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(blink::ViewportIntersectionState)
-  IPC_STRUCT_TRAITS_MEMBER(viewport_intersection)
-  IPC_STRUCT_TRAITS_MEMBER(main_frame_intersection)
-  IPC_STRUCT_TRAITS_MEMBER(compositor_visible_rect)
-  IPC_STRUCT_TRAITS_MEMBER(occlusion_state)
-  IPC_STRUCT_TRAITS_MEMBER(main_frame_viewport_size)
-  IPC_STRUCT_TRAITS_MEMBER(main_frame_scroll_offset)
-  IPC_STRUCT_TRAITS_MEMBER(main_frame_transform)
-IPC_STRUCT_TRAITS_END()
-
 IPC_STRUCT_TRAITS_BEGIN(content::FrameNavigateParams)
   IPC_STRUCT_TRAITS_MEMBER(nav_entry_id)
   IPC_STRUCT_TRAITS_MEMBER(item_sequence_number)
@@ -574,12 +560,6 @@
 IPC_MESSAGE_ROUTED1(FrameHostMsg_SynchronizeVisualProperties,
                     blink::FrameVisualProperties)
 
-// Sent by a parent frame to notify its child about the state of the child's
-// intersection with the parent's viewport, primarily for use by the
-// IntersectionObserver API.
-IPC_MESSAGE_ROUTED1(FrameHostMsg_UpdateViewportIntersection,
-                    blink::ViewportIntersectionState /* intersection_state */)
-
 // Used to tell the parent that the user right clicked on an area of the
 // content area, and a context menu should be shown for it. The params
 // object contains information about the node(s) that were selected when the
diff --git a/content/common/widget_messages.h b/content/common/widget_messages.h
index 1b326cea6..44f948c 100644
--- a/content/common/widget_messages.h
+++ b/content/common/widget_messages.h
@@ -16,7 +16,7 @@
 #include "ipc/ipc_message_macros.h"
 #include "third_party/blink/public/common/widget/visual_properties.h"
 #include "third_party/blink/public/mojom/page/record_content_to_visible_time_request.mojom-forward.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
+#include "third_party/blink/public/platform/web_float_rect.h"
 #include "ui/base/ime/text_input_action.h"
 #include "ui/base/ime/text_input_mode.h"
 #include "ui/base/ui_base_types.h"
@@ -28,11 +28,6 @@
 
 #define IPC_MESSAGE_START WidgetMsgStart
 
-IPC_STRUCT_TRAITS_BEGIN(blink::WebSize)
-  IPC_STRUCT_TRAITS_MEMBER(width)
-  IPC_STRUCT_TRAITS_MEMBER(height)
-IPC_STRUCT_TRAITS_END()
-
 //
 // Browser -> Renderer Messages.
 //
@@ -53,12 +48,6 @@
 // are in progress.
 IPC_MESSAGE_ROUTED0(WidgetMsg_SetBounds_ACK)
 
-// Sent by a parent frame to notify its child about the state of the child's
-// intersection with the parent's viewport, primarily for use by the
-// IntersectionObserver API. Also see FrameHostMsg_UpdateViewportIntersection.
-IPC_MESSAGE_ROUTED1(WidgetMsg_SetViewportIntersection,
-                    blink::ViewportIntersectionState /* intersection_state */)
-
 //
 // Renderer -> Browser Messages.
 //
diff --git a/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectInjector.java b/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectInjector.java
index d54b7171..42016f5 100644
--- a/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectInjector.java
+++ b/content/public/android/java/src/org/chromium/content/browser/remoteobjects/RemoteObjectInjector.java
@@ -71,6 +71,18 @@
         }
     }
 
+    @Override
+    public void renderFrameDeleted(int renderProcessId, int renderFrameId) {
+        WebContents webContents = mWebContents.get();
+        if (webContents == null) return;
+
+        RenderFrameHost frameHost =
+                webContents.getRenderFrameHostFromId(renderProcessId, renderFrameId);
+        if (frameHost == null) return;
+
+        mRemoteObjectGatewayHelpers.remove(frameHost);
+    }
+
     public void addInterface(
             Object object, String name, Class<? extends Annotation> requiredAnnotation) {
         WebContents webContents = mWebContents.get();
diff --git a/content/public/android/java/src/org/chromium/content/browser/sms/SmsUserConsentReceiver.java b/content/public/android/java/src/org/chromium/content/browser/sms/SmsUserConsentReceiver.java
index de9d71d..74c110e97 100644
--- a/content/public/android/java/src/org/chromium/content/browser/sms/SmsUserConsentReceiver.java
+++ b/content/public/android/java/src/org/chromium/content/browser/sms/SmsUserConsentReceiver.java
@@ -38,14 +38,14 @@
     private final long mSmsProviderAndroid;
     private boolean mDestroyed;
     private Wrappers.SmsRetrieverClientWrapper mClient;
-    private Wrappers.SmsReceiverContext mContext;
+    private Wrappers.WebOTPServiceContext mContext;
     private WindowAndroid mWindowAndroid;
 
     private SmsUserConsentReceiver(long smsProviderAndroid) {
         mDestroyed = false;
         mSmsProviderAndroid = smsProviderAndroid;
 
-        mContext = new Wrappers.SmsReceiverContext(ContextUtils.getApplicationContext());
+        mContext = new Wrappers.WebOTPServiceContext(ContextUtils.getApplicationContext());
 
         // A broadcast receiver is registered upon the creation of this class
         // which happens when the SMS Retriever API is used for the first time
diff --git a/content/public/android/java/src/org/chromium/content/browser/sms/SmsVerificationReceiver.java b/content/public/android/java/src/org/chromium/content/browser/sms/SmsVerificationReceiver.java
index 4bb9410..b94ae3d0 100644
--- a/content/public/android/java/src/org/chromium/content/browser/sms/SmsVerificationReceiver.java
+++ b/content/public/android/java/src/org/chromium/content/browser/sms/SmsVerificationReceiver.java
@@ -35,13 +35,13 @@
     private final long mSmsProviderAndroid;
     private boolean mDestroyed;
     private Wrappers.SmsRetrieverClientWrapper mClient;
-    private Wrappers.SmsReceiverContext mContext;
+    private Wrappers.WebOTPServiceContext mContext;
 
     private SmsVerificationReceiver(long smsProviderAndroid) {
         mDestroyed = false;
         mSmsProviderAndroid = smsProviderAndroid;
 
-        mContext = new Wrappers.SmsReceiverContext(ContextUtils.getApplicationContext());
+        mContext = new Wrappers.WebOTPServiceContext(ContextUtils.getApplicationContext());
 
         // A broadcast receiver is registered upon the creation of this class
         // which happens when the SMS Retriever API is used for the first time
diff --git a/content/public/android/java/src/org/chromium/content/browser/sms/Wrappers.java b/content/public/android/java/src/org/chromium/content/browser/sms/Wrappers.java
index 94027123..4aa8947 100644
--- a/content/public/android/java/src/org/chromium/content/browser/sms/Wrappers.java
+++ b/content/public/android/java/src/org/chromium/content/browser/sms/Wrappers.java
@@ -22,17 +22,17 @@
      */
     static class SmsRetrieverClientWrapper {
         private final SmsRetrieverClient mSmsRetrieverClient;
-        private SmsReceiverContext mContext;
+        private WebOTPServiceContext mContext;
 
         public SmsRetrieverClientWrapper(SmsRetrieverClient smsRetrieverClient) {
             mSmsRetrieverClient = smsRetrieverClient;
         }
 
-        public void setContext(SmsReceiverContext context) {
+        public void setContext(WebOTPServiceContext context) {
             mContext = context;
         }
 
-        public SmsReceiverContext getContext() {
+        public WebOTPServiceContext getContext() {
             return mContext;
         }
 
@@ -49,10 +49,10 @@
      * Extends android.content.ContextWrapper to store and retrieve the
      * registered BroadcastReceiver.
      */
-    static class SmsReceiverContext extends ContextWrapper {
+    static class WebOTPServiceContext extends ContextWrapper {
         private BroadcastReceiver mReceiver;
 
-        public SmsReceiverContext(Context context) {
+        public WebOTPServiceContext(Context context) {
             super(context);
         }
 
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
index 7ec66ba..d3429de 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
@@ -73,6 +73,14 @@
 
     @Override
     @CalledByNative
+    public void renderFrameDeleted(int renderProcessId, int renderFrameId) {
+        for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
+            mObserversIterator.next().renderFrameDeleted(renderProcessId, renderFrameId);
+        }
+    }
+
+    @Override
+    @CalledByNative
     public void renderViewReady() {
         for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
             mObserversIterator.next().renderViewReady();
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
index ad3c9ed..4c96122 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
@@ -39,6 +39,15 @@
     public void renderFrameCreated(int renderProcessId, int renderFrameId) {}
 
     /**
+     * Called when a RenderFrame for renderFrameHost is deleted in the
+     * renderer process.
+     * To avoid creating a RenderFrameHost object without necessity, only process id and frame id
+     * are passed. Call WebContents#getRenderFrameHostFromId() to get the RenderFrameHostImpl object
+     * if needed.
+     */
+    public void renderFrameDeleted(int renderProcessId, int renderFrameId) {}
+
+    /**
      * Called when the RenderView of the current RenderViewHost is ready, e.g. because we recreated
      * it after a crash.
      */
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/sms/SmsUserConsentFakes.java b/content/public/android/javatests/src/org/chromium/content/browser/sms/SmsUserConsentFakes.java
index e706835..f707c544 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/sms/SmsUserConsentFakes.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/sms/SmsUserConsentFakes.java
@@ -24,7 +24,7 @@
 @JNINamespace("content")
 @JNIAdditionalImport(Wrappers.class)
 class SmsUserConsentFakes {
-    private static final String TAG = "SmsReceiver";
+    private static final String TAG = "WebOTPService";
 
     /**
      * Fakes com.google.android.gms.auth.api.phone.SmsRetrieverClient.
@@ -42,7 +42,7 @@
 
         @CalledByNative("FakeSmsUserConsentRetrieverClient")
         private Task<Void> triggerUserConsentSms(String sms) {
-            Wrappers.SmsReceiverContext context = super.getContext();
+            Wrappers.WebOTPServiceContext context = super.getContext();
             if (context == null) {
                 Log.v(TAG,
                         "FakeSmsUserConsentRetrieverClient.triggerUserConsentSms failed: "
@@ -69,7 +69,7 @@
 
         @CalledByNative("FakeSmsUserConsentRetrieverClient")
         private Task<Void> triggerTimeout() {
-            Wrappers.SmsReceiverContext context = super.getContext();
+            Wrappers.WebOTPServiceContext context = super.getContext();
             if (context == null) {
                 Log.v(TAG,
                         "FakeSmsUserConsentRetrieverClient.triggerTimeout failed: "
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/sms/SmsVerificationFakes.java b/content/public/android/javatests/src/org/chromium/content/browser/sms/SmsVerificationFakes.java
index 00093205..e9aab43 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/sms/SmsVerificationFakes.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/sms/SmsVerificationFakes.java
@@ -22,7 +22,7 @@
 @JNINamespace("content")
 @JNIAdditionalImport(Wrappers.class)
 class SmsVerificationFakes {
-    private static final String TAG = "SmsReceiver";
+    private static final String TAG = "WebOTPService";
 
     /**
      * Fakes com.google.android.gms.auth.api.phone.SmsRetrieverClient.
@@ -40,7 +40,7 @@
 
         @CalledByNative("FakeSmsRetrieverClient")
         private Task<Void> triggerSmsVerificationSms(String sms) {
-            Wrappers.SmsReceiverContext context = super.getContext();
+            Wrappers.WebOTPServiceContext context = super.getContext();
             if (context == null) {
                 Log.v(TAG,
                         "FakeSmsRetrieverClient.triggerSmsVerificationSms failed: "
@@ -61,7 +61,7 @@
 
         @CalledByNative("FakeSmsRetrieverClient")
         private Task<Void> triggerTimeout() {
-            Wrappers.SmsReceiverContext context = super.getContext();
+            Wrappers.WebOTPServiceContext context = super.getContext();
             if (context == null) {
                 Log.v(TAG, "FakeSmsRetrieverClient.triggerTimeout failed: no context was set");
                 return Tasks.forResult(null);
@@ -87,7 +87,7 @@
     }
 
     /**
-     * Sets SmsRetrieverClient to SmsReceiver to allow faking SMSes from android
+     * Sets SmsRetrieverClient to WebOTPService to allow faking SMSes from android
      * client.
      **/
     @CalledByNative
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 8d9d112..0d0915e 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -637,7 +637,7 @@
       blink::mojom::HeavyAdResolutionStatus resolution,
       blink::mojom::HeavyAdReason reason) = 0;
 
-  // Returns whether a document uses WebOTP. Returns true if an SmsService is
+  // Returns whether a document uses WebOTP. Returns true if a WebOTPService is
   // created on the document.
   virtual bool DocumentUsedWebOTP() = 0;
 
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 4e3bafe..0af82536 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -582,12 +582,6 @@
     "SendBeaconThrowForBlobWithNonSimpleType",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-// If enabled, ServiceWorkerContextCore lives on the UI thread rather than the
-// IO thread.
-// https://crbug.com/824858
-const base::Feature kServiceWorkerOnUI{"ServiceWorkerOnUI",
-                                       base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Service worker based payment apps as defined by w3c here:
 // https://w3c.github.io/webpayments-payment-apps-api/
 // TODO(rouslan): Remove this.
@@ -642,11 +636,10 @@
 const base::Feature kSignedHTTPExchangePingValidity{
     "SignedHTTPExchangePingValidity", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// This is intended as a kill switch for the SMS Receiver feature. To enable
+// This is intended as a kill switch for the WebOTP Service feature. To enable
 // this feature, the experimental web platform features flag should be set,
 // or the site should obtain an Origin Trial token.
-const base::Feature kSmsReceiver{"SmsReceiver",
-                                 base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kWebOTP{"WebOTP", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls whether Site Isolation protects against spoofing of origin in
 // mojom::FileSystemManager::Open IPC from compromised renderer processes.  See
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index adfc593..dc467f8 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -123,7 +123,6 @@
 CONTENT_EXPORT extern const base::Feature kSecurePaymentConfirmationDebug;
 CONTENT_EXPORT extern const base::Feature
     kSendBeaconThrowForBlobWithNonSimpleType;
-CONTENT_EXPORT extern const base::Feature kServiceWorkerOnUI;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerPaymentApps;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerPrefersUnusedProcess;
 CONTENT_EXPORT extern const base::Feature
@@ -140,7 +139,7 @@
     kSiteIsolationEnforcementForFileSystemApi;
 CONTENT_EXPORT extern const base::Feature
     kSkipEarlyCommitPendingForCrashedFrame;
-CONTENT_EXPORT extern const base::Feature kSmsReceiver;
+CONTENT_EXPORT extern const base::Feature kWebOTP;
 CONTENT_EXPORT extern const base::Feature kSpareRendererForSitePerProcess;
 CONTENT_EXPORT extern const base::Feature kStoragePressureUI;
 CONTENT_EXPORT extern const base::Feature kStorageServiceOutOfProcess;
diff --git a/content/public/renderer/pepper_plugin_instance.h b/content/public/renderer/pepper_plugin_instance.h
index d346ac65..c1dc7df 100644
--- a/content/public/renderer/pepper_plugin_instance.h
+++ b/content/public/renderer/pepper_plugin_instance.h
@@ -34,7 +34,6 @@
 namespace ppapi {
 class PpapiPermissions;
 class VarTracker;
-struct URLRequestInfoData;
 }
 
 namespace IPC {
@@ -95,10 +94,6 @@
 
   virtual bool IsRectTopmost(const gfx::Rect& rect) = 0;
 
-  virtual int32_t Navigate(const ppapi::URLRequestInfoData& request,
-                           const char* target,
-                           bool from_user_action) = 0;
-
   // Creates a pending PepperFileRefRendererHost. Returns 0 on failure.
   virtual int MakePendingFileRefRendererHost(const base::FilePath& path) = 0;
 
diff --git a/content/public/test/fake_frame_widget.cc b/content/public/test/fake_frame_widget.cc
index 969f89a..2bc8c1a 100644
--- a/content/public/test/fake_frame_widget.cc
+++ b/content/public/test/fake_frame_widget.cc
@@ -8,7 +8,8 @@
 
 FakeFrameWidget::FakeFrameWidget(
     mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget> frame_widget)
-    : receiver_(this, std::move(frame_widget)) {}
+    : receiver_(this, std::move(frame_widget)),
+      intersection_state_(blink::mojom::ViewportIntersectionState::New()) {}
 
 FakeFrameWidget::~FakeFrameWidget() = default;
 
@@ -35,4 +36,14 @@
   active_ = active;
 }
 
+const blink::mojom::ViewportIntersectionStatePtr&
+FakeFrameWidget::GetIntersectionState() const {
+  return intersection_state_;
+}
+
+void FakeFrameWidget::SetViewportIntersection(
+    blink::mojom::ViewportIntersectionStatePtr intersection_state) {
+  intersection_state_ = std::move(intersection_state);
+}
+
 }  // namespace content
diff --git a/content/public/test/fake_frame_widget.h b/content/public/test/fake_frame_widget.h
index 8267663..cb1ac169 100644
--- a/content/public/test/fake_frame_widget.h
+++ b/content/public/test/fake_frame_widget.h
@@ -9,6 +9,7 @@
 #include "build/build_config.h"
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom.h"
 #include "third_party/blink/public/mojom/page/drag.mojom.h"
 #include "third_party/blink/public/mojom/page/widget.mojom.h"
 #include "ui/base/ui_base_types.h"
@@ -29,6 +30,9 @@
   void operator=(const FakeFrameWidget&) = delete;
 
   base::i18n::TextDirection GetTextDirection() const;
+  const blink::mojom::ViewportIntersectionStatePtr& GetIntersectionState()
+      const;
+
   base::Optional<bool> GetActive() const;
 
  private:
@@ -70,11 +74,14 @@
   }
   void BindInputTargetClient(
       mojo::PendingReceiver<viz::mojom::InputTargetClient> receiver) override {}
+  void SetViewportIntersection(
+      blink::mojom::ViewportIntersectionStatePtr intersection_state) override;
 
   mojo::AssociatedReceiver<blink::mojom::FrameWidget> receiver_;
   base::i18n::TextDirection text_direction_ =
       base::i18n::TextDirection::UNKNOWN_DIRECTION;
   base::Optional<bool> active_;
+  blink::mojom::ViewportIntersectionStatePtr intersection_state_;
 };
 
 }  // namespace content
diff --git a/content/public/test/fake_pepper_plugin_instance.cc b/content/public/test/fake_pepper_plugin_instance.cc
index 49e35c4..3e4ccc7 100644
--- a/content/public/test/fake_pepper_plugin_instance.cc
+++ b/content/public/test/fake_pepper_plugin_instance.cc
@@ -59,13 +59,6 @@
   return false;
 }
 
-int32_t FakePepperPluginInstance::Navigate(
-    const ppapi::URLRequestInfoData& request,
-    const char* target,
-    bool from_user_action) {
-  return PP_ERROR_FAILED;
-}
-
 int FakePepperPluginInstance::MakePendingFileRefRendererHost(
     const base::FilePath& path) {
   return 0;
diff --git a/content/public/test/fake_pepper_plugin_instance.h b/content/public/test/fake_pepper_plugin_instance.h
index c75baa28..84ac65af 100644
--- a/content/public/test/fake_pepper_plugin_instance.h
+++ b/content/public/test/fake_pepper_plugin_instance.h
@@ -35,9 +35,6 @@
   void SetAlwaysOnTop(bool on_top) override;
   bool IsFullPagePlugin() override;
   bool IsRectTopmost(const gfx::Rect& rect) override;
-  int32_t Navigate(const ppapi::URLRequestInfoData& request,
-                   const char* target,
-                   bool from_user_action) override;
   int MakePendingFileRefRendererHost(const base::FilePath& path) override;
   void SetEmbedProperty(PP_Var key, PP_Var value) override;
   void SetSelectedText(const base::string16& selected_text) override;
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index 0a644cb..837ff07 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -639,7 +639,6 @@
     case ax::mojom::Event::kDocumentTitleChanged:
     case ax::mojom::Event::kExpandedChanged:
     case ax::mojom::Event::kHide:
-    case ax::mojom::Event::kInvalidStatusChanged:
     case ax::mojom::Event::kLayoutComplete:
     case ax::mojom::Event::kLocationChanged:
     case ax::mojom::Event::kMenuListValueChanged:
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index f22f1fe..421c3de 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -531,7 +531,6 @@
       ax::mojom::Event::kDocumentTitleChanged,
       ax::mojom::Event::kExpandedChanged,
       ax::mojom::Event::kHide,
-      ax::mojom::Event::kInvalidStatusChanged,
       ax::mojom::Event::kLayoutComplete,
       ax::mojom::Event::kLocationChanged,
       ax::mojom::Event::kMenuListValueChanged,
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 620abbf0..69257ed 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -3007,53 +3007,6 @@
   return container_->IsRectTopmost(rect);
 }
 
-int32_t PepperPluginInstanceImpl::Navigate(
-    const ppapi::URLRequestInfoData& request,
-    const char* target,
-    bool from_user_action) {
-  if (!container_)
-    return PP_ERROR_FAILED;
-
-  WebDocument document = container_->GetDocument();
-  WebLocalFrame* frame = document.GetFrame();
-  if (!frame)
-    return PP_ERROR_FAILED;
-
-  ppapi::URLRequestInfoData completed_request = request;
-
-  WebURLRequest web_request;
-  if (!CreateWebURLRequest(
-          pp_instance_, &completed_request, frame, &web_request)) {
-    return PP_ERROR_FAILED;
-  }
-  web_request.SetSiteForCookies(document.SiteForCookies());
-  if (HasTransientUserActivation())
-    web_request.SetHasUserGesture(true);
-
-  GURL gurl(web_request.Url());
-  if (gurl.SchemeIs(url::kJavaScriptScheme)) {
-    // In imitation of the NPAPI implementation, only |target_frame == frame| is
-    // allowed for security reasons.
-    WebFrame* target_frame =
-        frame->FindFrameByName(WebString::FromUTF8(target));
-    if (target_frame != frame)
-      return PP_ERROR_NOACCESS;
-
-    // TODO(viettrungluu): NPAPI sends the result back to the plugin -- do we
-    // need that?
-    WebString result = container_->ExecuteScriptURL(gurl, false);
-    return result.IsNull() ? PP_ERROR_FAILED : PP_OK;
-  }
-
-  // Only GETs and POSTs are supported.
-  if (web_request.HttpMethod() != "GET" && web_request.HttpMethod() != "POST")
-    return PP_ERROR_BADARGUMENT;
-
-  WebString target_str = WebString::FromUTF8(target);
-  container_->LoadFrameRequest(web_request, target_str);
-  return PP_OK;
-}
-
 int PepperPluginInstanceImpl::MakePendingFileRefRendererHost(
     const base::FilePath& path) {
   RendererPpapiHostImpl* host_impl = module_->renderer_ppapi_host();
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.h b/content/renderer/pepper/pepper_plugin_instance_impl.h
index 04715d77b..5b059fe 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.h
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.h
@@ -357,9 +357,6 @@
   void SetAlwaysOnTop(bool on_top) override;
   bool IsFullPagePlugin() override;
   bool IsRectTopmost(const gfx::Rect& rect) override;
-  int32_t Navigate(const ppapi::URLRequestInfoData& request,
-                   const char* target,
-                   bool from_user_action) override;
   int MakePendingFileRefRendererHost(const base::FilePath& path) override;
   void SetEmbedProperty(PP_Var key, PP_Var value) override;
   void SetSelectedText(const base::string16& selected_text) override;
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 5be706a..f9beadc 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -52,6 +52,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/loader/previews_state.h"
 #include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink.h"
 #include "third_party/blink/public/mojom/page/record_content_to_visible_time_request.mojom.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_string.h"
@@ -442,21 +443,11 @@
 
 TEST_F(RenderFrameImplTest, MainFrameIntersectionRecorded) {
   RenderFrameTestObserver observer(frame());
-  blink::WebRect viewport_intersection(0, 11, 200, 89);
-  blink::WebRect mainframe_intersection(0, 0, 200, 140);
-  blink::FrameOcclusionState occlusion_state =
-      blink::FrameOcclusionState::kUnknown;
-  gfx::Transform transform;
-  transform.Translate(100, 100);
-
-  WidgetMsg_SetViewportIntersection set_viewport_intersection_message(
-      0, {viewport_intersection, mainframe_intersection, blink::WebRect(),
-          occlusion_state, blink::WebSize(), gfx::Point(), transform});
-  frame_widget()->OnMessageReceived(set_viewport_intersection_message);
+  gfx::Rect mainframe_intersection(0, 0, 200, 140);
+  frame()->OnMainFrameIntersectionChanged(mainframe_intersection);
   // Setting a new frame intersection in a local frame triggers the render frame
   // observer call.
-  EXPECT_EQ(observer.last_intersection_rect(),
-            blink::WebRect(100, 100, 200, 140));
+  EXPECT_EQ(observer.last_intersection_rect(), blink::WebRect(0, 0, 200, 140));
 }
 
 // Used to annotate the source of an interface request.
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index d32fa82..ffe016a 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -692,20 +692,6 @@
   SynchronizeVisualProperties();
 }
 
-void RenderFrameProxy::UpdateRemoteViewportIntersection(
-    const blink::ViewportIntersectionState& intersection_state) {
-  DCHECK(ancestor_render_widget_);
-  // TODO(szager): compositor_viewport is propagated twice, via
-  // ViewportIntersectionState and also via FrameVisualProperties. It should
-  // only go through FrameVisualProperties.
-  if (pending_visual_properties_.compositor_viewport !=
-      gfx::Rect(intersection_state.compositor_visible_rect)) {
-    SynchronizeVisualProperties();
-  }
-  Send(new FrameHostMsg_UpdateViewportIntersection(routing_id_,
-                                                   intersection_state));
-}
-
 base::UnguessableToken RenderFrameProxy::GetDevToolsFrameToken() {
   return devtools_frame_token_;
 }
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index 318662d..2e1975b1 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -162,8 +162,6 @@
 
   int provisional_frame_routing_id() { return provisional_frame_routing_id_; }
 
-  void SynchronizeVisualProperties();
-
   const gfx::Rect& screen_space_rect() const {
     return pending_visual_properties_.screen_space_rect;
   }
@@ -193,8 +191,7 @@
       const base::Optional<blink::WebImpression>& impression) override;
   void FrameRectsChanged(const blink::WebRect& local_frame_rect,
                          const blink::WebRect& screen_space_rect) override;
-  void UpdateRemoteViewportIntersection(
-      const blink::ViewportIntersectionState& intersection_state) override;
+  void SynchronizeVisualProperties() override;
   base::UnguessableToken GetDevToolsFrameToken() override;
   void ZoomLevelChanged(double zoom_level) override;
   void UpdateCaptureSequenceNumber(uint32_t capture_sequence_number) override;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index bbfa033..d3679df 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -327,8 +327,6 @@
   IPC_BEGIN_MESSAGE_MAP(RenderWidget, message)
     IPC_MESSAGE_HANDLER(WidgetMsg_Close, OnClose)
     IPC_MESSAGE_HANDLER(WidgetMsg_SetBounds_ACK, OnRequestSetBoundsAck)
-    IPC_MESSAGE_HANDLER(WidgetMsg_SetViewportIntersection,
-                        OnSetViewportIntersection)
     IPC_MESSAGE_HANDLER(DragMsg_TargetDragEnter, OnDragTargetDragEnter)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
@@ -624,13 +622,6 @@
 #endif
 }
 
-void RenderWidget::OnSetViewportIntersection(
-    const blink::ViewportIntersectionState& intersection_state) {
-  if (auto* frame_widget = GetFrameWidget()) {
-    frame_widget->SetRemoteViewportIntersection(intersection_state);
-  }
-}
-
 void RenderWidget::OnDragTargetDragEnter(
     const std::vector<DropData::Metadata>& drop_meta_data,
     const gfx::PointF& client_point,
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 9bc819f..64be0d4e 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -45,7 +45,6 @@
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/common/widget/screen_info.h"
 #include "third_party/blink/public/mojom/page/record_content_to_visible_time_request.mojom-forward.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
 #include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/public/platform/web_text_input_info.h"
 #include "third_party/blink/public/web/web_page_popup.h"
@@ -298,8 +297,6 @@
   void OnClose();
   void OnRequestSetBoundsAck();
 
-  void OnSetViewportIntersection(
-      const blink::ViewportIntersectionState& intersection_state);
   void OnDragTargetDragEnter(
       const std::vector<DropData::Metadata>& drop_meta_data,
       const gfx::PointF& client_pt,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1033efb..c829929 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1945,8 +1945,8 @@
     "../browser/site_instance_impl_unittest.cc",
     "../browser/sms/sms_fetcher_impl_unittest.cc",
     "../browser/sms/sms_parser_unittest.cc",
-    "../browser/sms/sms_service_unittest.cc",
     "../browser/sms/user_consent_handler_unittest.cc",
+    "../browser/sms/webotp_service_unittest.cc",
     "../browser/speech/tts_controller_unittest.cc",
     "../browser/startup_task_runner_unittest.cc",
     "../browser/storage_partition_impl_map_unittest.cc",
diff --git a/content/web_test/renderer/web_frame_test_proxy.cc b/content/web_test/renderer/web_frame_test_proxy.cc
index 24b3e654..8b48994 100644
--- a/content/web_test/renderer/web_frame_test_proxy.cc
+++ b/content/web_test/renderer/web_frame_test_proxy.cc
@@ -579,9 +579,6 @@
     case ax::mojom::Event::kHover:
       event_name = "Hover";
       break;
-    case ax::mojom::Event::kInvalidStatusChanged:
-      event_name = "InvalidStatusChanged";
-      break;
     case ax::mojom::Event::kLayoutComplete:
       event_name = "LayoutComplete";
       break;
diff --git a/extensions/browser/api/automation_internal/DIR_METADATA b/extensions/browser/api/automation_internal/DIR_METADATA
new file mode 100644
index 0000000..e9a08cb
--- /dev/null
+++ b/extensions/browser/api/automation_internal/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "UI>Accessibility"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/automation_internal/OWNERS b/extensions/browser/api/automation_internal/OWNERS
index 292ee758..976b955 100644
--- a/extensions/browser/api/automation_internal/OWNERS
+++ b/extensions/browser/api/automation_internal/OWNERS
@@ -1,3 +1 @@
 file://ui/accessibility/OWNERS
-
-# COMPONENT: UI>Accessibility
diff --git a/extensions/browser/api/cast_channel/DIR_METADATA b/extensions/browser/api/cast_channel/DIR_METADATA
new file mode 100644
index 0000000..92a50ac
--- /dev/null
+++ b/extensions/browser/api/cast_channel/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Cast>API"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/cast_channel/OWNERS b/extensions/browser/api/cast_channel/OWNERS
index 1be33ff6..78c2174 100644
--- a/extensions/browser/api/cast_channel/OWNERS
+++ b/extensions/browser/api/cast_channel/OWNERS
@@ -1,3 +1 @@
 file://components/cast_channel/OWNERS
-
-# COMPONENT: Internals>Cast>API
diff --git a/extensions/browser/api/crash_report_private/DIR_METADATA b/extensions/browser/api/crash_report_private/DIR_METADATA
new file mode 100644
index 0000000..fe26aa57
--- /dev/null
+++ b/extensions/browser/api/crash_report_private/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Apps>SystemWebApps"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/crash_report_private/OWNERS b/extensions/browser/api/crash_report_private/OWNERS
index c7441c16..9fda0b9 100644
--- a/extensions/browser/api/crash_report_private/OWNERS
+++ b/extensions/browser/api/crash_report_private/OWNERS
@@ -1,4 +1,2 @@
 shend@chromium.org
 ortuno@chromium.org
-
-# COMPONENT: Platform>Apps>SystemWebApps
diff --git a/extensions/browser/api/feedback_private/DIR_METADATA b/extensions/browser/api/feedback_private/DIR_METADATA
new file mode 100644
index 0000000..eff915f
--- /dev/null
+++ b/extensions/browser/api/feedback_private/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Apps>Feedback"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/feedback_private/OWNERS b/extensions/browser/api/feedback_private/OWNERS
index fae7a0ce..220e699 100644
--- a/extensions/browser/api/feedback_private/OWNERS
+++ b/extensions/browser/api/feedback_private/OWNERS
@@ -1,3 +1 @@
 file://components/feedback/OWNERS
-
-# COMPONENT: Platform>Apps>Feedback
diff --git a/extensions/browser/api/guest_view/DIR_METADATA b/extensions/browser/api/guest_view/DIR_METADATA
new file mode 100644
index 0000000..7fb737e
--- /dev/null
+++ b/extensions/browser/api/guest_view/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Apps>BrowserTag"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/guest_view/OWNERS b/extensions/browser/api/guest_view/OWNERS
index 74d34105..4da9c54 100644
--- a/extensions/browser/api/guest_view/OWNERS
+++ b/extensions/browser/api/guest_view/OWNERS
@@ -1,3 +1 @@
 file://components/guest_view/OWNERS
-
-# COMPONENT: Platform>Apps>BrowserTag
diff --git a/extensions/browser/api/hid/DIR_METADATA b/extensions/browser/api/hid/DIR_METADATA
new file mode 100644
index 0000000..2a4ac20a
--- /dev/null
+++ b/extensions/browser/api/hid/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Extensions>API"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/hid/OWNERS b/extensions/browser/api/hid/OWNERS
index 4179744..b259d5a 100644
--- a/extensions/browser/api/hid/OWNERS
+++ b/extensions/browser/api/hid/OWNERS
@@ -1,5 +1,3 @@
 mattreynolds@chromium.org
 reillyg@chromium.org
 rockot@google.com
-
-# COMPONENT: Platform>Extensions>API
diff --git a/extensions/browser/api/idle/DIR_METADATA b/extensions/browser/api/idle/DIR_METADATA
new file mode 100644
index 0000000..2a4ac20a
--- /dev/null
+++ b/extensions/browser/api/idle/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Extensions>API"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/idle/OWNERS b/extensions/browser/api/idle/OWNERS
index d016606..859c2c6 100644
--- a/extensions/browser/api/idle/OWNERS
+++ b/extensions/browser/api/idle/OWNERS
@@ -1,3 +1 @@
 dcheng@chromium.org
-
-# COMPONENT: Platform>Extensions>API
diff --git a/extensions/browser/api/messaging/DIR_METADATA b/extensions/browser/api/messaging/DIR_METADATA
new file mode 100644
index 0000000..2a4ac20a
--- /dev/null
+++ b/extensions/browser/api/messaging/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Extensions>API"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/messaging/OWNERS b/extensions/browser/api/messaging/OWNERS
index 5325484..b67eda4 100644
--- a/extensions/browser/api/messaging/OWNERS
+++ b/extensions/browser/api/messaging/OWNERS
@@ -1,3 +1 @@
 sergeyu@chromium.org
-
-# COMPONENT: Platform>Extensions>API
diff --git a/extensions/browser/api/printer_provider/DIR_METADATA b/extensions/browser/api/printer_provider/DIR_METADATA
new file mode 100644
index 0000000..6d77bb2a
--- /dev/null
+++ b/extensions/browser/api/printer_provider/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Printing"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/printer_provider/OWNERS b/extensions/browser/api/printer_provider/OWNERS
index d038a71..0fa42c6a4 100644
--- a/extensions/browser/api/printer_provider/OWNERS
+++ b/extensions/browser/api/printer_provider/OWNERS
@@ -1,4 +1,2 @@
 tbarzic@chromium.org
 vitalybuka@chromium.org
-
-# COMPONENT: Internals>Printing
diff --git a/extensions/browser/api/printer_provider_internal/DIR_METADATA b/extensions/browser/api/printer_provider_internal/DIR_METADATA
new file mode 100644
index 0000000..6d77bb2a
--- /dev/null
+++ b/extensions/browser/api/printer_provider_internal/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Printing"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/printer_provider_internal/OWNERS b/extensions/browser/api/printer_provider_internal/OWNERS
index d038a71..0fa42c6a4 100644
--- a/extensions/browser/api/printer_provider_internal/OWNERS
+++ b/extensions/browser/api/printer_provider_internal/OWNERS
@@ -1,4 +1,2 @@
 tbarzic@chromium.org
 vitalybuka@chromium.org
-
-# COMPONENT: Internals>Printing
diff --git a/extensions/browser/api/serial/DIR_METADATA b/extensions/browser/api/serial/DIR_METADATA
new file mode 100644
index 0000000..2a4ac20a
--- /dev/null
+++ b/extensions/browser/api/serial/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Extensions>API"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/serial/OWNERS b/extensions/browser/api/serial/OWNERS
index 6279377..c21268b 100644
--- a/extensions/browser/api/serial/OWNERS
+++ b/extensions/browser/api/serial/OWNERS
@@ -1,4 +1,2 @@
 reillyg@chromium.org
 rockot@google.com
-
-# COMPONENT: Platform>Extensions>API
\ No newline at end of file
diff --git a/extensions/browser/api/socket/DIR_METADATA b/extensions/browser/api/socket/DIR_METADATA
new file mode 100644
index 0000000..2a4ac20a
--- /dev/null
+++ b/extensions/browser/api/socket/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Extensions>API"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/socket/OWNERS b/extensions/browser/api/socket/OWNERS
index 1308dff..9c3e19a 100644
--- a/extensions/browser/api/socket/OWNERS
+++ b/extensions/browser/api/socket/OWNERS
@@ -1,5 +1,3 @@
 # Username must start with r
 reillyg@chromium.org
 rockot@google.com
-
-# COMPONENT: Platform>Extensions>API
\ No newline at end of file
diff --git a/extensions/browser/api/usb/DIR_METADATA b/extensions/browser/api/usb/DIR_METADATA
new file mode 100644
index 0000000..2a4ac20a
--- /dev/null
+++ b/extensions/browser/api/usb/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Extensions>API"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/usb/OWNERS b/extensions/browser/api/usb/OWNERS
index ea94ee08..3bd510d 100644
--- a/extensions/browser/api/usb/OWNERS
+++ b/extensions/browser/api/usb/OWNERS
@@ -2,5 +2,3 @@
 # chrome/browser/extensions/OWNERS
 reillyg@chromium.org
 rockot@google.com
-
-# COMPONENT: Platform>Extensions>API
\ No newline at end of file
diff --git a/extensions/browser/api/virtual_keyboard/DIR_METADATA b/extensions/browser/api/virtual_keyboard/DIR_METADATA
new file mode 100644
index 0000000..f33c0cee
--- /dev/null
+++ b/extensions/browser/api/virtual_keyboard/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "UI>Input>VirtualKeyboard"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/virtual_keyboard/OWNERS b/extensions/browser/api/virtual_keyboard/OWNERS
index ca1e0f59..26ce678 100644
--- a/extensions/browser/api/virtual_keyboard/OWNERS
+++ b/extensions/browser/api/virtual_keyboard/OWNERS
@@ -1,4 +1,2 @@
 tbarzic@chromium.org
 file://ash/keyboard/OWNERS
-
-# COMPONENT: UI>Input>VirtualKeyboard
diff --git a/extensions/browser/api/virtual_keyboard_private/DIR_METADATA b/extensions/browser/api/virtual_keyboard_private/DIR_METADATA
new file mode 100644
index 0000000..f33c0cee
--- /dev/null
+++ b/extensions/browser/api/virtual_keyboard_private/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "UI>Input>VirtualKeyboard"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/virtual_keyboard_private/OWNERS b/extensions/browser/api/virtual_keyboard_private/OWNERS
index 94fbd75..a1be014b 100644
--- a/extensions/browser/api/virtual_keyboard_private/OWNERS
+++ b/extensions/browser/api/virtual_keyboard_private/OWNERS
@@ -1,3 +1 @@
 file://ash/keyboard/OWNERS
-
-# COMPONENT: UI>Input>VirtualKeyboard
diff --git a/extensions/browser/api/vpn_provider/DIR_METADATA b/extensions/browser/api/vpn_provider/DIR_METADATA
new file mode 100644
index 0000000..30845fe
--- /dev/null
+++ b/extensions/browser/api/vpn_provider/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Enterprise"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/vpn_provider/OWNERS b/extensions/browser/api/vpn_provider/OWNERS
index e1cacf4d..a913a05 100644
--- a/extensions/browser/api/vpn_provider/OWNERS
+++ b/extensions/browser/api/vpn_provider/OWNERS
@@ -1,4 +1,2 @@
 bartfab@chromium.org
 emaxx@chromium.org
-
-# COMPONENT: Enterprise
diff --git a/extensions/browser/api/web_request/DIR_METADATA b/extensions/browser/api/web_request/DIR_METADATA
new file mode 100644
index 0000000..2a4ac20a
--- /dev/null
+++ b/extensions/browser/api/web_request/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Platform>Extensions>API"
+}
\ No newline at end of file
diff --git a/extensions/browser/api/web_request/OWNERS b/extensions/browser/api/web_request/OWNERS
index 1a3dbd6..b91e860 100644
--- a/extensions/browser/api/web_request/OWNERS
+++ b/extensions/browser/api/web_request/OWNERS
@@ -4,5 +4,3 @@
 # WebRequest + Network Service glue.
 per-file web_request_proxying*=cduvall@chromium.org
 per-file web_request_proxying*=jam@chromium.org
-
-# COMPONENT: Platform>Extensions>API
diff --git a/extensions/renderer/api/automation/automation_api_util.cc b/extensions/renderer/api/automation/automation_api_util.cc
index e339fd28..9e93bfe 100644
--- a/extensions/renderer/api/automation/automation_api_util.cc
+++ b/extensions/renderer/api/automation/automation_api_util.cc
@@ -31,7 +31,6 @@
     case ax::mojom::Event::kDocumentSelectionChanged:
     case ax::mojom::Event::kDocumentTitleChanged:
     case ax::mojom::Event::kExpandedChanged:
-    case ax::mojom::Event::kInvalidStatusChanged:
     case ax::mojom::Event::kLoadComplete:
     case ax::mojom::Event::kLoadStart:
     case ax::mojom::Event::kRowCollapsed:
diff --git a/infra/config/console-header.star b/infra/config/console-header.star
index 03d67a3..15db7bc 100644
--- a/infra/config/console-header.star
+++ b/infra/config/console-header.star
@@ -91,8 +91,8 @@
             url = "https://rota-ng.appspot.com/legacy/sheriff_gpu.json",
         ),
         _oncall(
-            name = "Angle",
-            url = "https://rota-ng.appspot.com/legacy/sheriff_angle.json",
+            name = "ANGLE",
+            url = "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler",
         ),
         _oncall(
             name = "Perf",
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index eca1033b..af8e7a10 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -522,8 +522,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -1216,8 +1216,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -1525,8 +1525,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -1972,8 +1972,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -2316,8 +2316,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -2804,8 +2804,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -3108,8 +3108,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -3447,8 +3447,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -3930,8 +3930,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -4329,8 +4329,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -4757,8 +4757,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -5418,8 +5418,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -5789,8 +5789,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -6177,8 +6177,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -6512,8 +6512,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -7201,8 +7201,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -7575,8 +7575,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -7919,8 +7919,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -8288,8 +8288,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -8586,8 +8586,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -8935,8 +8935,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -9259,8 +9259,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -9623,8 +9623,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -9957,8 +9957,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -10366,8 +10366,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
@@ -10696,8 +10696,8 @@
       url: "https://rota-ng.appspot.com/legacy/sheriff_gpu.json"
     }
     oncalls {
-      name: "Angle"
-      url: "https://rota-ng.appspot.com/legacy/sheriff_angle.json"
+      name: "ANGLE"
+      url: "https://chrome-ops-rotation-proxy.appspot.com/current/grotation:angle-wrangler"
     }
     oncalls {
       name: "Perf"
diff --git a/ios/chrome/browser/ios_chrome_main_parts.mm b/ios/chrome/browser/ios_chrome_main_parts.mm
index cf898da3..f0820a4 100644
--- a/ios/chrome/browser/ios_chrome_main_parts.mm
+++ b/ios/chrome/browser/ios_chrome_main_parts.mm
@@ -275,6 +275,9 @@
 
 void IOSChromeMainParts::PostMainMessageLoopRun() {
   TranslateServiceIOS::Shutdown();
+#if BUILDFLAG(ENABLE_RLZ)
+  rlz::RLZTracker::CleanupRlz();
+#endif  // BUILDFLAG(ENABLE_RLZ)
   application_context_->StartTearDown();
 }
 
diff --git a/rlz/lib/financial_ping.cc b/rlz/lib/financial_ping.cc
index 2efeead..6157994 100644
--- a/rlz/lib/financial_ping.cc
+++ b/rlz/lib/financial_ping.cc
@@ -13,6 +13,7 @@
 #include "base/atomicops.h"
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -165,21 +166,6 @@
 }
 
 #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
-// The pointer to URLRequestContextGetter used by FinancialPing::PingServer().
-// It is atomic pointer because it can be accessed and modified by multiple
-// threads.
-AtomicWord g_URLLoaderFactory;
-
-bool FinancialPing::SetURLLoaderFactory(
-    network::mojom::URLLoaderFactory* factory) {
-  base::subtle::Release_Store(&g_URLLoaderFactory,
-                              reinterpret_cast<AtomicWord>(factory));
-  return true;
-}
-
-// Signal to stop the ShutdownCheck() task.
-AtomicWord g_cancelShutdownCheck;
-
 namespace {
 
 // A waitable event used to detect when either:
@@ -250,22 +236,32 @@
 }  // namespace
 
 #if defined(RLZ_NETWORK_IMPLEMENTATION_CHROME_NET)
-void ShutdownCheck(scoped_refptr<RefCountedWaitableEvent> event) {
-  if (base::subtle::Acquire_Load(&g_cancelShutdownCheck))
-    return;
 
-  if (!base::subtle::Acquire_Load(&g_URLLoaderFactory)) {
+// The signal for the current ping request. It can be used to cancel the request
+// in case of a shutdown.
+scoped_refptr<RefCountedWaitableEvent>& GetPingResultEvent() {
+  static base::NoDestructor<scoped_refptr<RefCountedWaitableEvent>>
+      g_pingResultEvent;
+  return *g_pingResultEvent;
+}
+
+// The pointer to URLRequestContextGetter used by FinancialPing::PingServer().
+// It is atomic pointer because it can be accessed and modified by multiple
+// threads.
+AtomicWord g_URLLoaderFactory;
+
+bool FinancialPing::SetURLLoaderFactory(
+    network::mojom::URLLoaderFactory* factory) {
+  base::subtle::Release_Store(&g_URLLoaderFactory,
+                              reinterpret_cast<AtomicWord>(factory));
+  scoped_refptr<RefCountedWaitableEvent> event = GetPingResultEvent();
+  if (!factory && event) {
     send_financial_ping_interrupted_for_test = true;
     event->SignalShutdown();
-    return;
   }
-  // How frequently the financial ping thread should check
-  // the shutdown condition?
-  const base::TimeDelta kInterval = base::TimeDelta::FromMilliseconds(500);
-  base::ThreadPool::PostDelayedTask(
-      FROM_HERE, {base::TaskPriority::BEST_EFFORT},
-      base::BindOnce(&ShutdownCheck, event), kInterval);
+  return true;
 }
+
 #endif
 
 void PingRlzServer(std::string url,
@@ -389,11 +385,8 @@
   // Use a waitable event to cause this function to block, to match the
   // wininet implementation.
   auto event = base::MakeRefCounted<RefCountedWaitableEvent>();
-
-  base::subtle::Release_Store(&g_cancelShutdownCheck, 0);
-
-  base::ThreadPool::PostTask(FROM_HERE, {base::TaskPriority::BEST_EFFORT},
-                             base::BindOnce(&ShutdownCheck, event));
+  scoped_refptr<RefCountedWaitableEvent>& event_ref = GetPingResultEvent();
+  event_ref = event;
 
   // PingRlzServer must be run in a separate sequence so that the TimedWait()
   // call below does not block the URL fetch response from being handled by
@@ -411,8 +404,7 @@
     is_signaled = event->TimedWait(base::TimeDelta::FromMinutes(5));
   }
 
-  base::subtle::Release_Store(&g_cancelShutdownCheck, 1);
-
+  event_ref.reset();
   if (!is_signaled)
     return PING_FAILURE;
 
diff --git a/services/device/DIR_METADATA b/services/device/DIR_METADATA
new file mode 100644
index 0000000..f4708701
--- /dev/null
+++ b/services/device/DIR_METADATA
@@ -0,0 +1,12 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Services>Device"
+}
+team_email: "device-dev@chromium.org"
\ No newline at end of file
diff --git a/services/device/OWNERS b/services/device/OWNERS
index 8401519..d78bf3c 100644
--- a/services/device/OWNERS
+++ b/services/device/OWNERS
@@ -1,8 +1,4 @@
-# TEAM: device-dev@chromium.org
-
 blundell@chromium.org
 mattreynolds@chromium.org
 reillyg@chromium.org
 rockot@google.com
-
-# COMPONENT: Internals>Services>Device
diff --git a/services/device/bluetooth/DIR_METADATA b/services/device/bluetooth/DIR_METADATA
new file mode 100644
index 0000000..4435d72
--- /dev/null
+++ b/services/device/bluetooth/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "IO>Bluetooth"
+}
\ No newline at end of file
diff --git a/services/device/bluetooth/OWNERS b/services/device/bluetooth/OWNERS
index c88c064..1df589a 100644
--- a/services/device/bluetooth/OWNERS
+++ b/services/device/bluetooth/OWNERS
@@ -1,3 +1,2 @@
 ortuno@chromium.org
-reillyg@chromium.org
-# COMPONENT: IO>Bluetooth
+reillyg@chromium.org
\ No newline at end of file
diff --git a/services/device/fingerprint/DIR_METADATA b/services/device/fingerprint/DIR_METADATA
new file mode 100644
index 0000000..378d0eb2
--- /dev/null
+++ b/services/device/fingerprint/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "UI>Shell>LockScreen"
+}
\ No newline at end of file
diff --git a/services/device/fingerprint/OWNERS b/services/device/fingerprint/OWNERS
index e8d012e..66371f04 100644
--- a/services/device/fingerprint/OWNERS
+++ b/services/device/fingerprint/OWNERS
@@ -1,3 +1,2 @@
 xiaoyinh@chromium.org
-sammiequon@chromium.org
-# COMPONENT: UI>Shell>LockScreen
+sammiequon@chromium.org
\ No newline at end of file
diff --git a/services/device/generic_sensor/DIR_METADATA b/services/device/generic_sensor/DIR_METADATA
new file mode 100644
index 0000000..395c6238
--- /dev/null
+++ b/services/device/generic_sensor/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>Sensor"
+}
\ No newline at end of file
diff --git a/services/device/generic_sensor/OWNERS b/services/device/generic_sensor/OWNERS
index 3fd6380..77aaae18 100644
--- a/services/device/generic_sensor/OWNERS
+++ b/services/device/generic_sensor/OWNERS
@@ -1,6 +1,3 @@
 juncai@chromium.org
 raphael.kubo.da.costa@intel.com
 timvolodine@chromium.org
-
-# COMPONENT: Blink>Sensor
-# TEAM: device-dev@chromium.org
diff --git a/services/device/geolocation/DIR_METADATA b/services/device/geolocation/DIR_METADATA
new file mode 100644
index 0000000..78b63ec
--- /dev/null
+++ b/services/device/geolocation/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>Geolocation"
+}
\ No newline at end of file
diff --git a/services/device/geolocation/OWNERS b/services/device/geolocation/OWNERS
index 2596b2d..a87509a 100644
--- a/services/device/geolocation/OWNERS
+++ b/services/device/geolocation/OWNERS
@@ -3,6 +3,3 @@
 # Original (legacy) owners.
 mcasas@chromium.org
 timvolodine@chromium.org
-
-# COMPONENT: Blink>Geolocation
-# TEAM: device-dev@chromium.org
diff --git a/services/device/nfc/DIR_METADATA b/services/device/nfc/DIR_METADATA
new file mode 100644
index 0000000..881462d7
--- /dev/null
+++ b/services/device/nfc/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>NFC"
+}
\ No newline at end of file
diff --git a/services/device/nfc/OWNERS b/services/device/nfc/OWNERS
index 18960d0..3ced0f2 100644
--- a/services/device/nfc/OWNERS
+++ b/services/device/nfc/OWNERS
@@ -1,6 +1,3 @@
 blundell@chromium.org
 
 file://third_party/blink/renderer/modules/nfc/OWNERS
-
-# COMPONENT: Blink>NFC
-# TEAM: device-dev@chromium.org
\ No newline at end of file
diff --git a/services/device/screen_orientation/DIR_METADATA b/services/device/screen_orientation/DIR_METADATA
new file mode 100644
index 0000000..2a304f0
--- /dev/null
+++ b/services/device/screen_orientation/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>ScreenOrientation"
+}
\ No newline at end of file
diff --git a/services/device/screen_orientation/OWNERS b/services/device/screen_orientation/OWNERS
index 60c92e4..88b3579 100644
--- a/services/device/screen_orientation/OWNERS
+++ b/services/device/screen_orientation/OWNERS
@@ -1,4 +1,2 @@
 blundell@chromium.org
 mlamouri@chromium.org
-
-# COMPONENT: Blink>ScreenOrientation
diff --git a/services/device/usb/DIR_METADATA b/services/device/usb/DIR_METADATA
new file mode 100644
index 0000000..600c85a1
--- /dev/null
+++ b/services/device/usb/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "IO>USB"
+}
\ No newline at end of file
diff --git a/services/device/usb/OWNERS b/services/device/usb/OWNERS
index ddbf0e3..a6c2f1b9 100644
--- a/services/device/usb/OWNERS
+++ b/services/device/usb/OWNERS
@@ -1,5 +1,2 @@
 pfeldman@chromium.org
 file://chrome/browser/usb/OWNERS
-
-# COMPONENT: IO>USB
-# TEAM: device-dev@chromium.org
diff --git a/services/device/vibration/DIR_METADATA b/services/device/vibration/DIR_METADATA
new file mode 100644
index 0000000..7ca00b2
--- /dev/null
+++ b/services/device/vibration/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Blink>Vibration"
+}
\ No newline at end of file
diff --git a/services/device/vibration/OWNERS b/services/device/vibration/OWNERS
index 0834219..56c64582 100644
--- a/services/device/vibration/OWNERS
+++ b/services/device/vibration/OWNERS
@@ -1,4 +1,2 @@
 blundell@chromium.org
 timvolodine@chromium.org
-
-# COMPONENT: Blink>Vibration
diff --git a/services/device/wake_lock/power_save_blocker/DIR_METADATA b/services/device/wake_lock/power_save_blocker/DIR_METADATA
new file mode 100644
index 0000000..fb07a25
--- /dev/null
+++ b/services/device/wake_lock/power_save_blocker/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Core"
+}
\ No newline at end of file
diff --git a/services/device/wake_lock/power_save_blocker/OWNERS b/services/device/wake_lock/power_save_blocker/OWNERS
index fdd793c..df746bc 100644
--- a/services/device/wake_lock/power_save_blocker/OWNERS
+++ b/services/device/wake_lock/power_save_blocker/OWNERS
@@ -1,4 +1,2 @@
 boliu@chromium.org
 hashimoto@chromium.org
-
-# COMPONENT: Internals>Core
diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc
index a6abb4e..0bb4198 100644
--- a/services/network/public/cpp/url_request_mojom_traits.cc
+++ b/services/network/public/cpp/url_request_mojom_traits.cc
@@ -270,12 +270,8 @@
     return false;
   }
   if (data.type() == network::mojom::DataElementType::kBytes) {
-    mojo_base::BigBufferView big_buffer;
-    if (!data.ReadBuf(&big_buffer))
+    if (!data.ReadBuf(&out->buf_))
       return false;
-    out->buf_.clear();
-    out->buf_.insert(out->buf_.end(), big_buffer.data().begin(),
-                     big_buffer.data().end());
   }
   out->type_ = data.type();
   out->data_pipe_getter_ = data.TakeDataPipeGetter<
diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h
index e458ff0..6995a9f 100644
--- a/services/network/public/cpp/url_request_mojom_traits.h
+++ b/services/network/public/cpp/url_request_mojom_traits.h
@@ -10,7 +10,6 @@
 
 #include "base/component_export.h"
 #include "base/memory/scoped_refptr.h"
-#include "mojo/public/cpp/base/big_buffer_mojom_traits.h"
 #include "mojo/public/cpp/base/file_mojom_traits.h"
 #include "mojo/public/cpp/base/file_path_mojom_traits.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
@@ -309,8 +308,8 @@
       const network::DataElement& element) {
     return element.type_;
   }
-  static mojo_base::BigBufferView buf(const network::DataElement& element) {
-    return mojo_base::BigBufferView(element.buf_);
+  static const std::vector<uint8_t>& buf(const network::DataElement& element) {
+    return element.buf_;
   }
   static const base::FilePath& path(const network::DataElement& element) {
     return element.path_;
diff --git a/services/network/public/mojom/url_loader.mojom b/services/network/public/mojom/url_loader.mojom
index 399a2e2..972ff20 100644
--- a/services/network/public/mojom/url_loader.mojom
+++ b/services/network/public/mojom/url_loader.mojom
@@ -423,7 +423,7 @@
   DataElementType type;
 
   // For kBytes.
-  mojo_base.mojom.BigBuffer buf;
+  array<uint8> buf;
   // For kFile
   mojo_base.mojom.FilePath path;
   // For kDataPipe
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index d311130f..5ef1182 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -131,31 +131,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-19.0.2",
-              "os": "Ubuntu-19.04",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests",
@@ -279,6 +254,31 @@
         "test": "standalone_angle_unittests",
         "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_unittests/",
         "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-19.0.2",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
       }
     ],
     "isolated_scripts": [
@@ -547,31 +547,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-418.56",
-              "os": "Ubuntu-19.04",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests",
@@ -695,6 +670,31 @@
         "test": "standalone_angle_unittests",
         "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_unittests/",
         "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-418.56",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
       }
     ],
     "isolated_scripts": [
@@ -1718,53 +1718,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-gpu-in-tests",
           "--use-angle=d3d11",
           "--disable-gpu-sandbox"
@@ -1869,6 +1822,31 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -1890,6 +1868,31 @@
         "test": "standalone_angle_unittests",
         "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_unittests/",
         "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
       }
     ],
     "isolated_scripts": [
@@ -2160,53 +2163,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-gpu-in-tests",
           "--use-angle=d3d11",
           "--disable-gpu-sandbox"
@@ -2311,6 +2267,31 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -2332,6 +2313,31 @@
         "test": "standalone_angle_unittests",
         "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_unittests/",
         "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
       }
     ],
     "isolated_scripts": [
@@ -8115,31 +8121,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-418.56",
-              "os": "Ubuntu-19.04",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests",
@@ -8263,6 +8244,31 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-418.56",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -8837,32 +8843,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:2184",
-              "os": "Ubuntu",
-              "pool": "chromium.tests.gpu.experimental"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -9134,6 +9114,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:2184",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -10741,31 +10747,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-19.0.2",
-              "os": "Ubuntu-19.04",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests",
@@ -10917,6 +10898,31 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-19.0.2",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -11523,32 +11529,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:3e92-19.0.8",
-              "os": "Ubuntu-19.04",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 14400,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests",
@@ -11706,6 +11686,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:3e92-19.0.8",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 14400,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -12330,31 +12336,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-418.56",
-              "os": "Ubuntu-19.04",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests",
@@ -12506,6 +12487,31 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-418.56",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -20371,6 +20377,166 @@
       },
       {
         "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "maps_pixel_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "os": "Mac-11.0",
+              "pool": "chromium.tests.mac-arm64"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "maps",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "maps_pixel_validating_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "os": "Mac-11.0",
+              "pool": "chromium.tests.mac-arm64"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "os": "Mac-11.0",
+              "pool": "chromium.tests.mac-arm64"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_validating_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "os": "Mac-11.0",
+              "pool": "chromium.tests.mac-arm64"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chrome-gpu-gold@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "../../tools/perf/run_benchmark",
           "--benchmarks=rendering.desktop"
         ],
@@ -21705,31 +21871,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-19.0.2",
-              "os": "Ubuntu-19.04",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-gpu-in-tests"
         ],
         "merge": {
@@ -21780,6 +21921,31 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-19.0.2",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -21940,31 +22106,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0",
-          "--no-xvfb"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-418.56",
-              "os": "Ubuntu-19.04",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-gpu-in-tests"
         ],
         "merge": {
@@ -22015,6 +22156,31 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-418.56",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -23522,53 +23688,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "gles2_conform_test",
         "test_id_prefix": "ninja://gpu/gles2_conform_support:gles2_conform_test/"
       },
@@ -23671,6 +23790,56 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -24032,53 +24201,6 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
         "test": "gles2_conform_test",
         "test_id_prefix": "ninja://gpu/gles2_conform_support:gles2_conform_test/"
       },
@@ -24181,6 +24303,56 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -24592,53 +24764,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests"
@@ -24809,6 +24934,31 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -24832,6 +24982,31 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -25339,55 +25514,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-26.20.100.7870|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-26.20.100.7870|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -25593,6 +25719,32 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-26.20.100.7870|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -25617,6 +25769,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-26.20.100.7870|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -26300,55 +26478,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu.experimental"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu.experimental"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -26554,6 +26683,32 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -26578,6 +26733,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -27259,30 +27440,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:699f",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -27488,6 +27645,32 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:699f",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -28011,53 +28194,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -28255,6 +28391,31 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -28278,6 +28439,31 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-24.20.100.6286|8086:5912-26.20.100.8141|8086:3e92-26.20.100.8141",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -28940,55 +29126,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:3e92-24.20.100.6286",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 14400,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "8086:3e92-24.20.100.6286",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 14400,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -29194,6 +29331,32 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:3e92-24.20.100.6286",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 14400,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -29218,6 +29381,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:3e92-24.20.100.6286",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 14400,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -29899,55 +30088,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:2184",
-              "os": "Windows-10",
-              "pool": "chromium.tests.gpu.experimental"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -30153,6 +30293,32 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:2184",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -30177,6 +30343,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:2184",
+              "os": "Windows-10",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -30770,53 +30962,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -31014,6 +31159,31 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -31037,6 +31207,31 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -32287,53 +32482,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-27.21.14.5148",
-              "os": "Windows-10-18363",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -32531,6 +32679,31 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -32554,6 +32727,31 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-27.21.14.5148",
+              "os": "Windows-10-18363",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -33213,31 +33411,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "standalone_angle_end2end_tests",
           "--gtest_filter=-*Vulkan_SwiftShader*",
           "--bot-mode"
@@ -33288,6 +33461,32 @@
         "test": "standalone_angle_unittests",
         "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_unittests/",
         "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
       }
     ],
     "isolated_scripts": [
@@ -33398,55 +33597,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--use-cmd-decoder=passthrough",
           "--use-gl=angle",
           "--use-gpu-in-tests"
@@ -33624,6 +33774,32 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -33648,6 +33824,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -34055,55 +34257,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "1002:6613",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -34309,6 +34462,32 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -34333,6 +34512,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:6613",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -34808,55 +35013,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-24.21.14.1195",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-24.21.14.1195",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -35062,6 +35218,32 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-24.21.14.1195",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -35086,6 +35268,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-24.21.14.1195",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -35655,55 +35863,6 @@
     "gtest_tests": [
       {
         "args": [
-          "--use-gpu-in-tests"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-24.21.14.1195",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_gles1_conformance_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_gles1_conformance_tests/"
-      },
-      {
-        "args": [
-          "--test-launcher-retry-limit=0"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "containment_type": "AUTO",
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3-24.21.14.1195",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "expiration": 21600,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_white_box_tests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_white_box_tests/"
-      },
-      {
-        "args": [
           "--enable-gpu",
           "--test-launcher-bot-mode",
           "--test-launcher-jobs=1",
@@ -35909,6 +36068,32 @@
       },
       {
         "args": [
+          "standalone_angle_gles1_conformance_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-24.21.14.1195",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_gles1_conformance_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_gles1_conformance_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
           "standalone_angle_unittests"
         ],
         "merge": {
@@ -35933,6 +36118,32 @@
         "use_isolated_scripts_api": true
       },
       {
+        "args": [
+          "standalone_angle_white_box_tests",
+          "--bot-mode"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-24.21.14.1195",
+              "os": "Windows-2008ServerR2-SP1",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "standalone_angle_white_box_tests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:standalone_angle_white_box_tests/",
+        "use_isolated_scripts_api": true
+      },
+      {
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter b/testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter
index 710cb0c..ed277c2 100644
--- a/testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter
+++ b/testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter
@@ -31,7 +31,6 @@
 -WebRtcDesktopCaptureBrowserTest.RunsScreenshareFromOneTabToAnother
 -WebRtcGetDisplayMediaBrowserTestWithPicker.GetDisplayMediaVideo
 -WebRtcGetDisplayMediaBrowserTestWithPicker.GetDisplayMediaVideoAndAudio
--WebViewAccessibilityTest.FocusActionAccessibility
 -BrowserViewTest.GetAccessibleTabModalDialogTree
 -IconLoaderBrowserTest.LoadGroup
 -PrintBrowserTest.PDFPluginNotKeyboardFocusable
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index bf00a45..d9ca39d 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -156,11 +156,6 @@
     "label": "//third_party/angle/src/tests:angle_end2end_tests",
     "type": "windowed_test_launcher",
   },
-  "angle_gles1_conformance_tests": {
-    "args": [],
-    "label": "//third_party/angle/src/tests:angle_gles1_conformance_tests",
-    "type": "windowed_test_launcher",
-  },
   "angle_perftests": {
     "args": [
       "angle_perftests",
@@ -182,11 +177,6 @@
     "label": "//third_party/angle/src/tests:angle_unittests",
     "type": "console_test_launcher",
   },
-  "angle_white_box_tests": {
-    "args": [],
-    "label": "//third_party/angle/src/tests:angle_white_box_tests",
-    "type": "windowed_test_launcher",
-  },
   "app_list_unittests": {
     "label": "//ash/app_list:app_list_unittests",
     "type": "windowed_test_launcher",
@@ -1546,11 +1536,21 @@
     "script": "//third_party/angle/scripts/run_gtest_angle_test.py",
     "type": "script",
   },
+  "standalone_angle_gles1_conformance_tests": {
+    "label": "//third_party/angle/src/tests:standalone_angle_gles1_conformance_tests",
+    "script": "//third_party/angle/scripts/run_gtest_angle_test.py",
+    "type": "script",
+  },
   "standalone_angle_unittests": {
     "label": "//third_party/angle/src/tests:standalone_angle_unittests",
     "script": "//third_party/angle/scripts/run_gtest_angle_test.py",
     "type": "script",
   },
+  "standalone_angle_white_box_tests": {
+    "label": "//third_party/angle/src/tests:standalone_angle_white_box_tests",
+    "script": "//third_party/angle/scripts/run_gtest_angle_test.py",
+    "type": "script",
+  },
   "storage_unittests": {
     "label": "//storage:storage_unittests",
     "type": "console_test_launcher",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index d73f28e..148c449 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -82,16 +82,6 @@
       'Linux FYI GPU TSAN Release',  # crbug.com/950542
     ],
   },
-  'angle_white_box_tests': {
-    'remove_from': [
-      # https://crbug.com/1035178
-      'Win10 FYI x64 Release (AMD RX 550)',
-      # https://crbug.com/1035461
-      'Linux FYI GPU TSAN Release',
-      # https://crbug.com/1094629
-      'Linux FYI Release (AMD R7 240)',
-    ],
-  },
   'blink_platform_unittests': {
     'modifications': {
       # TODO(crbug.com/1108121): Remove this filter
@@ -1593,11 +1583,6 @@
     },
   },
   'maps_pixel_passthrough_test': {
-    'remove_from': [
-      # TODO(https://crbug.com/1113308): Enable this once the GPU Gold instance
-      # is merged into the general Chrome one.
-      'Mac FYI arm64 Release (Apple DTK)',
-    ],
     'replacements': {
       # The V8 builders pass the V8 revision for ${got_revision}, so instead
       # use ${got_cr_revision}, which is only set on the V8 bots.
@@ -1628,9 +1613,6 @@
     # exception once there is enough capacity to run these tests.
     'remove_from': [
       'Android FYI Release (Pixel 2)',
-      # TODO(https://crbug.com/1113308): Enable this once the GPU Gold instance
-      # is merged into the general Chrome one.
-      'Mac FYI arm64 Release (Apple DTK)',
     ],
     'replacements': {
       # The V8 builders pass the V8 revision for ${got_revision}, so instead
@@ -2358,11 +2340,6 @@
     },
   },
   'pixel_skia_gold_passthrough_test': {
-    'remove_from': [
-      # TODO(https://crbug.com/1113308): Enable this once the GPU Gold instance
-      # is merged into the general Chrome one.
-      'Mac FYI arm64 Release (Apple DTK)',
-    ],
     'replacements': {
       # The V8 builders pass the V8 revision for ${got_revision}, so instead
       # use ${got_cr_revision}, which is only set on the V8 bots.
@@ -2389,11 +2366,6 @@
     },
   },
   'pixel_skia_gold_validating_test': {
-    'remove_from': [
-      # TODO(https://crbug.com/1113308): Enable this once the GPU Gold instance
-      # is merged into the general Chrome one.
-      'Mac FYI arm64 Release (Apple DTK)',
-    ],
     'replacements': {
       # The V8 builders pass the V8 revision for ${got_revision}, so instead
       # use ${got_cr_revision}, which is only set on the V8 bots.
@@ -2552,6 +2524,16 @@
       },
     },
   },
+  'standalone_angle_white_box_tests': {
+    'remove_from': [
+      # https://crbug.com/1035178
+      'Win10 FYI x64 Release (AMD RX 550)',
+      # https://crbug.com/1035461
+      'Linux FYI GPU TSAN Release',
+      # https://crbug.com/1094629
+      'Linux FYI Release (AMD R7 240)',
+    ],
+  },
   'storage_service_content_browsertests': {
     'modifications': {
       'Linux Tests (dbg)(1)': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 66e1f28..c1e515d 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1977,6 +1977,22 @@
       },
     },
 
+    'gpu_angle_gles1_conformance_gtests': {
+      'standalone_angle_gles1_conformance_tests': {
+        'android_args': [
+          '--shard-timeout=180',
+          '-v',
+        ],
+        'args': [
+          'standalone_angle_gles1_conformance_tests',
+        ],
+        'desktop_args': [
+          '--bot-mode',
+        ],
+        'use_isolated_scripts_api': True,
+      },
+    },
+
     # Actually uploads results to the perf dashboard
     'gpu_angle_perf_isolated_scripts': {
       'angle_perftests': {
@@ -2054,14 +2070,18 @@
       # TODO(ynovikov): the old generator script said the white box
       # tests are supposed to run everywhere angle_end2end_tests do, but
       # they actually ran only on Windows and Linux.
-      'angle_white_box_tests': {
-        'desktop_args': [
-          # ANGLE test retries deliberately disabled to prevent flakiness.
-          # http://crbug.com/669196
-          '--test-launcher-retry-limit=0'
+      'standalone_angle_white_box_tests': {
+        'android_args': [
+          '--shard-timeout=180',
+          '-v',
         ],
-        'linux_args': ['--no-xvfb'],
-        'should_retry_with_patch': False,
+        'args': [
+          'standalone_angle_white_box_tests',
+        ],
+        'desktop_args': [
+          '--bot-mode',
+        ],
+        'use_isolated_scripts_api': True,
       },
     },
 
@@ -2303,10 +2323,6 @@
     },
 
     'gpu_fyi_and_optional_win_specific_gtests': {
-      'angle_gles1_conformance_tests': {
-        'args': ['--use-gpu-in-tests'],
-        'linux_args': ['--no-xvfb'],
-      },
       'gles2_conform_d3d9_test': {
         'args': [
           '--use-gpu-in-tests',
@@ -5050,6 +5066,7 @@
 
     'gpu_angle_win_gtests': [
       'gpu_angle_end2end_gtests',
+      'gpu_angle_gles1_conformance_gtests',
       'gpu_angle_unit_gtests',
       'gpu_angle_white_box_gtests',
       'gpu_fyi_and_optional_win_specific_gtests',
@@ -5327,6 +5344,7 @@
 
     'gpu_fyi_win7_gtests': [
       'gpu_angle_end2end_gtests',
+      'gpu_angle_gles1_conformance_gtests',
       'gpu_angle_unit_gtests',
       'gpu_angle_white_box_gtests',
       'gpu_common_gtests_passthrough',
@@ -5348,6 +5366,7 @@
 
     'gpu_fyi_win_gtests': [
       'gpu_angle_end2end_gtests',
+      'gpu_angle_gles1_conformance_gtests',
       'gpu_angle_unit_gtests',
       'gpu_angle_white_box_gtests',
       'gpu_common_gtests_passthrough',
@@ -5384,6 +5403,7 @@
 
     'gpu_fyi_win_optional_gtests': [
       'gpu_angle_end2end_gtests',
+      'gpu_angle_gles1_conformance_gtests',
       'gpu_angle_white_box_gtests',
       'gpu_default_and_optional_win_specific_gtests',
       'gpu_fyi_and_optional_non_linux_gtests',
diff --git a/testing/scripts/check_static_initializers.py b/testing/scripts/check_static_initializers.py
index 1130fda..8366dbb 100755
--- a/testing/scripts/check_static_initializers.py
+++ b/testing/scripts/check_static_initializers.py
@@ -36,7 +36,6 @@
         'debugallocation_shim.cc',  # TODO(crbug.com/973552): Remove.
         'iostream.cpp',  # TODO(crbug.com/973554): Remove.
         'spinlock.cc',  # TODO(crbug.com/973556): Remove.
-        'int256.cc',  # TODO(crbug.com/537099): Remove.
         'rpc.pb.cc',  # TODO(crbug.com/537099): Remove.
     ],
     'nacl_helper_bootstrap': [],
@@ -235,4 +234,4 @@
       'run': main_run,
       'compile_targets': main_compile_targets,
   }
-  sys.exit(common.run_script(sys.argv[1:], funcs))
\ No newline at end of file
+  sys.exit(common.run_script(sys.argv[1:], funcs))
diff --git a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
index afc98270..e8e56f5f 100644
--- a/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
+++ b/third_party/blink/common/scheduler/web_scheduler_tracked_feature.cc
@@ -107,7 +107,7 @@
       return "SpeechSynthesis";
     case WebSchedulerTrackedFeature::kKeyboardLock:
       return "KeyboardLock";
-    case WebSchedulerTrackedFeature::kSmsService:
+    case WebSchedulerTrackedFeature::kWebOTPService:
       return "SMSService";
     case WebSchedulerTrackedFeature::kOutstandingNetworkRequestDirectSocket:
       return "outstanding network request (direct socket)";
@@ -159,7 +159,7 @@
          FeatureToBit(WebSchedulerTrackedFeature::kIdleManager) |
          FeatureToBit(WebSchedulerTrackedFeature::kPaymentManager) |
          FeatureToBit(WebSchedulerTrackedFeature::kKeyboardLock) |
-         FeatureToBit(WebSchedulerTrackedFeature::kSmsService);
+         FeatureToBit(WebSchedulerTrackedFeature::kWebOTPService);
 }
 
 }  // namespace scheduler
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index e10f1a6..6cd29fe 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -155,7 +155,6 @@
     "platform/task_type.h",
     "platform/url_conversion.h",
     "platform/user_metrics_action.h",
-    "platform/viewport_intersection_state.h",
     "platform/weak_wrapper_resource_load_info_notifier.h",
     "platform/web_audio_bus.h",
     "platform/web_audio_device.h",
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index c5b5421..b4ff6b5a 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -175,8 +175,8 @@
     "service_worker/service_worker_status_code.h",
     "service_worker/service_worker_type_converters.h",
     "service_worker/service_worker_types.h",
-    "sms/sms_receiver_destroyed_reason.h",
-    "sms/sms_receiver_outcome.h",
+    "sms/webotp_service_destroyed_reason.h",
+    "sms/webotp_service_outcome.h",
     "switches.h",
     "thread_safe_browser_interface_broker_proxy.h",
     "tokens/multi_token.h",
diff --git a/third_party/blink/public/common/associated_interfaces/associated_interface_provider.h b/third_party/blink/public/common/associated_interfaces/associated_interface_provider.h
index 4004984..86b3c44 100644
--- a/third_party/blink/public/common/associated_interfaces/associated_interface_provider.h
+++ b/third_party/blink/public/common/associated_interfaces/associated_interface_provider.h
@@ -65,7 +65,7 @@
 
   template <typename Interface>
   void GetInterface(mojo::AssociatedRemote<Interface>* remote) {
-    GetInterface(remote->BindNewEndpointAndPassReceiver());
+    GetInterface(remote->BindNewEndpointAndPassReceiver(task_runner_));
   }
 
   void OverrideBinderForTesting(
diff --git a/third_party/blink/public/common/privacy_budget/identifiable_surface.h b/third_party/blink/public/common/privacy_budget/identifiable_surface.h
index 4a4000ab..d7531ba 100644
--- a/third_party/blink/public/common/privacy_budget/identifiable_surface.h
+++ b/third_party/blink/public/common/privacy_budget/identifiable_surface.h
@@ -201,11 +201,19 @@
     // The input should be an instance of CanvasRenderingContext::ContextType.
     kCanvasRenderingContext = 21,
 
+    // Represents a call to MediaDevices.getUserMedia. Input is the set of
+    // constraints.
+    kMediaDevices_GetUserMedia = 22,
+
     // NavigatorUAData.getHighEntropyValues() is, shockingly, a high entropy
     // API to provide more detailed User-Agent data. The output is keyed on
     // the hint parameter.
     kNavigatorUAData_GetHighEntropyValues = 24,
 
+    // Represents a call to Navigator.getUserMedia. Input is the set of
+    // constraints.
+    kNavigator_GetUserMedia = 27,
+
     // Represents loading a font locally. Input is the PostScript name.
     kLocalFontLoadPostScriptName = 29,
 
diff --git a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
index 03095af..87aaab4 100644
--- a/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
+++ b/third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h
@@ -95,7 +95,7 @@
   kPaymentManager = 49,
   kSpeechSynthesis = 50,
   kKeyboardLock = 51,
-  kSmsService = 52,
+  kWebOTPService = 52,
   kOutstandingNetworkRequestDirectSocket = 53,
 
   // NB: This enum is used in a bitmask, so kMaxValue must be less than 64.
diff --git a/third_party/blink/public/common/sms/sms_receiver_destroyed_reason.h b/third_party/blink/public/common/sms/sms_receiver_destroyed_reason.h
deleted file mode 100644
index 6fae95897..0000000
--- a/third_party/blink/public/common/sms/sms_receiver_destroyed_reason.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_SMS_RECEIVER_DESTROYED_REASON_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_SMS_RECEIVER_DESTROYED_REASON_H_
-
-namespace blink {
-
-// This enum describes the reason for desruction of the SmsService.
-enum class SmsReceiverDestroyedReason {
-  // Don't change the meaning of these values because they are being recorded
-  // in a metric.
-  kNavigateNewPage = 0,
-  kNavigateExistingPage = 1,
-  kNavigateSamePage = 2,
-  kMaxValue = kNavigateSamePage
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_SMS_RECEIVER_DESTROYED_REASON_H_
diff --git a/third_party/blink/public/common/sms/sms_receiver_outcome.h b/third_party/blink/public/common/sms/sms_receiver_outcome.h
deleted file mode 100644
index 1bf8a44..0000000
--- a/third_party/blink/public/common/sms/sms_receiver_outcome.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_SMS_RECEIVER_OUTCOME_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_SMS_RECEIVER_OUTCOME_H_
-
-namespace blink {
-
-// This enum describes the outcome of the call made to the SMSReceiver API.
-enum class SMSReceiverOutcome {
-  // Don't change the meaning of these values because they are being recorded
-  // in a metric.
-  kSuccess = 0,
-  kUnhandledRequest = 1,
-  kConnectionError = 2,
-  kCancelled = 3,
-  kAborted = 4,
-  kMaxValue = kAborted
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_SMS_RECEIVER_OUTCOME_H_
diff --git a/third_party/blink/public/common/sms/webotp_service_destroyed_reason.h b/third_party/blink/public/common/sms/webotp_service_destroyed_reason.h
new file mode 100644
index 0000000..c3e7873
--- /dev/null
+++ b/third_party/blink/public/common/sms/webotp_service_destroyed_reason.h
@@ -0,0 +1,22 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_WEBOTP_SERVICE_DESTROYED_REASON_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_WEBOTP_SERVICE_DESTROYED_REASON_H_
+
+namespace blink {
+
+// This enum describes the reason for desruction of the WebOTPService.
+enum class WebOTPServiceDestroyedReason {
+  // Don't change the meaning of these values because they are being recorded
+  // in a metric.
+  kNavigateNewPage = 0,
+  kNavigateExistingPage = 1,
+  kNavigateSamePage = 2,
+  kMaxValue = kNavigateSamePage
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_WEBOTP_SERVICE_DESTROYED_REASON_H_
diff --git a/third_party/blink/public/common/sms/webotp_service_outcome.h b/third_party/blink/public/common/sms/webotp_service_outcome.h
new file mode 100644
index 0000000..c230064
--- /dev/null
+++ b/third_party/blink/public/common/sms/webotp_service_outcome.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_WEBOTP_SERVICE_OUTCOME_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_WEBOTP_SERVICE_OUTCOME_H_
+
+namespace blink {
+
+// This enum describes the outcome of the call made to the WebOTPService API.
+enum class WebOTPServiceOutcome {
+  // Don't change the meaning of these values because they are being recorded
+  // in a metric.
+  kSuccess = 0,
+  kUnhandledRequest = 1,
+  kConnectionError = 2,
+  kCancelled = 3,
+  kAborted = 4,
+  kMaxValue = kAborted
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_SMS_WEBOTP_SERVICE_OUTCOME_H_
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 8e16ddf8..84419d44 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -157,7 +157,7 @@
     "service_worker/service_worker_state.mojom",
     "service_worker/service_worker_stream_handle.mojom",
     "site_engagement/site_engagement.mojom",
-    "sms/sms_receiver.mojom",
+    "sms/webotp_service.mojom",
     "speech/speech_recognition_error.mojom",
     "speech/speech_recognition_error_code.mojom",
     "speech/speech_recognition_grammar.mojom",
diff --git a/third_party/blink/public/mojom/frame/BUILD.gn b/third_party/blink/public/mojom/frame/BUILD.gn
index a2f9dd6a..d397d77 100644
--- a/third_party/blink/public/mojom/frame/BUILD.gn
+++ b/third_party/blink/public/mojom/frame/BUILD.gn
@@ -29,10 +29,12 @@
     "tree_scope_type.mojom",
     "user_activation_notification_type.mojom",
     "user_activation_update_types.mojom",
+    "viewport_intersection_state.mojom",
   ]
 
   public_deps = [
     "//ui/gfx/geometry/mojom",
+    "//ui/gfx/mojom",
     "//url/mojom:url_mojom_gurl",
   ]
 
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index ea18064..7f65ebef 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -27,6 +27,7 @@
 import "third_party/blink/public/mojom/favicon/favicon_url.mojom";
 import "third_party/blink/public/mojom/fetch/fetch_api_request.mojom";
 import "third_party/blink/public/mojom/frame/blocked_navigation_types.mojom";
+import "third_party/blink/public/mojom/frame/frame_owner_properties.mojom";
 import "third_party/blink/public/mojom/frame/frame_policy.mojom";
 import "third_party/blink/public/mojom/frame/fullscreen.mojom";
 import "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom";
@@ -36,7 +37,7 @@
 import "third_party/blink/public/mojom/frame/sudden_termination_disabler_type.mojom";
 import "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom";
 import "third_party/blink/public/mojom/frame/user_activation_update_types.mojom";
-import "third_party/blink/public/mojom/frame/frame_owner_properties.mojom";
+import "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom";
 import "third_party/blink/public/mojom/input/focus_type.mojom";
 import "third_party/blink/public/mojom/input/scroll_direction.mojom";
 import "third_party/blink/public/mojom/loader/referrer.mojom";
@@ -735,6 +736,12 @@
 
   // Notifies the browser that a child frame is detached from the DOM.
   Detach();
+
+  // Sent by a parent frame to notify its child about the state of the child's
+  // intersection with the parent's viewport, primarily for use by the
+  // IntersectionObserver API.
+  UpdateViewportIntersection(
+      ViewportIntersectionState intersection_state);
 };
 
 // Implemented in Blink, this interface defines frame-specific methods that will
diff --git a/third_party/blink/public/mojom/frame/viewport_intersection_state.mojom b/third_party/blink/public/mojom/frame/viewport_intersection_state.mojom
new file mode 100644
index 0000000..d470185
--- /dev/null
+++ b/third_party/blink/public/mojom/frame/viewport_intersection_state.mojom
@@ -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.
+
+module blink.mojom;
+
+import "ui/gfx/geometry/mojom/geometry.mojom";
+import "ui/gfx/mojom/transform.mojom";
+
+// These values are used to implement a browser intervention: if a cross-
+// origin iframe has moved more than 30 screen pixels (manhattan distance)
+// within its embedding page's viewport within the last 500 milliseconds, most
+// input events targeting the iframe will be quietly discarded.
+const uint32 kMaxChildFrameScreenRectMovement = 30;
+const uint32 kMinScreenRectStableTimeMs = 500;
+
+// Indicates whether a child frame is occluded or visually altered (e.g., with
+// CSS opacity or transform) by content or styles in the parent frame.
+enum FrameOcclusionState {
+  // No occlusion determination was made.
+  kUnknown = 0,
+  // The frame *may* be occluded or visually altered.
+  kPossiblyOccluded = 1,
+  // The frame is definitely not occluded or visually altered.
+  kGuaranteedNotOccluded = 2,
+};
+
+// Communicates information about the position and visibility of a child frame
+// within the viewport of the top-level main frame.
+struct ViewportIntersectionState {
+  // Portion of the child frame which is within the main frame's scrolling
+  gfx.mojom.Rect viewport_intersection;
+
+  // Same as viewport_intersection, but without applying the main frame's
+  // document-level overflow clip.
+  gfx.mojom.Rect main_frame_intersection;
+
+  // Area of the child frame that needs to be rastered, in physical pixels.
+  gfx.mojom.Rect compositor_visible_rect;
+
+  // Occlusion state, as described above.
+  FrameOcclusionState occlusion_state = FrameOcclusionState.kUnknown;
+
+  // Main frame's size.
+  gfx.mojom.Size main_frame_viewport_size;
+
+  // Main frame's scrolling offset.
+  gfx.mojom.Point main_frame_scroll_offset;
+
+  // Child frame's transform to the coordinate system of the main frame.
+  gfx.mojom.Transform main_frame_transform;
+};
diff --git a/third_party/blink/public/mojom/page/widget.mojom b/third_party/blink/public/mojom/page/widget.mojom
index 6c7b016..fbf9609 100644
--- a/third_party/blink/public/mojom/page/widget.mojom
+++ b/third_party/blink/public/mojom/page/widget.mojom
@@ -14,6 +14,7 @@
 import "services/viz/public/mojom/hit_test/input_target_client.mojom";
 import "skia/public/mojom/bitmap.mojom";
 import "third_party/blink/public/mojom/frame/intrinsic_sizing_info.mojom";
+import "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom";
 import "third_party/blink/public/mojom/input/input_handler.mojom";
 import "third_party/blink/public/mojom/input/touch_event.mojom";
 import "third_party/blink/public/mojom/page/drag.mojom";
@@ -111,6 +112,12 @@
 
   // Binds an InputTargetClient interface.
   BindInputTargetClient(pending_receiver<viz.mojom.InputTargetClient> host);
+
+  // Sent by a parent frame to notify its child about the state of the child's
+  // intersection with the parent's viewport, primarily for use by the
+  // IntersectionObserver API. Also see 'UpdateViewportIntersection' in
+  // third_party/blink/public/mojom/frame/viewport_intersection_state.mojom
+  SetViewportIntersection(ViewportIntersectionState intersection_state);
 };
 
 // Implemented in Browser, this interface defines frame-widget-specific methods that
diff --git a/third_party/blink/public/mojom/prerender/prerender.mojom b/third_party/blink/public/mojom/prerender/prerender.mojom
index eb35d8d..8928ce3 100644
--- a/third_party/blink/public/mojom/prerender/prerender.mojom
+++ b/third_party/blink/public/mojom/prerender/prerender.mojom
@@ -58,14 +58,4 @@
   // etc. This must be called after Start(). This does not trigger
   // OnPrerenderStop() on PrerenderProcessorClient.
   Cancel();
-
-  // Abandons the ongoing prerendering. This is supposed to be called when the
-  // page navigates away or gets suspended. This is a weaker signal than
-  // Cancel(), since the requester hasn't indicated that the prerender isn't
-  // wanted, and we may end up using it after, for example, a short redirect
-  // chain. This must be called after Start().
-  //
-  // TODO(https://crbug.com/1130360): The actual behavior doesn't match this
-  // comment due to the issue. Fix the behavior or update this comment.
-  Abandon();
 };
diff --git a/third_party/blink/public/mojom/sms/sms_receiver.mojom b/third_party/blink/public/mojom/sms/webotp_service.mojom
similarity index 81%
rename from third_party/blink/public/mojom/sms/sms_receiver.mojom
rename to third_party/blink/public/mojom/sms/webotp_service.mojom
index bfa6c1f7..d6dfff2 100644
--- a/third_party/blink/public/mojom/sms/sms_receiver.mojom
+++ b/third_party/blink/public/mojom/sms/webotp_service.mojom
@@ -6,7 +6,7 @@
 
 import "mojo/public/mojom/base/time.mojom";
 
-// Implementation of the proposed "Sms Detection API".
+// Implementation of the proposed "Web OTP API".
 //
 // Proposal: https://github.com/WICG/web-otp
 enum SmsStatus {
@@ -19,11 +19,11 @@
 // This interface is created per storage partition but its execution is context
 // associated: there is an origin associated with a request that is multiplexed
 // through one instance on a storage partition.
-interface SmsReceiver {
+interface WebOTPService {
   // Retrieves the next SMS message that arrives on the phone that is addressed
   // to the caller's origin.
-  // Returns the raw content of the received SMS.
-  // |otp|, |message| is only set if status == kSuccess.
+  // Returns the otp that was part of the received SMS.
+  // |otp| is only set if status == kSuccess.
   Receive() => (SmsStatus status, string? otp);
   // Aborts the current retrieval process and resolves it with an
   // kAborted SmsStatus.
diff --git a/third_party/blink/public/platform/viewport_intersection_state.h b/third_party/blink/public/platform/viewport_intersection_state.h
deleted file mode 100644
index cb6a6f2..0000000
--- a/third_party/blink/public/platform/viewport_intersection_state.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_VIEWPORT_INTERSECTION_STATE_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_VIEWPORT_INTERSECTION_STATE_H_
-
-#include "third_party/blink/public/platform/web_common.h"
-#include "third_party/blink/public/platform/web_rect.h"
-#include "third_party/blink/public/platform/web_size.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/transform.h"
-
-namespace blink {
-
-// Indicates whether a child frame is occluded or visually altered (e.g., with
-// CSS opacity or transform) by content or styles in the parent frame.
-enum class FrameOcclusionState {
-  // No occlusion determination was made.
-  kUnknown = 0,
-  // The frame *may* be occluded or visually altered.
-  kPossiblyOccluded = 1,
-  // The frame is definitely not occluded or visually altered.
-  kGuaranteedNotOccluded = 2,
-  kMaxValue = kGuaranteedNotOccluded,
-};
-
-// These values are used to implement a browser intervention: if a cross-
-// origin iframe has moved more than 30 screen pixels (manhattan distance)
-// within its embedding page's viewport within the last 500 milliseconds, most
-// input events targeting the iframe will be quietly discarded.
-static constexpr uint32_t kMaxChildFrameScreenRectMovement = 30;
-static constexpr uint32_t kMinScreenRectStableTimeMs = 500;
-
-// Communicates information about the position and visibility of a child frame
-// within the viewport of the top-level main frame.
-struct BLINK_PLATFORM_EXPORT ViewportIntersectionState {
-  bool operator==(const ViewportIntersectionState& other) const {
-    return viewport_intersection == other.viewport_intersection &&
-           compositor_visible_rect == other.compositor_visible_rect &&
-           occlusion_state == other.occlusion_state &&
-           main_frame_viewport_size == other.main_frame_viewport_size &&
-           main_frame_scroll_offset == other.main_frame_scroll_offset &&
-           main_frame_intersection == other.main_frame_intersection &&
-           main_frame_transform == other.main_frame_transform;
-  }
-  bool operator!=(const ViewportIntersectionState& other) const {
-    return !(*this == other);
-  }
-
-  // Portion of the child frame which is within the main frame's scrolling
-  WebRect viewport_intersection;
-  // Same as viewport_intersection, but without applying the main frame's
-  // document-level overflow clip.
-  WebRect main_frame_intersection;
-  // Area of the child frame that needs to be rastered, in physical pixels.
-  WebRect compositor_visible_rect;
-  // Occlusion state, as described above.
-  FrameOcclusionState occlusion_state = FrameOcclusionState::kUnknown;
-  // Main frame's size.
-  WebSize main_frame_viewport_size;
-  // Main frame's scrolling offset.
-  gfx::Point main_frame_scroll_offset;
-  // Child frame's transform to the coordinate system of the main frame.
-  gfx::Transform main_frame_transform;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_VIEWPORT_INTERSECTION_STATE_H_
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index a666132..77f99305 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -232,7 +232,7 @@
   BLINK_PLATFORM_EXPORT static void EnableSubresourceWebBundles(bool);
   BLINK_PLATFORM_EXPORT static void EnableIdleDetection(bool);
   BLINK_PLATFORM_EXPORT static void EnableSkipTouchEventFilter(bool);
-  BLINK_PLATFORM_EXPORT static void EnableSmsReceiver(bool);
+  BLINK_PLATFORM_EXPORT static void EnableWebOTP(bool);
   BLINK_PLATFORM_EXPORT static void EnableConsolidatedMovementXY(bool);
   BLINK_PLATFORM_EXPORT static void EnableMouseSubframeNoImplicitCapture(bool);
   BLINK_PLATFORM_EXPORT static void EnableBackForwardCache(bool);
diff --git a/third_party/blink/public/web/web_frame_widget.h b/third_party/blink/public/web/web_frame_widget.h
index 35530d32..8071d35 100644
--- a/third_party/blink/public/web/web_frame_widget.h
+++ b/third_party/blink/public/web/web_frame_widget.h
@@ -37,7 +37,6 @@
 #include "third_party/blink/public/common/page/drag_operation.h"
 #include "third_party/blink/public/mojom/page/widget.mojom-shared.h"
 #include "third_party/blink/public/platform/cross_variant_mojo_util.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_touch_action.h"
 #include "third_party/blink/public/web/web_swap_result.h"
@@ -128,13 +127,6 @@
   // ended.
   virtual void DragSourceSystemDragEnded() = 0;
 
-  // Constrains the viewport intersection for use by IntersectionObserver,
-  // and indicates whether the frame may be painted over or obscured in the
-  // parent. This is needed for out-of-process iframes to know if they are
-  // clipped or obscured by ancestor frames in another process.
-  virtual void SetRemoteViewportIntersection(const ViewportIntersectionState&) {
-  }
-
   // Sets the inherited effective touch action on an out-of-process iframe.
   virtual void SetInheritedEffectiveTouchAction(WebTouchAction) {}
 
diff --git a/third_party/blink/public/web/web_remote_frame_client.h b/third_party/blink/public/web/web_remote_frame_client.h
index a062029..c4a96196 100644
--- a/third_party/blink/public/web/web_remote_frame_client.h
+++ b/third_party/blink/public/web/web_remote_frame_client.h
@@ -13,7 +13,6 @@
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-shared.h"
 #include "third_party/blink/public/platform/cross_variant_mojo_util.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
 #include "third_party/blink/public/platform/web_impression.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/platform/web_touch_action.h"
@@ -21,6 +20,7 @@
 #include "third_party/blink/public/web/web_remote_frame.h"
 
 namespace blink {
+
 struct ScreenInfo;
 class WebURLRequest;
 struct WebRect;
@@ -49,8 +49,7 @@
   virtual void FrameRectsChanged(const WebRect& local_frame_rect,
                                  const WebRect& screen_space_rect) {}
 
-  virtual void UpdateRemoteViewportIntersection(
-      const ViewportIntersectionState& intersection_state) {}
+  virtual void SynchronizeVisualProperties() {}
 
   // Returns an AssociatedInterfaceProvider the frame can use to request
   // associated interfaces from the browser.
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index eb887be..6946676 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -65,6 +65,7 @@
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h"
 #include "third_party/blink/public/mojom/frame/frame_owner_element_type.mojom-blink.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink.h"
 #include "third_party/blink/public/mojom/page_state/page_state.mojom-blink.h"
 #include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
@@ -7332,6 +7333,51 @@
   EXPECT_TRUE(client.FrameLoadTypeReloadSeen());
 }
 
+class TestMainFrameIntersectionChanged
+    : public frame_test_helpers::TestWebFrameClient {
+ public:
+  TestMainFrameIntersectionChanged() = default;
+  ~TestMainFrameIntersectionChanged() override = default;
+
+  // frame_test_helpers::TestWebFrameClient:
+  void OnMainFrameIntersectionChanged(
+      const WebRect& intersection_rect) override {
+    main_frame_intersection_ = intersection_rect;
+  }
+
+  WebRect MainFrameIntersection() const { return main_frame_intersection_; }
+
+ private:
+  WebRect main_frame_intersection_;
+};
+
+TEST_F(WebFrameTest, MainFrameIntersectionChanged) {
+  TestMainFrameIntersectionChanged client;
+  frame_test_helpers::WebViewHelper helper;
+  helper.InitializeRemote();
+
+  WebLocalFrameImpl* local_frame = frame_test_helpers::CreateLocalChild(
+      *helper.RemoteMainFrame(), "frameName", WebFrameOwnerProperties(),
+      nullptr, &client);
+
+  WebFrameWidget* widget = local_frame->FrameWidget();
+  ASSERT_TRUE(widget);
+
+  gfx::Rect viewport_intersection(0, 11, 200, 89);
+  gfx::Rect mainframe_intersection(0, 0, 200, 140);
+  blink::mojom::FrameOcclusionState occlusion_state =
+      blink::mojom::FrameOcclusionState::kUnknown;
+  gfx::Transform transform;
+  transform.Translate(100, 100);
+
+  auto intersection_state = blink::mojom::blink::ViewportIntersectionState(
+      viewport_intersection, mainframe_intersection, gfx::Rect(),
+      occlusion_state, gfx::Size(), gfx::Point(), transform);
+  static_cast<WebFrameWidgetBase*>(widget)->SetRemoteViewportIntersection(
+      intersection_state);
+  EXPECT_EQ(client.MainFrameIntersection(), blink::WebRect(100, 100, 200, 140));
+}
+
 class TestSameDocumentWithImageWebFrameClient
     : public frame_test_helpers::TestWebFrameClient {
  public:
@@ -10638,21 +10684,24 @@
   EXPECT_FALSE(iframe->ContentFrame());
 }
 
-class ViewportIntersectionCatcher
-    : public frame_test_helpers::TestWebRemoteFrameClient {
+class TestViewportIntersection : public FakeRemoteFrameHost {
  public:
-  void UpdateRemoteViewportIntersection(
-      const ViewportIntersectionState& state) override {
-    intersection_state_ = state;
-    frame_test_helpers::TestWebRemoteFrameClient::
-        UpdateRemoteViewportIntersection(state);
-  }
-  const ViewportIntersectionState& GetState() const {
+  TestViewportIntersection() = default;
+  ~TestViewportIntersection() override = default;
+
+  const mojom::blink::ViewportIntersectionStatePtr& GetIntersectionState()
+      const {
     return intersection_state_;
   }
 
+  // FakeRemoteFrameHost:
+  void UpdateViewportIntersection(
+      mojom::blink::ViewportIntersectionStatePtr intersection_state) override {
+    intersection_state_ = std::move(intersection_state);
+  }
+
  private:
-  ViewportIntersectionState intersection_state_;
+  mojom::blink::ViewportIntersectionStatePtr intersection_state_;
 };
 
 TEST_F(WebFrameTest, RotatedIframeViewportIntersection) {
@@ -10672,22 +10721,26 @@
 </style>
 <iframe></iframe>
   )HTML");
-  ViewportIntersectionCatcher intersection_catcher;
+  frame_test_helpers::TestWebRemoteFrameClient remote_frame_client;
+  TestViewportIntersection remote_frame_host;
+  remote_frame_host.Init(remote_frame_client.GetRemoteAssociatedInterfaces());
   WebRemoteFrameImpl* remote_frame =
-      frame_test_helpers::CreateRemote(&intersection_catcher);
+      frame_test_helpers::CreateRemote(&remote_frame_client);
   web_view_helper.LocalMainFrame()->FirstChild()->Swap(remote_frame);
   web_view->MainFrameImpl()->GetFrame()->View()->UpdateAllLifecyclePhases(
       DocumentUpdateReason::kTest);
   web_view->MainFrameImpl()->GetFrame()->View()->RunPostLifecycleSteps();
-  ASSERT_TRUE(!intersection_catcher.GetState().viewport_intersection.IsEmpty());
-  EXPECT_TRUE(
-      IntRect(IntPoint(), remote_frame->GetFrame()->View()->Size())
-          .Contains(intersection_catcher.GetState().viewport_intersection));
-  ASSERT_TRUE(
-      !intersection_catcher.GetState().main_frame_intersection.IsEmpty());
-  EXPECT_TRUE(
-      IntRect(IntPoint(), remote_frame->GetFrame()->View()->Size())
-          .Contains(intersection_catcher.GetState().main_frame_intersection));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(!remote_frame_host.GetIntersectionState()
+                   ->viewport_intersection.IsEmpty());
+  EXPECT_TRUE(IntRect(IntPoint(), remote_frame->GetFrame()->View()->Size())
+                  .Contains(IntRect(remote_frame_host.GetIntersectionState()
+                                        ->viewport_intersection)));
+  ASSERT_TRUE(!remote_frame_host.GetIntersectionState()
+                   ->main_frame_intersection.IsEmpty());
+  EXPECT_TRUE(IntRect(IntPoint(), remote_frame->GetFrame()->View()->Size())
+                  .Contains(IntRect(remote_frame_host.GetIntersectionState()
+                                        ->main_frame_intersection)));
   remote_frame->Detach();
 }
 
@@ -13407,27 +13460,6 @@
   frame->PrintEnd();
 }
 
-class RemoteFrameIntersectionClient
-    : public frame_test_helpers::TestWebRemoteFrameClient {
- public:
-  RemoteFrameIntersectionClient() = default;
-  ~RemoteFrameIntersectionClient() override = default;
-
-  void UpdateRemoteViewportIntersection(
-      const ViewportIntersectionState& intersection_state) override {
-    intersection_state_ = intersection_state;
-    frame_test_helpers::TestWebRemoteFrameClient::
-        UpdateRemoteViewportIntersection(intersection_state);
-  }
-
-  const ViewportIntersectionState& GetIntersectionState() const {
-    return intersection_state_;
-  }
-
- private:
-  ViewportIntersectionState intersection_state_;
-};
-
 TEST_F(WebFrameSimTest, MainFrameTransformOffsetPixelSnapped) {
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
@@ -13435,19 +13467,24 @@
       <!DOCTYPE html>
       <iframe id="iframe" style="position:absolute;top:7px;left:13.5px;border:none"></iframe>
   )HTML");
-  RemoteFrameIntersectionClient client;
-  WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote(&client);
+  frame_test_helpers::TestWebRemoteFrameClient remote_frame_client;
+  TestViewportIntersection remote_frame_host;
+  remote_frame_host.Init(remote_frame_client.GetRemoteAssociatedInterfaces());
+  WebRemoteFrame* remote_frame =
+      frame_test_helpers::CreateRemote(&remote_frame_client);
   MainFrame().FirstChild()->Swap(remote_frame);
   Compositor().BeginFrame();
   RunPendingTasks();
-  EXPECT_TRUE(client.GetIntersectionState()
-                  .main_frame_transform.IsIdentityOrIntegerTranslation());
-  EXPECT_EQ(
-      client.GetIntersectionState().main_frame_transform.matrix().get(0, 3),
-      14.f);
-  EXPECT_EQ(
-      client.GetIntersectionState().main_frame_transform.matrix().get(1, 3),
-      7.f);
+  EXPECT_TRUE(remote_frame_host.GetIntersectionState()
+                  ->main_frame_transform.IsIdentityOrIntegerTranslation());
+  EXPECT_EQ(remote_frame_host.GetIntersectionState()
+                ->main_frame_transform.matrix()
+                .get(0, 3),
+            14.f);
+  EXPECT_EQ(remote_frame_host.GetIntersectionState()
+                ->main_frame_transform.matrix()
+                .get(1, 3),
+            7.f);
   MainFrame().FirstChild()->Detach();
 }
 
@@ -13507,13 +13544,14 @@
   ASSERT_TRUE(widget);
   gfx::Transform viewport_transform;
   viewport_transform.Translate(7, -11);
-  WebRect viewport_intersection(0, 11, 200, 89);
-  WebRect mainframe_intersection(0, 0, 200, 140);
-  FrameOcclusionState occlusion_state = FrameOcclusionState::kUnknown;
+  gfx::Rect viewport_intersection(0, 11, 200, 89);
+  gfx::Rect mainframe_intersection(0, 0, 200, 140);
+  blink::mojom::FrameOcclusionState occlusion_state =
+      blink::mojom::FrameOcclusionState::kUnknown;
 
-  widget->SetRemoteViewportIntersection(
+  static_cast<WebFrameWidgetBase*>(widget)->SetRemoteViewportIntersection(
       {viewport_intersection, mainframe_intersection, viewport_intersection,
-       occlusion_state, WebSize(), gfx::Point(), viewport_transform});
+       occlusion_state, gfx::Size(), gfx::Point(), viewport_transform});
 
   // The viewport intersection should be applied by the layout geometry mapping
   // code when these flags are used.
diff --git a/third_party/blink/renderer/core/frame/frame_client.h b/third_party/blink/renderer/core/frame/frame_client.h
index 9f531dc..aae81dd 100644
--- a/third_party/blink/renderer/core/frame/frame_client.h
+++ b/third_party/blink/renderer/core/frame/frame_client.h
@@ -15,6 +15,7 @@
 
 class LocalFrame;
 enum class FrameDetachType;
+class IntRect;
 
 class CORE_EXPORT FrameClient : public GarbageCollected<FrameClient> {
  public:
diff --git a/third_party/blink/renderer/core/frame/frame_view.cc b/third_party/blink/renderer/core/frame/frame_view.cc
index 3c52013..74da41c 100644
--- a/third_party/blink/renderer/core/frame/frame_view.cc
+++ b/third_party/blink/renderer/core/frame/frame_view.cc
@@ -65,11 +65,12 @@
   TransformationMatrix main_frame_transform_matrix;
   DocumentLifecycle::LifecycleState parent_lifecycle_state =
       owner_document.Lifecycle().GetState();
-  FrameOcclusionState occlusion_state =
+  mojom::blink::FrameOcclusionState occlusion_state =
       owner_document.GetFrame()->GetOcclusionState();
   bool should_compute_occlusion =
       needs_occlusion_tracking &&
-      occlusion_state == FrameOcclusionState::kGuaranteedNotOccluded &&
+      occlusion_state ==
+          mojom::blink::FrameOcclusionState::kGuaranteedNotOccluded &&
       parent_lifecycle_state >= DocumentLifecycle::kPrePaintClean;
 
   LayoutEmbeddedContent* owner_layout_object =
@@ -78,7 +79,7 @@
     // The frame is detached from layout, not visible, or zero size; leave
     // viewport_intersection empty, and signal the frame as occluded if
     // necessary.
-    occlusion_state = FrameOcclusionState::kPossiblyOccluded;
+    occlusion_state = mojom::blink::FrameOcclusionState::kPossiblyOccluded;
   } else if (parent_lifecycle_state >= DocumentLifecycle::kLayoutClean &&
              !owner_document.View()->NeedsLayout()) {
     unsigned geometry_flags =
@@ -93,7 +94,7 @@
     if (new_rect_in_parent.size != rect_in_parent_.size ||
         ((new_rect_in_parent.X() - rect_in_parent_.X()).Abs() +
              (new_rect_in_parent.Y() - rect_in_parent_.Y()).Abs() >
-         LayoutUnit(kMaxChildFrameScreenRectMovement))) {
+         LayoutUnit(mojom::blink::kMaxChildFrameScreenRectMovement))) {
       rect_in_parent_ = new_rect_in_parent;
       if (Page* page = GetFrame().GetPage()) {
         rect_in_parent_stable_since_ = page->Animator().Clock().CurrentTime();
@@ -102,7 +103,7 @@
       }
     }
     if (should_compute_occlusion && !geometry.IsVisible())
-      occlusion_state = FrameOcclusionState::kPossiblyOccluded;
+      occlusion_state = mojom::blink::FrameOcclusionState::kPossiblyOccluded;
 
     // Generate matrix to transform from the space of the containing document
     // to the space of the iframe's contents.
@@ -170,10 +171,11 @@
     }
     main_frame_transform_matrix =
         child_frame_to_root_frame.AccumulatedTransform();
-  } else if (occlusion_state == FrameOcclusionState::kGuaranteedNotOccluded) {
+  } else if (occlusion_state ==
+             mojom::blink::FrameOcclusionState::kGuaranteedNotOccluded) {
     // If the parent LocalFrameView is throttled and out-of-date, then we can't
     // get any useful information.
-    occlusion_state = FrameOcclusionState::kUnknown;
+    occlusion_state = mojom::blink::FrameOcclusionState::kUnknown;
   }
 
   // An iframe's content is always pixel-snapped, even if the iframe element has
@@ -181,17 +183,18 @@
   gfx::Transform main_frame_gfx_transform =
       TransformationMatrix::ToTransform(main_frame_transform_matrix);
   main_frame_gfx_transform.RoundTranslationComponents();
-  SetViewportIntersection(
-      {viewport_intersection, mainframe_intersection, WebRect(),
-       occlusion_state, frame.GetMainFrameViewportSize(),
-       frame.GetMainFrameScrollOffset(), main_frame_gfx_transform});
+
+  SetViewportIntersection(mojom::blink::ViewportIntersectionState(
+      viewport_intersection, mainframe_intersection, gfx::Rect(),
+      occlusion_state, gfx::Size(frame.GetMainFrameViewportSize()),
+      gfx::Point(frame.GetMainFrameScrollOffset()), main_frame_gfx_transform));
 
   UpdateFrameVisibility(!viewport_intersection.IsEmpty());
 
   if (ShouldReportMainFrameIntersection()) {
     IntRect projected_rect = EnclosingIntRect(PhysicalRect::EnclosingRect(
         main_frame_transform_matrix
-            .ProjectQuad(FloatRect(mainframe_intersection))
+            .ProjectQuad(FloatRect(IntRect(mainframe_intersection)))
             .BoundingBox()));
     // Return <0, 0, 0, 0> if there is no area.
     if (projected_rect.IsEmpty())
@@ -256,7 +259,8 @@
 bool FrameView::RectInParentIsStable(
     const base::TimeTicks& event_timestamp) const {
   if (event_timestamp - rect_in_parent_stable_since_ <
-      base::TimeDelta::FromMilliseconds(kMinScreenRectStableTimeMs)) {
+      base::TimeDelta::FromMilliseconds(
+          mojom::blink::kMinScreenRectStableTimeMs)) {
     return false;
   }
   LocalFrameView* parent = ParentFrameView();
diff --git a/third_party/blink/renderer/core/frame/frame_view.h b/third_party/blink/renderer/core/frame/frame_view.h
index ec343b2..74b4d0e 100644
--- a/third_party/blink/renderer/core/frame/frame_view.h
+++ b/third_party/blink/renderer/core/frame/frame_view.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_VIEW_H_
 
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/frame/embedded_content_view.h"
 #include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
 #include "third_party/blink/renderer/platform/wtf/casting.h"
@@ -59,7 +59,7 @@
  protected:
   virtual bool NeedsViewportOffset() const { return false; }
   virtual void SetViewportIntersection(
-      const ViewportIntersectionState& intersection_state) = 0;
+      const mojom::blink::ViewportIntersectionState& intersection_state) = 0;
   virtual void VisibilityForThrottlingChanged() = 0;
   virtual bool LifecycleUpdatesThrottled() const { return false; }
   void UpdateViewportIntersection(unsigned, bool);
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index fff54c3a..6cb9dee0 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1847,7 +1847,7 @@
 
   // Frame was already occluded, nothing more to do.
   if (intersection_state_.occlusion_state ==
-      FrameOcclusionState::kPossiblyOccluded) {
+      mojom::blink::FrameOcclusionState::kPossiblyOccluded) {
     return;
   }
 
@@ -1882,7 +1882,7 @@
 }
 
 void LocalFrame::SetViewportIntersectionFromParent(
-    const ViewportIntersectionState& intersection_state) {
+    const mojom::blink::ViewportIntersectionState& intersection_state) {
   DCHECK(IsLocalRoot());
   // Notify the render frame observers when the main frame intersection changes.
   if (intersection_state_.main_frame_intersection !=
@@ -1950,12 +1950,12 @@
   SetOpenerDoNotNotify(opener_frame);
 }
 
-FrameOcclusionState LocalFrame::GetOcclusionState() const {
+mojom::blink::FrameOcclusionState LocalFrame::GetOcclusionState() const {
   if (hidden_)
-    return FrameOcclusionState::kPossiblyOccluded;
+    return mojom::blink::FrameOcclusionState::kPossiblyOccluded;
   // TODO(dcheng): Get rid of this branch for the main frame.
   if (IsMainFrame())
-    return FrameOcclusionState::kGuaranteedNotOccluded;
+    return mojom::blink::FrameOcclusionState::kGuaranteedNotOccluded;
   if (IsLocalRoot())
     return intersection_state_.occlusion_state;
   return LocalFrameRoot().GetOcclusionState();
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 1fae732..648f45b 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -45,6 +45,7 @@
 #include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/frame/reporting_observer.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom-blink-forward.h"
@@ -52,7 +53,7 @@
 #include "third_party/blink/public/mojom/reporting/reporting.mojom-blink.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/task_type.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
+#include "third_party/blink/public/platform/web_rect.h"
 #include "third_party/blink/renderer/core/clipboard/raw_system_clipboard.h"
 #include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -419,25 +420,27 @@
   // Called on a view for a LocalFrame with a RemoteFrame parent. This makes
   // viewport intersection and occlusion/obscuration available that accounts for
   // remote ancestor frames and their respective scroll positions, clips, etc.
-  void SetViewportIntersectionFromParent(const ViewportIntersectionState&);
+  void SetViewportIntersectionFromParent(
+      const mojom::blink::ViewportIntersectionState& intersection_state);
 
   IntSize GetMainFrameViewportSize() const override;
   IntPoint GetMainFrameScrollOffset() const override;
 
   void SetOpener(Frame* opener) override;
 
-  // See viewport_intersection_state.h for more info on these methods.
+  // See viewport_intersection_state.mojom for more info on these
+  // methods.
   IntRect RemoteViewportIntersection() const {
-    return intersection_state_.viewport_intersection;
+    return IntRect(intersection_state_.viewport_intersection);
   }
   IntRect RemoteMainFrameIntersection() const {
-    return intersection_state_.main_frame_intersection;
+    return IntRect(intersection_state_.main_frame_intersection);
   }
   gfx::Transform RemoteMainFrameTransform() const {
     return intersection_state_.main_frame_transform;
   }
 
-  FrameOcclusionState GetOcclusionState() const;
+  mojom::blink::FrameOcclusionState GetOcclusionState() const;
 
   bool NeedsOcclusionTracking() const;
 
@@ -865,7 +868,7 @@
       text_input_host_{nullptr};
 #endif
 
-  ViewportIntersectionState intersection_state_;
+  mojom::blink::ViewportIntersectionState intersection_state_;
 
   // Per-frame URLLoader factory.
   std::unique_ptr<WebURLLoaderFactory> url_loader_factory_;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index daae425..62d6749 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3820,7 +3820,7 @@
 }
 
 void LocalFrameView::SetViewportIntersection(
-    const ViewportIntersectionState& intersection_state) {
+    const mojom::blink::ViewportIntersectionState& intersection_state) {
   // The viewport intersection of the main frame is not tracked.
   DCHECK(!GetFrame().IsMainFrame());
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 2f693002..0fe907d 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -31,6 +31,7 @@
 #include "base/callback_forward.h"
 #include "third_party/blink/public/common/metrics/document_update_reason.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink.h"
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/document_lifecycle.h"
@@ -737,8 +738,8 @@
   void SelfVisibleChanged() override;
   void ParentVisibleChanged() override;
   void NotifyFrameRectsChangedIfNeeded();
-  void SetViewportIntersection(
-      const ViewportIntersectionState& intersection_state) override;
+  void SetViewportIntersection(const mojom::blink::ViewportIntersectionState&
+                                   intersection_state) override;
   void VisibilityForThrottlingChanged() override;
   bool LifecycleUpdatesThrottled() const override {
     return lifecycle_updates_throttled_;
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client.h b/third_party/blink/renderer/core/frame/remote_frame_client.h
index a97fcd3..b9782dc 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_client.h
@@ -8,7 +8,7 @@
 #include "base/optional.h"
 #include "cc/paint/paint_canvas.h"
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-blink-forward.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/web_impression.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
 #include "third_party/blink/renderer/core/frame/frame_client.h"
@@ -62,8 +62,7 @@
   virtual void DidChangeVisibleViewportSize(
       const gfx::Size& visible_viewport_size) = 0;
 
-  virtual void UpdateRemoteViewportIntersection(
-      const ViewportIntersectionState& intersection_state) = 0;
+  virtual void SynchronizeVisualProperties() = 0;
 
   virtual AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() = 0;
 
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc b/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
index f80cfb0..32d626c 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_client_impl.cc
@@ -138,9 +138,8 @@
   web_frame_->Client()->DidChangeVisibleViewportSize(visible_viewport_size);
 }
 
-void RemoteFrameClientImpl::UpdateRemoteViewportIntersection(
-    const ViewportIntersectionState& intersection_state) {
-  web_frame_->Client()->UpdateRemoteViewportIntersection(intersection_state);
+void RemoteFrameClientImpl::SynchronizeVisualProperties() {
+  web_frame_->Client()->SynchronizeVisualProperties();
 }
 
 AssociatedInterfaceProvider*
diff --git a/third_party/blink/renderer/core/frame/remote_frame_client_impl.h b/third_party/blink/renderer/core/frame/remote_frame_client_impl.h
index aa19eb0..ab941bd 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_client_impl.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_client_impl.h
@@ -43,8 +43,7 @@
       const std::vector<gfx::Rect>& root_widget_window_segments) override;
   void DidChangeVisibleViewportSize(
       const gfx::Size& visible_viewport_size) override;
-  void UpdateRemoteViewportIntersection(
-      const ViewportIntersectionState& intersection_state) override;
+  void SynchronizeVisualProperties() override;
   AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() override;
   viz::FrameSinkId GetFrameSinkId() override;
   void WasEvicted() override;
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index d404502..f5d2eff 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -98,12 +98,14 @@
 }
 
 void RemoteFrameView::SetViewportIntersection(
-    const ViewportIntersectionState& intersection_state) {
-  ViewportIntersectionState new_state(intersection_state);
-  new_state.compositor_visible_rect = WebRect(compositing_rect_);
-  if (new_state != last_intersection_state_) {
+    const mojom::blink::ViewportIntersectionState& intersection_state) {
+  mojom::blink::ViewportIntersectionState new_state(intersection_state);
+  new_state.compositor_visible_rect = gfx::Rect(compositing_rect_);
+  if (!last_intersection_state_.Equals(new_state)) {
     last_intersection_state_ = new_state;
-    remote_frame_->Client()->UpdateRemoteViewportIntersection(new_state);
+    remote_frame_->Client()->SynchronizeVisualProperties();
+    remote_frame_->GetRemoteFrameHostRemote().UpdateViewportIntersection(
+        new_state.Clone());
   } else if (needs_frame_rect_propagation_) {
     PropagateFrameRects();
   }
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.h b/third_party/blink/renderer/core/frame/remote_frame_view.h
index 3cb29a4..b8da01a1 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.h
@@ -7,7 +7,7 @@
 
 #include "cc/paint/paint_canvas.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h"
-#include "third_party/blink/public/platform/viewport_intersection_state.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/document_lifecycle.h"
 #include "third_party/blink/renderer/core/frame/frame_view.h"
 #include "third_party/blink/renderer/core/layout/intrinsic_sizing_info.h"
@@ -81,8 +81,8 @@
  protected:
   bool NeedsViewportOffset() const override { return true; }
   // This is used to service IntersectionObservers in an OOPIF child document.
-  void SetViewportIntersection(
-      const ViewportIntersectionState& intersection_state) override;
+  void SetViewportIntersection(const mojom::blink::ViewportIntersectionState&
+                                   intersection_state) override;
   void ParentVisibleChanged() override;
 
  private:
@@ -97,7 +97,7 @@
   // and LocalFrameView. Please see the LocalFrameView::frame_ comment for
   // details.
   Member<RemoteFrame> remote_frame_;
-  ViewportIntersectionState last_intersection_state_;
+  mojom::blink::ViewportIntersectionState last_intersection_state_;
   IntRect compositing_rect_;
 
   IntrinsicSizingInfo intrinsic_sizing_info_;
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index 97f58e6..d0573c42b 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -398,6 +398,8 @@
       bool subtree_throttled) override {}
   void ShowContextMenu(ui::mojom::MenuSourceType source_type,
                        const gfx::Point& location) override;
+  void SetViewportIntersection(
+      mojom::blink::ViewportIntersectionStatePtr intersection_state) override {}
 
   // Sets the inert bit on an out-of-process iframe, causing it to ignore
   // input.
@@ -569,6 +571,13 @@
   viz::FrameSinkId GetFrameSinkIdAtPoint(const gfx::PointF& point,
                                          gfx::PointF* local_point);
 
+  // Constrains the viewport intersection for use by IntersectionObserver,
+  // and indicates whether the frame may be painted over or obscured in the
+  // parent. This is needed for out-of-process iframes to know if they are
+  // clipped or obscured by ancestor frames in another process.
+  virtual void SetRemoteViewportIntersection(
+      const mojom::blink::ViewportIntersectionState& intersection_state) {}
+
  protected:
   enum DragAction { kDragEnter, kDragOver };
 
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 08e2be2..cbb4ce9 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -697,18 +697,23 @@
 }
 
 void WebFrameWidgetImpl::SetRemoteViewportIntersection(
-    const ViewportIntersectionState& intersection_state) {
+    const mojom::blink::ViewportIntersectionState& intersection_state) {
+  SetViewportIntersection(intersection_state.Clone());
+}
+
+void WebFrameWidgetImpl::SetViewportIntersection(
+    mojom::blink::ViewportIntersectionStatePtr intersection_state) {
   // Remote viewports are only applicable to local frames with remote ancestors.
   DCHECK(LocalRootImpl()->Parent() &&
          LocalRootImpl()->Parent()->IsWebRemoteFrame() &&
          LocalRootImpl()->GetFrame());
 
   compositor_visible_rect_ =
-      gfx::Rect(intersection_state.compositor_visible_rect);
+      gfx::Rect(intersection_state->compositor_visible_rect);
   widget_base_->LayerTreeHost()->SetViewportVisibleRect(
       compositor_visible_rect_);
   LocalRootImpl()->GetFrame()->SetViewportIntersectionFromParent(
-      intersection_state);
+      *intersection_state);
 }
 
 void WebFrameWidgetImpl::SetIsInertForSubFrame(bool inert) {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index 9177418..5d9bfb2 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -104,12 +104,17 @@
   void SetCursorVisibilityState(bool is_visible) override;
 
   void MouseCaptureLost() override;
-  void SetRemoteViewportIntersection(const ViewportIntersectionState&) override;
+  void SetRemoteViewportIntersection(
+      const mojom::blink::ViewportIntersectionState& intersection_state)
+      override;
   void SetIsInertForSubFrame(bool) override;
   void SetInheritedEffectiveTouchActionForSubFrame(TouchAction) override;
   void UpdateRenderThrottlingStatusForSubFrame(bool is_throttled,
                                                bool subtree_throttled) override;
 
+  void SetViewportIntersection(
+      mojom::blink::ViewportIntersectionStatePtr intersection_state) override;
+
   // WebFrameWidget implementation.
   void DidDetachLocalFrameTree() override;
   WebInputMethodController* GetActiveWebInputMethodController() const override;
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.cc b/third_party/blink/renderer/core/inspector/inspect_tools.cc
index 93d4851..9e22275 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tools.cc
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.cc
@@ -118,6 +118,10 @@
   }
 }
 
+String SearchingForNodeTool::GetOverlayName() {
+  return OverlayNames::OVERLAY_HIGHLIGHT;
+}
+
 void SearchingForNodeTool::Trace(Visitor* visitor) const {
   InspectTool::Trace(visitor);
   visitor->Trace(dom_agent_);
@@ -275,6 +279,10 @@
       color_(color),
       outline_color_(outline_color) {}
 
+String QuadHighlightTool::GetOverlayName() {
+  return OverlayNames::OVERLAY_HIGHLIGHT;
+}
+
 bool QuadHighlightTool::ForwardEventsToOverlay() {
   return false;
 }
@@ -310,6 +318,10 @@
   contrast_info_ = FetchContrast(node_);
 }
 
+String NodeHighlightTool::GetOverlayName() {
+  return OverlayNames::OVERLAY_HIGHLIGHT;
+}
+
 bool NodeHighlightTool::ForwardEventsToOverlay() {
   return false;
 }
@@ -387,9 +399,8 @@
 }
 
 // GridHighlightTool -----------------------------------------------------------
-
-int GridHighlightTool::GetDataResourceId() {
-  return IDR_INSPECT_TOOL_HIGHLIGHT_GRID_JS;
+String GridHighlightTool::GetOverlayName() {
+  return OverlayNames::OVERLAY_HIGHLIGHT_GRID;
 }
 
 void GridHighlightTool::AddGridConfig(
@@ -457,6 +468,10 @@
   }
 }
 
+String SourceOrderTool::GetOverlayName() {
+  return OverlayNames::OVERLAY_SOURCE_ORDER;
+}
+
 void SourceOrderTool::Draw(float scale) {
   DrawParentNode();
 
@@ -501,10 +516,6 @@
   return false;
 }
 
-int SourceOrderTool::GetDataResourceId() {
-  return IDR_INSPECT_TOOL_SOURCE_ORDER_JS;
-}
-
 std::unique_ptr<protocol::DictionaryValue>
 SourceOrderTool::GetNodeInspectorSourceOrderHighlightAsJson() const {
   InspectorSourceOrderHighlight highlight(
@@ -519,8 +530,8 @@
 
 // NearbyDistanceTool ----------------------------------------------------------
 
-int NearbyDistanceTool::GetDataResourceId() {
-  return IDR_INSPECT_TOOL_DISTANCES_JS;
+String NearbyDistanceTool::GetOverlayName() {
+  return OverlayNames::OVERLAY_DISTANCES;
 }
 
 bool NearbyDistanceTool::HandleMouseDown(const WebMouseEvent& event,
@@ -590,8 +601,8 @@
   overlay_->EvaluateInOverlay("drawViewSize", "");
 }
 
-int ShowViewSizeTool::GetDataResourceId() {
-  return IDR_INSPECT_TOOL_VIEWPORT_SIZE_JS;
+String ShowViewSizeTool::GetOverlayName() {
+  return OverlayNames::OVERLAY_VIEWPORT_SIZE;
 }
 
 bool ShowViewSizeTool::ForwardEventsToOverlay() {
@@ -609,8 +620,8 @@
   client.SetCursorOverridden(true);
 }
 
-int ScreenshotTool::GetDataResourceId() {
-  return IDR_INSPECT_TOOL_SCREENSHOT_JS;
+String ScreenshotTool::GetOverlayName() {
+  return OverlayNames::OVERLAY_SCREENSHOT;
 }
 
 void ScreenshotTool::Dispatch(const String& message) {
@@ -686,8 +697,8 @@
 
 // PausedInDebuggerTool --------------------------------------------------------
 
-int PausedInDebuggerTool::GetDataResourceId() {
-  return IDR_INSPECT_TOOL_PAUSED_JS;
+String PausedInDebuggerTool::GetOverlayName() {
+  return OverlayNames::OVERLAY_PAUSED;
 }
 
 void PausedInDebuggerTool::Draw(float scale) {
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.h b/third_party/blink/renderer/core/inspector/inspect_tools.h
index 0cff030..c1c9705 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tools.h
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.h
@@ -38,6 +38,7 @@
   void NodeHighlightRequested(Node*);
   void Trace(Visitor* visitor) const override;
   bool SupportsPersistentOverlays() override;
+  String GetOverlayName() override;
 
   Member<InspectorDOMAgent> dom_agent_;
   bool ua_shadow_;
@@ -64,6 +65,7 @@
   bool ForwardEventsToOverlay() override;
   bool HideOnHideHighlight() override;
   void Draw(float scale) override;
+  String GetOverlayName() override;
   std::unique_ptr<FloatQuad> quad_;
   Color color_;
   Color outline_color_;
@@ -93,6 +95,7 @@
   void DrawNode();
   void DrawMatchingSelector();
   void Trace(Visitor* visitor) const override;
+  String GetOverlayName() override;
 
   bool is_locked_ancestor_ = false;
   Member<Node> node_;
@@ -117,11 +120,11 @@
  private:
   bool HideOnHideHighlight() override;
   bool HideOnMouseMove() override;
-  int GetDataResourceId() override;
   void Draw(float scale) override;
   void DrawNode(Node* node, int source_order_position);
   void DrawParentNode();
   void Trace(Visitor* visitor) const override;
+  String GetOverlayName() override;
 
   Member<Node> node_;
   std::unique_ptr<InspectorSourceOrderConfig> source_order_config_;
@@ -143,10 +146,10 @@
       const;
 
  private:
-  int GetDataResourceId() override;
   bool ForwardEventsToOverlay() override;
   bool HideOnMouseMove() override;
   bool HideOnHideHighlight() override;
+  String GetOverlayName() override;
 
   Vector<std::pair<Member<Node>, std::unique_ptr<InspectorGridHighlightConfig>>>
       grid_node_highlights_;
@@ -159,13 +162,13 @@
   using InspectTool::InspectTool;
 
  private:
-  int GetDataResourceId() override;
   bool HandleMouseDown(const WebMouseEvent& event,
                        bool* swallow_next_mouse_up) override;
   bool HandleMouseMove(const WebMouseEvent& event) override;
   bool HandleMouseUp(const WebMouseEvent& event) override;
   void Draw(float scale) override;
   void Trace(Visitor* visitor) const override;
+  String GetOverlayName() override;
 
   Member<Node> hovered_node_;
   DISALLOW_COPY_AND_ASSIGN(NearbyDistanceTool);
@@ -178,8 +181,8 @@
 
  private:
   bool ForwardEventsToOverlay() override;
-  int GetDataResourceId() override;
   void Draw(float scale) override;
+  String GetOverlayName() override;
   DISALLOW_COPY_AND_ASSIGN(ShowViewSizeTool);
 };
 
@@ -190,8 +193,8 @@
   ScreenshotTool(InspectorOverlayAgent* overlay, OverlayFrontend* frontend);
 
  private:
-  int GetDataResourceId() override;
   void Dispatch(const String& message) override;
+  String GetOverlayName() override;
 
   DISALLOW_COPY_AND_ASSIGN(ScreenshotTool);
 };
@@ -209,9 +212,9 @@
         message_(message) {}
 
  private:
-  int GetDataResourceId() override;
   void Draw(float scale) override;
   void Dispatch(const String& message) override;
+  String GetOverlayName() override;
   v8_inspector::V8InspectorSession* v8_session_;
   String message_;
   DISALLOW_COPY_AND_ASSIGN(PausedInDebuggerTool);
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 06c5aa9..1c77a42 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -109,12 +109,16 @@
 
 }  // namespace
 
+// OverlayNames ----------------------------------------------------------------
+const char* OverlayNames::OVERLAY_HIGHLIGHT = "highlight";
+const char* OverlayNames::OVERLAY_HIGHLIGHT_GRID = "highlightGrid";
+const char* OverlayNames::OVERLAY_SOURCE_ORDER = "sourceOrder";
+const char* OverlayNames::OVERLAY_DISTANCES = "distances";
+const char* OverlayNames::OVERLAY_VIEWPORT_SIZE = "viewportSize";
+const char* OverlayNames::OVERLAY_SCREENSHOT = "screenshot";
+const char* OverlayNames::OVERLAY_PAUSED = "paused";
+
 // InspectTool -----------------------------------------------------------------
-
-int InspectTool::GetDataResourceId() {
-  return IDR_INSPECT_TOOL_HIGHLIGHT_JS;
-}
-
 bool InspectTool::HandleInputEvent(LocalFrameView* frame_view,
                                    const WebInputEvent& input_event,
                                    bool* swallow_next_mouse_up) {
@@ -215,11 +219,10 @@
       outline_color_(outline_color),
       overlay_(overlay) {}
 
-// static
-int Hinge::GetDataResourceId() {
+String Hinge::GetOverlayName() {
   // TODO (soxia): In the future, we should make the hinge working properly
   // with tools using different resources.
-  return IDR_INSPECT_TOOL_HIGHLIGHT_JS;
+  return OverlayNames::OVERLAY_HIGHLIGHT;
 }
 
 void Hinge::Trace(Visitor* visitor) const {
@@ -453,7 +456,6 @@
   resize_timer_.Stop();
   resize_timer_active_ = false;
   frame_overlay_.reset();
-  frame_resource_name_ = 0;
   persistent_tool_ = nullptr;
   PickTheRightTool();
   SetNeedsUnbufferedInput(false);
@@ -665,11 +667,14 @@
 
   DCHECK(frame_impl_->GetFrameView() && GetFrame());
 
-  LoadFrameForTool(Hinge::GetDataResourceId());
-  EnsureEnableFrameOverlay();
   FloatQuad quad(FloatRect(x, y, width, height));
   hinge_ =
       MakeGarbageCollected<Hinge>(quad, content_color, outline_color, this);
+
+  LoadOverlayPageResource();
+  EvaluateInOverlay("setOverlay", hinge_->GetOverlayName());
+  EnsureEnableFrameOverlay();
+
   ScheduleUpdate();
 
   return Response::Success();
@@ -1034,20 +1039,10 @@
                                                                     1.0f);
 }
 
-void InspectorOverlayAgent::LoadFrameForTool(int data_resource_id) {
-  if (frame_resource_name_ == data_resource_id)
+void InspectorOverlayAgent::LoadOverlayPageResource() {
+  if (overlay_page_)
     return;
 
-  frame_resource_name_ = data_resource_id;
-
-  if (overlay_page_) {
-    overlay_page_->WillBeDestroyed();
-    overlay_page_.Clear();
-    overlay_chrome_client_.Clear();
-    overlay_host_->ClearDelegate();
-    overlay_host_.Clear();
-  }
-
   ScriptForbiddenScope::AllowUserAgentScript allow_script;
 
   Page::PageClients page_clients;
@@ -1100,7 +1095,7 @@
   data->Append(UncompressResourceAsBinary(IDR_INSPECT_COMMON_CSS));
   data->Append("</style>", static_cast<size_t>(8));
   data->Append("<script>", static_cast<size_t>(8));
-  data->Append(UncompressResourceAsBinary(frame_resource_name_));
+  data->Append(UncompressResourceAsBinary(IDR_INSPECT_TOOL_MAIN_JS));
   data->Append("</script>", static_cast<size_t>(9));
 
   frame->ForceSynchronousDocumentInstall("text/html", data);
@@ -1381,7 +1376,8 @@
   inspect_tool_ = inspect_tool;
   // If the tool supports persistent overlays, the resources of the persistent
   // tool will be included into the JS resource.
-  LoadFrameForTool(inspect_tool->GetDataResourceId());
+  LoadOverlayPageResource();
+  EvaluateInOverlay("setOverlay", inspect_tool->GetOverlayName());
   EnsureEnableFrameOverlay();
   ScheduleUpdate();
   return Response::Success();
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
index 02970f1..1bff203 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.h
@@ -76,13 +76,25 @@
 
 using OverlayFrontend = protocol::Overlay::Metainfo::FrontendClass;
 
+// Overlay names returned by GetOverlayName().
+class OverlayNames {
+ public:
+  static const char* OVERLAY_HIGHLIGHT;
+  static const char* OVERLAY_HIGHLIGHT_GRID;
+  static const char* OVERLAY_SOURCE_ORDER;
+  static const char* OVERLAY_DISTANCES;
+  static const char* OVERLAY_VIEWPORT_SIZE;
+  static const char* OVERLAY_SCREENSHOT;
+  static const char* OVERLAY_PAUSED;
+};
+
 class CORE_EXPORT InspectTool : public GarbageCollected<InspectTool> {
  public:
   InspectTool(InspectorOverlayAgent* overlay, OverlayFrontend* frontend)
       : overlay_(overlay), frontend_(frontend) {}
   virtual ~InspectTool() = default;
 
-  virtual int GetDataResourceId();
+  virtual String GetOverlayName() = 0;
   virtual bool HandleInputEvent(LocalFrameView* frame_view,
                                 const WebInputEvent& input_event,
                                 bool* swallow_next_mouse_up);
@@ -115,7 +127,7 @@
         Color outline_color,
         InspectorOverlayAgent* overlay);
   ~Hinge() = default;
-  static int GetDataResourceId();
+  String GetOverlayName();
   void Draw(float scale);
   void Trace(Visitor* visitor) const;
 
@@ -253,7 +265,7 @@
   // Set or clear a mode tool, or add a highlight tool
   protocol::Response SetInspectTool(InspectTool* inspect_tool);
   void ClearInspectTool();
-  void LoadFrameForTool(int data_resource_id);
+  void LoadOverlayPageResource();
   void EnsureEnableFrameOverlay();
   void DisableFrameOverlay();
   InspectorSourceOrderConfig SourceOrderConfigFromInspectorObject(
@@ -266,7 +278,6 @@
   Member<WebLocalFrameImpl> frame_impl_;
   Member<InspectedFrames> inspected_frames_;
   Member<Page> overlay_page_;
-  int frame_resource_name_;
   Member<InspectorOverlayChromeClient> overlay_chrome_client_;
   Member<InspectorOverlayHost> overlay_host_;
   bool resize_timer_active_;
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
index ff29c5b..d4b08017 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -132,7 +132,7 @@
 //   https://w3c.github.io/IntersectionObserver/v2/#calculate-visibility-algo
 bool ComputeIsVisible(const LayoutObject* target, const PhysicalRect& rect) {
   if (target->GetDocument().GetFrame()->LocalFrameRoot().GetOcclusionState() !=
-      FrameOcclusionState::kGuaranteedNotOccluded) {
+      mojom::blink::FrameOcclusionState::kGuaranteedNotOccluded) {
     return false;
   }
   if (target->HasDistortingVisualEffects())
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
index 8e3ab1a..1ca7b06 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
@@ -130,12 +130,12 @@
     return false;
   }
   if (target_->isConnected() && Observer()->trackVisibility()) {
-    FrameOcclusionState occlusion_state =
+    mojom::blink::FrameOcclusionState occlusion_state =
         target_->GetDocument().GetFrame()->GetOcclusionState();
     // If we're tracking visibility, and we don't have occlusion information
     // from our parent frame, then postpone computing intersections until a
     // later lifecycle when the occlusion information is known.
-    if (occlusion_state == FrameOcclusionState::kUnknown)
+    if (occlusion_state == mojom::blink::FrameOcclusionState::kUnknown)
       return false;
   }
   last_run_time_ = timestamp;
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
index e909331e..6911d05 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_layout_algorithm.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_space_utils.h"
 #include "third_party/blink/renderer/core/style/grid_positions_resolver.h"
 
 namespace blink {
@@ -275,17 +276,17 @@
 
 NGConstraintSpace NGGridLayoutAlgorithm::BuildSpaceForGridItem(
     const NGBlockNode node) const {
-  NGConstraintSpaceBuilder space_builder(ConstraintSpace(),
-                                         node.Style().GetWritingMode(),
-                                         node.CreatesNewFormattingContext());
-
-  space_builder.SetCacheSlot(NGCacheSlot::kMeasure);
-  space_builder.SetIsPaintedAtomically(true);
-  space_builder.SetAvailableSize(ChildAvailableSize());
-  space_builder.SetPercentageResolutionSize(child_percentage_size_);
-  space_builder.SetTextDirection(node.Style().Direction());
-  space_builder.SetIsShrinkToFit(node.Style().LogicalWidth().IsAuto());
-  return space_builder.ToConstraintSpace();
+  const auto& style = node.Style();
+  NGConstraintSpaceBuilder builder(ConstraintSpace(), style.GetWritingMode(),
+                                   /* is_new_fc */ true);
+  SetOrthogonalFallbackInlineSizeIfNeeded(Style(), node, &builder);
+  builder.SetCacheSlot(NGCacheSlot::kMeasure);
+  builder.SetIsPaintedAtomically(true);
+  builder.SetAvailableSize(ChildAvailableSize());
+  builder.SetPercentageResolutionSize(child_percentage_size_);
+  builder.SetTextDirection(style.Direction());
+  builder.SetIsShrinkToFit(style.LogicalWidth().IsAuto());
+  return builder.ToConstraintSpace();
 }
 
 void NGGridLayoutAlgorithm::SetSpecifiedTracks() {
@@ -504,18 +505,18 @@
 
       // Layout child nodes based on constraint space from grid row/column
       // definitions and the inline and block offsets being accumulated.
-      NGConstraintSpaceBuilder space_builder(
-          ConstraintSpace(), child_node.Style().GetWritingMode(),
-          /* is_new_fc */ true);
-      space_builder.SetIsPaintedAtomically(true);
-      space_builder.SetAvailableSize(
+      const auto& child_style = child_node.Style();
+      NGConstraintSpaceBuilder builder(ConstraintSpace(),
+                                       child_style.GetWritingMode(),
+                                       /* is_new_fc */ true);
+      SetOrthogonalFallbackInlineSizeIfNeeded(Style(), child_node, &builder);
+      builder.SetIsPaintedAtomically(true);
+      builder.SetAvailableSize(LogicalSize(column_base_size, row_base_size));
+      builder.SetPercentageResolutionSize(
           LogicalSize(column_base_size, row_base_size));
-      space_builder.SetPercentageResolutionSize(
-          LogicalSize(column_base_size, row_base_size));
-      space_builder.SetTextDirection(child_node.Style().Direction());
-      space_builder.SetIsShrinkToFit(
-          child_node.Style().LogicalWidth().IsAuto());
-      NGConstraintSpace constraint_space = space_builder.ToConstraintSpace();
+      builder.SetTextDirection(child_style.Direction());
+      builder.SetIsShrinkToFit(child_style.LogicalWidth().IsAuto());
+      NGConstraintSpace constraint_space = builder.ToConstraintSpace();
       scoped_refptr<const NGLayoutResult> result =
           child_node.Layout(constraint_space);
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 7ed1b99..50d7a57c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -877,10 +877,8 @@
       unsigned line_text_offset =
           item_result.StartOffset() - line_info->StartOffset();
       DCHECK_EQ(kObjectReplacementCharacter, line_text[line_text_offset]);
-      item_result.inline_size += spacing.ComputeSpacing(
-          line_text_offset, item_result.inline_size.ToFloat(),
-          0.0 /* advance-override */, 1.0 /* advance-proportional-override */,
-          offset);
+      item_result.inline_size +=
+          spacing.ComputeSpacing(line_text_offset, offset);
       // |offset| is non-zero only before CJK characters.
       DCHECK_EQ(offset, 0.f);
     }
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 50beea0..5db72aa 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -40,6 +40,7 @@
 #include "third_party/blink/public/common/input/web_menu_source_type.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/common/widget/screen_info.h"
+#include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_spell_check_panel_host_client.h"
@@ -405,8 +406,7 @@
   unsigned BackForwardLength() override { return 0; }
   void FrameRectsChanged(const IntRect& local_frame_rect,
                          const IntRect& transformed_frame_rect) override {}
-  void UpdateRemoteViewportIntersection(
-      const ViewportIntersectionState& intersection_state) override {}
+  void SynchronizeVisualProperties() override {}
   AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() override {
     return AssociatedInterfaceProvider::GetEmptyAssociatedInterfaceProvider();
   }
diff --git a/third_party/blink/renderer/core/loader/prerender_test.cc b/third_party/blink/renderer/core/loader/prerender_test.cc
index 7d658b6b..c28ee0a 100644
--- a/third_party/blink/renderer/core/loader/prerender_test.cc
+++ b/third_party/blink/renderer/core/loader/prerender_test.cc
@@ -83,14 +83,10 @@
     client_.Bind(std::move(client));
   }
   void Cancel() override { cancel_count_++; }
-  void Abandon() override { abandon_count_++; }
 
   // Returns the number of times |Cancel| was called.
   size_t CancelCount() const { return cancel_count_; }
 
-  // Returns the number of times |Abandon| was called.
-  size_t AbandonCount() const { return abandon_count_; }
-
   const KURL& Url() const { return attributes_->url; }
   mojom::blink::PrerenderRelType RelType() const {
     return attributes_->rel_type;
@@ -122,7 +118,6 @@
   mojo::Receiver<mojom::blink::PrerenderProcessor> receiver_{this};
 
   size_t cancel_count_ = 0;
-  size_t abandon_count_ = 0;
 };
 
 class PrerenderTest : public testing::Test {
@@ -242,7 +237,6 @@
   EXPECT_EQ(mojom::blink::PrerenderRelType::kPrerender, processor.RelType());
 
   EXPECT_EQ(0u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
 
   EXPECT_FALSE(IsUseCounted(WebFeature::kWebkitPrerenderStartEventFired));
   processor.NotifyDidStartPrerender();
@@ -277,33 +271,10 @@
   MockPrerenderProcessor& processor = *processors()[0];
 
   EXPECT_EQ(0u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
 
   ExecuteScript("removePrerender()");
 
   EXPECT_EQ(1u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
-}
-
-TEST_F(PrerenderTest, AbandonPrerender) {
-  Initialize("http://www.foo.com/", "prerender/single_prerender.html");
-  ASSERT_EQ(processors().size(), 1u);
-  MockPrerenderProcessor& processor = *processors()[0];
-
-  EXPECT_EQ(0u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
-
-  NavigateAway();
-
-  EXPECT_EQ(0u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
-
-  // Check that the prerender does not emit an extra cancel when
-  // garbage-collecting everything.
-  Close();
-
-  EXPECT_EQ(0u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
 }
 
 TEST_F(PrerenderTest, TwoPrerenders) {
@@ -316,9 +287,7 @@
   EXPECT_EQ(KURL("http://second-prerender.com/"), second_processor.Url());
 
   EXPECT_EQ(0u, first_processor.CancelCount());
-  EXPECT_EQ(0u, first_processor.AbandonCount());
   EXPECT_EQ(0u, second_processor.CancelCount());
-  EXPECT_EQ(0u, second_processor.AbandonCount());
 
   first_processor.NotifyDidStartPrerender();
   EXPECT_EQ(1u, ConsoleLength());
@@ -337,23 +306,17 @@
   MockPrerenderProcessor& second_processor = *processors()[1];
 
   EXPECT_EQ(0u, first_processor.CancelCount());
-  EXPECT_EQ(0u, first_processor.AbandonCount());
   EXPECT_EQ(0u, second_processor.CancelCount());
-  EXPECT_EQ(0u, second_processor.AbandonCount());
 
   ExecuteScript("removeFirstPrerender()");
 
   EXPECT_EQ(1u, first_processor.CancelCount());
-  EXPECT_EQ(0u, first_processor.AbandonCount());
   EXPECT_EQ(0u, second_processor.CancelCount());
-  EXPECT_EQ(0u, second_processor.AbandonCount());
 
   NavigateAway();
 
   EXPECT_EQ(1u, first_processor.CancelCount());
-  EXPECT_EQ(0u, first_processor.AbandonCount());
   EXPECT_EQ(0u, second_processor.CancelCount());
-  EXPECT_EQ(0u, second_processor.AbandonCount());
 }
 
 TEST_F(PrerenderTest, TwoPrerendersAddingThird) {
@@ -364,9 +327,7 @@
   MockPrerenderProcessor& second_processor = *processors()[1];
 
   EXPECT_EQ(0u, first_processor.CancelCount());
-  EXPECT_EQ(0u, first_processor.AbandonCount());
   EXPECT_EQ(0u, second_processor.CancelCount());
-  EXPECT_EQ(0u, second_processor.AbandonCount());
 
   ExecuteScript("addThirdPrerender()");
 
@@ -374,11 +335,8 @@
   MockPrerenderProcessor& third_processor = *processors()[2];
 
   EXPECT_EQ(0u, first_processor.CancelCount());
-  EXPECT_EQ(0u, first_processor.AbandonCount());
   EXPECT_EQ(0u, second_processor.CancelCount());
-  EXPECT_EQ(0u, second_processor.AbandonCount());
   EXPECT_EQ(0u, third_processor.CancelCount());
-  EXPECT_EQ(0u, third_processor.AbandonCount());
 }
 
 TEST_F(PrerenderTest, ShortLivedClient) {
@@ -387,7 +345,6 @@
   MockPrerenderProcessor& processor = *processors()[0];
 
   EXPECT_EQ(0u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
 
   NavigateAway();
   Close();
@@ -402,7 +359,6 @@
   MockPrerenderProcessor& processor = *processors()[0];
 
   EXPECT_EQ(0u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
 
   // Race removing & starting the prerender against each other, as if the
   // element was removed very quickly.
@@ -411,7 +367,6 @@
 
   // Removing the element should cancel prerendering.
   EXPECT_EQ(1u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
 
   // The page should be totally disconnected from the Prerender at this point,
   // so the console should not have updated.
@@ -426,7 +381,6 @@
   EXPECT_EQ(KURL("http://prerender.com/"), processor.Url());
 
   EXPECT_EQ(0u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
 
   // Change the href of this prerender, make sure this is treated as a remove
   // and add.
@@ -437,9 +391,7 @@
   EXPECT_EQ(KURL("http://mutated.com/"), mutated_processor.Url());
 
   EXPECT_EQ(1u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
   EXPECT_EQ(0u, mutated_processor.CancelCount());
-  EXPECT_EQ(0u, mutated_processor.AbandonCount());
 }
 
 TEST_F(PrerenderTest, MutateRel) {
@@ -450,13 +402,11 @@
   EXPECT_EQ(KURL("http://prerender.com/"), processor.Url());
 
   EXPECT_EQ(0u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
 
   // Change the rel of this prerender, make sure this is treated as a remove.
   ExecuteScript("mutateRel()");
 
   EXPECT_EQ(1u, processor.CancelCount());
-  EXPECT_EQ(0u, processor.AbandonCount());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/private/prerender_handle.cc b/third_party/blink/renderer/core/loader/private/prerender_handle.cc
index bc956ed8..0033081 100644
--- a/third_party/blink/renderer/core/loader/private/prerender_handle.cc
+++ b/third_party/blink/renderer/core/loader/private/prerender_handle.cc
@@ -89,8 +89,7 @@
     const KURL& url,
     HeapMojoRemote<mojom::blink::PrerenderProcessor> remote_processor,
     mojo::PendingReceiver<mojom::blink::PrerenderProcessorClient> receiver)
-    : ExecutionContextLifecycleObserver(context),
-      url_(url),
+    : url_(url),
       client_(client),
       remote_processor_(std::move(remote_processor)),
       receiver_(this, context) {
@@ -100,37 +99,17 @@
 
 PrerenderHandle::~PrerenderHandle() = default;
 
-void PrerenderHandle::Dispose() {
-  // TODO(https://crbug.com/1130360): This condition is never satisfied and
-  // Abandon() is not called. See the issue for details. We should fix this.
-  if (remote_processor_.is_bound() &&
-      !GetExecutionContext()->IsContextDestroyed())
-    remote_processor_->Abandon();
-  Detach();
-}
-
 void PrerenderHandle::Cancel() {
-  // Avoid both abandoning and canceling the same prerender. In the abandon
-  // case, the LinkLoader cancels the PrerenderHandle as the Document is
-  // destroyed, even through the ExecutionContextLifecycleObserver has already
-  // abandoned it.
   if (remote_processor_.is_bound())
     remote_processor_->Cancel();
-  Detach();
+  remote_processor_.reset();
+  receiver_.reset();
 }
 
 const KURL& PrerenderHandle::Url() const {
   return url_;
 }
 
-void PrerenderHandle::ContextDestroyed() {
-  // A PrerenderHandle is not removed from LifecycleNotifier::m_observers until
-  // the next GC runs. Thus contextDestroyed() can be called for a
-  // PrerenderHandle that is already cancelled (and thus detached). In that
-  // case, we should not detach the PrerenderHandle again.
-  Dispose();
-}
-
 void PrerenderHandle::OnPrerenderStart() {
   if (client_)
     client_->DidStartPrerender();
@@ -155,12 +134,6 @@
   visitor->Trace(client_);
   visitor->Trace(remote_processor_);
   visitor->Trace(receiver_);
-  ExecutionContextLifecycleObserver::Trace(visitor);
-}
-
-void PrerenderHandle::Detach() {
-  remote_processor_.reset();
-  receiver_.reset();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/private/prerender_handle.h b/third_party/blink/renderer/core/loader/private/prerender_handle.h
index 8fd335a..e57bee1 100644
--- a/third_party/blink/renderer/core/loader/private/prerender_handle.h
+++ b/third_party/blink/renderer/core/loader/private/prerender_handle.h
@@ -53,12 +53,16 @@
 // instantiated per prerender request, for example, when a new <link
 // rel=prerender> element is added, when the element's href is changed etc.
 //
-// TODO(https://crbug.com/1126305): Rename this to PrerenderProcessorHandle.
+// When you no longer need the prerendering page (e.g., when the
+// <link rel=prerender> element is removed), you can ask the browser process to
+// cancel the running prerender by Cancel(). If mojo connections are reset
+// without Cancel() call, the browser process considers this prerendering
+// request to be abandoned and may still use the prerendered page if a
+// navigation occurs to that URL shortly after.
+//
+// TODO(https://crbug.com/1126305): Rename this to PrerenderProcessorClient.
 class PrerenderHandle final : public GarbageCollected<PrerenderHandle>,
-                              public ExecutionContextLifecycleObserver,
                               public mojom::blink::PrerenderProcessorClient {
-  USING_PRE_FINALIZER(PrerenderHandle, Dispose);
-
  public:
   static PrerenderHandle* Create(
       Document&,
@@ -75,13 +79,11 @@
       HeapMojoRemote<mojom::blink::PrerenderProcessor>,
       mojo::PendingReceiver<mojom::blink::PrerenderProcessorClient>);
   ~PrerenderHandle() override;
-  void Dispose();
 
+  // Asks the browser process to cancel the running prerender.
   void Cancel();
-  const KURL& Url() const;
 
-  // ExecutionContextLifecycleObserver:
-  void ContextDestroyed() override;
+  const KURL& Url() const;
 
   // mojom::blink::PrerenderProcessorClient:
   void OnPrerenderStart() override;
@@ -89,13 +91,11 @@
   void OnPrerenderDomContentLoaded() override;
   void OnPrerenderStop() override;
 
-  void Trace(Visitor*) const override;
+  virtual void Trace(Visitor*) const;
 
  private:
-  void Detach();
-
-  KURL url_;
-  WeakMember<PrerenderClient> client_;
+  const KURL url_;
+  const WeakMember<PrerenderClient> client_;
   HeapMojoRemote<mojom::blink::PrerenderProcessor> remote_processor_;
   HeapMojoReceiver<mojom::blink::PrerenderProcessorClient, PrerenderHandle>
       receiver_;
diff --git a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
index 16d82e95..c98a83c 100644
--- a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -1665,12 +1666,13 @@
       "<iframe id=child-frame sandbox src=child-iframe.html></iframe>");
   child_frame_resource.Complete("");
 
-  ViewportIntersectionState intersection;
-  intersection.main_frame_intersection = WebRect(0, 0, 100, 100);
-  intersection.main_frame_viewport_size = WebSize(100, 100);
-  intersection.viewport_intersection = WebRect(0, 0, 100, 100);
+  mojom::blink::ViewportIntersectionState intersection;
+  intersection.main_frame_intersection = gfx::Rect(0, 0, 100, 100);
+  intersection.main_frame_viewport_size = gfx::Size(100, 100);
+  intersection.viewport_intersection = gfx::Rect(0, 0, 100, 100);
   LocalFrameRoot().FrameWidget()->Resize(gfx::Size(300, 200));
-  LocalFrameRoot().FrameWidget()->SetRemoteViewportIntersection(intersection);
+  static_cast<WebFrameWidgetBase*>(LocalFrameRoot().FrameWidget())
+      ->SetRemoteViewportIntersection(intersection);
 
   auto* root_frame = LocalFrameRoot().GetFrame();
   auto* frame_document =
@@ -1696,7 +1698,7 @@
   EXPECT_EQ(root_frame->RemoteViewportIntersection(), IntRect(0, 0, 100, 100));
   EXPECT_TRUE(root_frame->View()->CanThrottleRenderingForPropagation());
   EXPECT_EQ(root_frame->GetOcclusionState(),
-            FrameOcclusionState::kPossiblyOccluded);
+            mojom::FrameOcclusionState::kPossiblyOccluded);
   EXPECT_TRUE(frame_view->CanThrottleRendering());
   EXPECT_TRUE(child_view->CanThrottleRendering());
   EXPECT_FALSE(Compositor().NeedsBeginFrame());
@@ -1710,12 +1712,13 @@
 
   // Show the frame without any other change.
   LocalFrameRoot().WasShown();
-  LocalFrameRoot().FrameWidget()->SetRemoteViewportIntersection(intersection);
+  static_cast<WebFrameWidgetBase*>(LocalFrameRoot().FrameWidget())
+      ->SetRemoteViewportIntersection(intersection);
   CompositeFrame();
   EXPECT_EQ(root_frame->RemoteViewportIntersection(), IntRect(0, 0, 100, 100));
   EXPECT_FALSE(root_frame->View()->CanThrottleRenderingForPropagation());
   EXPECT_NE(root_frame->GetOcclusionState(),
-            FrameOcclusionState::kPossiblyOccluded);
+            mojom::FrameOcclusionState::kPossiblyOccluded);
   EXPECT_FALSE(frame_view->CanThrottleRendering());
   // The child frame's throtting status is not updated because the parent
   // document has pending visual update.
diff --git a/third_party/blink/renderer/core/testing/fake_remote_frame_host.cc b/third_party/blink/renderer/core/testing/fake_remote_frame_host.cc
index 86dfcdb..67e8205 100644
--- a/third_party/blink/renderer/core/testing/fake_remote_frame_host.cc
+++ b/third_party/blink/renderer/core/testing/fake_remote_frame_host.cc
@@ -51,6 +51,9 @@
 
 void FakeRemoteFrameHost::Detach() {}
 
+void FakeRemoteFrameHost::UpdateViewportIntersection(
+    blink::mojom::blink::ViewportIntersectionStatePtr intersection_state) {}
+
 void FakeRemoteFrameHost::BindFrameHostReceiver(
     mojo::ScopedInterfaceEndpointHandle handle) {
   receiver_.Bind(mojo::PendingAssociatedReceiver<mojom::blink::RemoteFrameHost>(
diff --git a/third_party/blink/renderer/core/testing/fake_remote_frame_host.h b/third_party/blink/renderer/core/testing/fake_remote_frame_host.h
index 1484703..3b8c6029 100644
--- a/third_party/blink/renderer/core/testing/fake_remote_frame_host.h
+++ b/third_party/blink/renderer/core/testing/fake_remote_frame_host.h
@@ -46,6 +46,9 @@
   void PrintCrossProcessSubframe(const gfx::Rect& rect,
                                  int document_cookie) override;
   void Detach() override;
+  void UpdateViewportIntersection(
+      blink::mojom::blink::ViewportIntersectionStatePtr intersection_state)
+      override;
 
  private:
   void BindFrameHostReceiver(mojo::ScopedInterfaceEndpointHandle handle);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 6b2192f..cac010c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1892,7 +1892,7 @@
   } else if (attr_name == html_names::kAriaHiddenAttr) {
     ChildrenChangedWithCleanLayout(element->parentNode());
   } else if (attr_name == html_names::kAriaInvalidAttr) {
-    PostNotification(element, ax::mojom::Event::kInvalidStatusChanged);
+    MarkElementDirty(element, false);
   } else if (attr_name == html_names::kAriaErrormessageAttr) {
     MarkElementDirty(element, false);
   } else if (attr_name == html_names::kAriaOwnsAttr) {
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.cc b/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.cc
index a273084a..5cee0d8 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.cc
@@ -15,7 +15,7 @@
     : Supplement<LocalDOMWindow>(window),
       authenticator_(window.GetExecutionContext()),
       credential_manager_(window.GetExecutionContext()),
-      sms_receiver_(window.GetExecutionContext()),
+      webotp_service_(window.GetExecutionContext()),
       payment_credential_(window.GetExecutionContext()) {
   LocalFrame* frame = window.GetFrame();
   DCHECK(frame);
@@ -29,15 +29,15 @@
 
 CredentialManagerProxy::~CredentialManagerProxy() = default;
 
-mojom::blink::SmsReceiver* CredentialManagerProxy::SmsReceiver() {
-  if (!sms_receiver_.is_bound()) {
+mojom::blink::WebOTPService* CredentialManagerProxy::WebOTPService() {
+  if (!webotp_service_.is_bound()) {
     LocalFrame* frame = GetSupplementable()->GetFrame();
     DCHECK(frame);
     frame->GetBrowserInterfaceBroker().GetInterface(
-        sms_receiver_.BindNewPipeAndPassReceiver(
+        webotp_service_.BindNewPipeAndPassReceiver(
             frame->GetTaskRunner(TaskType::kMiscPlatformAPI)));
   }
-  return sms_receiver_.get();
+  return webotp_service_.get();
 }
 
 payments::mojom::blink::PaymentCredential*
@@ -69,7 +69,7 @@
 void CredentialManagerProxy::Trace(Visitor* visitor) const {
   visitor->Trace(authenticator_);
   visitor->Trace(credential_manager_);
-  visitor->Trace(sms_receiver_);
+  visitor->Trace(webotp_service_);
   visitor->Trace(payment_credential_);
   Supplement<LocalDOMWindow>::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.h b/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.h
index ff4c8a84..d10c398 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.h
+++ b/third_party/blink/renderer/modules/credentialmanager/credential_manager_proxy.h
@@ -7,7 +7,7 @@
 
 #include "third_party/blink/public/mojom/credentialmanager/credential_manager.mojom-blink.h"
 #include "third_party/blink/public/mojom/payments/payment_credential.mojom-blink.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-blink.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom-blink.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom-blink.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -46,7 +46,7 @@
 
   mojom::blink::Authenticator* Authenticator() { return authenticator_.get(); }
 
-  mojom::blink::SmsReceiver* SmsReceiver();
+  mojom::blink::WebOTPService* WebOTPService();
 
   payments::mojom::blink::PaymentCredential* PaymentCredential();
 
@@ -59,7 +59,7 @@
  private:
   HeapMojoRemote<mojom::blink::Authenticator> authenticator_;
   HeapMojoRemote<mojom::blink::CredentialManager> credential_manager_;
-  HeapMojoRemote<mojom::blink::SmsReceiver> sms_receiver_;
+  HeapMojoRemote<mojom::blink::WebOTPService> webotp_service_;
   HeapMojoRemote<payments::mojom::blink::PaymentCredential> payment_credential_;
 };
 
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_metrics.cc b/third_party/blink/renderer/modules/credentialmanager/credential_metrics.cc
index 7543761..d06625b 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credential_metrics.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credential_metrics.cc
@@ -10,7 +10,7 @@
 
 namespace blink {
 
-void RecordSmsOutcome(SMSReceiverOutcome outcome,
+void RecordSmsOutcome(WebOTPServiceOutcome outcome,
                       ukm::SourceId source_id,
                       ukm::UkmRecorder* ukm_recorder) {
   DCHECK_NE(source_id, ukm::kInvalidSourceId);
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_metrics.h b/third_party/blink/renderer/modules/credentialmanager/credential_metrics.h
index 869eaac..123682e 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credential_metrics.h
+++ b/third_party/blink/renderer/modules/credentialmanager/credential_metrics.h
@@ -9,7 +9,7 @@
 
 #include "base/time/time.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
-#include "third_party/blink/public/common/sms/sms_receiver_outcome.h"
+#include "third_party/blink/public/common/sms/webotp_service_outcome.h"
 
 namespace ukm {
 class UkmRecorder;
@@ -18,22 +18,22 @@
 namespace blink {
 
 // Records the result of a call to navigator.credentials.get({otp}) using
-// the same histogram as SmsReceiver API to provide continuity with previous
+// the same histogram as WebOTPService API to provide continuity with previous
 // iterations of the API.
-void RecordSmsOutcome(SMSReceiverOutcome outcome,
+void RecordSmsOutcome(WebOTPServiceOutcome outcome,
                       ukm::SourceId source_id,
                       ukm::UkmRecorder* ukm_recorder);
 
 // Records the time from when the API is called to when the user successfully
 // receives the SMS and presses verify to move on with the verification flow.
-// This uses the same histogram as SmsReceiver API to provide continuity with
+// This uses the same histogram as WebOTPService API to provide continuity with
 // previous iterations of the API.
 void RecordSmsSuccessTime(base::TimeDelta duration,
                           ukm::SourceId source_id,
                           ukm::UkmRecorder* ukm_recorder);
 
 // Records the time from when the API is called to when the user dismisses the
-// infobar to abort SMS retrieval. This uses the same histogram as SmsReceiver
+// infobar to abort SMS retrieval. This uses the same histogram as WebOTPService
 // API to provide continuity with previous iterations of the API.
 void RecordSmsCancelTime(base::TimeDelta duration);
 
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_request_options.idl b/third_party/blink/renderer/modules/credentialmanager/credential_request_options.idl
index 4665db5..ffd56e8 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credential_request_options.idl
+++ b/third_party/blink/renderer/modules/credentialmanager/credential_request_options.idl
@@ -16,7 +16,7 @@
     boolean password = false;
     CredentialMediationRequirement mediation = "optional";
 
-    [RuntimeEnabled=SmsReceiver] OTPCredentialRequestOptions otp;
+    [RuntimeEnabled=WebOTP] OTPCredentialRequestOptions otp;
 
     PublicKeyCredentialRequestOptions publicKey;
     AbortSignal signal;
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
index b9f4717..3c0e6419 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -9,11 +9,11 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
-#include "third_party/blink/public/common/sms/sms_receiver_outcome.h"
+#include "third_party/blink/public/common/sms/webotp_service_outcome.h"
 #include "third_party/blink/public/mojom/credentialmanager/credential_manager.mojom-blink.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
 #include "third_party/blink/public/mojom/payments/payment_credential.mojom-blink.h"
-#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-blink.h"
+#include "third_party/blink/public/mojom/sms/webotp_service.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_authentication_extensions_client_inputs.h"
@@ -356,9 +356,9 @@
   if (!script_state->ContextIsValid())
     return;
 
-  auto* sms_receiver =
-      CredentialManagerProxy::From(script_state)->SmsReceiver();
-  sms_receiver->Abort();
+  auto* webotp_service =
+      CredentialManagerProxy::From(script_state)->WebOTPService();
+  webotp_service->Abort();
 }
 
 void OnStoreComplete(std::unique_ptr<ScopedPromiseResolver> scoped_resolver) {
@@ -560,19 +560,19 @@
   ukm::UkmRecorder* recorder = window.UkmRecorder();
 
   if (status == mojom::blink::SmsStatus::kUnhandledRequest) {
-    RecordSmsOutcome(SMSReceiverOutcome::kUnhandledRequest, source_id,
+    RecordSmsOutcome(WebOTPServiceOutcome::kUnhandledRequest, source_id,
                      recorder);
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidStateError,
         "OTP retrieval request not handled."));
     return;
   } else if (status == mojom::blink::SmsStatus::kAborted) {
-    RecordSmsOutcome(SMSReceiverOutcome::kAborted, source_id, recorder);
+    RecordSmsOutcome(WebOTPServiceOutcome::kAborted, source_id, recorder);
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kAbortError, "OTP retrieval was aborted."));
     return;
   } else if (status == mojom::blink::SmsStatus::kCancelled) {
-    RecordSmsOutcome(SMSReceiverOutcome::kCancelled, source_id, recorder);
+    RecordSmsOutcome(WebOTPServiceOutcome::kCancelled, source_id, recorder);
     RecordSmsCancelTime(base::TimeTicks::Now() - start_time);
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kAbortError, "OTP retrieval was cancelled."));
@@ -580,7 +580,7 @@
   }
   RecordSmsSuccessTime(base::TimeTicks::Now() - start_time, source_id,
                        recorder);
-  RecordSmsOutcome(SMSReceiverOutcome::kSuccess, source_id, recorder);
+  RecordSmsOutcome(WebOTPServiceOutcome::kSuccess, source_id, recorder);
   resolver->Resolve(MakeGarbageCollected<OTPCredential>(otp));
 }
 
@@ -940,10 +940,10 @@
       return promise;
     }
 
-    auto* sms_receiver =
-        CredentialManagerProxy::From(script_state)->SmsReceiver();
-    sms_receiver->Receive(WTF::Bind(&OnSmsReceive, WrapPersistent(resolver),
-                                    base::TimeTicks::Now()));
+    auto* webotp_service =
+        CredentialManagerProxy::From(script_state)->WebOTPService();
+    webotp_service->Receive(WTF::Bind(&OnSmsReceive, WrapPersistent(resolver),
+                                      base::TimeTicks::Now()));
     UMA_HISTOGRAM_ENUMERATION("Blink.UseCounter.Features", WebFeature::kWebOTP);
     return promise;
   }
diff --git a/third_party/blink/renderer/modules/credentialmanager/otp_credential.idl b/third_party/blink/renderer/modules/credentialmanager/otp_credential.idl
index 5e70763..a344177 100644
--- a/third_party/blink/renderer/modules/credentialmanager/otp_credential.idl
+++ b/third_party/blink/renderer/modules/credentialmanager/otp_credential.idl
@@ -4,7 +4,7 @@
 
 // https://github.com/WICG/web-otp/
 [
-    RuntimeEnabled=SmsReceiver,
+    RuntimeEnabled=WebOTP,
     Exposed=Window,
     SecureContext
 ] interface OTPCredential : Credential {
diff --git a/third_party/blink/renderer/modules/event_target_modules_names.json5 b/third_party/blink/renderer/modules/event_target_modules_names.json5
index 0a01bc8..95dcae88 100644
--- a/third_party/blink/renderer/modules/event_target_modules_names.json5
+++ b/third_party/blink/renderer/modules/event_target_modules_names.json5
@@ -58,7 +58,7 @@
     "ServiceWorkerContainer",
     "ServiceWorkerGlobalScope",
     "ServiceWorkerRegistration",
-    "SMSReceiver",
+    "WebOTPService",
     "SpeechRecognition",
     "SpeechSynthesis",
     "SpeechSynthesisUtterance",
diff --git a/third_party/blink/renderer/modules/mediastream/BUILD.gn b/third_party/blink/renderer/modules/mediastream/BUILD.gn
index 11b0207..ef7f768 100644
--- a/third_party/blink/renderer/modules/mediastream/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediastream/BUILD.gn
@@ -11,6 +11,8 @@
     "apply_constraints_request.cc",
     "apply_constraints_request.h",
     "dom_window_media_stream.h",
+    "identifiability_metrics.cc",
+    "identifiability_metrics.h",
     "input_device_info.cc",
     "input_device_info.h",
     "local_media_stream_audio_source.cc",
diff --git a/third_party/blink/renderer/modules/mediastream/identifiability_metrics.cc b/third_party/blink/renderer/modules/mediastream/identifiability_metrics.cc
new file mode 100644
index 0000000..0525e9ca
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediastream/identifiability_metrics.cc
@@ -0,0 +1,257 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/mediastream/identifiability_metrics.h"
+
+#include "base/callback.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
+#include "third_party/blink/public/common/privacy_budget/identifiable_token.h"
+#include "third_party/blink/renderer/bindings/modules/v8/boolean_or_constrain_boolean_parameters.h"
+#include "third_party/blink/renderer/bindings/modules/v8/boolean_or_double_or_constrain_double_range.h"
+#include "third_party/blink/renderer/bindings/modules/v8/boolean_or_media_track_constraints.h"
+#include "third_party/blink/renderer/bindings/modules/v8/double_or_constrain_double_range.h"
+#include "third_party/blink/renderer/bindings/modules/v8/long_or_constrain_long_range.h"
+#include "third_party/blink/renderer/bindings/modules/v8/point_2d_sequence_or_constrain_point_2d_parameters.h"
+#include "third_party/blink/renderer/bindings/modules/v8/string_or_string_sequence_or_constrain_dom_string_parameters.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/modules/mediastream/media_track_constraint_set.h"
+#include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
+
+namespace blink {
+
+namespace {
+
+template <typename T>
+void Visit(IdentifiableTokenBuilder& builder, const T* range) {
+  if (!range)
+    return;
+  builder.AddToken(range->hasExact() ? range->exact() : IdentifiableToken());
+  builder.AddToken(range->hasIdeal() ? range->ideal() : IdentifiableToken());
+  builder.AddToken(range->hasMax() ? range->max() : IdentifiableToken());
+  builder.AddToken(range->hasMin() ? range->min() : IdentifiableToken());
+}
+
+void Visit(IdentifiableTokenBuilder& builder,
+           const DoubleOrConstrainDoubleRange& d) {
+  if (d.IsDouble()) {
+    builder.AddToken(d.GetAsDouble());
+    return;
+  }
+  if (d.IsConstrainDoubleRange()) {
+    Visit(builder, d.GetAsConstrainDoubleRange());
+    return;
+  }
+  DCHECK(d.IsNull());
+  builder.AddToken(IdentifiableToken());
+}
+
+void Visit(IdentifiableTokenBuilder& builder,
+           const LongOrConstrainLongRange& l) {
+  if (l.IsLong()) {
+    builder.AddToken(l.GetAsLong());
+    return;
+  }
+  if (l.IsConstrainLongRange()) {
+    Visit(builder, l.GetAsConstrainLongRange());
+    return;
+  }
+  DCHECK(l.IsNull());
+  builder.AddToken(IdentifiableToken());
+}
+
+void Visit(IdentifiableTokenBuilder& builder, const StringOrStringSequence& s) {
+  if (s.IsString()) {
+    builder.AddToken(IdentifiabilityBenignStringToken(s.GetAsString()));
+    return;
+  }
+  if (s.IsStringSequence()) {
+    for (const String& str : s.GetAsStringSequence()) {
+      builder.AddToken(IdentifiabilityBenignStringToken(str));
+    }
+    return;
+  }
+  DCHECK(s.IsNull());
+  builder.AddToken(IdentifiableToken());
+}
+
+void Visit(IdentifiableTokenBuilder& builder,
+           const StringOrStringSequenceOrConstrainDOMStringParameters& s) {
+  if (s.IsString()) {
+    builder.AddToken(IdentifiabilityBenignStringToken(s.GetAsString()));
+    return;
+  }
+  if (s.IsStringSequence()) {
+    for (const String& str : s.GetAsStringSequence()) {
+      builder.AddToken(IdentifiabilityBenignStringToken(str));
+    }
+    return;
+  }
+  if (s.IsConstrainDOMStringParameters()) {
+    const ConstrainDOMStringParameters* params =
+        s.GetAsConstrainDOMStringParameters();
+    if (params->hasExact()) {
+      Visit(builder, params->exact());
+    } else {
+      builder.AddToken(IdentifiableToken());
+    }
+    if (params->hasIdeal()) {
+      Visit(builder, params->ideal());
+    } else {
+      builder.AddToken(IdentifiableToken());
+    }
+    return;
+  }
+  DCHECK(s.IsNull());
+  builder.AddToken(IdentifiableToken());
+}
+
+void Visit(IdentifiableTokenBuilder& builder,
+           const BooleanOrConstrainBooleanParameters& b) {
+  if (b.IsBoolean()) {
+    builder.AddToken(b.GetAsBoolean());
+    return;
+  }
+  if (b.IsConstrainBooleanParameters()) {
+    const ConstrainBooleanParameters* params =
+        b.GetAsConstrainBooleanParameters();
+    builder.AddToken(params->hasExact() ? params->exact()
+                                        : IdentifiableToken());
+    builder.AddToken(params->hasIdeal() ? params->ideal()
+                                        : IdentifiableToken());
+    return;
+  }
+  DCHECK(b.IsNull());
+  builder.AddToken(IdentifiableToken());
+}
+
+void Visit(IdentifiableTokenBuilder& builder,
+           const BooleanOrDoubleOrConstrainDoubleRange& x) {
+  if (x.IsBoolean()) {
+    builder.AddToken(x.GetAsBoolean());
+    return;
+  }
+  if (x.IsConstrainDoubleRange()) {
+    Visit(builder, x.GetAsConstrainDoubleRange());
+    return;
+  }
+  if (x.IsDouble()) {
+    builder.AddToken(x.GetAsDouble());
+    return;
+  }
+  DCHECK(x.IsNull());
+  builder.AddToken(IdentifiableToken());
+}
+
+void Visit(IdentifiableTokenBuilder& builder,
+           const HeapVector<Member<Point2D>>& points) {
+  for (const auto& point : points) {
+    builder.AddToken(point->hasX() ? point->x() : IdentifiableToken());
+    builder.AddToken(point->hasY() ? point->y() : IdentifiableToken());
+  }
+}
+
+void Visit(IdentifiableTokenBuilder& builder,
+           const Point2DSequenceOrConstrainPoint2DParameters& x) {
+  if (x.IsPoint2DSequence()) {
+    Visit(builder, x.GetAsPoint2DSequence());
+    return;
+  }
+  if (x.IsConstrainPoint2DParameters()) {
+    const ConstrainPoint2DParameters* params =
+        x.GetAsConstrainPoint2DParameters();
+    if (params->hasExact()) {
+      Visit(builder, params->exact());
+    } else {
+      builder.AddToken(IdentifiableToken());
+    }
+    if (params->hasIdeal()) {
+      Visit(builder, params->ideal());
+    } else {
+      builder.AddToken(IdentifiableToken());
+    }
+    return;
+  }
+  DCHECK(x.IsNull());
+  builder.AddToken(IdentifiableToken());
+}
+
+void Visit(IdentifiableTokenBuilder& builder,
+           const MediaTrackConstraintSet& set) {
+  Visit(builder, set.width());
+  Visit(builder, set.height());
+  Visit(builder, set.aspectRatio());
+  Visit(builder, set.frameRate());
+  Visit(builder, set.facingMode());
+  Visit(builder, set.sampleRate());
+  Visit(builder, set.sampleSize());
+  Visit(builder, set.echoCancellation());
+  Visit(builder, set.autoGainControl());
+  Visit(builder, set.latency());
+  Visit(builder, set.channelCount());
+  Visit(builder, set.videoKind());
+  Visit(builder, set.whiteBalanceMode());
+  Visit(builder, set.exposureMode());
+  Visit(builder, set.focusMode());
+  Visit(builder, set.pointsOfInterest());
+  Visit(builder, set.exposureCompensation());
+  Visit(builder, set.exposureTime());
+  Visit(builder, set.colorTemperature());
+  Visit(builder, set.iso());
+  Visit(builder, set.brightness());
+  Visit(builder, set.contrast());
+  Visit(builder, set.saturation());
+  Visit(builder, set.sharpness());
+  Visit(builder, set.focusDistance());
+  Visit(builder, set.pan());
+  Visit(builder, set.tilt());
+  Visit(builder, set.zoom());
+  Visit(builder, set.torch());
+}
+
+void Visit(IdentifiableTokenBuilder& builder,
+           const BooleanOrMediaTrackConstraints& constraint) {
+  if (constraint.IsBoolean()) {
+    builder.AddToken(constraint.GetAsBoolean());
+    return;
+  }
+  if (constraint.IsMediaTrackConstraints()) {
+    const MediaTrackConstraints* constraints =
+        constraint.GetAsMediaTrackConstraints();
+    if (constraints) {
+      if (constraints->hasAdvanced()) {
+        for (const auto& advanced : constraints->advanced()) {
+          Visit(builder, *advanced);
+        }
+        return;
+      }
+    }
+  }
+  builder.AddToken(IdentifiableToken());
+}
+
+}  // namespace
+
+IdentifiableToken TokenFromConstraints(
+    const MediaStreamConstraints* constraints) {
+  IdentifiableTokenBuilder builder;
+  if (constraints) {
+    Visit(builder, constraints->audio());
+    Visit(builder, constraints->video());
+  } else {
+    builder.AddToken(IdentifiableToken());
+  }
+  return builder.GetToken();
+}
+
+void RecordIdentifiabilityMetric(const IdentifiableSurface& surface,
+                                 ExecutionContext* context,
+                                 IdentifiableToken token) {
+  if (surface.IsValid() && context) {
+    IdentifiabilityMetricBuilder(context->UkmSourceID())
+        .Set(surface, token)
+        .Record(context->UkmRecorder());
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/identifiability_metrics.h b/third_party/blink/renderer/modules/mediastream/identifiability_metrics.h
new file mode 100644
index 0000000..a718c58
--- /dev/null
+++ b/third_party/blink/renderer/modules/mediastream/identifiability_metrics.h
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_IDENTIFIABILITY_METRICS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_IDENTIFIABILITY_METRICS_H_
+
+#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
+#include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_constraints.h"
+
+namespace blink {
+
+IdentifiableToken TokenFromConstraints(
+    const MediaStreamConstraints* constraints);
+
+void RecordIdentifiabilityMetric(const IdentifiableSurface& surface,
+                                 ExecutionContext* context,
+                                 IdentifiableToken token);
+
+}  // namespace blink
+
+#endif
diff --git a/third_party/blink/renderer/modules/mediastream/media_devices.cc b/third_party/blink/renderer/modules/mediastream/media_devices.cc
index 5a2adbb..e389841 100644
--- a/third_party/blink/renderer/modules/mediastream/media_devices.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_devices.cc
@@ -10,7 +10,7 @@
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
 #include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
-#include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h"
+#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/modules/mediastream/identifiability_metrics.h"
 #include "third_party/blink/renderer/modules/mediastream/input_device_info.h"
 #include "third_party/blink/renderer/modules/mediastream/media_error_state.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
@@ -122,9 +123,16 @@
 
   LocalDOMWindow* window = LocalDOMWindow::From(script_state);
   UserMediaController* user_media = UserMediaController::From(window);
+  constexpr IdentifiableSurface::Type surface_type =
+      IdentifiableSurface::Type::kMediaDevices_GetUserMedia;
+  IdentifiableSurface surface;
+  if (IdentifiabilityStudySettings::Get()->IsTypeAllowed(surface_type)) {
+    surface = IdentifiableSurface::FromTypeAndToken(
+        surface_type, TokenFromConstraints(options));
+  }
   MediaErrorState error_state;
   UserMediaRequest* request = UserMediaRequest::Create(
-      window, user_media, media_type, options, callbacks, error_state);
+      window, user_media, media_type, options, callbacks, error_state, surface);
   if (!request) {
     DCHECK(error_state.HadException());
     if (error_state.CanGenerateException()) {
@@ -132,6 +140,9 @@
       return ScriptPromise();
     }
     ScriptPromise rejected_promise = resolver->Promise();
+    RecordIdentifiabilityMetric(
+        surface, GetExecutionContext(),
+        IdentifiabilityBenignStringToken(error_state.GetErrorMessage()));
     resolver->Reject(error_state.CreateError());
     return rejected_promise;
   }
diff --git a/third_party/blink/renderer/modules/mediastream/media_devices.idl b/third_party/blink/renderer/modules/mediastream/media_devices.idl
index 4b5e20e..6166cee 100644
--- a/third_party/blink/renderer/modules/mediastream/media_devices.idl
+++ b/third_party/blink/renderer/modules/mediastream/media_devices.idl
@@ -18,7 +18,7 @@
     enumerateDevices();
     MediaTrackSupportedConstraints getSupportedConstraints();
     [
-      CallWith = ScriptState, RaisesException, MeasureAs = GetUserMediaPromise
+      CallWith = ScriptState, RaisesException, HighEntropy, MeasureAs = GetUserMediaPromise
     ] Promise<MediaStream>
     getUserMedia(optional MediaStreamConstraints constraints = {});
 
diff --git a/third_party/blink/renderer/modules/mediastream/navigator_media_stream.cc b/third_party/blink/renderer/modules/mediastream/navigator_media_stream.cc
index ea7625b..9edebfb 100644
--- a/third_party/blink/renderer/modules/mediastream/navigator_media_stream.cc
+++ b/third_party/blink/renderer/modules/mediastream/navigator_media_stream.cc
@@ -23,6 +23,9 @@
 
 #include "third_party/blink/renderer/modules/mediastream/navigator_media_stream.h"
 
+#include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
+#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 #include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_media_stream_constraints.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_navigator_user_media_error_callback.h"
@@ -31,10 +34,13 @@
 #include "third_party/blink/renderer/core/frame/navigator.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/modules/mediastream/identifiability_metrics.h"
 #include "third_party/blink/renderer/modules/mediastream/media_error_state.h"
 #include "third_party/blink/renderer/modules/mediastream/user_media_controller.h"
 #include "third_party/blink/renderer/modules/mediastream/user_media_request.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
 
@@ -56,10 +62,17 @@
 
   UserMediaController* user_media =
       UserMediaController::From(navigator.DomWindow());
+  IdentifiableSurface surface;
+  constexpr IdentifiableSurface::Type surface_type =
+      IdentifiableSurface::Type::kNavigator_GetUserMedia;
+  if (IdentifiabilityStudySettings::Get()->IsTypeAllowed(surface_type)) {
+    surface = IdentifiableSurface::FromTypeAndToken(
+        surface_type, TokenFromConstraints(options));
+  }
   MediaErrorState error_state;
-  UserMediaRequest* request =
-      UserMediaRequest::Create(navigator.DomWindow(), user_media, options,
-                               success_callback, error_callback, error_state);
+  UserMediaRequest* request = UserMediaRequest::Create(
+      navigator.DomWindow(), user_media, options, success_callback,
+      error_callback, error_state, surface);
   if (!request) {
     DCHECK(error_state.HadException());
     if (error_state.CanGenerateException()) {
@@ -68,12 +81,18 @@
       error_callback->InvokeAndReportException(nullptr,
                                                error_state.CreateError());
     }
+    RecordIdentifiabilityMetric(
+        surface, navigator.GetExecutionContext(),
+        IdentifiabilityBenignStringToken(error_state.GetErrorMessage()));
     return;
   }
 
   String error_message;
   if (!request->IsSecureContextUse(error_message)) {
     request->Fail(UserMediaRequest::Error::kSecurityError, error_message);
+    RecordIdentifiabilityMetric(
+        surface, navigator.GetExecutionContext(),
+        IdentifiabilityBenignStringToken(error_message));
     return;
   }
 
diff --git a/third_party/blink/renderer/modules/mediastream/navigator_media_stream.idl b/third_party/blink/renderer/modules/mediastream/navigator_media_stream.idl
index 79c37f1..5f6f084 100644
--- a/third_party/blink/renderer/modules/mediastream/navigator_media_stream.idl
+++ b/third_party/blink/renderer/modules/mediastream/navigator_media_stream.idl
@@ -21,7 +21,8 @@
 [
     ImplementedAs=NavigatorMediaStream
 ] partial interface Navigator {
-    [RaisesException,
+    [HighEntropy,
+     RaisesException,
      SecureContext,
      MeasureAs=GetUserMediaLegacy
     ] void getUserMedia(MediaStreamConstraints constraints,
@@ -29,7 +30,8 @@
                         NavigatorUserMediaErrorCallback errorCallback);
 
     // Non-standard
-    [RaisesException,
+    [HighEntropy,
+     RaisesException,
      SecureContext,
      ImplementedAs=getUserMedia,
      MeasureAs=GetUserMediaPrefixed
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_request.cc b/third_party/blink/renderer/modules/mediastream/user_media_request.cc
index b32d6a7b..8c57ab4 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_request.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_request.cc
@@ -35,6 +35,7 @@
 
 #include "base/macros.h"
 #include "base/strings/stringprintf.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h"
 #include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
@@ -44,6 +45,7 @@
 #include "third_party/blink/renderer/core/dom/space_split_string.h"
 #include "third_party/blink/renderer/core/frame/deprecation.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/modules/mediastream/identifiability_metrics.h"
 #include "third_party/blink/renderer/modules/mediastream/media_constraints_impl.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
 #include "third_party/blink/renderer/modules/mediastream/overconstrained_error.h"
@@ -51,6 +53,7 @@
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
+#include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
@@ -330,7 +333,8 @@
     UserMediaRequest::MediaType media_type,
     const MediaStreamConstraints* options,
     Callbacks* callbacks,
-    MediaErrorState& error_state) {
+    MediaErrorState& error_state,
+    IdentifiableSurface surface) {
   MediaConstraints audio = ParseOptions(context, options->audio(), error_state);
   if (error_state.HadException())
     return nullptr;
@@ -408,8 +412,8 @@
   if (!video.IsNull())
     CountVideoConstraintUses(context, video);
 
-  return MakeGarbageCollected<UserMediaRequest>(context, controller, media_type,
-                                                audio, video, callbacks);
+  return MakeGarbageCollected<UserMediaRequest>(
+      context, controller, media_type, audio, video, callbacks, surface);
 }
 
 UserMediaRequest* UserMediaRequest::Create(
@@ -418,11 +422,12 @@
     const MediaStreamConstraints* options,
     V8NavigatorUserMediaSuccessCallback* success_callback,
     V8NavigatorUserMediaErrorCallback* error_callback,
-    MediaErrorState& error_state) {
+    MediaErrorState& error_state,
+    IdentifiableSurface surface) {
   return Create(
       context, controller, UserMediaRequest::MediaType::kUserMedia, options,
       MakeGarbageCollected<V8Callbacks>(success_callback, error_callback),
-      error_state);
+      error_state, surface);
 }
 
 UserMediaRequest* UserMediaRequest::CreateForTesting(
@@ -430,7 +435,7 @@
     const MediaConstraints& video) {
   return MakeGarbageCollected<UserMediaRequest>(
       nullptr, nullptr, UserMediaRequest::MediaType::kUserMedia, audio, video,
-      nullptr);
+      nullptr, IdentifiableSurface());
 }
 
 UserMediaRequest::UserMediaRequest(ExecutionContext* context,
@@ -438,7 +443,8 @@
                                    UserMediaRequest::MediaType media_type,
                                    MediaConstraints audio,
                                    MediaConstraints video,
-                                   Callbacks* callbacks)
+                                   Callbacks* callbacks,
+                                   IdentifiableSurface surface)
     : ExecutionContextLifecycleObserver(context),
       media_type_(media_type),
       audio_(audio),
@@ -447,7 +453,8 @@
           RuntimeEnabledFeatures::DisableHardwareNoiseSuppressionEnabled(
               context)),
       controller_(controller),
-      callbacks_(callbacks) {
+      callbacks_(callbacks),
+      surface_(surface) {
   if (should_disable_hardware_noise_suppression_) {
     UseCounter::Count(context,
                       WebFeature::kUserMediaDisableHardwareNoiseSuppression);
@@ -548,6 +555,8 @@
     video_track->SetConstraints(video_);
 
   callbacks_->OnSuccess(nullptr, stream);
+  RecordIdentifiabilityMetric(surface_, GetExecutionContext(),
+                              IdentifiabilityBenignStringToken(g_empty_string));
   is_resolved_ = true;
 }
 
@@ -560,6 +569,8 @@
   callbacks_->OnError(
       nullptr, DOMExceptionOrOverconstrainedError::FromOverconstrainedError(
                    OverconstrainedError::Create(constraint_name, message)));
+  RecordIdentifiabilityMetric(surface_, GetExecutionContext(),
+                              IdentifiabilityBenignStringToken(message));
   is_resolved_ = true;
 }
 
@@ -602,6 +613,8 @@
       nullptr,
       DOMExceptionOrOverconstrainedError::FromDOMException(
           MakeGarbageCollected<DOMException>(exception_code, message)));
+  RecordIdentifiabilityMetric(surface_, GetExecutionContext(),
+                              IdentifiabilityBenignStringToken(message));
   is_resolved_ = true;
 }
 
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_request.h b/third_party/blink/renderer/modules/mediastream/user_media_request.h
index 2cf60da..6c9b02b 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_request.h
+++ b/third_party/blink/renderer/modules/mediastream/user_media_request.h
@@ -31,6 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_USER_MEDIA_REQUEST_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_USER_MEDIA_REQUEST_H_
 
+#include "third_party/blink/public/common/privacy_budget/identifiable_surface.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_navigator_user_media_error_callback.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_navigator_user_media_success_callback.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
@@ -94,13 +95,15 @@
                                   MediaType media_type,
                                   const MediaStreamConstraints* options,
                                   Callbacks*,
-                                  MediaErrorState&);
+                                  MediaErrorState&,
+                                  IdentifiableSurface surface);
   static UserMediaRequest* Create(ExecutionContext*,
                                   UserMediaController*,
                                   const MediaStreamConstraints* options,
                                   V8NavigatorUserMediaSuccessCallback*,
                                   V8NavigatorUserMediaErrorCallback*,
-                                  MediaErrorState&);
+                                  MediaErrorState&,
+                                  IdentifiableSurface surface);
   static UserMediaRequest* CreateForTesting(const MediaConstraints& audio,
                                             const MediaConstraints& video);
 
@@ -109,7 +112,8 @@
                    MediaType media_type,
                    MediaConstraints audio,
                    MediaConstraints video,
-                   Callbacks*);
+                   Callbacks*,
+                   IdentifiableSurface surface);
   virtual ~UserMediaRequest();
 
   LocalDOMWindow* GetWindow();
@@ -161,6 +165,7 @@
   Member<UserMediaController> controller_;
 
   Member<Callbacks> callbacks_;
+  IdentifiableSurface surface_;
   bool is_resolved_ = false;
 };
 
diff --git a/third_party/blink/renderer/modules/payments/payment_instruments.cc b/third_party/blink/renderer/modules/payments/payment_instruments.cc
index fa876684..9e4d4b0 100644
--- a/third_party/blink/renderer/modules/payments/payment_instruments.cc
+++ b/third_party/blink/renderer/modules/payments/payment_instruments.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_icon_sizes_parser.h"
+#include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index e7ff7f9..05cf9f6 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -659,8 +659,8 @@
   RuntimeEnabledFeatures::SetSkipTouchEventFilterEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableSmsReceiver(bool enable) {
-  RuntimeEnabledFeatures::SetSmsReceiverEnabled(enable);
+void WebRuntimeFeatures::EnableWebOTP(bool enable) {
+  RuntimeEnabledFeatures::SetWebOTPEnabled(enable);
 }
 
 void WebRuntimeFeatures::EnableConsolidatedMovementXY(bool enable) {
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
index de46a8b..64cdb66e 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -861,10 +861,13 @@
         continue;
       }
 
-      space = spacing.ComputeSpacing(
-          run_start_index + glyph_data.character_index, glyph_data.advance,
-          run->font_data_->GetAdvanceOverride(),
-          run->font_data_->GetAdvanceProportionalOverride(), offset);
+      typename ShapeResultSpacing<TextContainerType>::ComputeSpacingParameters
+          parameters{.index = run_start_index + glyph_data.character_index,
+                     .advance_override = run->font_data_->GetAdvanceOverride(),
+                     .original_advance = glyph_data.advance,
+                     .advance_proportional_override =
+                         run->font_data_->GetAdvanceProportionalOverride()};
+      space = spacing.ComputeSpacing(parameters, offset);
       glyph_data.advance += space;
       total_space_for_run += space;
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc
index b0244842..eefbd4e 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc
@@ -122,12 +122,10 @@
 
 template <typename TextContainerType>
 float ShapeResultSpacing<TextContainerType>::ComputeSpacing(
-    unsigned index,
-    float original_advance,
-    float advance_override,
-    float advance_proportional_override,
+    const ComputeSpacingParameters& parameters,
     float& offset) {
   DCHECK(has_spacing_);
+  unsigned index = parameters.index;
   UChar32 character = text_[index];
   bool treat_as_space =
       (Character::TreatAsSpace(character) ||
@@ -139,11 +137,12 @@
 
   float spacing = 0;
 
-  bool has_letter_spacing = letter_spacing_ || advance_override ||
-                            (advance_proportional_override != 1.0);
+  bool has_letter_spacing = letter_spacing_ || parameters.advance_override ||
+                            (parameters.advance_proportional_override != 1.0);
   if (has_letter_spacing && !Character::TreatAsZeroWidthSpace(character)) {
-    spacing += original_advance * (advance_proportional_override - 1.0) +
-               letter_spacing_ + advance_override;
+    spacing += parameters.original_advance *
+                   (parameters.advance_proportional_override - 1.0) +
+               letter_spacing_ + parameters.advance_override;
   }
 
   if (treat_as_space && (index || character == kNoBreakSpaceCharacter))
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
index 64830df..5af754a 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h
@@ -62,10 +62,16 @@
   // The |index| is for the |TextContainerType| given in the constructor.
   // For justification, this function must be called incrementally since it
   // keeps states and counts consumed justification opportunities.
-  float ComputeSpacing(unsigned index,
-                       float original_advance,
-                       float advance_override,
-                       float advance_proportional_override,
+  struct ComputeSpacingParameters {
+    unsigned index;
+    float advance_override = 0.0;
+    float original_advance = 0.0;
+    float advance_proportional_override = 1.0;
+  };
+  float ComputeSpacing(unsigned index, float& offset) {
+    return ComputeSpacing(ComputeSpacingParameters{.index = index}, offset);
+  }
+  float ComputeSpacing(const ComputeSpacingParameters& parameters,
                        float& offset);
 
  private:
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 0473def9..82d2801 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1774,10 +1774,6 @@
       settable_from_internals: true,
     },
     {
-      name: "SmsReceiver",
-      status: {"default": "experimental", "Android": "stable"},
-    },
-    {
       name: "SrcsetMaxDensity",
     },
     // Used as argument in attribute of stable-release functions/interfaces
@@ -2077,6 +2073,10 @@
       status: "experimental",
     },
     {
+      name: "WebOTP",
+      status: {"default": "experimental", "Android": "stable"},
+    },
+    {
       name: "WebScheduler",
       origin_trial_feature_name: "WebScheduler",
       status: "experimental",
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
new file mode 100644
index 0000000..f693f38
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduling_policy.cc
@@ -0,0 +1,67 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h"
+
+namespace blink {
+
+bool SchedulingPolicy::IsFeatureSticky(SchedulingPolicy::Feature feature) {
+  switch (feature) {
+    case Feature::kWebSocket:
+    case Feature::kWebRTC:
+    case Feature::kDedicatedWorkerOrWorklet:
+    case Feature::kOutstandingIndexedDBTransaction:
+    case Feature::kOutstandingNetworkRequestDirectSocket:
+    case Feature::kOutstandingNetworkRequestFetch:
+    case Feature::kOutstandingNetworkRequestOthers:
+    case Feature::kOutstandingNetworkRequestXHR:
+    case Feature::kBroadcastChannel:
+    case Feature::kIndexedDBConnection:
+    case Feature::kWebGL:
+    case Feature::kWebVR:
+    case Feature::kWebXR:
+    case Feature::kSharedWorker:
+    case Feature::kWebHID:
+    case Feature::kWebShare:
+    case Feature::kWebDatabase:
+    case Feature::kPortal:
+    case Feature::kSpeechRecognizer:
+    case Feature::kSpeechSynthesis:
+      return false;
+    case Feature::kMainResourceHasCacheControlNoStore:
+    case Feature::kMainResourceHasCacheControlNoCache:
+    case Feature::kSubresourceHasCacheControlNoStore:
+    case Feature::kSubresourceHasCacheControlNoCache:
+    case Feature::kPageShowEventListener:
+    case Feature::kPageHideEventListener:
+    case Feature::kBeforeUnloadEventListener:
+    case Feature::kUnloadEventListener:
+    case Feature::kFreezeEventListener:
+    case Feature::kResumeEventListener:
+    case Feature::kContainsPlugins:
+    case Feature::kDocumentLoaded:
+    case Feature::kRequestedGeolocationPermission:
+    case Feature::kRequestedNotificationsPermission:
+    case Feature::kRequestedMIDIPermission:
+    case Feature::kRequestedAudioCapturePermission:
+    case Feature::kRequestedVideoCapturePermission:
+    case Feature::kRequestedBackForwardCacheBlockedSensors:
+    case Feature::kRequestedBackgroundWorkPermission:
+    case Feature::kWebLocks:
+    case Feature::kWakeLock:
+    case Feature::kRequestedStorageAccessGrant:
+    case Feature::kWebNfc:
+    case Feature::kWebFileSystem:
+    case Feature::kAppBanner:
+    case Feature::kPrinting:
+    case Feature::kPictureInPicture:
+    case Feature::kIdleManager:
+    case Feature::kPaymentManager:
+    case Feature::kKeyboardLock:
+    case Feature::kWebOTPService:
+      return true;
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/tools/blinkpy/w3c/android_wpt_expectations_updater_unittest.py b/third_party/blink/tools/blinkpy/w3c/android_wpt_expectations_updater_unittest.py
index 5fc3016..a62e9ad 100644
--- a/third_party/blink/tools/blinkpy/w3c/android_wpt_expectations_updater_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/android_wpt_expectations_updater_unittest.py
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import json
 import logging
 
 from blinkpy.common.host_mock import MockHost
@@ -10,6 +11,7 @@
 from blinkpy.common.net.results_fetcher import Build
 from blinkpy.common.net.web_test_results import WebTestResults
 from blinkpy.common.system.log_testing import LoggingTestCase
+from blinkpy.w3c.wpt_manifest import BASE_MANIFEST_NAME
 from blinkpy.web_tests.builder_list import BuilderList
 from blinkpy.web_tests.port.factory_mock import MockPortFactory
 from blinkpy.web_tests.port.android import (
@@ -76,6 +78,18 @@
                 'is_try_builder': True,
             },
         })
+        host.filesystem.write_text_file(
+            host.port_factory.get().web_tests_dir() + '/external/' +
+            BASE_MANIFEST_NAME,
+            json.dumps({
+                'items': {
+                    'testharness': {
+                        'ghi.html': ['abcdef123', [None, {}]],
+                        'van.html': ['abcdef123', [None, {}]],
+                    },
+                },
+            }))
+
         # Write dummy expectations
         for path in PRODUCTS_TO_EXPECTATION_FILE_PATHS.values():
             host.filesystem.write_text_file(
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer.py b/third_party/blink/tools/blinkpy/w3c/test_importer.py
index 2929ddd..d029e7e 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer.py
@@ -45,9 +45,10 @@
 
 
 class TestImporter(object):
-    def __init__(self, host, wpt_github=None):
+    def __init__(self, host, wpt_github=None, wpt_manifests=None):
         self.host = host
         self.wpt_github = wpt_github
+        self.port = host.port_factory.get()
 
         self.executive = host.executive
         self.fs = host.filesystem
@@ -72,7 +73,8 @@
 
         args = ['--clean-up-affected-tests-only',
                 '--clean-up-test-expectations']
-        self._expectations_updater = WPTExpectationsUpdater(self.host, args)
+        self._expectations_updater = WPTExpectationsUpdater(
+            self.host, args, wpt_manifests)
 
     def main(self, argv=None):
         # TODO(robertma): Test this method! Split it to make it easier to test
@@ -155,14 +157,15 @@
         # TODO(robertma): Implement `add --all` in Git (it is different from `commit --all`).
         self.chromium_git.run(['add', '--all', self.dest_path])
 
+        # Remove expectations for tests that were deleted and rename tests
+        # in expectations for renamed tests.
+        self._expectations_updater.cleanup_test_expectations_files()
+
         self._generate_manifest()
 
         # TODO(crbug.com/800570 robertma): Re-enable it once we fix the bug.
         # self._delete_orphaned_baselines()
 
-        # Remove expectations for tests that were deleted and rename tests
-        # in expectations for renamed tests.
-        self._expectations_updater.cleanup_test_expectations_files()
 
         if not self.chromium_git.has_working_directory_changes():
             _log.info('Done: no changes to import.')
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
index f725d7ba..f023cbc 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
@@ -24,7 +24,13 @@
 from blinkpy.web_tests.port.android import PRODUCTS_TO_EXPECTATION_FILE_PATHS
 
 MOCK_WEB_TESTS = '/mock-checkout/' + RELATIVE_WEB_TESTS
-
+MANIFEST_INSTALL_CMD = [
+    'python',
+    '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt',
+    'manifest',
+    '--no-download',
+    '--tests-root',
+    MOCK_WEB_TESTS + 'external/wpt']
 
 class TestImporterTest(LoggingTestCase):
 
@@ -34,11 +40,19 @@
             host.filesystem.write_text_file(path, '')
         return host
 
+    @staticmethod
+    def _get_test_importer(host, wpt_github=None):
+        port = host.port_factory.get()
+        return TestImporter(
+            host,
+            wpt_github=wpt_github,
+            wpt_manifests=[port.wpt_manifest('external/wpt')])
+
     def test_update_expectations_for_cl_no_results(self):
         host = self.mock_host()
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'W3CImportExpectations', '')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer.git_cl = MockGitCL(host, time_out=True)
         success = importer.update_expectations_for_cl()
         self.assertFalse(success)
@@ -52,7 +66,7 @@
         host = self.mock_host()
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'W3CImportExpectations', '')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer.git_cl = MockGitCL(
             host,
             status='closed',
@@ -70,7 +84,7 @@
         host = self.mock_host()
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'W3CImportExpectations', '')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer.git_cl = MockGitCL(
             host,
             status='lgtm',
@@ -88,7 +102,7 @@
         host = self.mock_host()
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'W3CImportExpectations', '')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer.git_cl = MockGitCL(
             host,
             status='lgtm',
@@ -107,7 +121,7 @@
         host = self.mock_host()
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'W3CImportExpectations', '')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         # Only the latest job for each builder is counted.
         importer.git_cl = MockGitCL(
             host,
@@ -136,7 +150,7 @@
         host = self.mock_host()
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'W3CImportExpectations', '')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer.git_cl = MockGitCL(
             host,
             status='lgtm',
@@ -165,7 +179,7 @@
         host = self.mock_host()
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'W3CImportExpectations', '')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         # Only the latest job for each builder is counted.
         importer.git_cl = MockGitCL(
             host,
@@ -196,7 +210,7 @@
         host = self.mock_host()
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'W3CImportExpectations', '')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer.git_cl = MockGitCL(
             host,
             status='closed',
@@ -219,7 +233,7 @@
     def test_run_commit_queue_for_cl_timeout(self):
         # This simulates the case where we time out while waiting for try jobs.
         host = self.mock_host()
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer.git_cl = MockGitCL(host, time_out=True)
         success = importer.run_commit_queue_for_cl()
         self.assertFalse(success)
@@ -237,7 +251,7 @@
         host = self.mock_host()
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'W3CImportExpectations', '')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         # Define some error text that looks like a typical ScriptError.
         git_error_text = (
             'This is a git Script Error\n'
@@ -277,7 +291,7 @@
     def test_apply_exportable_commits_locally(self):
         # TODO(robertma): Consider using MockLocalWPT.
         host = self.mock_host()
-        importer = TestImporter(
+        importer = self._get_test_importer(
             host, wpt_github=MockWPTGitHub(pull_requests=[]))
         importer.wpt_git = MockGit(cwd='/tmp/wpt', executive=host.executive)
         fake_commit = MockChromiumCommit(
@@ -296,6 +310,12 @@
         # This assertion is implementation details of LocalWPT.apply_patch.
         # TODO(robertma): Move this to local_wpt_unittest.py.
         self.assertEqual(host.executive.full_calls, [
+            MockCall(MANIFEST_INSTALL_CMD,
+                     kwargs={
+                         'input': None,
+                         'cwd': None,
+                         'env': None
+                     }),
             MockCall(
                 ['git', 'apply', '-'], {
                     'input': ('Fake patch contents...\n'
@@ -322,7 +342,7 @@
     def test_apply_exportable_commits_locally_returns_none_on_failure(self):
         host = self.mock_host()
         wpt_github = MockWPTGitHub(pull_requests=[])
-        importer = TestImporter(host, wpt_github=wpt_github)
+        importer = self._get_test_importer(host, wpt_github=wpt_github)
         commit = MockChromiumCommit(host, subject='My fake commit')
         importer.exportable_but_not_exported_commits = lambda _: [commit]
         # Failure to apply patch.
@@ -337,7 +357,7 @@
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'external/wpt/foo/OWNERS',
             'someone@chromium.org\n')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer.chromium_git.changed_files = lambda: [RELATIVE_WEB_TESTS + 'external/wpt/foo/x.html']
         self.assertEqual(importer.get_directory_owners(),
                          {('someone@chromium.org', ): ['external/wpt/foo']})
@@ -349,20 +369,20 @@
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'external/wpt/foo/OWNERS',
             'someone@chromium.org\n')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         self.assertEqual(importer.get_directory_owners(), {})
 
     # Tests for protected methods - pylint: disable=protected-access
 
     def test_commit_changes(self):
         host = self.mock_host()
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer._commit_changes('dummy message')
         self.assertEqual(importer.chromium_git.local_commits(),
                          [['dummy message']])
 
     def test_commit_message(self):
-        importer = TestImporter(self.mock_host())
+        importer = self._get_test_importer(self.mock_host())
         self.assertEqual(
             importer._commit_message('aaaa', '1111'), 'Import 1111\n\n'
             'Using wpt-import in Chromium aaaa.\n\n'
@@ -371,7 +391,7 @@
     def test_cl_description_with_empty_environ(self):
         host = self.mock_host()
         host.executive = MockExecutive(output='Last commit message\n\n')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         description = importer._cl_description(directory_owners={})
         self.assertEqual(
             description, 'Last commit message\n\n'
@@ -385,20 +405,22 @@
             'No-Export: true\n'
             'Cq-Include-Trybots: luci.chromium.try:linux-wpt-identity-fyi-rel,'
             'linux-wpt-payments-fyi-rel')
+        print host.executive.calls
         self.assertEqual(host.executive.calls,
+                         [MANIFEST_INSTALL_CMD] +
                          [['git', 'log', '-1', '--format=%B']])
 
     def test_cl_description_moves_noexport_tag(self):
         host = self.mock_host()
         host.executive = MockExecutive(output='Summary\n\nNo-Export: true\n\n')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         description = importer._cl_description(directory_owners={})
         self.assertIn('No-Export: true', description)
 
     def test_cl_description_with_directory_owners(self):
         host = self.mock_host()
         host.executive = MockExecutive(output='Last commit message\n\n')
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         description = importer._cl_description(
             directory_owners={
                 ('someone@chromium.org', ):
@@ -415,7 +437,7 @@
 
     def test_tbr_reviewer_no_response_uses_backup(self):
         host = self.mock_host()
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         self.assertEqual(TBR_FALLBACK, importer.tbr_reviewer())
         self.assertLog([
             'ERROR: Exception while fetching current sheriff: '
@@ -426,7 +448,7 @@
         host = self.mock_host()
         host.web.urls[ROTATIONS_URL] = json.dumps(
             {'updated_unix_timestamp': '1591108191'})
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         self.assertEqual(TBR_FALLBACK, importer.tbr_reviewer())
         self.assertLog([
             'ERROR: No email found for current sheriff. Retrieved content: %s\n'
@@ -440,7 +462,7 @@
             'updated_unix_timestamp':
             '1591108191'
         })
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         self.assertEqual(TBR_FALLBACK, importer.tbr_reviewer())
         self.assertLog([
             'ERROR: No email found for current sheriff. Retrieved content: %s\n'
@@ -453,7 +475,7 @@
 
         host = self.mock_host()
         host.web.get_binary = raise_exception
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         self.assertEqual(TBR_FALLBACK, importer.tbr_reviewer())
         self.assertLog(['ERROR: Cannot fetch %s\n' % ROTATIONS_URL])
 
@@ -464,14 +486,14 @@
             'updated_unix_timestamp':
             '1591108191',
         })
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         self.assertEqual('current-sheriff@chromium.org',
                          importer.tbr_reviewer())
         self.assertLog([])
 
     def test_tbr_reviewer_skips_non_committer(self):
         host = self.mock_host()
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer._fetch_ecosystem_infra_sheriff_email = lambda: 'kyleju@google.com'
         self.assertEqual(TBR_FALLBACK, importer.tbr_reviewer())
         self.assertLog(
@@ -481,24 +503,17 @@
         # This test doesn't test any aspect of the real manifest script, it just
         # asserts that TestImporter._generate_manifest would invoke the script.
         host = self.mock_host()
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         host.filesystem.write_text_file(
             MOCK_WEB_TESTS + 'external/wpt/MANIFEST.json', '{}')
         importer._generate_manifest()
-        self.assertEqual(host.executive.calls, [[
-            'python',
-            '/mock-checkout/third_party/blink/tools/blinkpy/third_party/wpt/wpt/wpt',
-            'manifest',
-            '--no-download',
-            '--tests-root',
-            MOCK_WEB_TESTS + 'external/wpt',
-        ]])
+        self.assertEqual(host.executive.calls, [MANIFEST_INSTALL_CMD] * 2)
         self.assertEqual(importer.chromium_git.added_paths,
                          {MOCK_WEB_TESTS + 'external/' + BASE_MANIFEST_NAME})
 
     def test_only_wpt_manifest_changed(self):
         host = self.mock_host()
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         importer.chromium_git.changed_files = lambda: [
             RELATIVE_WEB_TESTS + 'external/' + BASE_MANIFEST_NAME,
             RELATIVE_WEB_TESTS + 'external/wpt/foo/x.html']
@@ -513,7 +528,7 @@
     @unittest.skip('Finding orphaned baselines is broken')
     def test_delete_orphaned_baselines_basic(self):
         host = self.mock_host()
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         dest_path = importer.dest_path
         host.filesystem.write_text_file(
             dest_path + '/MANIFEST.json',
@@ -542,7 +557,7 @@
         # This test checks that baselines for existing tests shouldn't be
         # deleted, even if the test name isn't the same as the file name.
         host = self.mock_host()
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         dest_path = importer.dest_path
         host.filesystem.write_text_file(
             dest_path + '/MANIFEST.json',
@@ -585,7 +600,7 @@
 
     def test_clear_out_dest_path(self):
         host = self.mock_host()
-        importer = TestImporter(host)
+        importer = self._get_test_importer(host)
         dest_path = importer.dest_path
         host.filesystem.write_text_file(dest_path + '/foo-test.html', '')
         host.filesystem.write_text_file(dest_path + '/foo-test-expected.txt',
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
index 91efdea..2190c10 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
@@ -11,6 +11,7 @@
 import argparse
 import copy
 import logging
+import re
 from collections import defaultdict, namedtuple
 
 from blinkpy.common.memoized import memoized
@@ -37,7 +38,7 @@
     MARKER_COMMENT = '# ====== New tests from wpt-importer added here ======'
     UMBRELLA_BUG = 'crbug.com/626703'
 
-    def __init__(self, host, args=None):
+    def __init__(self, host, args=None, wpt_manifests=None):
         self.host = host
         self.port = self.host.port_factory.get()
         self.finder = PathFinder(self.host.filesystem)
@@ -45,6 +46,9 @@
         self.git = self.host.git(self.finder.chromium_base())
         self.configs_with_no_results = []
         self.patchset = None
+        self.wpt_manifests = (
+            wpt_manifests or
+            [self.port.wpt_manifest(d) for d in self.port.WPT_DIRS])
 
         # Get options from command line arguments.
         parser = argparse.ArgumentParser(description=__doc__)
@@ -718,106 +722,31 @@
         through this script. If that command line argument is not used then
         expectations for test files that no longer exist will be deleted.
         """
-        deleted_test_files = self._list_deleted_test_files()
-        renamed_test_files = self._list_renamed_test_files()
+        deleted_files = self._list_deleted_files()
+        renamed_files = self._list_renamed_files()
 
         for path in self._test_expectations.expectations_dict:
             _log.info(
                 'Updating %s for any removed or renamed tests.' %
                 self.host.filesystem.basename(path))
             self._clean_single_test_expectations_file(
-                path, deleted_test_files, renamed_test_files)
+                path, deleted_files, renamed_files)
         self._test_expectations.commit_changes()
 
-    def _clean_single_test_expectations_file(
-            self, path, deleted_files, renamed_files):
-        """Cleans up a single test expectations file.
+    def _list_deleted_files(self):
+        # TODO(robertma): Improve Git.changed_files so that we can use
+        # it here.
+        paths = self.git.run(
+            ['diff', 'origin/master', '-M100%', '--diff-filter=D',
+             '--name-only']).splitlines()
+        deleted_files = []
+        for p in paths:
+            rel_path = self._relative_to_web_test_dir(p)
+            if rel_path:
+                deleted_files.append(rel_path)
+        return deleted_files
 
-        Args:
-            path: Path of expectations file that is being cleaned up.
-            deleted_files: List of test file paths relative to the web tests
-                directory which were deleted.
-            renamed_files: Dictionary mapping test file paths to their new file
-                name after renaming.
-        """
-        for line in self._test_expectations.get_updated_lines(path):
-            # if a test is a glob type expectation or empty line or comment then
-            # add it to the updated expectations file without modifications
-            if not line.test or line.is_glob:
-                continue
-            root_test_file = self._get_root_file(line.test)
-
-            if root_test_file in renamed_files:
-                self._test_expectations.remove_expectations(path, [line])
-                new_file_name = renamed_files[root_test_file]
-                if self.finder.is_webdriver_test_path(root_test_file):
-                    _, subtest_suffix = self.port.split_webdriver_test_name(
-                        line.test)
-                    line.test = self.port.add_webdriver_subtest_suffix(
-                        new_file_name, subtest_suffix)
-                elif '?' in line.test:
-                    line.test = (
-                        new_file_name + line.test[line.test.find('?'):])
-                else:
-                    line.test = new_file_name
-                self._test_expectations.add_expectations(
-                    path, [line], lineno=line.lineno)
-            elif root_test_file in deleted_files:
-                self._test_expectations.remove_expectations(
-                    path, [line])
-
-    @memoized
-    def _get_root_file(self, test_name):
-        """Strips arguments from a web test name in order to get the file name.
-
-        It also removes the arguments for web driver tests. For instances for
-        the test test1/example.html?Hello this function will return
-        test1/example.html. For a webdriver test it would include arguments and
-        would have the following format, {test file}>>{argument}.
-
-        Args:
-            test_name: Test name which may include test arguments.
-
-        Returns:
-            Returns the test file which is the root of a test.
-        """
-        if self.finder.is_webdriver_test_path(test_name):
-            root_test_file, _ = (
-                self.port.split_webdriver_test_name(test_name))
-        elif '?' in test_name:
-            root_test_file = test_name[:test_name.find('?')]
-        else:
-            root_test_file = test_name
-        return root_test_file
-
-    def _list_deleted_test_files(self):
-        """Returns a list of web tests that have been deleted.
-
-        If --clean-up-affected-tests-only is true then only test files deleted
-        in the current CL may be removed from expectations. Otherwise, any test
-        file may be removed from expectations if it has been deleted.
-
-        Returns: A list of web test files that have been deleted.
-        """
-        if self.options.clean_up_affected_tests_only:
-            # TODO(robertma): Improve Git.changed_files so that we can use
-            # it here.
-            paths = set(self.git.run(
-                ['diff', 'origin/master', '-M100%', '--diff-filter=D',
-                 '--name-only']).splitlines())
-            deleted_tests = set()
-            for path in paths:
-                test = self._relative_to_web_test_dir(path)
-                if test:
-                    deleted_tests.add(test)
-        else:
-            # Remove expectations for all test which have files that
-            # were deleted. Paths are already relative to the web_tests
-            # directory
-            deleted_tests = self._deleted_test_files_in_expectations()
-        return deleted_tests
-
-    def _list_renamed_test_files(self):
+    def _list_renamed_files(self):
         """Returns a dictionary mapping tests to their new name.
 
         Regardless of the command line arguments used this test will only
@@ -837,6 +766,83 @@
                 renamed_tests[source_test] = dest_test
         return renamed_tests
 
+    def _clean_single_test_expectations_file(
+            self, path, deleted_files, renamed_files):
+        """Cleans up a single test expectations file.
+
+        Args:
+            path: Path of expectations file that is being cleaned up.
+            deleted_files: List of file paths relative to the web tests
+                directory which were deleted.
+            renamed_files: Dictionary mapping file paths to their new file
+                name after renaming.
+        """
+        deleted_files = set(deleted_files)
+        for line in self._test_expectations.get_updated_lines(path):
+            # if a test is a glob type expectation or empty line or comment then
+            # add it to the updated expectations file without modifications
+            if not line.test or line.is_glob:
+                continue
+            root_file = self._get_root_file(line.test)
+            if root_file in deleted_files:
+                self._test_expectations.remove_expectations(path, [line])
+            elif root_file in renamed_files:
+                self._test_expectations.remove_expectations(path, [line])
+                new_file_name = renamed_files[root_file]
+                if self.finder.is_webdriver_test_path(line.test):
+                    _, subtest_suffix = self.port.split_webdriver_test_name(line.test)
+                    line.test = self.port.add_webdriver_subtest_suffix(
+                        new_file_name, subtest_suffix)
+                elif self.port.is_wpt_test(line.test):
+                    # Based on logic in Base._wpt_test_urls_matching_paths
+                    line.test = line.test.replace(
+                        re.sub(r'\.js$', '.', root_file),
+                        re.sub(r'\.js$', '.', new_file_name))
+                else:
+                    line.test = new_file_name
+                self._test_expectations.add_expectations(
+                    path, [line], lineno=line.lineno)
+            elif not root_file or not self.port.test_isfile(root_file):
+                if not self.options.clean_up_affected_tests_only:
+                    self._test_expectations.remove_expectations(path, [line])
+
+    @memoized
+    def _get_root_file(self, test_name):
+        """Finds the physical file in web tests directory for a test
+
+        If a test is a WPT test then it will look in each of the WPT manifests
+        for the physical file. If test name cannot be found in any of the manifests
+        then the test no longer exists and the function will return None. If a file
+        is webdriver test then it will strip all subtest arguments and return the
+        file path. If a test is a legacy web test then it will return the test name.
+
+        Args:
+            test_name: Test name which may include test arguments.
+
+        Returns:
+            Returns the path of the physical file that backs
+            up a test. The path is relative to the web_tests directory.
+        """
+        if self.finder.is_webdriver_test_path(test_name):
+            root_test_file, _ = (
+                self.port.split_webdriver_test_name(test_name))
+            return root_test_file
+        elif self.port.is_wpt_test(test_name):
+            for wpt_manifest in self.wpt_manifests:
+                if test_name.startswith(wpt_manifest.wpt_dir):
+                    wpt_test = test_name[len(wpt_manifest.wpt_dir) + 1:]
+                    if wpt_manifest.is_test_url(wpt_test):
+                        return self.host.filesystem.join(
+                            wpt_manifest.wpt_dir,
+                            wpt_manifest.file_path_for_test_url(wpt_test))
+            # The test was not found in any of the wpt manifests, therefore
+            # the test does not exist. So we will return None in this case.
+            return None
+        else:
+            # Non WPT and non webdriver tests have no file parameters, and
+            # the physical file path is the actual name of the test.
+            return test_name
+
     def _relative_to_web_test_dir(self, path_relative_to_repo_root):
         """Returns a path that's relative to the web tests directory."""
         abs_path = self.finder.path_from_chromium_base(
@@ -846,25 +852,6 @@
         return self.host.filesystem.relpath(
             abs_path, self.finder.web_tests_dir())
 
-    def _deleted_test_files_in_expectations(self):
-        """Returns a list of test files that were deleted.
-
-        Returns a list of test file names that are still in the expectations
-        files but no longer exists in the web tests directory.
-        """
-        deleted_files = set()
-        existing_files = {
-            self._get_root_file(p)
-            for p in self.port.tests()}
-        for path in self._test_expectations.expectations_dict:
-            for line in self._test_expectations.get_updated_lines(path):
-                if not line.test or line.is_glob:
-                    continue
-                root_test_file = self._get_root_file(line.test)
-                if root_test_file not in existing_files:
-                    deleted_files.add(root_test_file)
-        return deleted_files
-
     # TODO(robertma): Unit test this method.
     def download_text_baselines(self, test_results):
         """Fetches new baseline files for tests that should be rebaselined.
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
index fa8964d..81b808e 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
@@ -18,7 +18,8 @@
 
 from blinkpy.w3c.wpt_expectations_updater import (
     WPTExpectationsUpdater, SimpleTestResult, DesktopConfig)
-from blinkpy.w3c.wpt_manifest import BASE_MANIFEST_NAME
+from blinkpy.w3c.wpt_manifest import (
+    WPTManifest, BASE_MANIFEST_NAME, MANIFEST_NAME)
 
 from blinkpy.web_tests.builder_list import BuilderList
 from blinkpy.web_tests.port.android import PRODUCTS_TO_EXPECTATION_FILE_PATHS
@@ -84,6 +85,14 @@
                     'testharness': {
                         'test/path.html': ['abcdef123', [None, {}]],
                         'test/zzzz.html': ['ghijkl456', [None, {}]],
+                        'fake/some_test.html': [
+                            'ghijkl456', ['fake/some_test.html?HelloWorld', {}]],
+                        'fake/file/deleted_path.html': [
+                            'ghijkl456', [None, {}]],
+                        'test/task.js': [
+                            'mnpqrs789',
+                            ['test/task.html', {}],
+                            ['test/task2.html', {}]],
                     },
                     'manual': {
                         'x-manual.html': ['abcdef123', [None, {}]],
@@ -801,6 +810,63 @@
                     WPTExpectationsUpdater.MARKER_COMMENT + '\n' +
                     '[ linux ] external/wpt/fake/new.html?HelloWorld [ Failure ]\n'))
 
+    def test_clean_expectations_for_deleted_test_harness(self):
+        host = self.mock_host()
+        port = host.port_factory.get()
+        expectations_path = \
+            port.path_to_generic_test_expectations_file()
+        host.filesystem.write_text_file(
+            expectations_path,
+            '# tags: [ Win Linux ]\n' +
+            '# results: [ Pass Failure ]\n' +
+            WPTExpectationsUpdater.MARKER_COMMENT + '\n' +
+            '[ linux ] wpt_internal/test/task.html [ Failure ]\n' +
+            '[ win ] wpt_internal/test/task2.html [ Failure ]\n' +
+            '[ linux ] external/wpt/test/task.html [ Failure ]\n' +
+            'external/wpt/test/task2.html [ Pass ]\n')
+
+        def _git_command_return_val(cmd):
+            if '--diff-filter=D' in cmd:
+                return '\n'.join(['external/wpt/test/task.js',
+                                  'wpt_internal/test/task.js'])
+            return ''
+
+        wpt_manifest = port.wpt_manifest('external/wpt')
+        host.filesystem.maybe_make_directory(
+            port.web_tests_dir(), 'wpt_internal')
+        host.filesystem.copyfile(
+            host.filesystem.join(port.web_tests_dir(),
+                                 'external', 'wpt', MANIFEST_NAME),
+            host.filesystem.join(port.web_tests_dir(), 'wpt_internal',
+                                 MANIFEST_NAME))
+        wpt_internal_manifest = WPTManifest(host, host.filesystem.join(
+            port.web_tests_dir(), 'wpt_internal', MANIFEST_NAME))
+
+        updater = WPTExpectationsUpdater(
+            host,
+            ['--clean-up-affected-tests-only',
+             '--clean-up-test-expectations-only'],
+            [wpt_manifest, wpt_internal_manifest])
+        updater.git.run = _git_command_return_val
+        updater._relative_to_web_test_dir = lambda test_path: test_path
+        updater.cleanup_test_expectations_files()
+
+        test_expectations = {'external/wpt/fake/file/path.html': {
+            tuple([DesktopConfig(port_name='test-linux-trusty')]):
+            SimpleTestResult(actual='PASS', expected='', bug='crbug.com/123')}}
+        skip_path = host.port_factory.get().path_to_never_fix_tests_file()
+        skip_value_origin = host.filesystem.read_text_file(skip_path)
+
+        updater.write_to_test_expectations(test_expectations)
+        value = host.filesystem.read_text_file(expectations_path)
+        self.assertMultiLineEqual(
+            value, ('# tags: [ Win Linux ]\n' +
+                    '# results: [ Pass Failure ]\n\n' +
+                    WPTExpectationsUpdater.MARKER_COMMENT + '\n' +
+                    'crbug.com/123 [ Trusty ] external/wpt/fake/file/path.html [ Pass ]'))
+        skip_value = host.filesystem.read_text_file(skip_path)
+        self.assertMultiLineEqual(skip_value, skip_value_origin)
+
     def test_write_to_test_expectations_and_cleanup_expectations(self):
         host = self.mock_host()
         expectations_path = \
@@ -1225,16 +1291,21 @@
 
     def test_cleanup_all_deleted_tests_in_expectations_files(self):
         host = MockHost()
-
+        port = host.port_factory.get()
         host.filesystem.files[MOCK_WEB_TESTS + 'TestExpectations'] = (
             '# results: [ Failure ]\n'
-            'some/test/a.html?hello%20world [ Failure ]\n'
+            'external/wpt/some/test/a.html?hello%20world [ Failure ]\n'
             'some/test/b.html [ Failure ]\n'
+            '# This line should be deleted\n'
+            'some/test/c.html [ Failure ]\n'
             '# line below should exist in new file\n'
             'some/test/d.html [ Failure ]\n')
         host.filesystem.files[MOCK_WEB_TESTS + 'VirtualTestSuites'] = '[]'
         host.filesystem.files[MOCK_WEB_TESTS + 'new/a.html'] = ''
         host.filesystem.files[MOCK_WEB_TESTS + 'new/b.html'] = ''
+        host.filesystem.files[
+            host.filesystem.join(
+                port.web_tests_dir(), 'some', 'test', 'd.html')] = ''
         # TODO(rmhasan): Remove creation of Android files within
         # tests.
         for path in PRODUCTS_TO_EXPECTATION_FILE_PATHS.values():
@@ -1248,7 +1319,6 @@
             return ''
 
         updater.git.run = _git_command_return_val
-        updater.port.tests = lambda: ['some/test/d.html']
         updater._relative_to_web_test_dir = lambda test_path: test_path
         updater.cleanup_test_expectations_files()
         self.assertMultiLineEqual(
@@ -1297,8 +1367,8 @@
             'external/wpt/webdriver/some/test/a*.html': 'old/a*.html',
             'external/wpt/webdriver/some/test/c.html': 'old/c.html',
         }
-        updater._list_deleted_test_files = lambda: deleted_files
-        updater._list_renamed_test_files = lambda: renamed_file_pairs
+        updater._list_deleted_files = lambda: deleted_files
+        updater._list_renamed_files = lambda: renamed_file_pairs
         updater.cleanup_test_expectations_files()
         self.assertMultiLineEqual(
             host.filesystem.read_text_file(MOCK_WEB_TESTS +
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py b/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
index 28a1a00c..d17abc7 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_manifest.py
@@ -84,8 +84,11 @@
         [[reference_url1, "=="], [reference_url2, "!="], ...]
     """
 
-    def __init__(self, json_content):
-        self.raw_dict = json.loads(json_content)
+    def __init__(self, host, manifest_path):
+        self.host = host
+        self.port = self.host.port_factory.get()
+        self.raw_dict = json.loads(
+            self.host.filesystem.read_text_file(manifest_path))
         # As a workaround to handle the change from a flat-list to a trie
         # structure in the v8 manifest, flatten the items back to the v7 format.
         #
@@ -93,9 +96,16 @@
         self.raw_dict['items'] = self._flatten_items(
             self.raw_dict.get('items', {}))
 
+        self.wpt_manifest_path = manifest_path
         self.test_types = ('manual', 'reftest', 'testharness', 'crashtest')
         self.test_name_to_file = {}
 
+    @property
+    def wpt_dir(self):
+        return self.host.filesystem.dirname(
+            self.host.filesystem.relpath(
+                self.wpt_manifest_path, self.port.web_tests_dir()))
+
     def _items_for_file_path(self, path_in_wpt):
         """Finds manifest items for the given WPT path.
 
@@ -275,9 +285,8 @@
     @staticmethod
     def generate_manifest(host, dest_path):
         """Generates MANIFEST.json on the specified directory."""
-        finder = PathFinder(host.filesystem)
-        wpt_exec_path = finder.path_from_blink_tools('blinkpy', 'third_party',
-                                                     'wpt', 'wpt', 'wpt')
+        wpt_exec_path = PathFinder(host.filesystem).path_from_blink_tools(
+            'blinkpy', 'third_party', 'wpt', 'wpt', 'wpt')
         cmd = [
             'python', wpt_exec_path, 'manifest', '--no-download',
             '--tests-root', dest_path
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py
index b9d94f5..75038ff 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_manifest_unittest.py
@@ -89,7 +89,11 @@
     }
 }
         '''
-        manifest = WPTManifest(manifest_json)
+        host = MockHost()
+        host.filesystem.write_text_file(
+            WEB_TEST_DIR + '/external/wpt/MANIFEST.json', manifest_json)
+        manifest = WPTManifest(
+            host, WEB_TEST_DIR + '/external/wpt/MANIFEST.json')
         self.assertTrue(manifest.is_test_file('test.any.js'))
         self.assertEqual(manifest.all_url_items(),
                          {u'test.any.html': [u'test.any.html', {}]})
@@ -111,7 +115,11 @@
     }
 }
         '''
-        manifest = WPTManifest(manifest_json)
+        host = MockHost()
+        host.filesystem.write_text_file(
+            WEB_TEST_DIR + '/external/wpt/MANIFEST.json', manifest_json)
+        manifest = WPTManifest(
+            host, WEB_TEST_DIR + '/external/wpt/MANIFEST.json')
         self.assertEqual(manifest.all_url_items(),
                          {u'test.any.html': [u'test.any.html', {}]})
 
@@ -132,7 +140,11 @@
         }
     }
 }       '''
-        manifest = WPTManifest(manifest_json)
+        host = MockHost()
+        host.filesystem.write_text_file(
+            WEB_TEST_DIR + '/external/wpt/MANIFEST.json', manifest_json)
+        manifest = WPTManifest(
+            host, WEB_TEST_DIR + '/external/wpt/MANIFEST.json')
         self.assertEqual(
             manifest.all_url_items(), {
                 u'test.any.html': [u'test.any.html', {}],
@@ -168,7 +180,11 @@
     }
 }
         '''
-        manifest = WPTManifest(manifest_json)
+        host = MockHost()
+        host.filesystem.write_text_file(
+            WEB_TEST_DIR + '/external/wpt/MANIFEST.json', manifest_json)
+        manifest = WPTManifest(
+            host, WEB_TEST_DIR + '/external/wpt/MANIFEST.json')
         self.assertEqual(
             manifest.all_url_items(), {
                 u'test.html': [u'test.html', {}],
diff --git a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations_unittest.py b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations_unittest.py
index bdfb015b..833979b 100644
--- a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations_unittest.py
@@ -144,7 +144,7 @@
         port = host.port_factory.get(options.platform, options=options)
         port.expectations_dict = lambda: {'foo': '-- syntax error1', 'bar': '-- syntax error2'}
 
-        host.port_factory.get = lambda platform, options=None: port
+        host.port_factory.get = lambda platform=None, options=None: port
         host.port_factory.all_port_names = lambda platform=None: [port.name()]
 
         failures, warnings = lint_test_expectations.lint(host, options)
@@ -166,7 +166,7 @@
         port = host.port_factory.get(options.platform, options=options)
         port.expectations_dict = lambda: {}
 
-        host.port_factory.get = lambda platform, options=None: port
+        host.port_factory.get = lambda platform=None, options=None: port
         host.port_factory.all_port_names = lambda platform=None: [port.name()]
         host.filesystem.write_text_file(WEB_TEST_DIR + '/LeakExpectations',
                                         '-- syntax error')
@@ -189,7 +189,7 @@
         port = host.port_factory.get(options.platform, options=options)
         port.expectations_dict = lambda: {'flag-specific': 'does/not/exist', 'noproblem': ''}
 
-        host.port_factory.get = lambda platform, options=None: port
+        host.port_factory.get = lambda platform=None, options=None: port
         host.port_factory.all_port_names = lambda platform=None: [port.name()]
 
         failures, warnings = lint_test_expectations.lint(host, options)
@@ -218,7 +218,7 @@
         port.expectations_dict = lambda: {
             'testexpectations': test_expectations}
 
-        host.port_factory.get = lambda platform, options=None: port
+        host.port_factory.get = lambda platform=None, options=None: port
         host.port_factory.all_port_names = lambda platform=None: [port.name()]
 
         failures, warnings = lint_test_expectations.lint(host, options)
@@ -267,7 +267,7 @@
             host.filesystem.join(port.web_tests_dir(), 'virtual', 'foo',
                                  'README.md'), 'foo')
 
-        host.port_factory.get = lambda platform, options=None: port
+        host.port_factory.get = lambda platform=None, options=None: port
         host.port_factory.all_port_names = lambda platform=None: [port.name()]
 
         failures, warnings = lint_test_expectations.lint(host, options)
@@ -300,7 +300,7 @@
                             'non-wpt/test.html [ Failure ]\n')
         for path in PRODUCTS_TO_EXPECTATION_FILE_PATHS.values():
             host.filesystem.write_text_file(path, raw_expectations)
-        host.port_factory.get = lambda platform, options=None: port
+        host.port_factory.get = lambda platform=None, options=None: port
         host.port_factory.all_port_names = lambda platform=None: [port.name()]
         port.test_exists = lambda _: True
         port.tests = lambda _: {'external/wpt/test.html', 'non-wpt/test.html'}
@@ -325,7 +325,7 @@
         host.filesystem.maybe_make_directory(
             host.filesystem.join(port.web_tests_dir(), 'test2'))
 
-        host.port_factory.get = lambda platform, options=None: port
+        host.port_factory.get = lambda platform=None, options=None: port
         host.port_factory.all_port_names = lambda platform=None: [port.name()]
 
         failures, warnings = lint_test_expectations.lint(host, options)
@@ -364,7 +364,7 @@
             'testexpectations': test_expectations
         }
         port.test_exists = lambda test: True
-        host.port_factory.get = lambda platform, options=None: port
+        host.port_factory.get = lambda platform=None, options=None: port
         host.port_factory.all_port_names = lambda platform=None: [port.name()]
 
         failures, warnings = lint_test_expectations.lint(host, options)
@@ -399,7 +399,7 @@
                              'virtual/foo/test1/* [ Pass ]\n')
         port.expectations_dict = lambda: {'NeverFixTests': test_expectations}
         port.test_exists = lambda test: True
-        host.port_factory.get = lambda platform, options=None: port
+        host.port_factory.get = lambda platform=None, options=None: port
         host.port_factory.all_port_names = lambda platform=None: [port.name()]
 
         failures, warnings = lint_test_expectations.lint(host, options)
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index bb65e8aa..b2b6307 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -959,7 +959,7 @@
                 'manifest_update', True):
             _log.debug('Generating MANIFEST.json for %s...', path)
             WPTManifest.ensure_manifest(self, path)
-        return WPTManifest(self._filesystem.read_text_file(manifest_path))
+        return WPTManifest(self.host, manifest_path)
 
     def is_wpt_crash_test(self, test_file):
         """Returns whether a WPT test is a crashtest.
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py b/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
index 908bdab..e6664711 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base_unittest.py
@@ -32,6 +32,7 @@
 import mock
 
 from blinkpy.common.path_finder import RELATIVE_WEB_TESTS
+from blinkpy.common.host_mock import MockHost
 from blinkpy.common.system.executive_mock import MockExecutive
 from blinkpy.common.system.log_testing import LoggingTestCase
 from blinkpy.common.system.output_capture import OutputCapture
@@ -51,7 +52,7 @@
                   with_tests=False,
                   port_name=None,
                   **kwargs):
-        host = MockSystemHost()
+        host = MockHost()
         if executive:
             host.executive = executive
         if with_tests:
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index d679b2f..03719a8 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -542,16 +542,3 @@
 crbug.com/1133836 external/wpt/scroll-to-text-fragment/redirects.html [ Slow ]
 
 crbug.com/1134580 virtual/eye-dropper/color-picker-show-eye-dropper.html [ Slow ]
-
-crbug.com/919361 [ Mac ] http/tests/fetch/serviceworker/fetch_upload-base-https-other-https.html [ Slow ]
-crbug.com/919361 [ Mac ] http/tests/fetch/serviceworker/fetch_upload.html [ Slow ]
-crbug.com/919361 [ Mac ] http/tests/fetch/window/fetch_upload-base-https-other-https.html [ Slow ]
-crbug.com/919361 [ Mac ] http/tests/fetch/window/fetch_upload.html [ Slow ]
-crbug.com/919361 [ Mac ] http/tests/fetch/workers/fetch_upload-base-https-other-https.html [ Slow ]
-crbug.com/919361 [ Mac ] http/tests/fetch/workers/fetch_upload.html [ Slow ]
-crbug.com/919361 [ Linux ] http/tests/fetch/serviceworker/fetch_upload-base-https-other-https.html [ Slow ]
-crbug.com/919361 [ Linux ] http/tests/fetch/serviceworker/fetch_upload.html [ Slow ]
-crbug.com/919361 [ Linux ] http/tests/fetch/window/fetch_upload-base-https-other-https.html [ Slow ]
-crbug.com/919361 [ Linux ] http/tests/fetch/window/fetch_upload.html [ Slow ]
-crbug.com/919361 [ Linux ] http/tests/fetch/workers/fetch_upload-base-https-other-https.html [ Slow ]
-crbug.com/919361 [ Linux ] http/tests/fetch/workers/fetch_upload.html [ Slow ]
\ No newline at end of file
diff --git a/third_party/blink/web_tests/accessibility/notification-listeners.html b/third_party/blink/web_tests/accessibility/notification-listeners.html
index cec1bde..5ccc9f8d 100644
--- a/third_party/blink/web_tests/accessibility/notification-listeners.html
+++ b/third_party/blink/web_tests/accessibility/notification-listeners.html
@@ -22,7 +22,7 @@
       ["Blur", new Map([
         ["select", 1],
       ])],
-      ["InvalidStatusChanged", new Map([
+      ["MarkDirty", new Map([
         ["select", 1],
       ])],
       ["ValueChanged", new Map([
@@ -38,7 +38,7 @@
       ["Blur", new Map([
         ["AXRole: AXPopUpButton", 1],
       ])],
-      ["InvalidStatusChanged", new Map([
+      ["MarkDirty", new Map([
         ["AXRole: AXPopUpButton", 1],
       ])],
       ["ValueChanged", new Map([
@@ -117,7 +117,7 @@
       // followed by a "Focus" notification on the slider.
       document.getElementById("slider").focus();
 
-      // This should trigger a "invalid status changed" notification on the select.
+      // This should trigger a "MarkDirty" notification on the select.
       document.getElementById("select").setAttribute("aria-invalid", "true");
 
       // This should trigger a "value changed" notification on the slider.
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 29f22cf..0c242e2 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -125326,6 +125326,19 @@
        {}
       ]
      ],
+     "translucent-outline.html": [
+      "38109a56a52d6b1fb0c9f9e0f2503206a610b2d1",
+      [
+       null,
+       [
+        [
+         "/css/css-ui/translucent-outline-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "webkit-appearance-auto-001.html": [
       "fb0261b020f6fc2b1e3bfccb3da7d899f0337f79",
       [
@@ -203202,7 +203215,11 @@
        "010635dddc80817eb9ce06cd21b1c22de9171eeb",
        []
       ]
-     }
+     },
+     "translucent-outline-ref.html": [
+      "383e256533d3e9d716e3ec850d58a8c889aa2829",
+      []
+     ]
     },
     "css-values": {
      "META.yml": [
@@ -237272,7 +237289,7 @@
       []
      ],
      "webxr-test.js": [
-      "c661971285cd01b31ab57ede8e543293dfc56948",
+      "01d2ec23396a9fbe21d25fba90dc8dcd0f9bd0d3",
       []
      ],
      "webxr-test.js.headers": [
@@ -254818,7 +254835,7 @@
       []
      ],
      "webxr_test_constants.js": [
-      "139ab775684c427e39bc50719226c96fcbd3a5d5",
+      "40643d0c6abbd91a51a7940397eed90a9e91fa8f",
       []
      ],
      "webxr_test_constants_fake_world.js": [
@@ -416528,7 +416545,7 @@
        ]
       ],
       "audioworkletnode-constructor-options.https.html": [
-       "cee9ec82c20f42a5941698fc394e6babb639ace9",
+       "d3347d265e42365dcba1190c9a91a30a7e2a6589",
        [
         null,
         {}
@@ -425501,7 +425518,14 @@
       ]
      ],
      "xrSession_environmentBlendMode.https.html": [
-      "da2ddc28d8dc8c4903b985dbe08f9ad034ef0f1b",
+      "28f31da7827abbb197f5d37fe4b4fbf78158fdcc",
+      [
+       null,
+       {}
+      ]
+     ],
+     "xrSession_interactionMode.https.html": [
+      "89cdc80132fb3dd4eb90fa477e280edc132912db",
       [
        null,
        {}
@@ -425510,7 +425534,7 @@
     },
     "dom-overlay": {
      "ar_dom_overlay.https.html": [
-      "78a8ba2080a76f2e2ad0b1023e73e0eaa68fa052",
+      "250adf9b246b259c4d13804e5e465e35ae98b46f",
       [
        null,
        {}
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/otpcredential-helper.js b/third_party/blink/web_tests/external/wpt/credential-management/support/otpcredential-helper.js
index 3cf4510..0c6ce8b 100644
--- a/third_party/blink/web_tests/external/wpt/credential-management/support/otpcredential-helper.js
+++ b/third_party/blink/web_tests/external/wpt/credential-management/support/otpcredential-helper.js
@@ -13,7 +13,7 @@
 async function loadChromiumResources() {
   const resources = [
     '/gen/mojo/public/mojom/base/time.mojom-lite.js',
-    '/gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js',
+    '/gen/third_party/blink/public/mojom/sms/webotp_service.mojom-lite.js',
   ];
 
   await loadMojoResources(resources, true);
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-sms-receiver.js b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-sms-receiver.js
index c4aa9a8..903456d 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/mock-sms-receiver.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/mock-sms-receiver.js
@@ -2,13 +2,13 @@
 
 const SmsProvider = (() => {
 
-  class MockSmsReceiver {
+  class MockWebOTPService {
 
     constructor() {
-      this.mojoReceiver_ = new blink.mojom.SmsReceiverReceiver(this);
+      this.mojoReceiver_ = new blink.mojom.WebOTPServiceReceiver(this);
 
       this.interceptor_ =
-          new MojoInterfaceInterceptor(blink.mojom.SmsReceiver.$interfaceName);
+          new MojoInterfaceInterceptor(blink.mojom.WebOTPService.$interfaceName);
 
       this.interceptor_.oninterfacerequest = (e) => {
         this.mojoReceiver_.$.bindHandle(e.handle);
@@ -35,7 +35,7 @@
     }
   }
 
-  const mockSmsReceiver = new MockSmsReceiver();
+  const mockWebOTPService = new MockWebOTPService();
 
   class SmsProviderChromium {
     constructor() {
@@ -43,7 +43,7 @@
     }
 
     pushReturnValuesForTesting(callName, callback) {
-      mockSmsReceiver.pushReturnValuesForTesting(callName, callback);
+      mockWebOTPService.pushReturnValuesForTesting(callName, callback);
     }
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworkletnode-constructor-options.https.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworkletnode-constructor-options.https.html
index cee9ec8..d3347d2 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworkletnode-constructor-options.https.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworkletnode-constructor-options.https.html
@@ -137,7 +137,7 @@
         const options1 = {channelInterpretation: 'foobar'};
         should(
             () => new AudioWorkletNode(context, 'dummy', options1),
-            'Creating AudioWorkletNode with channelCountMode "foobar"')
+            'Creating AudioWorkletNode with channelInterpretation "foobar"')
             .throw(TypeError);
 
         task.done();
diff --git a/third_party/blink/web_tests/http/conf/php.ini b/third_party/blink/web_tests/http/conf/php.ini
index 4af42343..dcd2322f 100644
--- a/third_party/blink/web_tests/http/conf/php.ini
+++ b/third_party/blink/web_tests/http/conf/php.ini
@@ -1,3 +1,2 @@
 ; Let <meta charset> work. A charset provided in Content-Type takes precedence, and PHP 5.6+ defaults to UTF-8.
 default_charset = ""
-memory_limit = 512M
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-create-basics.html b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-create-basics.html
index 49cc4b8..9029f1d4 100644
--- a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-create-basics.html
+++ b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-create-basics.html
@@ -10,7 +10,7 @@
 <script src="/gen/url/mojom/origin.mojom-lite.js"></script>
 <script src="/gen/url/mojom/url.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom-lite.js"></script>
-<script src="/gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js"></script>
+<script src="/gen/third_party/blink/public/mojom/sms/webotp_service.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom-lite.js"></script>
 <script src="resources/test-inputs.js"></script>
 <script src="resources/mock-navigator-credentials.js"></script>
diff --git a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-get-basics.html b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-get-basics.html
index 5f9b773..0cb9ef1 100644
--- a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-get-basics.html
+++ b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-get-basics.html
@@ -10,7 +10,7 @@
 <script src="/gen/url/mojom/origin.mojom-lite.js"></script>
 <script src="/gen/url/mojom/url.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom-lite.js"></script>
-<script src="/gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js"></script>
+<script src="/gen/third_party/blink/public/mojom/sms/webotp_service.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom-lite.js"></script>
 <script src="resources/test-inputs.js"></script>
 <script src="resources/mock-navigator-credentials.js"></script>
@@ -23,7 +23,7 @@
 
 add_completion_callback(() => {
   mockCredentialManager.reset();
-  mockSmsReceiver.reset();
+  mockWebOTPService.reset();
 });
 
 promise_test(_ => {
@@ -132,35 +132,35 @@
 
 promise_test(_ => {
   var otp = "ABC123";
-  mockSmsReceiver.setOtp(otp);
-  mockSmsReceiver.setStatus(blink.mojom.SmsStatus.kSuccess);
+  mockWebOTPService.setOtp(otp);
+  mockWebOTPService.setStatus(blink.mojom.SmsStatus.kSuccess);
   return navigator.credentials.get({otp: {transport: ["sms"]}}).then(r => {
     assert_equals(r.type, "otp", "type");
     assert_equals(r.id, "", "id");
     assert_equals(r.code, otp, "code");
   });
-}, "Verify that mockSmsReceiver returns the values we give it.");
+}, "Verify that mockWebOTPService returns the values we give it.");
 
 promise_test(t => {
-  mockSmsReceiver.setStatus(blink.mojom.SmsStatus.kCancelled);
+  mockWebOTPService.setStatus(blink.mojom.SmsStatus.kCancelled);
   return promise_rejects_dom(t, "AbortError",
     navigator.credentials.get({otp: {transport: ["sms"]}}));
 }, "Verify that cancelled status returned by mock is properly handled.");
 
 promise_test(t => {
-  mockSmsReceiver.setStatus(blink.mojom.SmsStatus.kAborted);
+  mockWebOTPService.setStatus(blink.mojom.SmsStatus.kAborted);
   return promise_rejects_dom(t, "AbortError",
     navigator.credentials.get({otp: {transport: ["sms"]}}));
 }, "Verify that abort status returned by mock is properly handled.");
 
 promise_test(t => {
-  mockSmsReceiver.setStatus(blink.mojom.SmsStatus.kUnhandledRequest);
+  mockWebOTPService.setStatus(blink.mojom.SmsStatus.kUnhandledRequest);
   return promise_rejects_dom(t, "InvalidStateError",
     navigator.credentials.get({otp: {transport: ["sms"]}}));
 }, "Verify that unhandled request status returned by mock is properly handled.");
 
 promise_test(t => {
-  mockSmsReceiver.setStatus(blink.mojom.SmsStatus.kTimeout);
+  mockWebOTPService.setStatus(blink.mojom.SmsStatus.kTimeout);
   return promise_rejects_dom(t, "NotSupportedError",
     navigator.credentials.get({otp: {transport: []}}));
 }, "Verify that invalid transport is properly handled");
diff --git a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-get-errors.html b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-get-errors.html
index ef006fe..8e607c1 100644
--- a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-get-errors.html
+++ b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-get-errors.html
@@ -9,7 +9,7 @@
 <script src="/gen/url/mojom/origin.mojom-lite.js"></script>
 <script src="/gen/url/mojom/url.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom-lite.js"></script>
-<script src="/gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js"></script>
+<script src="/gen/third_party/blink/public/mojom/sms/webotp_service.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom-lite.js"></script>
 <script src="resources/test-inputs.js"></script>
 <script src="resources/mock-navigator-credentials.js"></script>
diff --git a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-preventsilentaccess-basics.html b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-preventsilentaccess-basics.html
index fc89cfd..8ee2982 100644
--- a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-preventsilentaccess-basics.html
+++ b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-preventsilentaccess-basics.html
@@ -10,7 +10,7 @@
 <script src="/gen/url/mojom/origin.mojom-lite.js"></script>
 <script src="/gen/url/mojom/url.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom-lite.js"></script>
-<script src="/gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js"></script>
+<script src="/gen/third_party/blink/public/mojom/sms/webotp_service.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom-lite.js"></script>
 <script src="resources/test-inputs.js"></script>
 <script src="resources/mock-navigator-credentials.js"></script>
diff --git a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-store-basics.html b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-store-basics.html
index 456b3fa..acbd6b8 100644
--- a/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-store-basics.html
+++ b/third_party/blink/web_tests/http/tests/credentialmanager/credentialscontainer-store-basics.html
@@ -10,7 +10,7 @@
 <script src="/gen/url/mojom/origin.mojom-lite.js"></script>
 <script src="/gen/url/mojom/url.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom-lite.js"></script>
-<script src="/gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js"></script>
+<script src="/gen/third_party/blink/public/mojom/sms/webotp_service.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom-lite.js"></script>
 <script src="resources/test-inputs.js"></script>
 <script src="resources/mock-navigator-credentials.js"></script>
diff --git a/third_party/blink/web_tests/http/tests/credentialmanager/mock-authenticator.html b/third_party/blink/web_tests/http/tests/credentialmanager/mock-authenticator.html
index b8ef744..1bfff3c 100644
--- a/third_party/blink/web_tests/http/tests/credentialmanager/mock-authenticator.html
+++ b/third_party/blink/web_tests/http/tests/credentialmanager/mock-authenticator.html
@@ -9,7 +9,7 @@
 <script src="/gen/url/mojom/origin.mojom-lite.js"></script>
 <script src="/gen/url/mojom/url.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom-lite.js"></script>
-<script src="/gen/third_party/blink/public/mojom/sms/sms_receiver.mojom-lite.js"></script>
+<script src="/gen/third_party/blink/public/mojom/sms/webotp_service.mojom-lite.js"></script>
 <script src="/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom-lite.js"></script>
 <script src="resources/test-inputs.js"></script>
 <script src="resources/mock-navigator-credentials.js"></script>
@@ -20,6 +20,6 @@
 test(function(t) {
   assert_true(mockAuthenticator instanceof Object);
   assert_true(mockCredentialManager instanceof Object);
-  assert_true(mockSmsReceiver instanceof Object);
+  assert_true(mockWebOTPService instanceof Object);
 }, "Authenticator Mojo bindings and mock interfaces are available to tests.")
 </script>
diff --git a/third_party/blink/web_tests/http/tests/credentialmanager/resources/mock-navigator-credentials.js b/third_party/blink/web_tests/http/tests/credentialmanager/resources/mock-navigator-credentials.js
index 8495a55..2ad8109 100644
--- a/third_party/blink/web_tests/http/tests/credentialmanager/resources/mock-navigator-credentials.js
+++ b/third_party/blink/web_tests/http/tests/credentialmanager/resources/mock-navigator-credentials.js
@@ -206,13 +206,13 @@
   }
 }
 
-// Mocks the SmsReceiver interface defined in sms_receiver.mojom.
-class MockSmsReceiver {
+// Mocks the WebOTPService interface defined in webotp_service.mojom.
+class MockWebOTPService {
   constructor() {
     this.reset();
 
     this.interceptor_ = new MojoInterfaceInterceptor(
-        blink.mojom.SmsReceiver.$interfaceName, 'context', true);
+        blink.mojom.WebOTPService.$interfaceName, 'context', true);
     this.interceptor_.oninterfacerequest = (e) => {
       this.bindHandleToReceiver(e.handle);
     };
@@ -220,7 +220,7 @@
   }
 
   bindHandleToReceiver(handle) {
-    this.receiver_ = new blink.mojom.SmsReceiverReceiver(this);
+    this.receiver_ = new blink.mojom.WebOTPServiceReceiver(this);
     this.receiver_.$.bindHandle(handle);
   }
 
@@ -231,7 +231,7 @@
 
   async abort() {}
 
-  // Resets state of mock SmsReceiver.
+  // Resets state of mock WebOTPService.
   reset() {
     this.otp_ = '';
     this.status_ = blink.mojom.SmsStatus.kTimeout;
@@ -248,4 +248,4 @@
 
 var mockAuthenticator = new MockAuthenticator();
 var mockCredentialManager = new MockCredentialManager();
-var mockSmsReceiver = new MockSmsReceiver();
+var mockWebOTPService = new MockWebOTPService();
diff --git a/third_party/blink/web_tests/http/tests/fetch/script-tests/fetch_upload.js b/third_party/blink/web_tests/http/tests/fetch/script-tests/fetch_upload.js
index 21d9f44..40d8fb58 100644
--- a/third_party/blink/web_tests/http/tests/fetch/script-tests/fetch_upload.js
+++ b/third_party/blink/web_tests/http/tests/fetch/script-tests/fetch_upload.js
@@ -69,63 +69,27 @@
   return crypto.subtle.digest('SHA-256', array);
 }
 
-function compare(a, b) {
-  if (a.length != b.length)
-    return [false, 'length is not equal'];
-  for (let i = 0; - 1 < i; i -= 1) {
-    if ((a[i] !== b[i]))
-      return [false, `a[${i}](${a[i]}) != b[${i}](${b[i]})`];
-  }
-  return [true, ''];
-}
-
-async function compare_long_array(a, b, description) {
-  const a_hash = await hash256(a);
-  const b_hash = await hash256(b);
-  const [eq, fail_reason] = compare(a_hash, b_hash);
-  if (eq)
-    return;
-
-  const [raw_eq, raw_fail_reason] = compare(a, b);
-  assert_false(raw_eq);
-  assert_(false, description + ': ' + raw_fail_reason);
-}
-
-async function test_echo_long_array(upload_body, expected_array) {
-  const response = await fetch_echo(upload_body);
-  const reader = response.body.getReader();
-  let index = 0;
-  while (index < expected_array.length) {
-    const chunk = await reader.read();
-    assert_false(chunk.done, `chunk.done@${index}/${expected_array.length}`);
-    const chunk_length = chunk.value.length;
-    await compare_long_array(
-        chunk.value, expected_array.subarray(index, index + chunk_length),
-        `Array of [${index}, ${index + chunk_length - 1}] should be same.`);
-    index += chunk_length;
-  }
-  const final_chunk = await reader.read();
-  assert_true(final_chunk.done, 'final_chunk.done');
-}
-
 promise_test(async () => {
   const length = 1000 * 1000;  // 1Mbytes
   const array = random_values_array(length);
   const stream = create_stream([array]);
-  await test_echo_long_array(stream, array);
-}, 'Fetch with ReadableStream body with 1Mbytes Uint8Array');
-
-promise_test(async () => {
-  const length = 1000 * 1000;  // 1 Mbytes
-  const array = random_values_array(length);
-  await test_echo_long_array(array, array);
-}, 'Fetch with Array body with 1 Mbytes Uint8Array');
-
-promise_test(async () => {
-  const length = 150 * 1000 * 1000;  // 150 Mbytes
-  const array = random_values_array(length);
-  await test_echo_long_array(array, array);
-}, 'Fetch with Array body with 150 Mbytes Uint8Array');
+  const response = await fetch_echo(stream);
+  const reader = response.body.getReader();
+  let index = 0;
+  while (index < length) {
+    const chunk = await reader.read();
+    assert_false(chunk.done);
+    const chunk_length = chunk.value.length;
+    const chunk_hash = await hash256(chunk.value);
+    const src_hash = await hash256(array.subarray(index, index + chunk_length));
+    assert_array_equals(
+        new Uint8Array(chunk_hash), new Uint8Array(src_hash),
+        `Array of [${index}, ${index + length - 1}] should be same.`);
+    index += chunk_length;
+  }
+  const final_chunk = await reader.read();
+  assert_true(final_chunk.done);
+}, 'Fetch with ReadableStream body with long Uint8Array');
 
 promise_test(async (t) => {
   const stream = create_stream(['Foobar']);
diff --git a/third_party/private_membership/README.chromium b/third_party/private_membership/README.chromium
index 343e1e1..0f58cfa 100644
--- a/third_party/private_membership/README.chromium
+++ b/third_party/private_membership/README.chromium
@@ -3,7 +3,7 @@
 Version: unknown
 License: Apache Version 2.0
 License File: LICENSE
-Security Critical: no
+Security Critical: yes
 
 Description:
 
diff --git a/third_party/shell-encryption/README.chromium b/third_party/shell-encryption/README.chromium
index 127ca1f..caf9903 100644
--- a/third_party/shell-encryption/README.chromium
+++ b/third_party/shell-encryption/README.chromium
@@ -1,9 +1,9 @@
 Name: Simple Homomorphic Encryption Library with Lattices
 URL: https://github.com/google/shell-encryption
-Version: 1c3aa80075974a241f2e0f9e7b3c7eccada6d8fd
+Version: f94f58852e9b3edaf2766e348a69a70f596bb9dd
 License: Apache Version 2.0
 License File: src/LICENSE
-Security Critical: no
+Security Critical: yes
 
 Description: This project is a library for fully-homomorphic symmetric-key
 encryption. It uses Ring Learning with Errors (RLWE)-based encryption to make it
diff --git a/third_party/shell-encryption/src/int256.cc b/third_party/shell-encryption/src/int256.cc
index f9a40fc..f10513ed 100644
--- a/third_party/shell-encryption/src/int256.cc
+++ b/third_party/shell-encryption/src/int256.cc
@@ -24,8 +24,6 @@
 
 namespace rlwe {
 
-const uint256_pod kuint256max = {absl::kuint128max, absl::kuint128max};
-
 // Returns the 0-based position of the last set bit (i.e., most significant bit)
 // in the given uint64. The argument may not be 0.
 //
diff --git a/third_party/shell-encryption/src/int256.h b/third_party/shell-encryption/src/int256.h
index 842620c..540dce2d 100644
--- a/third_party/shell-encryption/src/int256.h
+++ b/third_party/shell-encryption/src/int256.h
@@ -131,7 +131,7 @@
   absl::uint128 lo;
 };
 
-extern const uint256_pod kuint256max;
+constexpr uint256_pod kuint256max = {absl::Uint128Max(), absl::Uint128Max()};
 
 // allow uint256 to be logged
 extern std::ostream& operator<<(std::ostream& o, const uint256& b);
diff --git a/third_party/usrsctp/BUILD.gn b/third_party/usrsctp/BUILD.gn
index b869852..0c5947f 100644
--- a/third_party/usrsctp/BUILD.gn
+++ b/third_party/usrsctp/BUILD.gn
@@ -3,8 +3,9 @@
 # found in the LICENSE file.
 
 import("//build/toolchain/toolchain.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
 
-config("usrsctp_config") {
+config("usrsctp_public_config") {
   include_dirs = [
     "usrsctplib/usrsctplib",
     "usrsctplib/usrsctplib/netinet",
@@ -17,6 +18,16 @@
   }
 }
 
+# Used by both usrsctp static library and fuzzers.
+config("usrsctp_internal_config") {
+  defines = []
+  if (is_linux || is_chromeos || is_android) {
+    defines += [ "_GNU_SOURCE" ]
+  } else if (is_apple) {
+    defines += [ "__APPLE_USE_RFC_2292" ]
+  }
+}
+
 config("usrsctp_warnings") {
   if (is_clang) {
     cflags = [
@@ -118,9 +129,10 @@
     # correctly.
     "//build/config/compiler:no_incompatible_pointer_warnings",
     ":usrsctp_warnings",
+    ":usrsctp_internal_config",
   ]
 
-  public_configs = [ ":usrsctp_config" ]
+  public_configs = [ ":usrsctp_public_config" ]
 
   cflags = [
     "-UINET",
@@ -128,14 +140,10 @@
   ]
 
   if (is_linux || is_chromeos || is_android) {
-    defines += [
-      "__Userspace_os_Linux",
-      "_GNU_SOURCE",
-    ]
+    defines += [ "__Userspace_os_Linux" ]
   } else if (is_apple) {
     defines += [
       "HAVE_SA_LEN",
-      "__APPLE_USE_RFC_2292",
       "__Userspace_os_Darwin",
     ]
   }
@@ -149,5 +157,51 @@
   if (is_fuchsia) {
     defines += [ "__Userspace_os_Fuchsia" ]
   }
+
+  if (use_fuzzing_engine) {
+    defines += [ "INVARIANTS" ]
+  }
   deps = [ "//third_party/boringssl" ]
 }
+
+fuzzer_test("usrsctp_fuzzer_listen") {
+  sources = [
+    "usrsctplib/fuzzer/fuzzer_listen.c",
+    "usrsctplib/programs/programs_helper.c",
+  ]
+  additional_configs = [ ":usrsctp_internal_config" ]
+  deps = [ ":usrsctp" ]
+  seed_corpus = "usrsctplib/fuzzer/CORPUS_LISTEN"
+  libfuzzer_options = [ "max_len=4086" ]
+}
+
+fuzzer_test("usrsctp_fuzzer_fragment") {
+  sources = [
+    "usrsctplib/fuzzer/fuzzer_fragment.c",
+    "usrsctplib/programs/programs_helper.c",
+  ]
+  additional_configs = [ ":usrsctp_internal_config" ]
+  deps = [ ":usrsctp" ]
+  seed_corpus = "usrsctplib/fuzzer/CORPUS_FRAGMENT"
+  libfuzzer_options = [ "max_len=4086" ]
+}
+
+fuzzer_test("usrsctp_fuzzer_connect_multi") {
+  sources = [
+    "usrsctplib/fuzzer/fuzzer_connect.c",
+    "usrsctplib/programs/programs_helper.c",
+  ]
+
+  additional_configs = [ ":usrsctp_internal_config" ]
+
+  # See usrsctplib/fuzzer/CMakeLists.txt.
+  # Stage 0 is "multi", which can start fuzzing at different points during the
+  # handshake based on the first fuzzed byte. There were plans to have dedicated
+  # fuzzers for various stages, but this idea was abandoned what was found that
+  # the "multi" fuzzer performed just as well.
+  defines = [ "FUZZING_STAGE=0" ]
+
+  deps = [ ":usrsctp" ]
+  seed_corpus = "usrsctplib/fuzzer/CORPUS_CONNECT"
+  libfuzzer_options = [ "max_len=32000" ]
+}
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index c0f7030e..6e62022d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -972,6 +972,9 @@
     Promo is not shown due to sign-in being disallowed either by an enterprise
     policy or by |Allow Chrome sign-in| toggle.
   </int>
+  <int value="8" label="SignedInWithAddedAccount">
+    User has added an account and signed in with this account.
+  </int>
 </enum>
 
 <enum name="AccountManagerAccountAdditionSource">
@@ -41955,6 +41958,7 @@
   <int value="-1596489715" label="AutoScreenBrightness:enabled"/>
   <int value="-1594964830" label="FilesCameraFolder:disabled"/>
   <int value="-1594298767" label="FullscreenToolbarReveal:enabled"/>
+  <int value="-1588702520" label="WebOTPCrossDevice:disabled"/>
   <int value="-1586642651" label="MaterialDesignExtensions:disabled"/>
   <int value="-1585733447" label="ContextualSearchUnityIntegration:disabled"/>
   <int value="-1584944853" label="TrilinearFiltering:enabled"/>
@@ -44675,6 +44679,7 @@
   <int value="1138690502" label="PreconnectToSearch:enabled"/>
   <int value="1139226452" label="enable-nacl-debug"/>
   <int value="1139363314" label="disable-supervised-user-blacklist"/>
+  <int value="1139756271" label="WebOTPCrossDevice:enabled"/>
   <int value="1140541604" label="WinrtGeolocationImplementation:enabled"/>
   <int value="1142515376" label="enable-nacl"/>
   <int value="1142788238" label="FontCacheScaling:disabled"/>
@@ -59547,15 +59552,15 @@
 
 <enum name="Profile">
   <int value="0" label="Guest Profile"/>
-  <int value="1" label="Profile 1"/>
-  <int value="2" label="Profile 2"/>
-  <int value="3" label="Profile 3"/>
-  <int value="4" label="Profile 4"/>
-  <int value="5" label="Profile 5"/>
-  <int value="6" label="Profile 6"/>
-  <int value="7" label="Profile 7"/>
-  <int value="8" label="Profile 8"/>
-  <int value="9" label="Profile 9"/>
+  <int value="1" label="Profile 01"/>
+  <int value="2" label="Profile 02"/>
+  <int value="3" label="Profile 03"/>
+  <int value="4" label="Profile 04"/>
+  <int value="5" label="Profile 05"/>
+  <int value="6" label="Profile 06"/>
+  <int value="7" label="Profile 07"/>
+  <int value="8" label="Profile 08"/>
+  <int value="9" label="Profile 09"/>
   <int value="10" label="Profile 10"/>
   <int value="11" label="Profile 11"/>
   <int value="12" label="Profile 12"/>
@@ -59646,7 +59651,7 @@
   <int value="97" label="Profile 97"/>
   <int value="98" label="Profile 98"/>
   <int value="99" label="Profile 99"/>
-  <int value="100" label="Profile 100"/>
+  <int value="100" label="Profile 99+"/>
 </enum>
 
 <enum name="ProfileAddNewUser">
@@ -63310,6 +63315,9 @@
 </enum>
 
 <enum name="RevokeTokenAction">
+  <obsolete>
+    Removed in M88.
+  </obsolete>
   <int value="0" label="None"/>
   <int value="1" label="Invalidate token for primary account"/>
   <int value="2" label="Revoke token for secondary accounts"/>
@@ -67713,25 +67721,6 @@
   <int value="3" label="GURL not valid"/>
 </enum>
 
-<enum name="SmsReceiverDestroyedReason">
-  <int value="0" label="Navigated to new page"/>
-  <int value="1" label="Navigated to existing page"/>
-  <int value="2" label="Nagivated to same page"/>
-</enum>
-
-<enum name="SMSReceiverInfobarAction">
-  <int value="0" label="Baseline: InfobarShown"/>
-  <int value="1" label="KeyboardDismissed"/>
-</enum>
-
-<enum name="SMSReceiverOutcome">
-  <int value="0" label="Success"/>
-  <int value="1" label="Unhandled Request"/>
-  <int value="2" label="Connection Error"/>
-  <int value="3" label="Cancelled"/>
-  <int value="4" label="Aborted"/>
-</enum>
-
 <enum name="SnackbarIdentifier">
   <int value="-2" label="TEST_SNACKBAR"/>
   <int value="-1" label="UNKNOWN"/>
@@ -75721,6 +75710,25 @@
   <int value="4" label="FlingingRendererClientFactory"/>
 </enum>
 
+<enum name="WebOTPServiceDestroyedReason">
+  <int value="0" label="Navigated to new page"/>
+  <int value="1" label="Navigated to existing page"/>
+  <int value="2" label="Nagivated to same page"/>
+</enum>
+
+<enum name="WebOTPServiceInfobarAction">
+  <int value="0" label="Baseline: InfobarShown"/>
+  <int value="1" label="KeyboardDismissed"/>
+</enum>
+
+<enum name="WebOTPServiceOutcome">
+  <int value="0" label="Success"/>
+  <int value="1" label="Unhandled Request"/>
+  <int value="2" label="Connection Error"/>
+  <int value="3" label="Cancelled"/>
+  <int value="4" label="Aborted"/>
+</enum>
+
 <enum name="WebpDecodedFormat">
   <int value="1" label="JPEG"/>
   <int value="2" label="PNG"/>
@@ -76127,7 +76135,7 @@
   <int value="49" label="PaymentManager"/>
   <int value="50" label="SpeechSynthesis"/>
   <int value="51" label="KeyboardLock"/>
-  <int value="52" label="SmsService"/>
+  <int value="52" label="WebOTPService"/>
   <int value="53" label="OutstandingNetworkRequestDirectSocket"/>
 </enum>
 
diff --git a/tools/metrics/histograms/histograms_xml/blink/histograms.xml b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
index bedc2fe2..cd60127 100644
--- a/tools/metrics/histograms/histograms_xml/blink/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
@@ -1680,7 +1680,7 @@
 </histogram>
 
 <histogram name="Blink.Sms.Receive.DestroyedReason"
-    enum="SmsReceiverDestroyedReason" expires_after="M92">
+    enum="WebOTPServiceDestroyedReason" expires_after="M92">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
@@ -1690,7 +1690,7 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Sms.Receive.Infobar" enum="SMSReceiverInfobarAction"
+<histogram name="Blink.Sms.Receive.Infobar" enum="WebOTPServiceInfobarAction"
     expires_after="M92">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
@@ -1702,13 +1702,13 @@
   </summary>
 </histogram>
 
-<histogram name="Blink.Sms.Receive.Outcome" enum="SMSReceiverOutcome"
+<histogram name="Blink.Sms.Receive.Outcome" enum="WebOTPServiceOutcome"
     expires_after="M92">
   <owner>goto@chromium.org</owner>
   <owner>reillyg@chromium.org</owner>
   <owner>yigu@chromium.org</owner>
   <owner>web-identity@google.com</owner>
-  <summary>Records the result of a call to the SmsReceiver API.</summary>
+  <summary>Records the result of a call to the WebOTP API.</summary>
 </histogram>
 
 <histogram name="Blink.Sms.Receive.SmsParsingStatus" enum="SmsParsingStatus"
diff --git a/tools/metrics/histograms/histograms_xml/navigation/histograms.xml b/tools/metrics/histograms/histograms_xml/navigation/histograms.xml
index c1dd423..fb4bff7 100644
--- a/tools/metrics/histograms/histograms_xml/navigation/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/navigation/histograms.xml
@@ -665,10 +665,6 @@
 
 <histogram name="NavigationSuggestion.Event" enum="NavigationSuggestionEvent"
     expires_after="M89">
-  <obsolete>
-    Replaced with NavigationSuggestion.Event2 on 2020/10 because of
-    crbug.com/1136296.
-  </obsolete>
   <owner>meacer@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -678,23 +674,6 @@
   </summary>
 </histogram>
 
-<histogram name="NavigationSuggestion.Event2" enum="NavigationSuggestionEvent"
-    expires_after="M91">
-  <owner>meacer@chromium.org</owner>
-  <owner>security-enamel@chromium.org</owner>
-  <summary>
-    Tracks events when the currently navigated domain name is a lookalike to one
-    of the top 10K domains or a domain that the user interacted with, resulting
-    in a navigation suggestion interstitial.
-
-    Before M88, NavigationSuggestion.Event recorded a separate entry for each
-    lookalike URL in a redirect chain. E.g. lookalike1.com -&gt; site.com -&gt;
-    lookalike2.com recorded two events. This updated histogram only records an
-    entry for the first or last URL in the redirect if any of those is a
-    lookalike.
-  </summary>
-</histogram>
-
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 2e6e04a..7eb6e2c 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -1502,6 +1502,9 @@
 
 <histogram name="Bookmarks.CreateBookmarkIndexTime" units="ms"
     expires_after="M88">
+  <obsolete>
+    Removed in M88.
+  </obsolete>
   <owner>mastiz@chromium.org</owner>
   <owner>sky@chromium.org</owner>
   <summary>
@@ -1511,6 +1514,9 @@
 </histogram>
 
 <histogram name="Bookmarks.DecodeTime" units="ms" expires_after="M88">
+  <obsolete>
+    Removed in M88.
+  </obsolete>
   <owner>mastiz@chromium.org</owner>
   <owner>sky@chromium.org</owner>
   <summary>
@@ -1521,6 +1527,9 @@
 
 <histogram name="Bookmarks.DuplicateAndEmptyTitleDetectionTime" units="ms"
     expires_after="2019-09-24">
+  <obsolete>
+    Removed in M88.
+  </obsolete>
   <owner>mamir@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -2479,7 +2488,7 @@
 </histogram>
 
 <histogram name="ConfigureDisplays.External.Modeset.Resolution"
-    enum="DisplayResolution" expires_after="2020-10-30">
+    enum="DisplayResolution" expires_after="2021-10-30">
   <owner>dcastagna@chromium.org</owner>
   <owner>marcheu@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
@@ -2507,7 +2516,7 @@
 </histogram>
 
 <histogram name="ConfigureDisplays.Internal.Modeset.FinalStatus"
-    enum="BooleanSuccess" expires_after="2020-10-04">
+    enum="BooleanSuccess" expires_after="2021-10-04">
   <owner>dcastagna@chromium.org</owner>
   <owner>marcheu@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
@@ -2520,7 +2529,7 @@
 </histogram>
 
 <histogram name="ConfigureDisplays.Internal.Modeset.RefreshRate" units="Hz"
-    expires_after="2020-07-30">
+    expires_after="2021-07-30">
   <owner>dcastagna@chromium.org</owner>
   <owner>marcheu@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
@@ -4708,6 +4717,78 @@
   </summary>
 </histogram>
 
+<histogram name="FamilyUser.ArcAppsCount" units="Number of Apps"
+    expires_after="2021-08-12">
+  <owner>tobyhuang@chromium.org</owner>
+  <owner>cros-families-eng@google.com</owner>
+  <summary>
+    Around once per day, records the number of recently used arc apps in the
+    past 28 days.
+  </summary>
+</histogram>
+
+<histogram name="FamilyUser.BorealisAppsCount" units="Number of Apps"
+    expires_after="2021-08-12">
+  <owner>tobyhuang@chromium.org</owner>
+  <owner>cros-families-eng@google.com</owner>
+  <summary>
+    Around once per day, records the number of recently used borealis apps in
+    the past 28 days.
+  </summary>
+</histogram>
+
+<histogram name="FamilyUser.CrostiniAppsCount" units="Number of Apps"
+    expires_after="2021-08-12">
+  <owner>tobyhuang@chromium.org</owner>
+  <owner>cros-families-eng@google.com</owner>
+  <summary>
+    Around once per day, records the number of recently used crostini apps in
+    the past 28 days.
+  </summary>
+</histogram>
+
+<histogram name="FamilyUser.EnabledExtensionsCount"
+    units="Number of Extensions" expires_after="2021-08-12">
+  <owner>tobyhuang@chromium.org</owner>
+  <owner>cros-families-eng@google.com</owner>
+  <summary>
+    Records the number of non-component enabled regular browser extensions and
+    themes around once per day, regardless of when they were last used.
+  </summary>
+</histogram>
+
+<histogram name="FamilyUser.ExtensionAppsCount" units="Number of Apps"
+    expires_after="2021-08-12">
+  <owner>tobyhuang@chromium.org</owner>
+  <owner>cros-families-eng@google.com</owner>
+  <summary>
+    Around once per day, records the number of recently used extension apps in
+    the past 28 days. Compared to FamilyUser.InstalledExtensionsCount, this
+    metric only includes apps and excludes regular browser extensions.
+  </summary>
+</histogram>
+
+<histogram name="FamilyUser.InstalledExtensionsCount"
+    units="Number of Extensions" expires_after="2021-08-12">
+  <owner>tobyhuang@chromium.org</owner>
+  <owner>cros-families-eng@google.com</owner>
+  <summary>
+    Records the number of non-component installed regular browser extensions and
+    themes around once per day, regardless of when they were last used.
+  </summary>
+</histogram>
+
+<histogram name="FamilyUser.OtherAppsCount" units="Number of Apps"
+    expires_after="2021-08-12">
+  <owner>tobyhuang@chromium.org</owner>
+  <owner>cros-families-eng@google.com</owner>
+  <summary>
+    Around once per day, records the number of recently used apps in the past 28
+    days in a catch-all other category. Includes unknown, built-in, mac native,
+    plugin vm, lacros, and remote apps.
+  </summary>
+</histogram>
+
 <histogram name="FamilyUser.SessionEngagement.Duration" units="ms"
     expires_after="2021-07-14">
   <owner>agawronska@chromium.org</owner>
@@ -4761,6 +4842,26 @@
   </summary>
 </histogram>
 
+<histogram name="FamilyUser.TotalAppsCount" units="Number of Apps"
+    expires_after="2021-08-12">
+  <owner>tobyhuang@chromium.org</owner>
+  <owner>cros-families-eng@google.com</owner>
+  <summary>
+    Around once per day, records the total number of recently used apps in the
+    past 28 days.
+  </summary>
+</histogram>
+
+<histogram name="FamilyUser.WebAppsCount" units="Number of Apps"
+    expires_after="2021-08-12">
+  <owner>tobyhuang@chromium.org</owner>
+  <owner>cros-families-eng@google.com</owner>
+  <summary>
+    Around once per day, records the number of recently used web apps in the
+    past 28 days.
+  </summary>
+</histogram>
+
 <histogram name="FaultTolerantHeap" enum="FaultTolerantHeap"
     expires_after="M77">
   <owner>brucedawson@chromium.org</owner>
@@ -5240,6 +5341,9 @@
 
 <histogram name="ForceDiceMigration.RevokeTokenAction" enum="RevokeTokenAction"
     expires_after="2020-10-04">
+  <obsolete>
+    Removed in M88.
+  </obsolete>
   <owner>msalama@chromium.org</owner>
   <owner>msarda@chromium.org</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index e89babd4..57f4b78 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,12 +5,12 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/win/935a915963e1482109b102c82585d78c12112b31/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "5c6a76e29cb34a936433d215fa3b8445fb589ae0",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/a08dc37d19e590ad61a693fc4c07571a512e1076/trace_processor_shell"
+            "hash": "369fa3ea912a637db68ae6b91bf396e78b18cbb3",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/0150a791fcb7070c81c7e43ce5a7de06eaa4b1cf/trace_processor_shell"
         },
         "linux": {
             "hash": "409f72eb54dc820138d8c22966decdbbdf554a2d",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/095444c2126249c308a855b984ace9cff340f414/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/0150a791fcb7070c81c7e43ce5a7de06eaa4b1cf/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/win/DebugVisualizers/blink.natvis b/tools/win/DebugVisualizers/blink.natvis
index c734b13f..6fd480d 100644
--- a/tools/win/DebugVisualizers/blink.natvis
+++ b/tools/win/DebugVisualizers/blink.natvis
@@ -27,12 +27,12 @@
     <DisplayString IncludeView="bare"
                    Condition="*(int*)&amp;hash_and_flags_ &amp; 1">
         {(this+1),[length_]sb}</DisplayString>
-    <DisplayString>[{length_}] {(this+1),[length_]s}</DisplayString>
     <DisplayString IncludeView="bare"
-                   Condition="*(int*)&amp;hash_and_flags_ &amp; 1">
+                   Condition="!(*(int*)&amp;hash_and_flags_ &amp; 1)">
         {(this+1),[length_]sub}</DisplayString>
-    <DisplayString Condition="(*(int*)&amp;hash_and_flags_ &amp; 1)==0">
-        [{length_}] {(this+1),[length_]su}</DisplayString>
+    <DisplayString Condition="*(int*)&amp;hash_and_flags_ &amp; 1">
+        [{length_}] {(this+1),[length_]s}</DisplayString>
+    <DisplayString>[{length_}] {(this+1),[length_]su}</DisplayString>
     <Expand>
       <Item Name="Length">length_</Item>
       <Item Name="AsciiText"
@@ -288,14 +288,13 @@
   </Type>
   <Type Name="blink::NGFragmentItem">
     <Expand>
-      <Item Name="Text" Condition="type_ == 0">text_</Item>
+      <Item Name="Type">(blink::NGFragmentItem::ItemType)type_</Item>
       <Item Name="TextType" Condition="type_ == 0">(blink::NGTextType)sub_type_</Item>
-      <Item Name="GeneratedText" Condition="type_ == 1">generated_text_</Item>
-      <Item Name="Line" Condition="type_ == 2">line_</Item>
       <Item Name="LineBoxType" Condition="type_ == 2">(NGPhysicalLineBoxFragment::NGLineBoxType)sub_type_</Item>
-      <Item Name="Box" Condition="type_ == 3">box_</Item>
       <Item Name="layout_object_">layout_object_</Item>
       <Item Name="rect_">rect_</Item>
+      <Item Name="ink_overflow_type_">(NGInkOverflow::Type)ink_overflow_type_</Item>
+      <Item Name="is_dirty_">is_dirty_</Item>
     </Expand>
   </Type>
   <Type Name="blink::NGFragmentItems">
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index 07e402c4..653225e 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -55,8 +55,6 @@
       return "hover";
     case ax::mojom::Event::kImageFrameUpdated:
       return "imageFrameUpdated";
-    case ax::mojom::Event::kInvalidStatusChanged:
-      return "invalidStatusChanged";
     case ax::mojom::Event::kLayoutComplete:
       return "layoutComplete";
     case ax::mojom::Event::kLiveRegionCreated:
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index 0019dd45..3449e17 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -47,7 +47,6 @@
   kHitTestResult,
   kHover,
   kImageFrameUpdated,     // Web
-  kInvalidStatusChanged,  // Implicit
   kLayoutComplete,        // Web
   kLiveRegionCreated,     // Implicit
   kLiveRegionChanged,     // Web
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 14da2c3d..900fe8d 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -4066,9 +4066,6 @@
     case ax::mojom::Event::kValueChanged:
       OnValueChanged();
       break;
-    case ax::mojom::Event::kInvalidStatusChanged:
-      OnInvalidStatusChanged();
-      break;
     case ax::mojom::Event::kWindowActivated:
       if (AtkUtilAuraLinux::GetInstance()->IsAtSpiReady()) {
         OnWindowActivated();
diff --git a/ui/file_manager/file_manager/background/js/test_util.js b/ui/file_manager/file_manager/background/js/test_util.js
index f743213d..2a2afadf 100644
--- a/ui/file_manager/file_manager/background/js/test_util.js
+++ b/ui/file_manager/file_manager/background/js/test_util.js
@@ -896,5 +896,32 @@
   return fake.callCounter_;
 };
 
+/**
+ * Send progress item to Foreground page to display.
+ * @param {string} id Progress item id.
+ * @param {ProgressItemType} type Type of progress item.
+ * @param {ProgressItemState} state State of the progress item.
+ * @param {string} message Message of the progress item.
+ * @param {number} remainingTime The remaining time of the progress in second.
+ * @param {number} progressMax Max value of the progress.
+ * @param {number} progressValue Current value of the progress.
+ * @param {number} count Number of items being processed.
+ */
+test.util.sync.sendProgressItem =
+    (id, type, state, message, remainingTime, progressMax = 1,
+     progressValue = 0, count = 1) => {
+      const item = new ProgressCenterItem();
+      item.id = id;
+      item.type = type;
+      item.state = state;
+      item.message = message;
+      item.remainingTime = remainingTime;
+      item.progressMax = progressMax;
+      item.progressValue = progressValue;
+      item.itemCount = count;
+
+      background.progressCenter.updateItem(item);
+    };
+
 // Register the test utils.
 test.util.registerRemoteTestUtils();
diff --git a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
index c0feccb..d2c7b75 100644
--- a/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
+++ b/ui/file_manager/file_manager/foreground/elements/xf_panel_item.js
@@ -66,7 +66,8 @@
                   border-radius: 4px;
                   display: flex;
                   flex-direction: row;
-                  max-width: 400px;
+                  height: 68px;
+                  width: 400px;
               }
 
               .xf-button {
@@ -75,9 +76,9 @@
               }
 
               .xf-panel-text {
+                  flex: 1;
                   font: 13px Roboto;
                   line-height: 20px;
-                  max-width: 216px;
                   overflow: hidden;
                   text-overflow: ellipsis;
                   white-space: nowrap;
@@ -87,10 +88,6 @@
                   outline: none;
               }
 
-              :host([panel-type='3']) .xf-panel-text {
-                  width: 216px;
-              }
-
               :host([panel-type='3']) .xf-panel-label-text {
                   display: -webkit-box;
                   -webkit-line-clamp: 2;
@@ -105,7 +102,6 @@
 
               .xf-panel-label-text {
                   color: var(--google-grey-900);
-                  max-width: 216px;
                   text-overflow: ellipsis;
                   overflow: hidden;
                   white-space: nowrap;
@@ -124,7 +120,6 @@
               }
 
               :host(:not([detailed-panel])) .xf-grow-padder {
-                  flex-grow: 16;
                   width: 24px;
               }
 
@@ -153,15 +148,6 @@
                   padding-bottom: var(--progress-padding-bottom);
               }
 
-              :host(:not([panel-type='0'])) .xf-panel-item {
-                  height: 68px;
-              }
-
-              :host([detailed-panel]) .xf-panel-item {
-                  height: 68px;
-                  width: 400px;
-              }
-
               :host([detailed-panel]:not([detailed-summary])) .xf-panel-text {
                   margin-inline-end: 24px;
                   margin-inline-start: 24px;
@@ -173,7 +159,6 @@
 
               :host([detailed-panel]:not([detailed-summary])) xf-button {
                   margin-inline-end: 12px;
-                  margin-inline-start: auto;
               }
 
               :host([detailed-panel]:not([detailed-summary])) #indicator {
diff --git a/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js b/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
index 26e068d..9842f9f 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
@@ -519,6 +519,12 @@
       return str('PENDING_LABEL');
     }
 
+    // Return empty string for not supported operation (didn't set
+    // remainingTime) or 0 sec remainingTime in non progressing state.
+    if (!seconds) {
+      return '';
+    }
+
     const hours = Math.floor(seconds / 3600);
     const minutes = Math.floor((seconds % 3600) / 60);
 
@@ -633,6 +639,7 @@
         case 'error':
           panelItem.panelType = panelItem.panelTypeError;
           panelItem.primaryText = item.message;
+          panelItem.secondaryText = '';
           // Make sure the panel is attached so it shows immediately.
           this.feedbackHost_.attachPanelItem(panelItem);
           break;
diff --git a/ui/file_manager/image_loader/piex_loader.js b/ui/file_manager/image_loader/piex_loader.js
index 68cc66d..635bfda 100644
--- a/ui/file_manager/image_loader/piex_loader.js
+++ b/ui/file_manager/image_loader/piex_loader.js
@@ -546,7 +546,7 @@
    * @private
    * @param {!PiexWasmPreviewImageMetadata} preview
    * @param {!Uint8Array} view
-   * return {!Uint8Array}
+   * @return {!Uint8Array}
    */
   createImageDataArray_(view, preview) {
     const jpeg = view.byteLength > 2 && view[0] === 0xff && view[1] === 0xd8;
diff --git a/ui/file_manager/integration_tests/file_manager/quick_view.js b/ui/file_manager/integration_tests/file_manager/quick_view.js
index 5b586da..11fd5ad4 100644
--- a/ui/file_manager/integration_tests/file_manager/quick_view.js
+++ b/ui/file_manager/integration_tests/file_manager/quick_view.js
@@ -529,9 +529,39 @@
     // Open a DocumentsProvider file in Quick View.
     await openQuickView(appId, ENTRIES.hello.nameText);
 
-    // crbug.com/1131298 The text file content is not displayed. The <webview>
-    // instead shows a "site cannot be reached" error.
-    return IGNORE_APP_ERRORS;
+    /**
+     * The text <webview> resides in the #quick-view shadow DOM, as a child of
+     * the #dialog element.
+     */
+    const webView = ['#quick-view', '#dialog[open] webview.text-content'];
+
+    // Wait for the Quick View <webview> to load and display its content.
+    const caller = getCaller();
+    function checkWebViewTextLoaded(elements) {
+      let haveElements = Array.isArray(elements) && elements.length === 1;
+      if (haveElements) {
+        haveElements = elements[0].styles.display.includes('block');
+      }
+      if (!haveElements || !elements[0].attributes.src) {
+        return pending(caller, 'Waiting for <webview> to load.');
+      }
+      return;
+    }
+    await repeatUntil(async () => {
+      return checkWebViewTextLoaded(await remoteCall.callRemoteTestUtil(
+          'deepQueryAllElements', appId, [webView, ['display']]));
+    });
+
+    // Wait until the <webview> displays the file's content.
+    await repeatUntil(async () => {
+      const getTextContent = 'window.document.body.textContent';
+      const text = await remoteCall.callRemoteTestUtil(
+          'deepExecuteScriptInWebView', appId, [webView, getTextContent]);
+      // Check: the content of text file should be shown.
+      if (!text || !text[0].includes('I like chocolate and chips.')) {
+        return pending(caller, 'Waiting for <webview> content.');
+      }
+    });
   };
 
   /**
diff --git a/ui/file_manager/integration_tests/file_manager/transfer.js b/ui/file_manager/integration_tests/file_manager/transfer.js
index 7b9f014e..a6b16b4 100644
--- a/ui/file_manager/integration_tests/file_manager/transfer.js
+++ b/ui/file_manager/integration_tests/file_manager/transfer.js
@@ -1093,3 +1093,90 @@
       appId, ['#progress-panel', 'xf-panel-item']);
   chrome.test.assertEq('progress', progressPanel.attributes['indicator']);
 };
+
+/**
+ * Tests no remaining time displayed for not supported operations like format.
+ */
+testcase.transferNotSupportedOperationHasNoRemainingTimeText = async () => {
+  const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
+
+  // Show a |format| progress panel.
+  await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
+    'item-id-1',
+    /* ProgressItemType.FORMAT */ 'format',
+    /* ProgressItemState.PROGRESSING */ 'progressing', 'Formatting'
+  ]);
+
+  // Check the progress panel is open.
+  let panel = await remoteCall.waitForElement(
+      appId, ['#progress-panel', 'xf-panel-item']);
+
+  // Check no remaining time shown for 'format' panel type.
+  chrome.test.assertEq('', panel.attributes['secondary-text']);
+
+  // Show a |format| error panel.
+  await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
+    'item-id-2', /* ProgressItemType.FORMAT */ 'format',
+    /* ProgressItemState.ERROR */ 'error', 'Failed'
+  ]);
+
+  // Check the progress panel is open.
+  panel = await remoteCall.waitForElement(
+      appId, ['#progress-panel', 'xf-panel-item#item-id-2']);
+
+  // Check no remaining time shown for 'format' error panel type.
+  chrome.test.assertEq('', panel.attributes['secondary-text']);
+};
+
+/**
+ * Tests updating same panel keeps same message.
+ * Use case: crbug/1137229
+ */
+testcase.transferUpdateSamePanelItem = async () => {
+  const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
+
+  // Show a |format| error in feedback panel.
+  await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
+    'item-id', /* ProgressItemType.FORMAT */ 'format',
+    /* ProgressItemState.ERROR */ 'error', 'Failed'
+  ]);
+
+  // Check the error panel is open.
+  let panel = await remoteCall.waitForElement(
+      appId, ['#progress-panel', 'xf-panel-item']);
+
+  // Dispatch another |format| feedback panel with the same id and panel type.
+  await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
+    'item-id', /* ProgressItemType.FORMAT */ 'format',
+    /* ProgressItemState.ERROR */ 'error', 'Failed new message'
+  ]);
+
+  // Check the progress panel is open.
+  panel = await remoteCall.waitForElement(
+      appId, ['#progress-panel', 'xf-panel-item']);
+
+  // Check secondary text is still empty for the error panel.
+  chrome.test.assertEq('', panel.attributes['secondary-text']);
+};
+
+/**
+ * Tests pending message shown when the remaining time is zero.
+ */
+testcase.transferShowPendingMessageForZeroRemainingTime = async () => {
+  const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
+
+  // Show a |copy| progress in feedback panel.
+  await remoteCall.callRemoteTestUtil('sendProgressItem', null, [
+    'item-id', /* ProgressItemType.COPY */ 'copy',
+    /* ProgressItemState.PROGRESSING */ 'progressing',
+    'Copying File1.txt to Downloads',
+    /* remainingTime*/ 0
+  ]);
+
+  // Check the error panel is open.
+  const panel = await remoteCall.waitForElement(
+      appId, ['#progress-panel', 'xf-panel-item']);
+
+  // Check secondary text is pending message.
+  chrome.test.assertEq('Pending', panel.attributes['secondary-text']);
+};
diff --git a/ui/webui/resources/js/cr/ui/BUILD.gn b/ui/webui/resources/js/cr/ui/BUILD.gn
index 9dc0bba..15ae9e83 100644
--- a/ui/webui/resources/js/cr/ui/BUILD.gn
+++ b/ui/webui/resources/js/cr/ui/BUILD.gn
@@ -249,6 +249,8 @@
 js_modulizer("modulize") {
   input_files = [
     "array_data_model.js",
+    "command.js",
+    "context_menu_handler.js",
     "dialogs.js",
     "drag_wrapper.js",
     "focus_grid.js",
@@ -256,12 +258,16 @@
     "focus_row.js",
     "focus_row_behavior.js",
     "focus_without_ink.js",
+    "grid.js",
     "keyboard_shortcut_list.js",
     "list.js",
     "list_item.js",
     "list_selection_controller.js",
     "list_selection_model.js",
     "list_single_selection_model.js",
+    "menu.js",
+    "menu_button.js",
+    "menu_item.js",
     "position_util.js",
     "splitter.js",
     "store.js",
@@ -273,6 +279,8 @@
     "cr.PropertyKind|PropertyKind",
     "cr.dispatchPropertyChange|dispatchPropertyChange",
     "cr.EventTarget|EventTarget",
+    "cr.ui.Command|Command",
+    "cr.ui.decorate|decorate",
     "cr.ui.dialogs.BaseDialog|BaseDialog",
     "cr.ui.dialogs.AlertDialog|AlertDialog",
     "cr.ui.dialogs.ConfirmDialog|ConfirmDialog",
@@ -285,12 +293,18 @@
     "cr.ui.DragWrapperDelegate|DragWrapperDelegate",
     "cr.ui.FocusRowDelegate|FocusRowDelegate",
     "cr.ui.FocusRow|FocusRow",
+    "cr.ui.Grid|Grid",
+    "cr.ui.HideType|HideType",
     "cr.ui.limitInputWidth|limitInputWidth",
     "cr.ui.List|List",
-    "cr.ui.Size|Size",
     "cr.ui.ListItem|ListItem",
     "cr.ui.ListSelectionModel|ListSelectionModel",
     "cr.ui.ListSelectionController|ListSelectionController",
+    "cr.ui.Menu|Menu",
+    "cr.ui.positionPopupAroundElement|positionPopupAroundElement",
+    "cr.ui.positionPopupAtPoint|positionPopupAtPoint",
+    "cr.ui.swallowDoubleClick|swallowDoubleClick",
+    "cr.ui.Size|Size",
     "cr.ui.StoreObserver|StoreObserver",
     "cr.ui.Tree|Tree",
     "cr.ui.TreeItem|TreeItem",
@@ -302,6 +316,8 @@
   is_polymer3 = true
   deps = [
     ":array_data_model.m",
+    ":command.m",
+    ":context_menu_handler.m",
     ":dialogs.m",
     ":drag_wrapper.m",
     ":focus_grid.m",
@@ -309,12 +325,16 @@
     ":focus_row.m",
     ":focus_row_behavior.m",
     ":focus_without_ink.m",
+    ":grid.m",
     ":keyboard_shortcut_list.m",
     ":list.m",
     ":list_item.m",
     ":list_selection_controller.m",
     ":list_selection_model.m",
     ":list_single_selection_model.m",
+    ":menu.m",
+    ":menu_button.m",
+    ":menu_item.m",
     ":position_util.m",
     ":splitter.m",
     ":store.m",
@@ -334,6 +354,36 @@
   extra_deps = [ ":modulize" ]
 }
 
+js_library("command.m") {
+  sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/command.m.js" ]
+  deps = [
+    ":keyboard_shortcut_list.m",
+    "../:ui.m",
+    "../..:assert.m",
+    "../..:cr.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
+js_library("context_menu_handler.m") {
+  sources =
+      [ "$root_gen_dir/ui/webui/resources/js/cr/ui/context_menu_handler.m.js" ]
+  deps = [
+    ":menu.m",
+    ":menu_button.m",
+    ":menu_item.m",
+    ":position_util.m",
+    "..:event_target.m",
+    "../:ui.m",
+    "../..:assert.m",
+    "../..:cr.m",
+    "../..:event_tracker.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("dialogs.m") {
   sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/dialogs.m.js" ]
   extra_deps = [ ":modulize" ]
@@ -391,6 +441,18 @@
   extra_deps = [ ":modulize" ]
 }
 
+js_library("grid.m") {
+  sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/grid.m.js" ]
+  deps = [
+    ":list.m",
+    ":list_item.m",
+    ":list_selection_controller.m",
+    ":list_selection_model.m",
+    "../:ui.m",
+  ]
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("keyboard_shortcut_list.m") {
   sources = [
     "$root_gen_dir/ui/webui/resources/js/cr/ui/keyboard_shortcut_list.m.js",
@@ -447,6 +509,43 @@
   extra_deps = [ ":modulize" ]
 }
 
+js_library("menu.m") {
+  sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/menu.m.js" ]
+  deps = [
+    ":menu_item.m",
+    "../:ui.m",
+    "../..:assert.m",
+    "../..:cr.m",
+  ]
+  extra_deps = [ ":modulize" ]
+}
+
+js_library("menu_button.m") {
+  sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/menu_button.m.js" ]
+  deps = [
+    ":menu.m",
+    ":menu_item.m",
+    ":position_util.m",
+    "../:ui.m",
+    "../..:assert.m",
+    "../..:cr.m",
+    "../..:event_tracker.m",
+  ]
+  extra_deps = [ ":modulize" ]
+}
+
+js_library("menu_item.m") {
+  sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/menu_item.m.js" ]
+  deps = [
+    ":command.m",
+    "../:ui.m",
+    "../..:assert.m",
+    "../..:cr.m",
+    "../..:load_time_data.m",
+  ]
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("position_util.m") {
   sources = [ "$root_gen_dir/ui/webui/resources/js/cr/ui/position_util.m.js" ]
   extra_deps = [ ":modulize" ]
diff --git a/ui/webui/resources/js/cr/ui/command.js b/ui/webui/resources/js/cr/ui/command.js
index 4d467c0..53257df 100644
--- a/ui/webui/resources/js/cr/ui/command.js
+++ b/ui/webui/resources/js/cr/ui/command.js
@@ -15,13 +15,21 @@
  * command if there might be other command listeners higher up in the DOM tree.
  */
 
+// clang-format off
+// #import {assert} from '../../assert.m.js';
+// #import {define as crUiDefine} from '../ui.m.js';
+// #import {KeyboardShortcutList} from './keyboard_shortcut_list.m.js';
+// #import {dispatchPropertyChange, getPropertyDescriptor, PropertyKind} from '../../cr.m.js';
+// #import {MenuItem} from './menu_item.m.js';
+// clang-format on
+
 cr.define('cr.ui', function() {
   /**
    * Creates a new command element.
    * @constructor
    * @extends {HTMLElement}
    */
-  const Command = cr.ui.define('command');
+  /* #export */ const Command = cr.ui.define('command');
 
   Command.prototype = {
     __proto__: HTMLElement.prototype,
@@ -113,23 +121,37 @@
 
   /**
    * The label of the command.
+   * @type {string}
    */
-  cr.defineProperty(Command, 'label', cr.PropertyKind.ATTR);
+  Command.prototype.label;
+  Object.defineProperty(
+      Command.prototype, 'label',
+      cr.getPropertyDescriptor('label', cr.PropertyKind.ATTR));
 
   /**
    * Whether the command is disabled or not.
+   * @type {boolean}
    */
-  cr.defineProperty(Command, 'disabled', cr.PropertyKind.BOOL_ATTR);
+  Command.prototype.disabled;
+  Object.defineProperty(
+      Command.prototype, 'disabled',
+      cr.getPropertyDescriptor('disabled', cr.PropertyKind.BOOL_ATTR));
 
   /**
    * Whether the command is hidden or not.
    */
-  cr.defineProperty(Command, 'hidden', cr.PropertyKind.BOOL_ATTR);
+  Object.defineProperty(
+      Command.prototype, 'hidden',
+      cr.getPropertyDescriptor('hidden', cr.PropertyKind.BOOL_ATTR));
 
   /**
    * Whether the command is checked or not.
+   * @type {boolean}
    */
-  cr.defineProperty(Command, 'checked', cr.PropertyKind.BOOL_ATTR);
+  Command.prototype.checked;
+  Object.defineProperty(
+      Command.prototype, 'checked',
+      cr.getPropertyDescriptor('checked', cr.PropertyKind.BOOL_ATTR));
 
   /**
    * The flag that prevents the shortcut text from being displayed on menu.
@@ -137,8 +159,12 @@
    * If false, the keyboard shortcut text (eg. "Ctrl+X" for the cut command)
    * is displayed in menu when the command is associated with a menu item.
    * Otherwise, no text is displayed.
+   * @type {boolean}
    */
-  cr.defineProperty(Command, 'hideShortcutText', cr.PropertyKind.BOOL_ATTR);
+  Command.prototype.hideShortcutText;
+  Object.defineProperty(
+      Command.prototype, 'hideShortcutText',
+      cr.getPropertyDescriptor('hideShortcutText', cr.PropertyKind.BOOL_ATTR));
 
   /**
    * Dispatches a canExecute event on the target.
@@ -240,7 +266,7 @@
    * @constructor
    * @class
    */
-  function CanExecuteEvent(command) {
+  /* #export */ function CanExecuteEvent(command) {
     const e = new Event('canExecute', {bubbles: true, cancelable: true});
     e.__proto__ = CanExecuteEvent.prototype;
     e.command = command;
@@ -274,6 +300,7 @@
   };
 
   // Export
+  // #cr_define_end
   return {
     Command: Command,
     CanExecuteEvent: CanExecuteEvent,
diff --git a/ui/webui/resources/js/cr/ui/context_menu_handler.js b/ui/webui/resources/js/cr/ui/context_menu_handler.js
index ca032d85..4a1bf52 100644
--- a/ui/webui/resources/js/cr/ui/context_menu_handler.js
+++ b/ui/webui/resources/js/cr/ui/context_menu_handler.js
@@ -4,8 +4,20 @@
 
 // require: event_target.js
 
+// clang-format off
+// #import {assertInstanceof} from '../../assert.m.js';
+// #import {NativeEventTarget as EventTarget} from '../event_target.m.js'
+// #import {EventTracker} from '../../event_tracker.m.js'
+// #import {isWindows, isLinux, isMac, isLacros, dispatchPropertyChange} from '../../cr.m.js';
+// #import {decorate} from '../ui.m.js';
+// #import {Menu} from './menu.m.js';
+// #import {MenuItem} from './menu_item.m.js';
+// #import {HideType} from './menu_button.m.js';
+// #import {positionPopupAtPoint} from './position_util.m.js';
+// clang-format on
+
 cr.define('cr.ui', function() {
-  /** @const */ const Menu = cr.ui.Menu;
+  /* #ignore */ /** @const */ const Menu = cr.ui.Menu;
 
   /**
    * Handles context menus.
@@ -304,9 +316,10 @@
    * The singleton context menu handler.
    * @type {!ContextMenuHandler}
    */
-  const contextMenuHandler = new ContextMenuHandler;
+  /* #export */ const contextMenuHandler = new ContextMenuHandler;
 
   // Export
+  // #cr_define_end
   return {
     contextMenuHandler: contextMenuHandler,
   };
diff --git a/ui/webui/resources/js/cr/ui/grid.js b/ui/webui/resources/js/cr/ui/grid.js
index 69fdb08..7c37234 100644
--- a/ui/webui/resources/js/cr/ui/grid.js
+++ b/ui/webui/resources/js/cr/ui/grid.js
@@ -2,9 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// require: list_selection_model.js
-// require: list_selection_controller.js
-// require: list.js
+// clang-format off
+// #import {define as crUiDefine} from '../ui.m.js';
+// #import {ListSelectionModel} from './list_selection_model.m.js';
+// #import {ListSelectionController} from './list_selection_controller.m.js';
+// #import {List} from './list.m.js';
+// #import {ListItem} from './list_item.m.js';
+// clang-format on
 
 /**
  * @fileoverview This implements a grid control. Grid contains a bunch of
@@ -13,9 +17,10 @@
  */
 
 cr.define('cr.ui', function() {
-  /** @const */ const ListSelectionController = cr.ui.ListSelectionController;
-  /** @const */ const List = cr.ui.List;
-  /** @const */ const ListItem = cr.ui.ListItem;
+  /* #ignore */ /** @const */ const ListSelectionController =
+      /* #ignore */ cr.ui.ListSelectionController;
+  /* #ignore */ /** @const */ const List = cr.ui.List;
+  /* #ignore */ /** @const */ const ListItem = cr.ui.ListItem;
 
   /**
    * Creates a new grid item element.
@@ -23,7 +28,7 @@
    * @constructor
    * @extends {cr.ui.ListItem}
    */
-  function GridItem(dataItem) {
+  /* #export */ function GridItem(dataItem) {
     const el = document.createElement('li');
     el.dataItem = dataItem;
     el.__proto__ = GridItem.prototype;
@@ -48,7 +53,7 @@
    * @constructor
    * @extends {cr.ui.List}
    */
-  const Grid = cr.ui.define('grid');
+  /* #export */ const Grid = cr.ui.define('grid');
 
   Grid.prototype = {
     __proto__: List.prototype,
@@ -357,7 +362,7 @@
    * @constructor
    * @extends {cr.ui.ListSelectionController}
    */
-  function GridSelectionController(selectionModel, grid) {
+  /* #export */ function GridSelectionController(selectionModel, grid) {
     this.selectionModel_ = selectionModel;
     this.grid_ = grid;
   }
@@ -438,6 +443,7 @@
     }
   };
 
+  // #cr_define_end
   return {
     Grid: Grid,
     GridItem: GridItem,
diff --git a/ui/webui/resources/js/cr/ui/menu.js b/ui/webui/resources/js/cr/ui/menu.js
index 5fb640f..1f4fe26 100644
--- a/ui/webui/resources/js/cr/ui/menu.js
+++ b/ui/webui/resources/js/cr/ui/menu.js
@@ -2,8 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// #import {assert, assertInstanceof} from '../../assert.m.js';
+// #import {define as crUiDefine, decorate} from '../ui.m.js';
+// #import {getPropertyDescriptor, PropertyKind} from '../../cr.m.js';
+// #import {MenuItem} from './menu_item.m.js';
+
 cr.define('cr.ui', function() {
-  /** @const */ const MenuItem = cr.ui.MenuItem;
+  /* #ignore */ /** @const */ const MenuItem = cr.ui.MenuItem;
 
   /**
    * Creates a new menu element. Menu dispatches all commands on the element it
@@ -13,7 +18,7 @@
    * @constructor
    * @extends {HTMLElement}
    */
-  const Menu = cr.ui.define('cr-menu');
+  /* #export */ const Menu = cr.ui.define('cr-menu');
 
   Menu.prototype = {
     __proto__: HTMLElement.prototype,
@@ -361,6 +366,7 @@
     }
   };
 
+  /** @suppress {globalThis} This standalone function is used like method. */
   function selectedIndexChanged(selectedIndex, oldSelectedIndex) {
     const oldSelectedItem = this.menuItems[oldSelectedIndex];
     if (oldSelectedItem) {
@@ -375,16 +381,24 @@
 
   /**
    * The selected menu item.
-   * type {number}
+   * @type {number}
    */
-  cr.defineProperty(
-      Menu, 'selectedIndex', cr.PropertyKind.JS, selectedIndexChanged);
+  Menu.prototype.selectedIndex;
+  Object.defineProperty(
+      Menu.prototype, 'selectedIndex',
+      cr.getPropertyDescriptor(
+          'selectedIndex', cr.PropertyKind.JS, selectedIndexChanged));
 
   /**
    * Selector for children which are menu items.
+   * @type {string}
    */
-  cr.defineProperty(Menu, 'menuItemSelector', cr.PropertyKind.ATTR);
+  Menu.prototype.menuItemSelector;
+  Object.defineProperty(
+      Menu.prototype, 'menuItemSelector',
+      cr.getPropertyDescriptor('menuItemSelector', cr.PropertyKind.ATTR));
 
   // Export
+  // #cr_define_end
   return {Menu: Menu};
 });
diff --git a/ui/webui/resources/js/cr/ui/menu_button.js b/ui/webui/resources/js/cr/ui/menu_button.js
index 0f95dc5..84c8956 100644
--- a/ui/webui/resources/js/cr/ui/menu_button.js
+++ b/ui/webui/resources/js/cr/ui/menu_button.js
@@ -4,23 +4,30 @@
 
 // <include src="../../assert.js">
 
+// #import {assert} from '../../assert.m.js';
+// #import {isWindows} from '../../cr.m.js';
+// #import {EventTracker} from '../../event_tracker.m.js'
+// #import {define as crUiDefine, decorate} from '../ui.m.js';
+// #import {positionPopupAroundElement, AnchorType} from './position_util.m.js';
+// #import {Menu} from './menu.m.js';
+// #import {MenuItem} from './menu_item.m.js';
 
 cr.define('cr.ui', function() {
-  /** @const */
-  const Menu = cr.ui.Menu;
+  /* #ignore */ const Menu = cr.ui.Menu;
 
   /**
    * Enum for type of hide. Delayed is used when called by clicking on a
    * checkable menu item.
    * @enum {number}
    */
-  const HideType = {
+  /* #export */ const HideType = {
     INSTANT: 0,
     DELAYED: 1,
   };
 
   /** @const */
-  const positionPopupAroundElement = cr.ui.positionPopupAroundElement;
+  /* #ignore */ const positionPopupAroundElement =
+      /* #ignore */ cr.ui.positionPopupAroundElement;
 
   /**
    * Creates a new menu button element.
@@ -29,7 +36,7 @@
    * @extends {HTMLButtonElement}
    * @implements {EventListener}
    */
-  const MenuButton = cr.ui.define('button');
+  /* #export */ const MenuButton = cr.ui.define('button');
 
   MenuButton.prototype = {
     __proto__: HTMLButtonElement.prototype,
@@ -345,6 +352,7 @@
   };
 
   // Export
+  // #cr_define_end
   return {
     HideType: HideType,
     MenuButton: MenuButton,
diff --git a/ui/webui/resources/js/cr/ui/menu_item.js b/ui/webui/resources/js/cr/ui/menu_item.js
index dc81baed..2dba74b 100644
--- a/ui/webui/resources/js/cr/ui/menu_item.js
+++ b/ui/webui/resources/js/cr/ui/menu_item.js
@@ -2,8 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {loadTimeData} from '../../load_time_data.m.js';
+// #import {assert} from '../../assert.m.js';
+// #import {Command} from './command.m.js';
+// #import {define as crUiDefine, decorate, swallowDoubleClick} from '../ui.m.js';
+// #import {getPropertyDescriptor, PropertyKind} from '../../cr.m.js';
+// clang-format on
+
 cr.define('cr.ui', function() {
-  /** @const */ const Command = cr.ui.Command;
+  /* #ignore */ const Command = cr.ui.Command;
 
   /**
    * Creates a new menu item element.
@@ -12,7 +20,7 @@
    * @extends {HTMLElement}
    * @implements {EventListener}
    */
-  const MenuItem = cr.ui.define('cr-menu-item');
+  /* #export */ const MenuItem = cr.ui.define('cr-menu-item');
 
   /**
    * Creates a new menu separator element.
@@ -20,7 +28,9 @@
    */
   MenuItem.createSeparator = function() {
     const el = /** @type {!cr.ui.MenuItem} */ (document.createElement('hr'));
-    MenuItem.decorate(el);
+    if (MenuItem.decorate) {
+      MenuItem.decorate(el);
+    }
     return el;
   };
 
@@ -251,32 +261,50 @@
       }
     }
   };
-
   /**
    * Whether the menu item is disabled or not.
+   * @type {boolean}
    */
-  cr.defineProperty(MenuItem, 'disabled', cr.PropertyKind.BOOL_ATTR);
+  MenuItem.prototype.disabled;
+  Object.defineProperty(
+      MenuItem.prototype, 'disabled',
+      cr.getPropertyDescriptor('disabled', cr.PropertyKind.BOOL_ATTR));
 
   /**
    * Whether the menu item is hidden or not.
    */
-  cr.defineProperty(MenuItem, 'hidden', cr.PropertyKind.BOOL_ATTR);
+  Object.defineProperty(
+      MenuItem.prototype, 'hidden',
+      cr.getPropertyDescriptor('hidden', cr.PropertyKind.BOOL_ATTR));
 
   /**
    * Whether the menu item is selected or not.
+   * @type {boolean}
    */
-  cr.defineProperty(MenuItem, 'selected', cr.PropertyKind.BOOL_ATTR);
+  MenuItem.prototype.selected;
+  Object.defineProperty(
+      MenuItem.prototype, 'selected',
+      cr.getPropertyDescriptor('selected', cr.PropertyKind.BOOL_ATTR));
 
   /**
    * Whether the menu item is checked or not.
+   * @type {boolean}
    */
-  cr.defineProperty(MenuItem, 'checked', cr.PropertyKind.BOOL_ATTR);
+  MenuItem.prototype.checked;
+  Object.defineProperty(
+      MenuItem.prototype, 'checked',
+      cr.getPropertyDescriptor('checked', cr.PropertyKind.BOOL_ATTR));
 
   /**
    * Whether the menu item is checkable or not.
+   * @type {boolean}
    */
-  cr.defineProperty(MenuItem, 'checkable', cr.PropertyKind.BOOL_ATTR);
+  MenuItem.prototype.checkable;
+  Object.defineProperty(
+      MenuItem.prototype, 'checkable',
+      cr.getPropertyDescriptor('checkable', cr.PropertyKind.BOOL_ATTR));
 
   // Export
+  // #cr_define_end
   return {MenuItem: MenuItem};
 });
diff --git a/ui/webui/resources/webui_resources.grd b/ui/webui/resources/webui_resources.grd
index 2bf6927..e8b36f4 100644
--- a/ui/webui/resources/webui_resources.grd
+++ b/ui/webui/resources/webui_resources.grd
@@ -89,9 +89,33 @@
         <include name="IDR_WEBUI_JS_CR_UI_ARRAY_DATA_MODEL_M_JS"
                  file="${root_gen_dir}/ui/webui/resources/js/cr/ui/array_data_model.m.js"
                  use_base_dir="false" type="BINDATA" />
+        <include name="IDR_WEBUI_JS_CR_UI_COMMAND_M_JS"
+                 file="${root_gen_dir}/ui/webui/resources/js/cr/ui/command.m.js"
+                 use_base_dir="false" type="BINDATA" />
+        <include name="IDR_WEBUI_JS_CR_UI_CONTEXT_MENU_HANDLER_M_JS"
+                 file="${root_gen_dir}/ui/webui/resources/js/cr/ui/context_menu_handler.m.js"
+                 use_base_dir="false" type="BINDATA" />
         <include name="IDR_WEBUI_JS_CR_UI_DIALOGS_M_JS"
                  file="${root_gen_dir}/ui/webui/resources/js/cr/ui/dialogs.m.js"
                  use_base_dir="false" type="BINDATA" />
+        <include name="IDR_WEBUI_JS_CR_UI_GRID_M_JS"
+                 file="${root_gen_dir}/ui/webui/resources/js/cr/ui/grid.m.js"
+                 use_base_dir="false" type="BINDATA" />
+        <include name="IDR_WEBUI_JS_CR_UI_MENU_M_JS"
+                 file="${root_gen_dir}/ui/webui/resources/js/cr/ui/menu.m.js"
+                 use_base_dir="false" type="BINDATA" />
+        <include name="IDR_WEBUI_JS_CR_UI_MENU_BUTTON_M_JS"
+                 file="${root_gen_dir}/ui/webui/resources/js/cr/ui/menu_button.m.js"
+                 use_base_dir="false" type="BINDATA" />
+        <include name="IDR_WEBUI_JS_CR_UI_MENU_ITEM_M_JS"
+                 file="${root_gen_dir}/ui/webui/resources/js/cr/ui/menu_item.m.js"
+                 use_base_dir="false" type="BINDATA" />
+        <include name="IDR_WEBUI_JS_CR_UI_LIST_M_JS"
+                 file="${root_gen_dir}/ui/webui/resources/js/cr/ui/list.m.js"
+                 use_base_dir="false" type="BINDATA" />
+        <include name="IDR_WEBUI_JS_CR_UI_LIST_ITEM_M_JS"
+                 file="${root_gen_dir}/ui/webui/resources/js/cr/ui/list_item.m.js"
+                 use_base_dir="false" type="BINDATA" />
         <include name="IDR_WEBUI_JS_CR_UI_LIST_SELECTION_CONTROLLER_M_JS"
                  file="${root_gen_dir}/ui/webui/resources/js/cr/ui/list_selection_controller.m.js"
                  use_base_dir="false" type="BINDATA" />
@@ -107,6 +131,9 @@
         <include name="IDR_WEBUI_JS_CR_UI_SPLITTER_M_JS"
                  file="${root_gen_dir}/ui/webui/resources/js/cr/ui/splitter.m.js"
                  use_base_dir="false" type="BINDATA" />
+        <include name="IDR_WEBUI_JS_CR_UI_TREE_M_JS"
+                 file="${root_gen_dir}/ui/webui/resources/js/cr/ui/tree.m.js"
+                 use_base_dir="false" type="BINDATA" />
       </if>
       <!-- Web UI shared JS module resources. -->
       <include name="IDR_WEBUI_JS_ASSERT_M_JS"
diff --git a/weblayer/app/content_main_delegate_impl.cc b/weblayer/app/content_main_delegate_impl.cc
index f05bcb0a..4368897 100644
--- a/weblayer/app/content_main_delegate_impl.cc
+++ b/weblayer/app/content_main_delegate_impl.cc
@@ -177,8 +177,8 @@
     ::features::kDisableDeJelly,
     ::features::kDynamicColorGamut,
 #else
-    // TODO(crbug.com/1131021): Support SMS Receiver on WebLayer.
-    ::features::kSmsReceiver,
+    // TODO(crbug.com/1131021): Support WebOTP Service on WebLayer.
+    ::features::kWebOTP,
 #endif
   };