Chrome · Transactions Platform
CVE-2026-79022
Logic Error in Transactions Platform
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
switchchrome/browser/ui/autofill/autofill_popup_hide_helper.cc |
modified | |
NavigationHandlechrome/browser/ui/autofill/autofill_popup_hide_helper.h |
modified | |
RenderFrameHostchrome/browser/ui/autofill/autofill_popup_hide_helper.h |
modified | |
RenderWidgetHostchrome/browser/ui/autofill/autofill_popup_hide_helper.h |
modified |
Files Changed
chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.ccchrome/browser/ui/autofill/autofill_popup_hide_helper.ccchrome/browser/ui/autofill/autofill_popup_hide_helper.hchrome/browser/ui/autofill/autofill_suggestion_controller_unittest.ccchrome/browser/ui/autofill/email_verifier/email_verification_popup_controller.cccomponents/autofill/core/browser/suggestions/suggestion_hiding_reason.h
Patch
From c3968d605c3d42a036dd6fd6cc4193457e7a6464 Mon Sep 17 00:00:00 2001 From: Christoph Schwering <[email protected]> Date: Mon, 13 Jul 2026 06:16:29 -0700 Subject: [PATCH] [Autofill] Hide popup in RenderFrameHostStateChanged() The CL replaces RenderFrameDeleted() and DidFinishNavigation() with RenderFrameHostStateChanged()... but keeps RenderFrameDeleted() again because of a bug in RenderFrameHostStateChanged(). The intention is that RenderFrameHostStateChanged() is also notified when a navigation in a parent frame makes the descendant frame go into bfcache. Bug: 514038302 Change-Id: I3fae9459774c66233a6236e473ab77c46a62225a Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8071266 Reviewed-by: Jan Keitel <[email protected]> Commit-Queue: Christoph Schwering <[email protected]> Cr-Commit-Position: refs/heads/main@{#1661091} --- diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc index db7b0ad6..3c1edf4 100644 --- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc +++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc @@ -185,10 +185,10 @@ SuggestionType::kAutocompleteEntry)}); EXPECT_CALL(manager().external_delegate(), - OnSuggestionsHidden(SuggestionHidingReason::kNavigation)); + OnSuggestionsHidden(SuggestionHidingReason::kRendererEvent)); client().suggestion_controller(manager()).Hide( - SuggestionHidingReason::kNavigation); + SuggestionHidingReason::kRendererEvent); } TEST_F(AutofillKeyboardAccessoryControllerImplTest, diff --git a/chrome/browser/ui/autofill/autofill_popup_hide_helper.cc b/chrome/browser/ui/autofill/autofill_popup_hide_helper.cc index 45ca6723..b4600df 100644 --- a/chrome/browser/ui/autofill/autofill_popup_hide_helper.cc +++ b/chrome/browser/ui/autofill/autofill_popup_hide_helper.cc @@ -69,23 +69,42 @@ } } -void AutofillPopupHideHelper::RenderFrameDeleted( - content::RenderFrameHost* rfh) { +void AutofillPopupHideHelper::RenderFrameHostStateChanged( + content::RenderFrameHost* rfh, + content::RenderFrameHost::LifecycleState old_state, + content::RenderFrameHost::LifecycleState new_state) { + auto should_hide_popup = [](content::RenderFrameHost::LifecycleState state) { + switch (state) { + case content::RenderFrameHost::LifecycleState::kActive: + return false; + case content::RenderFrameHost::LifecycleState::kPendingCommit: + case content::RenderFrameHost::LifecycleState::kPrerendering: + case content::RenderFrameHost::LifecycleState::kInBackForwardCache: + case content::RenderFrameHost::LifecycleState::kPendingDeletion: + return true; + } + NOTREACHED(); + }; + // If the popup menu has been triggered from within an iframe and that frame // is deleted, hide the popup. This is necessary because the popup may // actually be shown by the `AutofillExternalDelegate` of an ancestor frame, // which is not notified about `rfh`'s destruction and therefore won't close // the popup. - if (rfh_id_ == rfh->GetGlobalId()) { + if (rfh_id_ == rfh->GetGlobalId() && should_hide_popup(new_state)) { hiding_callback_.Run(SuggestionHidingReason::kRendererEvent); } } -void AutofillPopupHideHelper::DidFinishNavigation( - content::NavigationHandle* navigation_handle) { - if (rfh_id_ == navigation_handle->GetPreviousRenderFrameHostId() && - !navigation_handle->IsSameDocument()) { - hiding_callback_.Run(SuggestionHidingReason::kNavigation); +void AutofillPopupHideHelper::RenderFrameDeleted( + content::RenderFrameHost* rfh) { + // RenderFrameHostStateChanged() is not called when on FrameTree::Shutdown(): + // crbug.com/40693086. + // For the primary frame tree, this is caught by WebContentsDestroyed(), but + // for embedded frame trees we observe RenderFrameDeleted() to compensate for + // the missing RenderFrameHostStateChanged(). + if (rfh_id_ == rfh->GetGlobalId()) { + hiding_callback_.Run(SuggestionHidingReason::kRendererEvent); } } diff --git a/chrome/browser/ui/autofill/autofill_popup_hide_helper.h b/chrome/browser/ui/autofill/autofill_popup_hide_helper.h index 34a5d434..80e5198 100644 --- a/chrome/browser/ui/autofill/autofill_popup_hide_helper.h +++ b/chrome/browser/ui/autofill/autofill_popup_hide_helper.h @@ -16,7 +16,6 @@ #endif // BUILDFLAG(IS_ANDROID) namespace content { -class NavigationHandle; class RenderFrameHost; class RenderWidgetHost; enum class Visibility; @@ -70,9 +69,11 @@ content::RenderWidgetHost* render_widget_host) override; void PrimaryMainFrameWasResized(bool width_changed) override; void OnVisibilityChanged(content::Visibility visibility) override; + void RenderFrameHostStateChanged( + content::RenderFrameHost* render_frame_host, + content::RenderFrameHost::LifecycleState old_state, + content::RenderFrameHost::LifecycleState new_state) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; - void DidFinishNavigation( - content::NavigationHandle* navigation_handle) override; #if !BUILDFLAG(IS_ANDROID) // ZoomObserver: diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc index 3eba931..9d7c1926 100644 --- a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc +++ b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc @@ -493,15 +493,7 @@ ShowSuggestions(manager(), {SuggestionType::kAddressEntry}); test::GenerateTestAutofillPopup(&manager().external_delegate()); // The navigation generates a PrimaryMainFrameWasResized callback. - SuggestionHidingReason reason; - // On Android, keyboard accessory is not hidden if the Chrome native widget - // changes its size. The keyboard accessory is still hidden because the input - // field looses. - if constexpr (BUILDFLAG(IS_ANDROID)) { - reason = SuggestionHidingReason::kNavigation; - } else { - reason = SuggestionHidingReason::kWidgetChanged; - } + SuggestionHidingReason reason = SuggestionHidingReason::kRendererEvent; EXPECT_CALL(client().suggestion_controller(manager()), Hide(reason)); NavigateAndCommitFrame(main_frame(), GURL("https://bar.com/")); // Verify and clear before TearDown() closes the popup. @@ -514,14 +506,8 @@ HideInSubFrameOnSubFrameNavigation) { ShowSuggestions(sub_manager(), {SuggestionType::kAddressEntry}); test::GenerateTestAutofillPopup(&sub_manager().external_delegate()); - if (sub_frame()->ShouldChangeRenderFrameHostOnSameSiteNavigation()) { - // If the RenderFrameHost changes, a RenderFrameDeleted will fire first. - EXPECT_CALL(client().suggestion_controller(sub_manager()), - Hide(SuggestionHidingReason::kRendererEvent)); - } else { - EXPECT_CALL(client().suggestion_controller(sub_manager()), - Hide(SuggestionHidingReason::kNavigation)); - } + EXPECT_CALL(client().suggestion_controller(sub_manager()), + Hide(SuggestionHidingReason::kRendererEvent)); NavigateAndCommitFrame(sub_frame(), GURL("https://bar.com/")); // Verify and clear before TearDown() closes the popup. Mock::VerifyAndClearExpectations( @@ -539,15 +525,7 @@ HideInSubFrameOnMainFrameNavigation) { ShowSuggestions(sub_manager(), {SuggestionType::kAddressEntry}); test::GenerateTestAutofillPopup(&sub_manager().external_delegate()); - SuggestionHidingReason reason; - // On Android, keyboard accessory is not hidden if the Chrome native widget - // changes its size. The keyboard accessory is still hidden because the input - // field looses. - if constexpr (BUILDFLAG(IS_ANDROID)) { - reason = SuggestionHidingReason::kRendererEvent; - } else { - reason = SuggestionHidingReason::kWidgetChanged; - } + SuggestionHidingReason reason = SuggestionHidingReason::kRendererEvent; EXPECT_CALL(client().suggestion_controller(sub_manager()), Hide(reason)); NavigateAndCommitFrame(main_frame(), GURL("https://bar.com/")); } diff --git a/chrome/browser/ui/autofill/email_verifier/email_verification_popup_controller.cc b/chrome/browser/ui/autofill/email_verifier/email_verification_popup_controller.cc index ed79c0b4..b380205c 100644 --- a/chrome/browser/ui/autofill/email_verifier/email_verification_popup_controller.cc +++ b/chrome/browser/ui/autofill/email_verifier/email_verification_popup_controller.cc @@ -22,9 +22,6 @@ case SuggestionHidingReason::kEndEditing: return EmailVerificationPopupController::EvpPermissionUiStatus:: kUserAborted; - case SuggestionHidingReason::kNavigation: - return EmailVerificationPopupController::EvpPermissionUiStatus:: - kNavigation; case SuggestionHidingReason::kTabGone: return EmailVerificationPopupController::EvpPermissionUiStatus::kTabGone; case SuggestionHidingReason::kWidgetChanged: diff --git a/components/autofill/core/browser/suggestions/suggestion_hiding_reason.h b/components/autofill/core/browser/suggestions/suggestion_hiding_reason.h index 1c93163..42d22bb 100644 --- a/components/autofill/core/browser/suggestions/suggestion_hiding_reason.h +++ b/components/autofill/core/browser/suggestions/suggestion_hiding_reason.h @@ -25,8 +25,6 @@ kFocusChanged = 3, // Scrolling or zooming into the page displaces the popup. kContentAreaMoved = 4, - // A navigation on the page or frame level. - kNavigation = 5, // The popup is or would become empty. kNoSuggestions = 6,
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
index db7b0ad6..3c1edf4 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_controller_impl_unittest.cc
@@ -185,10 +185,10 @@
SuggestionType::kAutocompleteEntry)});
EXPECT_CALL(manager().external_delegate(),
- OnSuggestionsHidden(SuggestionHidingReason::kNavigation));
+ OnSuggestionsHidden(SuggestionHidingReason::kRendererEvent));
client().suggestion_controller(manager()).Hide(
- SuggestionHidingReason::kNavigation);
+ SuggestionHidingReason::kRendererEvent);
}
TEST_F(AutofillKeyboardAccessoryControllerImplTest,
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
index 3eba931..9d7c1926 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller_unittest.cc
@@ -493,15 +493,7 @@
ShowSuggestions(manager(), {SuggestionType::kAddressEntry});
test::GenerateTestAutofillPopup(&manager().external_delegate());
// The navigation generates a PrimaryMainFrameWasResized callback.
- SuggestionHidingReason reason;
- // On Android, keyboard accessory is not hidden if the Chrome native widget
- // changes its size. The keyboard accessory is still hidden because the input
- // field looses.
- if constexpr (BUILDFLAG(IS_ANDROID)) {
- reason = SuggestionHidingReason::kNavigation;
- } else {
- reason = SuggestionHidingReason::kWidgetChanged;
- }
+ SuggestionHidingReason reason = SuggestionHidingReason::kRendererEvent;
EXPECT_CALL(client().suggestion_controller(manager()), Hide(reason));
NavigateAndCommitFrame(main_frame(), GURL("https://bar.com/"));
// Verify and clear before TearDown() closes the popup.
@@ -514,14 +506,8 @@
HideInSubFrameOnSubFrameNavigation) {
ShowSuggestions(sub_manager(), {SuggestionType::kAddressEntry});
test::GenerateTestAutofillPopup(&sub_manager().external_delegate());
- if (sub_frame()->ShouldChangeRenderFrameHostOnSameSiteNavigation()) {
- // If the RenderFrameHost changes, a RenderFrameDeleted will fire first.
- EXPECT_CALL(client().suggestion_controller(sub_manager()),
- Hide(SuggestionHidingReason::kRendererEvent));
- } else {
- EXPECT_CALL(client().suggestion_controller(sub_manager()),
- Hide(SuggestionHidingReason::kNavigation));
- }
+ EXPECT_CALL(client().suggestion_controller(sub_manager()),
+ Hide(SuggestionHidingReason::kRendererEvent));
NavigateAndCommitFrame(sub_frame(), GURL("https://bar.com/"));
// Verify and clear before TearDown() closes the popup.
Mock::VerifyAndClearExpectations(
@@ -539,15 +525,7 @@
HideInSubFrameOnMainFrameNavigation) {
ShowSuggestions(sub_manager(), {SuggestionType::kAddressEntry});
test::GenerateTestAutofillPopup(&sub_manager().external_delegate());
- SuggestionHidingReason reason;
- // On Android, keyboard accessory is not hidden if the Chrome native widget
- // changes its size. The keyboard accessory is still hidden because the input
- // field looses.
- if constexpr (BUILDFLAG(IS_ANDROID)) {
- reason = SuggestionHidingReason::kRendererEvent;
- } else {
- reason = SuggestionHidingReason::kWidgetChanged;
- }
+ SuggestionHidingReason reason = SuggestionHidingReason::kRendererEvent;
EXPECT_CALL(client().suggestion_controller(sub_manager()), Hide(reason));
NavigateAndCommitFrame(main_frame(), GURL("https://bar.com/"));
}
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page