[M139] [ProfilePicker] Fix double clicking issues on main page

Original change's description:
> [ProfilePicker] Fix double clicking issues on main page
> 
> The profile picker main page usually redirect to a secondary
> `ProfileTypeChoice` screen where the buttons are controlled upon click
> to avoid double clicking.
> However in the context of Force Signin, even the main page can start
> actions, such as Signin and Reauth, which should not be triggered
> twice in a row (possible with double clicking) and creating unexpected
> behaviors.
> 
> The fix here is to disable all buttons upon first click, and only
> re-enable them when navigating back to the page or if the action
> is completed (information sent back from the browser).
> 
> Fixed: 431992755
> Change-Id: Id700432391a765bf08db230f6d0a718e38e78d90
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6763618
> Commit-Queue: Ryan Sultanem <rsult@google.com>
> Reviewed-by: Nicolas Dossou-Gbété <dgn@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1492175}

Bug: 437911835,431992755
Change-Id: Id700432391a765bf08db230f6d0a718e38e78d90
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6837357
Commit-Queue: Krishna Govind <govind@chromium.org>
Reviewed-by: Krishna Govind <govind@chromium.org>
Auto-Submit: Chrome Cherry Picker <chrome-cherry-picker@chops-service-accounts.iam.gserviceaccount.com>
Reviewed-by: Srinivas Sista <srinivassista@chromium.org>
Cr-Commit-Position: refs/branch-heads/7258@{#2580}
Cr-Branched-From: f600d0656fd5b5fe4a82981f533d31ed6939e2e4-refs/heads/main@{#1477651}
diff --git a/chrome/browser/resources/signin/profile_picker/profile_card.html.ts b/chrome/browser/resources/signin/profile_picker/profile_card.html.ts
index 1aaf591..194f419a 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_card.html.ts
+++ b/chrome/browser/resources/signin/profile_picker/profile_card.html.ts
@@ -11,6 +11,7 @@
   return html`<!--_html_template_start_-->
 <div id="profileCardContainer">
   <cr-button id="profileCardButton" @click="${this.onProfileClick_}"
+      ?disabled="${this.disabled}"
       aria-label="${this.profileState.profileCardButtonLabel}">
     <div id="avatarContainer">
       <img class="profile-avatar" alt="" .src="${this.profileState.avatarIcon}">
diff --git a/chrome/browser/resources/signin/profile_picker/profile_card.ts b/chrome/browser/resources/signin/profile_picker/profile_card.ts
index 59d59adc..6a0a03c 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_card.ts
+++ b/chrome/browser/resources/signin/profile_picker/profile_card.ts
@@ -52,10 +52,12 @@
     return {
       profileState: {type: Object},
       pattern_: {type: String},
+      disabled: {type: Boolean},
     };
   }
 
   accessor profileState: ProfileState = createDummyProfileState();
+  accessor disabled: boolean = false;
   protected accessor pattern_: string = '.*\\S.*';
   private manageProfilesBrowserProxy_: ManageProfilesBrowserProxy =
       ManageProfilesBrowserProxyImpl.getInstance();
@@ -124,18 +126,18 @@
   }
 
   protected onProfileClick_() {
+    this.fire('disable-all-picker-buttons');
+
     this.manageProfilesBrowserProxy_.launchSelectedProfile(
         this.profileState.profilePath);
   }
 
   protected onNameInputPointerEnter_() {
-    this.dispatchEvent(new CustomEvent(
-        'toggle-drag', {composed: true, detail: {toggle: false}}));
+    this.fire('toggle-drag', {toggle: false});
   }
 
   protected onNameInputPointerLeave_() {
-    this.dispatchEvent(new CustomEvent(
-        'toggle-drag', {composed: true, detail: {toggle: true}}));
+    this.fire('toggle-drag', {toggle: true});
   }
 
   /**
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html.ts b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html.ts
index e3b5ead..31b6cc3 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html.ts
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.html.ts
@@ -27,13 +27,16 @@
   <div id="profilesWrapper" ?hidden="${(this.shouldHideProfilesWrapper_())}">
     <div id="profilesContainer" class="custom-scrollbar">
       ${this.profilesList_.map((item, index) => html`
-        <profile-card class="profile-item" .profileState="${item}"
-            data-index="${index}">
+        <profile-card class="profile-item" data-index="${index}"
+            .profileState="${item}" .disabled="${this.pickerButtonsDisabled_}"
+            @toggle-drag="${this.toggleDrag_}"
+            @disable-all-picker-buttons="${this.disableAllPickerButtons_}">
         </profile-card>
       `)}
       <cr-button id="addProfile" class="profile-item"
           @click="${this.onAddProfileClick_}"
           ?hidden="${!this.profileCreationAllowed_}"
+          ?disabled="${this.pickerButtonsDisabled_}"
           aria-labelledby="addProfileButtonLabel">
         <div id="addProfileButtonLabel"
             class="profile-card-info prominent-text">
@@ -53,7 +56,8 @@
 <div class="footer">
   <cr-button id="browseAsGuestButton"
       @click="${this.onLaunchGuestProfileClick_}"
-      ?hidden="${!this.guestModeEnabled_}">
+      ?hidden="${!this.guestModeEnabled_}"
+      ?disabled="${this.pickerButtonsDisabled_}">
     <cr-icon icon="profiles:account-circle" slot="prefix-icon"></cr-icon>
     $i18n{browseAsGuestButton}
   </cr-button>
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.ts b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.ts
index 508f608..a27a013 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.ts
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_main_view.ts
@@ -11,6 +11,7 @@
 import '/strings.m.js';
 
 import {HelpBubbleMixinLit} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin_lit.js';
+import type {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
 import type {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 import type {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
 import {I18nMixinLit} from 'chrome://resources/cr_elements/i18n_mixin_lit.js';
@@ -32,7 +33,7 @@
 
 export interface ProfilePickerMainViewElement {
   $: {
-    addProfile: HTMLElement,
+    addProfile: CrButtonElement,
     askOnStartup: CrCheckboxElement,
     'picker-logo': HTMLElement,
     browseAsGuestButton: HTMLElement,
@@ -70,6 +71,7 @@
       askOnStartup_: {type: Boolean},
       guestModeEnabled_: {type: Boolean},
       profileCreationAllowed_: {type: Boolean},
+      pickerButtonsDisabled_: {type: Boolean},
       forceSigninErrorDialogTitle_: {type: String},
       forceSigninErrorDialogBody_: {type: String},
       forceSigninErrorProfilePath_: {type: String},
@@ -100,6 +102,8 @@
   private dragDelegate_: DragDropReorderTileListDelegate|null = null;
   private dragDuration_: number = 300;
 
+  protected accessor pickerButtonsDisabled_: boolean = false;
+
   // TODO(crbug.com/40280498): Move the dialog into it's own element with the
   // below members. This dialog state should be independent of the Profile
   // Picker itself.
@@ -112,8 +116,6 @@
 
   override firstUpdated() {
     this.addEventListener('view-enter-finish', this.onViewEnterFinish_);
-
-    this.addEventListener('toggle-drag', this.toggleDrag_);
   }
 
   override connectedCallback() {
@@ -127,6 +129,9 @@
         'display-force-signin-error-dialog',
         (title: string, body: string, profilePath: string) =>
             this.showForceSigninErrorDialog(title, body, profilePath));
+    this.addWebUiListener('reset-picker-buttons', () => {
+      this.enableAllPickerButtons_();
+    });
     if (!this.isGlic_) {
       this.addWebUiListener(
           'guest-mode-availability-updated',
@@ -170,6 +175,9 @@
 
   override onRouteChange(route: Routes) {
     if (route === Routes.MAIN) {
+      // Every time we go back to the main route, we re-enable all the profile
+      // card buttons.
+      this.enableAllPickerButtons_();
       return;
     }
     this.previousRoute_ = route;
@@ -255,6 +263,8 @@
     if (!isProfileCreationAllowed()) {
       return;
     }
+
+    this.disableAllPickerButtons_();
     chrome.metricsPrivate.recordUserAction('ProfilePicker_AddClicked');
     navigateTo(Routes.NEW_PROFILE);
   }
@@ -291,7 +301,7 @@
     return !isAskOnStartupAllowed() || this.profilesList_.length < 2;
   }
 
-  private toggleDrag_(e: Event) {
+  protected toggleDrag_(e: Event) {
     if (!this.dragDelegate_) {
       return;
     }
@@ -300,6 +310,20 @@
     this.dragDelegate_.toggleDrag(customEvent.detail.toggle);
   }
 
+  protected disableAllPickerButtons_() {
+    this.pickerButtonsDisabled_ = true;
+    if (this.dragDelegate_) {
+      this.dragDelegate_.toggleDrag(false);
+    }
+  }
+
+  private enableAllPickerButtons_() {
+    this.pickerButtonsDisabled_ = false;
+    if (this.dragDelegate_) {
+      this.dragDelegate_.toggleDrag(true);
+    }
+  }
+
   // Redirects the call to the handler, to create/use a browser to show the
   // Help page.
   private onLearnMoreClicked_(): void {
diff --git a/chrome/browser/ui/profiles/profile_picker.h b/chrome/browser/ui/profiles/profile_picker.h
index 8b88087..2e7b963 100644
--- a/chrome/browser/ui/profiles/profile_picker.h
+++ b/chrome/browser/ui/profiles/profile_picker.h
@@ -231,8 +231,12 @@
   // the `profile` will be opened. On unsuccessful reauth, the user will be
   // redirected to the profile picker main page, with a popup error dialog
   // displayed through `on_error_callback`.
+  // `switch_finished_callback` will be called once the step was switched (or
+  // failed to switch to), the bool parameter indicating the success of the
+  // switch.
   static void SwitchToReauth(
       Profile* profile,
+      base::OnceCallback<void(bool)> switch_finished_callback,
       base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback);
 #endif
 
@@ -252,8 +256,13 @@
   };
 
   // Picks the profile with `profile_path`.
-  static void PickProfile(const base::FilePath& profile_path,
-                          ProfilePickingArgs args);
+  // `pick_profile_complete_callback` will be called when a browser is opened
+  // with the profile associated with `profile_path`, the boolean parameter
+  // returning whether a browser was successfully opened or not.
+  static void PickProfile(
+      const base::FilePath& profile_path,
+      ProfilePickingArgs args,
+      base::OnceCallback<void(bool)> pick_profile_complete_callback);
 
   // Cancel the signed-in flow and returns back to the main picker screen (if
   // the original EntryPoint was to open the picker). Must only be called from
diff --git a/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.cc b/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.cc
index a3120fe..6c63b30 100644
--- a/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.cc
+++ b/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.cc
@@ -541,7 +541,8 @@
 
 void FirstRunFlowControllerDice::PickProfile(
     const base::FilePath& profile_path,
-    ProfilePicker::ProfilePickingArgs args) {
+    ProfilePicker::ProfilePickingArgs args,
+    base::OnceCallback<void(bool)> pick_profile_complete_callback) {
   NOTREACHED() << "FRE is not expected to handle this flow";
 }
 
diff --git a/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.h b/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.h
index 8458a1a..c0a44ad 100644
--- a/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.h
+++ b/chrome/browser/ui/views/profiles/first_run_flow_controller_dice.h
@@ -42,8 +42,10 @@
   // ProfileManagementFlowControllerImpl:
   void Init() override;
   void CancelPostSignInFlow() override;
-  void PickProfile(const base::FilePath& profile_path,
-                   ProfilePicker::ProfilePickingArgs args) override;
+  void PickProfile(
+      const base::FilePath& profile_path,
+      ProfilePicker::ProfilePickingArgs args,
+      base::OnceCallback<void(bool)> pick_profile_complete_callback) override;
 
  protected:
   // ProfileManagementFlowControllerImpl
diff --git a/chrome/browser/ui/views/profiles/profile_management_flow_controller.h b/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
index a7ae4be..f6e665f 100644
--- a/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_management_flow_controller.h
@@ -101,8 +101,11 @@
   virtual void CancelPostSignInFlow() = 0;
 
   // Picks the profile with `profile_path`.
-  virtual void PickProfile(const base::FilePath& profile_path,
-                           ProfilePicker::ProfilePickingArgs args) = 0;
+  // `pick_profile_complete_callback` will be called on profile load.
+  virtual void PickProfile(
+      const base::FilePath& profile_path,
+      ProfilePicker::ProfilePickingArgs args,
+      base::OnceCallback<void(bool)> pick_profile_complete_callback) = 0;
 
   // Clears the current state and reset it to the initial state that shows the
   // main screen. When calling this function the state should not be the
diff --git a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
index d50cc5c..e88a9754 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.cc
@@ -518,6 +518,7 @@
 
 void ProfilePickerFlowController::SwitchToReauth(
     Profile* profile,
+    StepSwitchFinishedCallback switch_finished_callback,
     base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback) {
   DCHECK_EQ(Step::kProfilePicker, current_step());
 
@@ -541,7 +542,7 @@
                          std::move(on_error_callback))));
 
   SwitchToStep(
-      Step::kReauth, true, StepSwitchFinishedCallback(),
+      Step::kReauth, true, std::move(switch_finished_callback),
       /*pop_step_callback=*/CreateSwitchToStepPopCallback(current_step()));
 }
 
@@ -672,7 +673,8 @@
 
 void ProfilePickerFlowController::PickProfile(
     const base::FilePath& profile_path,
-    ProfilePicker::ProfilePickingArgs args) {
+    ProfilePicker::ProfilePickingArgs args,
+    base::OnceCallback<void(bool)> pick_profile_complete_callback) {
   if (args.should_record_startup_metrics &&
       // Avoid overriding the picked time if already recorded. This can happen
       // for example if multiple profiles are picked: https://crbug.com/1277466.
@@ -683,17 +685,26 @@
   profiles::SwitchToProfile(
       profile_path, /*always_create=*/false,
       base::BindOnce(&ProfilePickerFlowController::OnSwitchToProfileComplete,
-                     weak_ptr_factory_.GetWeakPtr(), args.open_settings));
+                     weak_ptr_factory_.GetWeakPtr(), args.open_settings,
+                     std::move(pick_profile_complete_callback)));
 }
 
-void ProfilePickerFlowController::OnSwitchToProfileComplete(bool open_settings,
-                                                            Browser* browser) {
+void ProfilePickerFlowController::OnSwitchToProfileComplete(
+    bool open_settings,
+    base::OnceCallback<void(bool)> pick_profile_complete_callback,
+    Browser* browser) {
   if (!browser || browser->is_delete_scheduled()) {
     // The browser is destroyed or about to be destroyed.
+    if (pick_profile_complete_callback) {
+      std::move(pick_profile_complete_callback).Run(false);
+    }
     return;
   }
 
   DCHECK(browser->window());
+  if (pick_profile_complete_callback) {
+    std::move(pick_profile_complete_callback).Run(true);
+  }
   Profile* profile = browser->profile();
   TRACE_EVENT1("browser",
                "ProfilePickerFlowController::OnSwitchToProfileComplete",
diff --git a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h
index 0572aa6..967c5d6 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_flow_controller.h
@@ -35,6 +35,7 @@
 
   void SwitchToReauth(
       Profile* profile,
+      StepSwitchFinishedCallback switch_finished_callback,
       base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback);
 #endif
 
@@ -49,8 +50,10 @@
   void SwitchToSignedOutPostIdentityFlow(Profile* profile);
 
   // ProfileManagementFlowControllerImpl:
-  void PickProfile(const base::FilePath& profile_path,
-                   ProfilePicker::ProfilePickingArgs args) override;
+  void PickProfile(
+      const base::FilePath& profile_path,
+      ProfilePicker::ProfilePickingArgs args,
+      base::OnceCallback<void(bool)> pick_profile_complete_callback) override;
 
  protected:
   // ProfileManagementFlowControllerImpl
@@ -78,7 +81,10 @@
       std::unique_ptr<content::WebContents> contents) override;
 
   // Callback after loading a profile and opening a browser.
-  void OnSwitchToProfileComplete(bool open_settings, Browser* browser);
+  void OnSwitchToProfileComplete(
+      bool open_settings,
+      base::OnceCallback<void(bool)> pick_profile_complete_callback,
+      Browser* browser);
 
   const ProfilePicker::EntryPoint entry_point_;
   const GURL selected_profile_target_url_;
diff --git a/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller.cc b/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller.cc
index b615ea5..2a79b811 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/views/profiles/profile_management_step_controller.h"
+#include "chrome/browser/ui/views/profiles/profile_management_types.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 
@@ -52,19 +53,30 @@
 
 void ProfilePickerGlicFlowController::PickProfile(
     const base::FilePath& profile_path,
-    ProfilePicker::ProfilePickingArgs args) {
+    ProfilePicker::ProfilePickingArgs args,
+    base::OnceCallback<void(bool)> pick_profile_complete_callback) {
   g_browser_process->profile_manager()->LoadProfileByPath(
       profile_path, /*incognito=*/false,
       base::BindOnce(&ProfilePickerGlicFlowController::OnPickedProfileLoaded,
-                     base::Unretained(this)));
+                     base::Unretained(this),
+                     std::move(pick_profile_complete_callback)));
 }
 
-void ProfilePickerGlicFlowController::OnPickedProfileLoaded(Profile* profile) {
+void ProfilePickerGlicFlowController::OnPickedProfileLoaded(
+    base::OnceCallback<void(bool)> pick_profile_complete_callback,
+    Profile* profile) {
   if (!profile) {
+    if (pick_profile_complete_callback) {
+      std::move(pick_profile_complete_callback).Run(false);
+    }
     Clear();
     return;
   }
 
+  if (pick_profile_complete_callback) {
+    std::move(pick_profile_complete_callback).Run(true);
+  }
+
   loaded_profile_ = profile;
 
   signin::IdentityManager* identity_manager =
diff --git a/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller.h b/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller.h
index 93d8b4e..1753ff1 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller.h
@@ -37,8 +37,10 @@
   // Loads the profile with `profile_path` asynchronously then attempts to run
   // the `picked_profile_callback_` callback with the loaded profile.
   // `args` are actually not used in this implementation.
-  void PickProfile(const base::FilePath& profile_path,
-                   ProfilePicker::ProfilePickingArgs args) override;
+  void PickProfile(
+      const base::FilePath& profile_path,
+      ProfilePicker::ProfilePickingArgs args,
+      base::OnceCallback<void(bool)> pick_profile_complete_callback) override;
 
  private:
   // ProfileManagementFlowController:
@@ -49,7 +51,9 @@
 
   // Callback after loading the picked profile. Prepares `loaded_profile_` and
   // attempts to exit the flow with the loaded profile.
-  void OnPickedProfileLoaded(Profile* profile);
+  void OnPickedProfileLoaded(
+      base::OnceCallback<void(bool)> pick_profile_complete_callback,
+      Profile* profile);
 
   // Returns `loaded_profile_` through `picked_profile_callback_` while ensuring
   // that the refresh tokens are loaded.
diff --git a/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller_browsertest.cc
index 91d52ad0..4323d25c7 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_glic_flow_controller_browsertest.cc
@@ -81,7 +81,10 @@
   ProfilePickerGlicFlowController controller(
       host(), ClearHostClosure(clear_host_callback.Get()),
       picked_profile_callback.Get());
-  controller.PickProfile(new_profile_path, ProfilePicker::ProfilePickingArgs());
+  base::MockCallback<base::OnceCallback<void(bool)>> mock_callback;
+  EXPECT_CALL(mock_callback, Run(true));
+  controller.PickProfile(new_profile_path, ProfilePicker::ProfilePickingArgs(),
+                         mock_callback.Get());
 
   Profile* loaded_profile = profile_waiter.WaitForProfileAdded();
   signin::WaitForRefreshTokensLoaded(
@@ -108,8 +111,11 @@
   ProfilePickerGlicFlowController controller(
       host(), ClearHostClosure(clear_host_callback.Get()),
       picked_profile_callback.Get());
+  base::MockCallback<base::OnceCallback<void(bool)>> mock_callback;
+  EXPECT_CALL(mock_callback, Run(true));
   controller.PickProfile(browser()->profile()->GetPath(),
-                         ProfilePicker::ProfilePickingArgs());
+                         ProfilePicker::ProfilePickingArgs(),
+                         mock_callback.Get());
 }
 
 IN_PROC_BROWSER_TEST_F(ProfilePickerGlicFlowControllerBrowserTest,
@@ -129,8 +135,11 @@
   // as it was not created yet.
   base::FilePath non_profile_file_path =
       g_browser_process->profile_manager()->GenerateNextProfileDirectoryPath();
+  base::MockCallback<base::OnceCallback<void(bool)>> mock_callback;
+  EXPECT_CALL(mock_callback, Run(false));
   controller.PickProfile(non_profile_file_path,
-                         ProfilePicker::ProfilePickingArgs());
+                         ProfilePicker::ProfilePickingArgs(),
+                         mock_callback.Get());
 }
 
 IN_PROC_BROWSER_TEST_F(ProfilePickerGlicFlowControllerBrowserTest,
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index d82ec02..d88d90f 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -200,17 +200,21 @@
     base::OnceCallback<void(bool)> switch_finished_callback) {
   if (g_profile_picker_view) {
     g_profile_picker_view->SwitchToDiceSignIn(
-        std::move(profile_info), std::move(switch_finished_callback));
+        std::move(profile_info),
+        StepSwitchFinishedCallback(std::move(switch_finished_callback)));
   }
 }
 
 // static
 void ProfilePicker::SwitchToReauth(
     Profile* profile,
+    base::OnceCallback<void(bool)> switch_finished_callback,
     base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback) {
   if (g_profile_picker_view) {
-    g_profile_picker_view->SwitchToReauth(profile,
-                                          std::move(on_error_callback));
+    g_profile_picker_view->SwitchToReauth(
+        profile,
+        StepSwitchFinishedCallback(std::move(switch_finished_callback)),
+        std::move(on_error_callback));
   }
 }
 #endif
@@ -224,10 +228,13 @@
 }
 
 // static
-void ProfilePicker::PickProfile(const base::FilePath& profile_path,
-                                ProfilePickingArgs args) {
+void ProfilePicker::PickProfile(
+    const base::FilePath& profile_path,
+    ProfilePickingArgs args,
+    base::OnceCallback<void(bool)> pick_profile_complete_callback) {
   if (g_profile_picker_view) {
-    g_profile_picker_view->flow_controller_->PickProfile(profile_path, args);
+    g_profile_picker_view->flow_controller_->PickProfile(
+        profile_path, args, std::move(pick_profile_complete_callback));
   }
 }
 
@@ -711,17 +718,18 @@
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 void ProfilePickerView::SwitchToDiceSignIn(
     ProfilePicker::ProfileInfo profile_info,
-    base::OnceCallback<void(bool)> switch_finished_callback) {
+    StepSwitchFinishedCallback switch_finished_callback) {
   GetProfilePickerFlowController()->SwitchToDiceSignIn(
-      std::move(profile_info),
-      StepSwitchFinishedCallback(std::move(switch_finished_callback)));
+      std::move(profile_info), std::move(switch_finished_callback));
 }
 
 void ProfilePickerView::SwitchToReauth(
     Profile* profile,
+    StepSwitchFinishedCallback switch_finished_callback,
     base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback) {
   GetProfilePickerFlowController()->SwitchToReauth(
-      profile, std::move(on_error_callback));
+      profile, std::move(switch_finished_callback),
+      std::move(on_error_callback));
 }
 #endif
 
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.h b/chrome/browser/ui/views/profiles/profile_picker_view.h
index b11e3fa..fb24a5c6 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.h
@@ -15,6 +15,7 @@
 #include "build/buildflag.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/profiles/profile_picker.h"
+#include "chrome/browser/ui/views/profiles/profile_management_types.h"
 #include "chrome/browser/ui/views/profiles/profile_picker_web_contents_host.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/user_education/common/feature_promo/feature_promo_controller.h"
@@ -193,9 +194,8 @@
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   // Switches the layout to the sign-in screen (and creates a new profile or
   // load an existing one based on the `profile_info` content).
-  void SwitchToDiceSignIn(
-      ProfilePicker::ProfileInfo profile_info,
-      base::OnceCallback<void(bool)> switch_finished_callback);
+  void SwitchToDiceSignIn(ProfilePicker::ProfileInfo profile_info,
+                          StepSwitchFinishedCallback switch_finished_callback);
 
   // Switches the profile picker layout to display the reauth page to the main
   // account of the given `profile` if needed. On success the `profile` is
@@ -204,6 +204,7 @@
   // `on_error_callback`.
   void SwitchToReauth(
       Profile* profile,
+      StepSwitchFinishedCallback switch_finished_callback,
       base::OnceCallback<void(const ForceSigninUIError&)> on_error_callback);
 #endif
 
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_test_utils.cc b/chrome/browser/ui/views/profiles/profile_picker_view_test_utils.cc
index 4228bcf..b62666e 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_test_utils.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_test_utils.cc
@@ -85,8 +85,10 @@
 
   void CancelPostSignInFlow() override { NOTREACHED(); }
 
-  void PickProfile(const base::FilePath& profile_path,
-                   ProfilePicker::ProfilePickingArgs args) override {
+  void PickProfile(
+      const base::FilePath& profile_path,
+      ProfilePicker::ProfilePickingArgs args,
+      base::OnceCallback<void(bool)> pick_profile_complete_callback) override {
     NOTREACHED();
   }
 
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.cc b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
index c743fb8..d4e672c 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.cc
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.cc
@@ -43,6 +43,7 @@
 #include "chrome/browser/ui/profiles/profile_colors_util.h"
 #include "chrome/browser/ui/profiles/profile_picker.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/profiles/profile_management_types.h"
 #include "chrome/browser/ui/webui/profile_helper.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
@@ -377,7 +378,9 @@
       *profile_path,
       ProfilePicker::ProfilePickingArgs{
           .open_settings = open_settings,
-          .should_record_startup_metrics = should_record_startup_metrics});
+          .should_record_startup_metrics = should_record_startup_metrics},
+      base::BindOnce(&ProfilePickerHandler::OnResetPickerButtons,
+                     weak_factory_.GetWeakPtr()));
 }
 
 void ProfilePickerHandler::TryLaunchLockedProfile(
@@ -398,6 +401,7 @@
       DisplayForceSigninErrorDialog(
           /*profile_path=*/base::FilePath(),
           ForceSigninUIError::ReauthNotSupportedByGlicFlow());
+      OnResetPickerButtons(false);
       return;
     }
 
@@ -415,9 +419,14 @@
     // Triggers a fresh sign in via profile picker without existing email
     // address.
     ProfilePicker::SwitchToDiceSignIn(
-        entry.GetPath(),
-        base::BindOnce(&ProfilePickerHandler::OnLoadSigninFinished,
-                       weak_factory_.GetWeakPtr()));
+        entry.GetPath(), CombineCallbacks<StepSwitchFinishedCallback, bool>(
+                             StepSwitchFinishedCallback(base::BindOnce(
+                                 &ProfilePickerHandler::OnLoadSigninFinished,
+                                 weak_factory_.GetWeakPtr())),
+                             StepSwitchFinishedCallback(base::BindOnce(
+                                 &ProfilePickerHandler::OnResetPickerButtons,
+                                 weak_factory_.GetWeakPtr())))
+                             .value());
     return;
   }
 
@@ -428,6 +437,7 @@
   DisplayForceSigninErrorDialog(
       /*profile_path=*/base::FilePath(),
       ForceSigninUIError::ReauthNotAllowed());
+  OnResetPickerButtons(false);
 }
 
 void ProfilePickerHandler::OnProfileLoadedForSwitchToReauth(Profile* profile) {
@@ -436,6 +446,8 @@
   }
   ProfilePicker::SwitchToReauth(
       profile,
+      base::BindOnce(&ProfilePickerHandler::OnResetPickerButtons,
+                     weak_factory_.GetWeakPtr()),
       base::BindOnce(&ProfilePickerHandler::DisplayForceSigninErrorDialog,
                      weak_factory_.GetWeakPtr(), profile->GetPath()));
 }
@@ -457,8 +469,10 @@
   // checking has been added to the UI.
   ProfilePicker::PickProfile(
       ProfileManager::GetGuestProfilePath(),
-      ProfilePicker::ProfilePickingArgs{
-          .open_settings = false, .should_record_startup_metrics = false});
+      ProfilePicker::ProfilePickingArgs{.open_settings = false,
+                                        .should_record_startup_metrics = false},
+      base::BindOnce(&ProfilePickerHandler::OnResetPickerButtons,
+                     weak_factory_.GetWeakPtr()));
 }
 
 void ProfilePickerHandler::HandleAskOnStartupChanged(
@@ -576,8 +590,10 @@
   // flow.
   ProfilePicker::PickProfile(
       *profile_path,
-      ProfilePicker::ProfilePickingArgs{
-          .open_settings = false, .should_record_startup_metrics = false});
+      ProfilePicker::ProfilePickingArgs{.open_settings = false,
+                                        .should_record_startup_metrics = false},
+      base::BindOnce(&ProfilePickerHandler::OnResetPickerButtons,
+                     weak_factory_.GetWeakPtr()));
 }
 
 void ProfilePickerHandler::HandleCancelProfileSwitch(
@@ -734,8 +750,14 @@
     profile_color = GenerateNewProfileColor().color;
   }
   ProfilePicker::SwitchToDiceSignIn(
-      profile_color, base::BindOnce(&ProfilePickerHandler::OnLoadSigninFinished,
-                                    weak_factory_.GetWeakPtr()));
+      profile_color, CombineCallbacks<StepSwitchFinishedCallback, bool>(
+                         StepSwitchFinishedCallback(base::BindOnce(
+                             &ProfilePickerHandler::OnLoadSigninFinished,
+                             weak_factory_.GetWeakPtr())),
+                         StepSwitchFinishedCallback(base::BindOnce(
+                             &ProfilePickerHandler::OnResetPickerButtons,
+                             weak_factory_.GetWeakPtr())))
+                         .value());
 #else
   NOTERACHED();
 #endif
@@ -746,6 +768,11 @@
   FireWebUIListener("load-signin-finished", base::Value(success));
 }
 
+void ProfilePickerHandler::OnResetPickerButtons(bool success) {
+  AllowJavascript();
+  FireWebUIListener("reset-picker-buttons", base::Value(success));
+}
+
 void ProfilePickerHandler::PushProfilesList() {
   DCHECK(IsJavascriptAllowed());
   FireWebUIListener("profiles-list-changed", GetProfilesList());
diff --git a/chrome/browser/ui/webui/signin/profile_picker_handler.h b/chrome/browser/ui/webui/signin/profile_picker_handler.h
index 8d312ff..6af2dd5 100644
--- a/chrome/browser/ui/webui/signin/profile_picker_handler.h
+++ b/chrome/browser/ui/webui/signin/profile_picker_handler.h
@@ -120,6 +120,7 @@
   void HandleRecordSignInPromoImpression(const base::Value::List& args);
 
   void OnLoadSigninFinished(bool success);
+  void OnResetPickerButtons(bool success);
   void GatherProfileStatistics(Profile* profile);
   void OnProfileStatisticsReceived(const base::FilePath& profile_path,
                                    profiles::ProfileCategoryStats result);
diff --git a/chrome/test/data/webui/signin/profile_picker_browsertest.cc b/chrome/test/data/webui/signin/profile_picker_browsertest.cc
index 536bbd5a..4e0d719 100644
--- a/chrome/test/data/webui/signin/profile_picker_browsertest.cc
+++ b/chrome/test/data/webui/signin/profile_picker_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/strings/stringprintf.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/web_ui_mocha_browser_test.h"
 #include "content/public/test/browser_test.h"
diff --git a/chrome/test/data/webui/signin/profile_picker_main_view_test.ts b/chrome/test/data/webui/signin/profile_picker_main_view_test.ts
index 2e8b9a74a..7fa5dab2 100644
--- a/chrome/test/data/webui/signin/profile_picker_main_view_test.ts
+++ b/chrome/test/data/webui/signin/profile_picker_main_view_test.ts
@@ -5,10 +5,10 @@
 import 'chrome://profile-picker/profile_picker.js';
 
 import type {ProfileCardElement, ProfilePickerMainViewElement, ProfileState} from 'chrome://profile-picker/profile_picker.js';
-import {loadTimeData, ManageProfilesBrowserProxyImpl, NavigationMixin, Routes} from 'chrome://profile-picker/profile_picker.js';
+import {loadTimeData, ManageProfilesBrowserProxyImpl, navigateTo, NavigationMixin, Routes} from 'chrome://profile-picker/profile_picker.js';
 import {webUIListenerCallback} from 'chrome://resources/js/cr.js';
 import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
-import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertGE, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {TestManageProfilesBrowserProxy} from './test_manage_profiles_browser_proxy.js';
@@ -118,8 +118,10 @@
       const profile = profiles[i]!;
       const expectedProfile = expectedProfiles[i]!;
       assertTrue(!!profile.shadowRoot.querySelector('profile-card-menu'));
-      profile.shadowRoot.querySelector('cr-button')!.click();
+      profile.$.profileCardButton.click();
       await browserProxy.whenCalled('launchSelectedProfile');
+      webUIListenerCallback('reset-picker-buttons');
+      await microtasksFinished();
       assertEquals(
           profile.shadowRoot
               .querySelector<HTMLElement>('#forceSigninContainer')!.hidden,
@@ -165,6 +167,8 @@
     assertTrue(isVisible(mainViewElement.$.addProfile));
     mainViewElement.$.browseAsGuestButton.click();
     await browserProxy.whenCalled('launchGuestProfile');
+    webUIListenerCallback('reset-picker-buttons');
+    await microtasksFinished();
     // Ask when chrome opens.
     mainViewElement.$.askOnStartup.click();
     await browserProxy.whenCalled('askOnStartupChanged');
@@ -259,6 +263,79 @@
     assertFalse(navigationElement.changeCalled);
   });
 
+  test('ProfileCreationDisableButtons', async function() {
+    resetTest();
+    const addProfileButton = mainViewElement.$.addProfile;
+    assertFalse(isVisible(addProfileButton));
+    await browserProxy.whenCalled('initializeMainView');
+    await simulateProfilesListChanged(generateProfilesList(2));
+    navigationElement.reset();
+    assertTrue(isVisible(addProfileButton));
+    assertFalse(addProfileButton.disabled);
+    const profileCards =
+        mainViewElement.shadowRoot.querySelectorAll<ProfileCardElement>(
+            'profile-card');
+    profileCards.forEach(profileCard => {
+      assertFalse(profileCard.$.profileCardButton.disabled);
+    });
+
+    // Clicking on the add profile button should disable all buttons.
+    addProfileButton.click();
+    await microtasksFinished();
+    assertTrue(navigationElement.changeCalled);
+    assertEquals(navigationElement.route, Routes.NEW_PROFILE);
+    assertTrue(addProfileButton.disabled);
+    profileCards.forEach(profileCard => {
+      assertTrue(profileCard.$.profileCardButton.disabled);
+    });
+
+    // Navigating back to the main route should re-enable the buttons.
+    navigationElement.reset();
+    navigateTo(Routes.MAIN);
+    await microtasksFinished();
+    assertTrue(navigationElement.changeCalled);
+    assertEquals(navigationElement.route, Routes.MAIN);
+    assertFalse(addProfileButton.disabled);
+    profileCards.forEach(profileCard => {
+      assertFalse(profileCard.$.profileCardButton.disabled);
+    });
+  });
+
+  test('ProfileCardClickDisableButtons', async function() {
+    resetTest();
+    const addProfileButton = mainViewElement.$.addProfile;
+    assertFalse(isVisible(addProfileButton));
+    await browserProxy.whenCalled('initializeMainView');
+    await simulateProfilesListChanged(generateProfilesList(2));
+    assertTrue(isVisible(addProfileButton));
+    assertFalse(addProfileButton.disabled);
+    const profileCards =
+        mainViewElement.shadowRoot.querySelectorAll<ProfileCardElement>(
+            'profile-card');
+    profileCards.forEach(profileCard => {
+      assertFalse(profileCard.$.profileCardButton.disabled);
+    });
+
+    // Clicking on a profile button should disable all buttons.
+    assertGE(profileCards.length, 1);
+    const firstProfileCard = profileCards[0]!;
+    firstProfileCard.$.profileCardButton.click();
+    await microtasksFinished();
+    assertTrue(addProfileButton.disabled);
+    profileCards.forEach(profileCard => {
+      assertTrue(profileCard.$.profileCardButton.disabled);
+    });
+
+    // Receiving the signal that the step was completed should re-enable the
+    // buttons.
+    webUIListenerCallback('reset-picker-buttons');
+    await microtasksFinished();
+    assertFalse(addProfileButton.disabled);
+    profileCards.forEach(profileCard => {
+      assertFalse(profileCard.$.profileCardButton.disabled);
+    });
+  });
+
   test('AskOnStartupSingleToMultipleProfiles', async function() {
     await browserProxy.whenCalled('initializeMainView');
     // Hidden while profiles list is not yet defined.