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 <japhet@chromium.org>
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 <japhet@chromium.org>
Reviewed-by: Allen Bauer <kylixrd@chromium.org>
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