Chrome · Geometry
CVE-2026-79283
Logic Error in Geometry
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fthird_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc |
modified |
Files Changed
third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
Patch
From ec8e14090e80ee238dbc34ef907967f879c3662d Mon Sep 17 00:00:00 2001 From: Stefan Zager <[email protected]> Date: Fri, 10 Jul 2026 16:29:04 -0700 Subject: [PATCH] [IOv2] Hit test table parts and rows for occlusion Bug: 518035396 Change-Id: Ibfd1df5ce1e62c4d0de73728215348bd4cb8c339 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8068547 Reviewed-by: Philip Rogers <[email protected]> Commit-Queue: Stefan Zager <[email protected]> Cr-Commit-Position: refs/heads/main@{#1660595} --- diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc index 3f8e018..f0f73f2e 100644 --- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc +++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc @@ -2810,6 +2810,251 @@ EXPECT_TRUE(observer_delegate->LastEntry()->isVisible()); } +TEST_F(IntersectionObserverV2Test, TableRowOcclusion) { + WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600)); + SimRequest main_resource("https://example.com/", "text/html"); + LoadURL("https://example.com/"); + main_resource.Complete(R"HTML( + <style> + #target { + width: 100px; + height: 100px; + } + #table { + position: absolute; + left: 200px; + top: 0; + visibility: hidden; + border-collapse: collapse; + } + #row { + filter: drop-shadow(-200px 0 0 black); + } + #cell { + visibility: visible; + width: 100px; + height: 100px; + background: black; + } + </style> + <div id='target'></div> + <table id='table'> + <tr id='row'> + <td id='cell'></td> + </tr> + </table> + )HTML"); + Compositor().BeginFrame(); + + IntersectionObserverInit* observer_init = IntersectionObserverInit::Create(); + observer_init->setTrackVisibility(true); + observer_init->setDelay(100); + DummyExceptionStateForTesting exception_state; + TestIntersectionObserverDelegate* observer_delegate = + MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument()); + IntersectionObserver* observer = IntersectionObserver::Create( + observer_init, *observer_delegate, + LocalFrameUkmAggregator::kJavascriptIntersectionObserver, + exception_state); + ASSERT_FALSE(exception_state.HadException()); + Element* target = GetDocument().getElementById(AtomicString("target")); + ASSERT_TRUE(target); + observer->observe(target); + + Compositor().BeginFrame(); + test::RunPendingTasks(); + ASSERT_FALSE(Compositor().NeedsBeginFrame()); + EXPECT_EQ(observer_delegate->CallCount(), 1); + EXPECT_EQ(observer_delegate->EntryCount(), 1); + EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting()); + // The target is occluded by the drop-shadow of the table row. + EXPECT_FALSE(observer_delegate->LastEntry()->isVisible()); +} + +TEST_F(IntersectionObserverV2Test, TableCellOcclusion) { + WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600)); + SimRequest main_resource("https://example.com/", "text/html"); + LoadURL("https://example.com/"); + main_resource.Complete(R"HTML( + <style> + #target { + width: 100px; + height: 100px; + } + #table { + position: absolute; + left: 200px; + top: 0; + border-collapse: collapse; + } + #cell { + visibility: hidden; + filter: drop-shadow(-200px 0 0 black); + } + #child { + visibility: visible; + width: 100px; + height: 100px; + background: black; + } + </style> + <div id='target'></div> + <table id='table'> + <tr> + <td id='cell'><div id='child'></div></td> + </tr> + </table> + )HTML"); + Compositor().BeginFrame(); + + IntersectionObserverInit* observer_init = IntersectionObserverInit::Create(); + observer_init->setTrackVisibility(true); + observer_init->setDelay(100); + DummyExceptionStateForTesting exception_state; + TestIntersectionObserverDelegate* observer_delegate = + MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument()); + IntersectionObserver* observer = IntersectionObserver::Create( + observer_init, *observer_delegate, + LocalFrameUkmAggregator::kJavascriptIntersectionObserver, + exception_state); + ASSERT_FALSE(exception_state.HadException()); + Element* target = GetDocument().getElementById(AtomicString("target")); + ASSERT_TRUE(target); + observer->observe(target); + + Compositor().BeginFrame(); + test::RunPendingTasks(); + ASSERT_FALSE(Compositor().NeedsBeginFrame()); + EXPECT_EQ(observer_delegate->CallCount(), 1); + EXPECT_EQ(observer_delegate->EntryCount(), 1); + EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting()); + // The target is occluded by the drop-shadow of the table cell. + EXPECT_FALSE(observer_delegate->LastEntry()->isVisible()); +} + +TEST_F(IntersectionObserverV2Test, TableHeaderGroupOcclusion) { + WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600)); + SimRequest main_resource("https://example.com/", "text/html"); + LoadURL("https://example.com/"); + main_resource.Complete(R"HTML( + <style> + #target { + width: 100px; + height: 100px; + } + #table { + position: absolute; + left: 200px; + top: 0; + visibility: hidden; + border-collapse: collapse; + } + #header { + filter: drop-shadow(-200px 0 0 black); + } + #cell { + visibility: visible; + width: 100px; + height: 100px; + background: black; + } + </style> + <div id='target'></div> + <table id='table'> + <thead id='header'> + <tr> + <td id='cell'></td> + </tr> + </thead> + </table> + )HTML"); + Compositor().BeginFrame(); + + IntersectionObserverInit* observer_init = IntersectionObserverInit::Create(); + observer_init->setTrackVisibility(true); + observer_init->setDelay(100); + DummyExceptionStateForTesting exception_state; + TestIntersectionObserverDelegate* observer_delegate = + MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument()); + IntersectionObserver* observer = IntersectionObserver::Create( + observer_init, *observer_delegate, + LocalFrameUkmAggregator::kJavascriptIntersectionObserver, + exception_state); + ASSERT_FALSE(exception_state.HadException()); + Element* target = GetDocument().getElementById(AtomicString("target")); + ASSERT_TRUE(target); + observer->observe(target); + + Compositor().BeginFrame(); + test::RunPendingTasks(); + ASSERT_FALSE(Compositor().NeedsBeginFrame()); + EXPECT_EQ(observer_delegate->CallCount(), 1);
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
index 3f8e018..f0f73f2e 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
@@ -2810,6 +2810,251 @@
EXPECT_TRUE(observer_delegate->LastEntry()->isVisible());
}
+TEST_F(IntersectionObserverV2Test, TableRowOcclusion) {
+ WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
+ SimRequest main_resource("https://example.com/", "text/html");
+ LoadURL("https://example.com/");
+ main_resource.Complete(R"HTML(
+ <style>
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+ #table {
+ position: absolute;
+ left: 200px;
+ top: 0;
+ visibility: hidden;
+ border-collapse: collapse;
+ }
+ #row {
+ filter: drop-shadow(-200px 0 0 black);
+ }
+ #cell {
+ visibility: visible;
+ width: 100px;
+ height: 100px;
+ background: black;
+ }
+ </style>
+ <div id='target'></div>
+ <table id='table'>
+ <tr id='row'>
+ <td id='cell'></td>
+ </tr>
+ </table>
+ )HTML");
+ Compositor().BeginFrame();
+
+ IntersectionObserverInit* observer_init = IntersectionObserverInit::Create();
+ observer_init->setTrackVisibility(true);
+ observer_init->setDelay(100);
+ DummyExceptionStateForTesting exception_state;
+ TestIntersectionObserverDelegate* observer_delegate =
+ MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument());
+ IntersectionObserver* observer = IntersectionObserver::Create(
+ observer_init, *observer_delegate,
+ LocalFrameUkmAggregator::kJavascriptIntersectionObserver,
+ exception_state);
+ ASSERT_FALSE(exception_state.HadException());
+ Element* target = GetDocument().getElementById(AtomicString("target"));
+ ASSERT_TRUE(target);
+ observer->observe(target);
+
+ Compositor().BeginFrame();
+ test::RunPendingTasks();
+ ASSERT_FALSE(Compositor().NeedsBeginFrame());
+ EXPECT_EQ(observer_delegate->CallCount(), 1);
+ EXPECT_EQ(observer_delegate->EntryCount(), 1);
+ EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting());
+ // The target is occluded by the drop-shadow of the table row.
+ EXPECT_FALSE(observer_delegate->LastEntry()->isVisible());
+}
+
+TEST_F(IntersectionObserverV2Test, TableCellOcclusion) {
+ WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
+ SimRequest main_resource("https://example.com/", "text/html");
+ LoadURL("https://example.com/");
+ main_resource.Complete(R"HTML(
+ <style>
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+ #table {
+ position: absolute;
+ left: 200px;
+ top: 0;
+ border-collapse: collapse;
+ }
+ #cell {
+ visibility: hidden;
+ filter: drop-shadow(-200px 0 0 black);
+ }
+ #child {
+ visibility: visible;
+ width: 100px;
+ height: 100px;
+ background: black;
+ }
+ </style>
+ <div id='target'></div>
+ <table id='table'>
+ <tr>
+ <td id='cell'><div id='child'></div></td>
+ </tr>
+ </table>
+ )HTML");
+ Compositor().BeginFrame();
+
+ IntersectionObserverInit* observer_init = IntersectionObserverInit::Create();
+ observer_init->setTrackVisibility(true);
+ observer_init->setDelay(100);
+ DummyExceptionStateForTesting exception_state;
+ TestIntersectionObserverDelegate* observer_delegate =
+ MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument());
+ IntersectionObserver* observer = IntersectionObserver::Create(
+ observer_init, *observer_delegate,
+ LocalFrameUkmAggregator::kJavascriptIntersectionObserver,
+ exception_state);
+ ASSERT_FALSE(exception_state.HadException());
+ Element* target = GetDocument().getElementById(AtomicString("target"));
+ ASSERT_TRUE(target);
+ observer->observe(target);
+
+ Compositor().BeginFrame();
+ test::RunPendingTasks();
+ ASSERT_FALSE(Compositor().NeedsBeginFrame());
+ EXPECT_EQ(observer_delegate->CallCount(), 1);
+ EXPECT_EQ(observer_delegate->EntryCount(), 1);
+ EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting());
+ // The target is occluded by the drop-shadow of the table cell.
+ EXPECT_FALSE(observer_delegate->LastEntry()->isVisible());
+}
+
+TEST_F(IntersectionObserverV2Test, TableHeaderGroupOcclusion) {
+ WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
+ SimRequest main_resource("https://example.com/", "text/html");
+ LoadURL("https://example.com/");
+ main_resource.Complete(R"HTML(
+ <style>
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+ #table {
+ position: absolute;
+ left: 200px;
+ top: 0;
+ visibility: hidden;
+ border-collapse: collapse;
+ }
+ #header {
+ filter: drop-shadow(-200px 0 0 black);
+ }
+ #cell {
+ visibility: visible;
+ width: 100px;
+ height: 100px;
+ background: black;
+ }
+ </style>
+ <div id='target'></div>
+ <table id='table'>
+ <thead id='header'>
+ <tr>
+ <td id='cell'></td>
+ </tr>
+ </thead>
+ </table>
+ )HTML");
+ Compositor().BeginFrame();
+
+ IntersectionObserverInit* observer_init = IntersectionObserverInit::Create();
+ observer_init->setTrackVisibility(true);
+ observer_init->setDelay(100);
+ DummyExceptionStateForTesting exception_state;
+ TestIntersectionObserverDelegate* observer_delegate =
+ MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument());
+ IntersectionObserver* observer = IntersectionObserver::Create(
+ observer_init, *observer_delegate,
+ LocalFrameUkmAggregator::kJavascriptIntersectionObserver,
+ exception_state);
+ ASSERT_FALSE(exception_state.HadException());
+ Element* target = GetDocument().getElementById(AtomicString("target"));
+ ASSERT_TRUE(target);
+ observer->observe(target);
+
+ Compositor().BeginFrame();
+ test::RunPendingTasks();
+ ASSERT_FALSE(Compositor().NeedsBeginFrame());
+ EXPECT_EQ(observer_delegate->CallCount(), 1);
+ EXPECT_EQ(observer_delegate->EntryCount(), 1);
+ EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting());
+ // The target is occluded by the drop-shadow of the table header group
+ // (thead).
+ EXPECT_FALSE(observer_delegate->LastEntry()->isVisible());
+}
+
+TEST_F(IntersectionObserverV2Test, TableOcclusion) {
+ WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
+ SimRequest main_resource("https://example.com/", "text/html");
+ LoadURL("https://example.com/");
+ main_resource.Complete(R"HTML(
+ <style>
+ #target {
+ width: 100px;
+ height: 100px;
+ }
+ #table {
+ position: absolute;
+ left: 200px;
+ top: 0;
+ visibility: hidden;
+ border-collapse: collapse;
+ filter: drop-shadow(-200px 0 0 black);
+ }
+ #cell {
+ visibility: visible;
+ width: 100px;
+ height: 100px;
+ background: black;
+ }
+ </style>
+ <div id='target'></div>
+ <table id='table'>
+ <tr>
+ <td id='cell'></td>
+ </tr>
+ </table>
+ )HTML");
+ Compositor().BeginFrame();
+
+ IntersectionObserverInit* observer_init = IntersectionObserverInit::Create();
+ observer_init->setTrackVisibility(true);
+ observer_init->setDelay(100);
+ DummyExceptionStateForTesting exception_state;
+ TestIntersectionObserverDelegate* observer_delegate =
+ MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument());
+ IntersectionObserver* observer = IntersectionObserver::Create(
+ observer_init, *observer_delegate,
+ LocalFrameUkmAggregator::kJavascriptIntersectionObserver,
+ exception_state);
+ ASSERT_FALSE(exception_state.HadException());
+ Element* target = GetDocument().getElementById(AtomicString("target"));
+ ASSERT_TRUE(target);
+ observer->observe(target);
+
+ Compositor().BeginFrame();
+ test::RunPendingTasks();
+ ASSERT_FALSE(Compositor().NeedsBeginFrame());
+ EXPECT_EQ(observer_delegate->CallCount(), 1);
+ EXPECT_EQ(observer_delegate->EntryCount(), 1);
+ EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting());
+ // The target is occluded by the drop-shadow of the table.
+ EXPECT_FALSE(observer_delegate->LastEntry()->isVisible());
+}
+
TEST_F(IntersectionObserverV2Test, BasicOpacity) {
WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
SimRequest main_resource("https://example.com/", "text/html");
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