Chrome · Tab Hover Cards
CVE-2026-11227
Logic Error in Tab Hover Cards
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/views/tabs/hovercard/fade_label_view.cc |
modified | |
ifchrome/browser/ui/views/tabs/hovercard/hover_card_anchor_target.cc |
modified | |
TabHoverCardBubbleViewTestchrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc |
modified | |
TEST_Fchrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc |
modified | |
TEST_Fui/views/controls/label_unittest.cc |
modified |
Files Changed
chrome/browser/ui/views/tabs/hovercard/fade_label_view.ccchrome/browser/ui/views/tabs/hovercard/fade_label_view.hchrome/browser/ui/views/tabs/hovercard/hover_card_anchor_target.ccchrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.ccui/views/controls/label.ccui/views/controls/label.hui/views/controls/label_unittest.cc
Patch
From cb262a7368abf1bab2ebbd122e2f8eabc4bc4066 Mon Sep 17 00:00:00 2001 From: Alison Gale <[email protected]> Date: Tue, 14 Apr 2026 14:28:05 -0700 Subject: [PATCH] [Hovercards] Use the correct directionality for hovercard domains Relevant documentation: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/url_display_guidelines/url_display_guidelines.md#rtl Screenshot: https://screenshot.googleplex.com/AGAkRJEPEq9DzEn Note that the title of the page is the opposite direction but titles aren't urls so that should be okay. Bug: 448421954 Change-Id: If556b49e0764c6f73edebeb490d3c4b0c5dccb41 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7762370 Reviewed-by: David Yeung <[email protected]> Commit-Queue: Alison Gale <[email protected]> Reviewed-by: Eshwar Stalin <[email protected]> Cr-Commit-Position: refs/heads/main@{#1614711} --- diff --git a/chrome/browser/ui/views/tabs/hovercard/fade_label_view.cc b/chrome/browser/ui/views/tabs/hovercard/fade_label_view.cc index cfebb56..922a881 100644 --- a/chrome/browser/ui/views/tabs/hovercard/fade_label_view.cc +++ b/chrome/browser/ui/views/tabs/hovercard/fade_label_view.cc @@ -24,6 +24,9 @@ std::u16string text = data.text; const bool is_filename = data.is_filename; SetElideBehavior(is_filename ? gfx::NO_ELIDE : data.elide); + SetDirectionalityMode( + data.is_domain_url ? gfx::DirectionalityMode::DIRECTIONALITY_AS_URL + : gfx::DirectionalityMode::DIRECTIONALITY_FROM_TEXT); if (is_filename) { text = TruncateFilenameToTwoLines(text); } diff --git a/chrome/browser/ui/views/tabs/hovercard/fade_label_view.h b/chrome/browser/ui/views/tabs/hovercard/fade_label_view.h index e3f14e56..f440008 100644 --- a/chrome/browser/ui/views/tabs/hovercard/fade_label_view.h +++ b/chrome/browser/ui/views/tabs/hovercard/fade_label_view.h @@ -18,6 +18,7 @@ std::u16string text; bool is_filename = false; gfx::ElideBehavior elide = gfx::ELIDE_TAIL; + bool is_domain_url = false; }; using FadeWrapper_Label_FadeLabelViewData = diff --git a/chrome/browser/ui/views/tabs/hovercard/hover_card_anchor_target.cc b/chrome/browser/ui/views/tabs/hovercard/hover_card_anchor_target.cc index d571b8b3..ae25d69 100644 --- a/chrome/browser/ui/views/tabs/hovercard/hover_card_anchor_target.cc +++ b/chrome/browser/ui/views/tabs/hovercard/hover_card_anchor_target.cc @@ -96,6 +96,7 @@ } std::u16string domain; + bool is_domain_url = false; if (domain_url.SchemeIsFile()) { domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_FILE_URL_SOURCE); } else if (domain_url.SchemeIsBlob()) { @@ -104,6 +105,7 @@ domain = l10n_util::GetStringUTF16(IDS_HOVER_CARD_VIEW_SOURCE_URL_SOURCE); } else { if (tab_data.should_display_url) { + is_domain_url = true; // Hide the domain when necessary. This leaves an empty space in the // card, but this scenario is very rare. Also, shrinking the card to // remove the space would result in visual noise, so we keep it simple. @@ -121,7 +123,7 @@ card_data.thumbnail = tab_data.thumbnail; card_data.title_data = GetTabTitleLabel(tab_data); - card_data.domain_data = {domain, false, gfx::ELIDE_HEAD}; + card_data.domain_data = {domain, false, gfx::ELIDE_HEAD, is_domain_url}; // Now set the collaboration data. using collaboration::messaging::CollaborationEvent; diff --git a/chrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc b/chrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc index f9aea07..c4e581a 100644 --- a/chrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc +++ b/chrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc @@ -9,13 +9,19 @@ #include "base/logging.h" #include "build/build_config.h" +#include "chrome/browser/ui/tabs/tab_data.h" +#include "chrome/browser/ui/views/tabs/fake_tab_slot_controller.h" #include "chrome/browser/ui/views/tabs/hovercard/filename_elider.h" +#include "chrome/browser/ui/views/tabs/hovercard/hover_card_anchor_target.h" +#include "chrome/browser/ui/views/tabs/tab.h" +#include "chrome/test/views/chrome_views_test_base.h" #include "content/public/test/browser_task_environment.h" #include "skia/ext/font_utils.h" #include "testing/gtest/include/gtest/gtest-param-test.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/render_text.h" +#include "ui/views/widget/widget.h" #if BUILDFLAG(IS_MAC) #include "base/mac/mac_util.h" @@ -374,3 +380,40 @@ const FindImageDimensionsTestParams& params = GetParam(); EXPECT_EQ(params.expected, FilenameElider::FindImageDimensions(params.text)); } + +class TabHoverCardBubbleViewTest : public ChromeViewsTestBase { + public: + TabHoverCardBubbleViewTest() = default; + ~TabHoverCardBubbleViewTest() override = default; +}; + +TEST_F(TabHoverCardBubbleViewTest, HoverCardLabel_DomainIsUrl) { + auto tab_slot_controller = std::make_unique<FakeTabSlotController>(); + std::unique_ptr<views::Widget> widget = + CreateTestWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET); + Tab* tab = widget->SetContentsView( + std::make_unique<Tab>(tabs::TabHandle(1), tab_slot_controller.get())); + + TabHoverCardBubbleView* hover_card = + new TabHoverCardBubbleView(tab, {.show_domain = true}); + + tabs::TabData data; + data.last_committed_url = GURL("https://example.com"); + data.should_display_url = true; + tab->SetDataForTesting(data); + + hover_card->UpdateCardContent(tab); + + FadeLabelView* domain_view = hover_card->GetDomainViewForTesting(); + FadeLabel* primary_label = domain_view->GetPrimaryViewForTesting(); + EXPECT_EQ(gfx::DirectionalityMode::DIRECTIONALITY_AS_URL, + primary_label->GetDirectionalityMode()); + + data.should_display_url = false; + tab->SetDataForTesting(data); + hover_card->UpdateCardContent(tab); + EXPECT_EQ(gfx::DirectionalityMode::DIRECTIONALITY_FROM_TEXT, + primary_label->GetDirectionalityMode()); + + hover_card->GetWidget()->CloseNow(); +} diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc index ac350ea7..cf0139f 100644 --- a/ui/views/controls/label.cc +++ b/ui/views/controls/label.cc @@ -536,6 +536,14 @@ OnDisplayTextTruncation(); } +void Label::SetDirectionalityMode(gfx::DirectionalityMode mode) { + full_text_->SetDirectionalityMode(mode); +} + +gfx::DirectionalityMode Label::GetDirectionalityMode() const { + return full_text_->directionality_mode(); +} + void Label::SetCustomTooltipText(std::u16string_view tooltip_text) { custom_tooltip_text_ = std::u16string(tooltip_text); diff --git a/ui/views/controls/label.h b/ui/views/controls/label.h index 0d89a9cd..b1ccc70 100644 --- a/ui/views/controls/label.h +++ b/ui/views/controls/label.h @@ -241,6 +241,10 @@ gfx::ElideBehavior GetElideBehavior() const; void SetElideBehavior(gfx::ElideBehavior elide_behavior); + // Sets the directionality mode of the label. + void SetDirectionalityMode(gfx::DirectionalityMode mode); + gfx::DirectionalityMode GetDirectionalityMode() const; + // Sets the custom local tooltip text. Default behavior for a label // (single-line) is to show the full text if it is wider than its bounds. // Calling this overrides the default behavior and lets you set a custom diff --git a/ui/views/controls/label_unittest.cc b/ui/views/controls/label_unittest.cc index 8cbbb3e..5eaabc2 100644 --- a/ui/views/controls/label_unittest.cc +++ b/ui/views/controls/label_unittest.cc @@ -1255,6 +1255,26 @@ ltr_text_force_rtl.GetTextDirectionForTesting()); } +TEST_F(LabelTest, SetDirectionalityMode) { + Label label(ToRTL("0123456")); + EXPECT_EQ(base::i18n::TextDirection::RIGHT_TO_LEFT, + label.GetTextDirectionForTesting()); + + label.SetDirectionalityMode( + gfx::DirectionalityMode::DIRECTIONALITY_FORCE_LTR); + EXPECT_EQ(base::i18n::TextDirection::LEFT_TO_RIGHT, + label.GetTextDirectionForTesting()); + + label.SetDirectionalityMode( + gfx::DirectionalityMode::DIRECTIONALITY_FORCE_RTL); + EXPECT_EQ(base::i18n::TextDirection::RIGHT_TO_LEFT, + label.GetTextDirectionForTesting()); + + label.SetDirectionalityMode(gfx::DirectionalityMode::DIRECTIONALITY_AS_URL);
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc b/chrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc
index f9aea07..c4e581a 100644
--- a/chrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/tabs/hovercard/tab_hover_card_bubble_view_unittest.cc
@@ -9,13 +9,19 @@
#include "base/logging.h"
#include "build/build_config.h"
+#include "chrome/browser/ui/tabs/tab_data.h"
+#include "chrome/browser/ui/views/tabs/fake_tab_slot_controller.h"
#include "chrome/browser/ui/views/tabs/hovercard/filename_elider.h"
+#include "chrome/browser/ui/views/tabs/hovercard/hover_card_anchor_target.h"
+#include "chrome/browser/ui/views/tabs/tab.h"
+#include "chrome/test/views/chrome_views_test_base.h"
#include "content/public/test/browser_task_environment.h"
#include "skia/ext/font_utils.h"
#include "testing/gtest/include/gtest/gtest-param-test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/render_text.h"
+#include "ui/views/widget/widget.h"
#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
@@ -374,3 +380,40 @@
const FindImageDimensionsTestParams& params = GetParam();
EXPECT_EQ(params.expected, FilenameElider::FindImageDimensions(params.text));
}
+
+class TabHoverCardBubbleViewTest : public ChromeViewsTestBase {
+ public:
+ TabHoverCardBubbleViewTest() = default;
+ ~TabHoverCardBubbleViewTest() override = default;
+};
+
+TEST_F(TabHoverCardBubbleViewTest, HoverCardLabel_DomainIsUrl) {
+ auto tab_slot_controller = std::make_unique<FakeTabSlotController>();
+ std::unique_ptr<views::Widget> widget =
+ CreateTestWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET);
+ Tab* tab = widget->SetContentsView(
+ std::make_unique<Tab>(tabs::TabHandle(1), tab_slot_controller.get()));
+
+ TabHoverCardBubbleView* hover_card =
+ new TabHoverCardBubbleView(tab, {.show_domain = true});
+
+ tabs::TabData data;
+ data.last_committed_url = GURL("https://example.com");
+ data.should_display_url = true;
+ tab->SetDataForTesting(data);
+
+ hover_card->UpdateCardContent(tab);
+
+ FadeLabelView* domain_view = hover_card->GetDomainViewForTesting();
+ FadeLabel* primary_label = domain_view->GetPrimaryViewForTesting();
+ EXPECT_EQ(gfx::DirectionalityMode::DIRECTIONALITY_AS_URL,
+ primary_label->GetDirectionalityMode());
+
+ data.should_display_url = false;
+ tab->SetDataForTesting(data);
+ hover_card->UpdateCardContent(tab);
+ EXPECT_EQ(gfx::DirectionalityMode::DIRECTIONALITY_FROM_TEXT,
+ primary_label->GetDirectionalityMode());
+
+ hover_card->GetWidget()->CloseNow();
+}
diff --git a/ui/views/controls/label_unittest.cc b/ui/views/controls/label_unittest.cc
index 8cbbb3e..5eaabc2 100644
--- a/ui/views/controls/label_unittest.cc
+++ b/ui/views/controls/label_unittest.cc
@@ -1255,6 +1255,26 @@
ltr_text_force_rtl.GetTextDirectionForTesting());
}
+TEST_F(LabelTest, SetDirectionalityMode) {
+ Label label(ToRTL("0123456"));
+ EXPECT_EQ(base::i18n::TextDirection::RIGHT_TO_LEFT,
+ label.GetTextDirectionForTesting());
+
+ label.SetDirectionalityMode(
+ gfx::DirectionalityMode::DIRECTIONALITY_FORCE_LTR);
+ EXPECT_EQ(base::i18n::TextDirection::LEFT_TO_RIGHT,
+ label.GetTextDirectionForTesting());
+
+ label.SetDirectionalityMode(
+ gfx::DirectionalityMode::DIRECTIONALITY_FORCE_RTL);
+ EXPECT_EQ(base::i18n::TextDirection::RIGHT_TO_LEFT,
+ label.GetTextDirectionForTesting());
+
+ label.SetDirectionalityMode(gfx::DirectionalityMode::DIRECTIONALITY_AS_URL);
+ EXPECT_EQ(base::i18n::TextDirection::LEFT_TO_RIGHT,
+ label.GetTextDirectionForTesting());
+}
+
TEST_F(LabelTest, DefaultDirectionalityIsFromText) {
Label ltr(u"Foo");
EXPECT_EQ(base::i18n::TextDirection::LEFT_TO_RIGHT,
Loading diff…
Original Bug Report
reported by [email protected]
Tab hovercard doesn't show the origin correctly
VULNERABILITY DETAILS This vulnerability is similar to this one: https://issues.chromium.org/issues/40075024 but in this bug Popup window tab doesn’t show correctly. in the popup window it shows https://pwr.wtf.کورد/ which should be https://کورد.pwr.wtf/
VERSION Chrome Version 142.0.7444.0 (Official Build) canary (64-bit) Operating System: Windows 11
REPRODUCTION CASE
- Go to https://کورد.pwr.wtf/poc.html?
- Hover over the tab
References
On This Page