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 <agale@chromium.org>
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 <dayeung@chromium.org>
Commit-Queue: Alison Gale <agale@chromium.org>
Reviewed-by: Eshwar Stalin <estalin@chromium.org>
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 sa...@gmail.com
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