Chrome · Omnibox
CVE-2026-113206
Memory Corruption in Omnibox
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/views/location_bar/custom_tab_bar_view.cc |
modified | |
ifchrome/browser/ui/views/toolbar/toolbar_view.cc |
modified | |
SecurityStateWaiterchrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc |
modified | |
ifchrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc |
modified | |
CustomTabBarViewBrowserTestchrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc |
modified |
Files Changed
chrome/browser/ui/views/location_bar/custom_tab_bar_view.ccchrome/browser/ui/views/toolbar/toolbar_view.ccchrome/browser/ui/views/location_bar/custom_tab_bar_view.hchrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc
Patch
From b64675fd64d47caa50fec2b065e6352f391a39f2 Mon Sep 17 00:00:00 2001 From: Nate Chapin <[email protected]> Date: Mon, 21 Sep 2026 12:29:24 -0700 Subject: [PATCH] Update CustomTabBarView when visible page state changes dynamically When a Progressive Web App window is displayed in custom tab mode, dynamic in-place status updates on an already visible custom tab bar did not refresh `CustomTabBarView`. Consequently, `LocationIconView` and related UI elements remained out of sync with the current `WebContents` state. This CL adds state change verification to `CustomTabBarView` and ensures `ToolbarView` triggers `UpdateContents()` when the visible page state changes. It also adds a null check for the visible navigation entry when updating the title. TAG=agy Fixed: 517407917 Change-Id: Id5e838d6a5cf62673e4cec4f77fc3ea3897db9bb Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8422705 Commit-Queue: Nate Chapin <[email protected]> Reviewed-by: Allen Bauer <[email protected]> Cr-Commit-Position: refs/heads/main@{#1701963} --- diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc index 5f581da..35b4ef5 100644 --- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc @@ -348,6 +348,11 @@ } } +bool CustomTabBarView::HasSecurityStateChanged() const { + return GetVisible() && location_icon_view_ && + location_icon_view_->HasSecurityStateChanged(); +} + void CustomTabBarView::UpdateContents() { // If the toolbar should not be shown don't update the UI, as the toolbar may // be animating out and it looks messy. @@ -364,8 +369,10 @@ content::NavigationEntry* entry = contents->GetController().GetVisibleEntry(); std::u16string title, location; - title = WindowMetadataController::FormatTitleForDisplay( - entry->GetTitleForDisplay()); + if (entry) { + title = WindowMetadataController::FormatTitleForDisplay( + entry->GetTitleForDisplay()); + } if (ShouldDisplayUrl(contents)) { location = web_app::AppBrowserController::FormatUrlOrigin( contents->GetVisibleURL(), url_formatter::kFormatUrlOmitDefaults); diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h index f1adede..f0a534a 100644 --- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h +++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h @@ -54,6 +54,13 @@ LocationIconView* location_icon_view() { return location_icon_view_; } + // Returns true if the custom tab bar's security state does not match + // the currently visible state. + bool HasSecurityStateChanged() const; + + // Populates child elements with page details from the current WebContents. + void UpdateContents(); + // views::AccessiblePaneView: gfx::Rect GetAnchorBoundsInScreen() const override; void SetVisible(bool visible) override; @@ -123,9 +130,6 @@ return web_app::AppBrowserController::From(browser_); } - // Populates child elements with page details from the current WebContents. - void UpdateContents(); - bool GetShowTitle() const; SkColor title_bar_color_ = SK_ColorTRANSPARENT; diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc index 2853c5c4..67f28a8 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_view.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc @@ -1175,7 +1175,9 @@ } void ToolbarView::Update(WebContents* tab) { - if (location_bar_) { + if (custom_tab_bar_) { + custom_tab_bar_->UpdateContents(); + } else if (location_bar_) { location_bar_->Update(tab); } @@ -1194,7 +1196,12 @@ } bool ToolbarView::UpdateSecurityState() { - if (location_bar_ && location_bar_->HasSecurityStateChanged()) { + const bool has_security_state_changed = + display_mode_ == DisplayMode::kCustomTab + ? (custom_tab_bar_ && custom_tab_bar_->HasSecurityStateChanged()) + : (location_bar_ && location_bar_->HasSecurityStateChanged()); + + if (has_security_state_changed) { Update(nullptr); return true; } diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc index 56fd97e..202ed937 100644 --- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc +++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc @@ -23,14 +23,17 @@ #include "chrome/common/chrome_switches.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/omnibox/browser/location_bar_model.h" #include "components/security_interstitials/content/security_interstitial_controller_client.h" #include "components/security_interstitials/content/security_interstitial_page.h" #include "components/security_interstitials/content/security_interstitial_tab_helper.h" #include "components/security_interstitials/content/settings_page_helper.h" #include "components/security_interstitials/core/metrics_helper.h" +#include "components/security_state/core/security_state.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents_observer.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_mock_cert_verifier.h" @@ -180,6 +183,29 @@ bool install_interstitial_ = true; }; +// Runs the message loop until WebContents::DidChangeVisibleSecurityState fires +// at least once. +class SecurityStateWaiter : public content::WebContentsObserver { + public: + explicit SecurityStateWaiter(content::WebContents* contents) + : content::WebContentsObserver(contents) {} + void DidChangeVisibleSecurityState() override { + fired_ = true; + if (run_loop_.running()) { + run_loop_.Quit(); + } + } + void Wait() { + if (!fired_) { + run_loop_.Run(); + } + } + + private: + bool fired_ = false; + base::RunLoop run_loop_; +}; + } // namespace class CustomTabBarViewBrowserTest : public web_app::WebAppBrowserTestBase { @@ -750,3 +776,61 @@ u"/", base::ASCIIToUTF16(embedded_https_test_server().GetURL("/").spec())); } + +// Check that the CustomTabBarView security chip updates when the visible +// security state changes while the bar is already visible. +IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest, + SecurityChipUpdatesOnVisibleSecurityStateChange) { + const GURL app_url = + embedded_https_test_server().GetURL("app.com", "/ssl/google.html"); + InstallPWA(app_url); + ASSERT_TRUE(app_browser_); + EXPECT_EQ(app_browser_->GetType(), BrowserWindowInterface::Type::TYPE_APP); + + BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_); + CustomTabBarView* bar = app_view->toolbar()->custom_tab_bar(); + ASSERT_TRUE(bar); + content::WebContents* contents = app_view->GetActiveWebContents(); + + // In-scope: custom tab bar is not shown. + EXPECT_FALSE(app_controller_->ShouldShowCustomTabBar()); + EXPECT_FALSE(bar->GetVisible()); + + // Navigate off-scope to a secure HTTPS page. Custom tab bar becomes visible. + const GURL offscope_url = embedded_https_test_server().GetURL( + "example.com", "/ssl/blank_page.html"); + NavigateAndWait(contents, offscope_url); + + EXPECT_TRUE(app_controller_->ShouldShowCustomTabBar()); + EXPECT_TRUE(bar->GetVisible()); + + LocationIconView* icon = bar->location_icon_view(); + ASSERT_TRUE(icon); + + // Baseline: model says SECURE, and displayed chip is in sync. + EXPECT_EQ(security_state::SECURE, + bar->GetLocationBarModel()->GetSecurityLevel()); + EXPECT_FALSE(icon->HasSecurityStateChanged()); + EXPECT_TRUE(icon->GetText().empty()); + EXPECT_FALSE(icon->ShouldShowLabel()); + EXPECT_FALSE(app_view->UpdateToolbarSecurityState()); + + // Trigger a visible security state change by loading mixed content. + SecurityStateWaiter sec_waiter(contents); + EXPECT_TRUE(content::ExecJs(contents, R"( + let img = document.createElement('img'); + img.src = 'http://sample-image.test/img.png'; + document.body.appendChild(img); + )")); + sec_waiter.Wait(); + + // The model now reports a non-SECURE security level. + const security_state::SecurityLevel new_level = + bar->GetLocationBarModel()->GetSecurityLevel(); + EXPECT_NE(security_state::SECURE, new_level); + + // The custom tab bar chip should update and remain in sync with the model. + EXPECT_FALSE(icon->HasSecurityStateChanged()); + EXPECT_EQ(icon->GetShowText(), icon->ShouldShowLabel()); + EXPECT_FALSE(app_view->UpdateToolbarSecurityState()); +}
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc
index 56fd97e..202ed937 100644
--- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc
@@ -23,14 +23,17 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
+#include "components/omnibox/browser/location_bar_model.h"
#include "components/security_interstitials/content/security_interstitial_controller_client.h"
#include "components/security_interstitials/content/security_interstitial_page.h"
#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
#include "components/security_interstitials/content/settings_page_helper.h"
#include "components/security_interstitials/core/metrics_helper.h"
+#include "components/security_state/core/security_state.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_mock_cert_verifier.h"
@@ -180,6 +183,29 @@
bool install_interstitial_ = true;
};
+// Runs the message loop until WebContents::DidChangeVisibleSecurityState fires
+// at least once.
+class SecurityStateWaiter : public content::WebContentsObserver {
+ public:
+ explicit SecurityStateWaiter(content::WebContents* contents)
+ : content::WebContentsObserver(contents) {}
+ void DidChangeVisibleSecurityState() override {
+ fired_ = true;
+ if (run_loop_.running()) {
+ run_loop_.Quit();
+ }
+ }
+ void Wait() {
+ if (!fired_) {
+ run_loop_.Run();
+ }
+ }
+
+ private:
+ bool fired_ = false;
+ base::RunLoop run_loop_;
+};
+
} // namespace
class CustomTabBarViewBrowserTest : public web_app::WebAppBrowserTestBase {
@@ -750,3 +776,61 @@
u"/",
base::ASCIIToUTF16(embedded_https_test_server().GetURL("/").spec()));
}
+
+// Check that the CustomTabBarView security chip updates when the visible
+// security state changes while the bar is already visible.
+IN_PROC_BROWSER_TEST_F(CustomTabBarViewBrowserTest,
+ SecurityChipUpdatesOnVisibleSecurityStateChange) {
+ const GURL app_url =
+ embedded_https_test_server().GetURL("app.com", "/ssl/google.html");
+ InstallPWA(app_url);
+ ASSERT_TRUE(app_browser_);
+ EXPECT_EQ(app_browser_->GetType(), BrowserWindowInterface::Type::TYPE_APP);
+
+ BrowserView* app_view = BrowserView::GetBrowserViewForBrowser(app_browser_);
+ CustomTabBarView* bar = app_view->toolbar()->custom_tab_bar();
+ ASSERT_TRUE(bar);
+ content::WebContents* contents = app_view->GetActiveWebContents();
+
+ // In-scope: custom tab bar is not shown.
+ EXPECT_FALSE(app_controller_->ShouldShowCustomTabBar());
+ EXPECT_FALSE(bar->GetVisible());
+
+ // Navigate off-scope to a secure HTTPS page. Custom tab bar becomes visible.
+ const GURL offscope_url = embedded_https_test_server().GetURL(
+ "example.com", "/ssl/blank_page.html");
+ NavigateAndWait(contents, offscope_url);
+
+ EXPECT_TRUE(app_controller_->ShouldShowCustomTabBar());
+ EXPECT_TRUE(bar->GetVisible());
+
+ LocationIconView* icon = bar->location_icon_view();
+ ASSERT_TRUE(icon);
+
+ // Baseline: model says SECURE, and displayed chip is in sync.
+ EXPECT_EQ(security_state::SECURE,
+ bar->GetLocationBarModel()->GetSecurityLevel());
+ EXPECT_FALSE(icon->HasSecurityStateChanged());
+ EXPECT_TRUE(icon->GetText().empty());
+ EXPECT_FALSE(icon->ShouldShowLabel());
+ EXPECT_FALSE(app_view->UpdateToolbarSecurityState());
+
+ // Trigger a visible security state change by loading mixed content.
+ SecurityStateWaiter sec_waiter(contents);
+ EXPECT_TRUE(content::ExecJs(contents, R"(
+ let img = document.createElement('img');
+ img.src = 'http://sample-image.test/img.png';
+ document.body.appendChild(img);
+ )"));
+ sec_waiter.Wait();
+
+ // The model now reports a non-SECURE security level.
+ const security_state::SecurityLevel new_level =
+ bar->GetLocationBarModel()->GetSecurityLevel();
+ EXPECT_NE(security_state::SECURE, new_level);
+
+ // The custom tab bar chip should update and remain in sync with the model.
+ EXPECT_FALSE(icon->HasSecurityStateChanged());
+ EXPECT_EQ(icon->GetShowText(), icon->ShouldShowLabel());
+ EXPECT_FALSE(app_view->UpdateToolbarSecurityState());
+}
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