diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc index 17844c0e413ec..0fe91fdbfcb17 100644 --- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc +++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc @@ -188,6 +188,13 @@ void MandatoryReauthBubbleControllerImpl::OnBubbleClosed( view_android_.reset(); #endif + // On macOS without biometrics, the accept/cancel callbacks below + // synchronously enter AuthorizationCopyRights() which spins a nested + // CFRunLoop. UI tasks pumped by that nested loop can destroy the + // WebContents (and with it this WebContentsUserData controller), so guard + // against `this` being freed before we touch members again below. + auto weak_this = weak_ptr_factory_.GetWeakPtr(); + if (current_bubble_type_ == MandatoryReauthBubbleType::kOptIn) { if (!bubble_hide_initiated_by_bubble_manager_) { LogBubbleCloseOptInMetrics(closed_reason); @@ -226,6 +233,11 @@ void MandatoryReauthBubbleControllerImpl::OnBubbleClosed( current_bubble_type_ = MandatoryReauthBubbleType::kInactive; } + if (!weak_this) { + // `this` was freed inside the callback (e.g. WebContents destroyed during + // a nested run loop on macOS). Do not touch members. + return; + } UpdatePageActionIcon(); }