Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUI misrepresentation in PermissionElement
DescriptionUI misrepresentation in PermissionElement
ComponentPermissionElement
Bug ClassLogic Error
Tracker514010111
Fix commit3a9b444e1f27 (chromium/src) +61/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/core/html/html_capability_element_base.cc
modified
TEST_F
third_party/blink/renderer/core/html/html_capability_element_base_test.cc
modified

Files Changed

  • third_party/blink/renderer/core/html/html_capability_element_base.cc
  • third_party/blink/renderer/core/html/html_capability_element_base.h
  • third_party/blink/renderer/core/html/html_capability_element_base_test.cc
From 3a9b444e1f27bbf25c28289f93de51e443678f5a Mon Sep 17 00:00:00 2001
From: Stefan Zager <[email protected]>
Date: Fri, 10 Jul 2026 12:49:50 -0700
Subject: [PATCH] [IOv2] Delete visibility observer when pepc element removed from DOM

Bug: 514010111
Change-Id: I6b5f58b149505ed315bb91bbd5c4de9110967971
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8067916
Reviewed-by: Philip Rogers <[email protected]>
Commit-Queue: Stefan Zager <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1660470}
---

diff --git a/third_party/blink/renderer/core/html/html_capability_element_base.cc b/third_party/blink/renderer/core/html/html_capability_element_base.cc
index ba61a89..69460a67c 100644
--- a/third_party/blink/renderer/core/html/html_capability_element_base.cc
+++ b/third_party/blink/renderer/core/html/html_capability_element_base.cc
@@ -360,6 +360,10 @@
   if (auto* view = GetDocument().View()) {
     view->UnregisterFromLifecycleNotifications(this);
   }
+  if (!performing_reattach && intersection_observer_) {
+    intersection_observer_->disconnect();
+    intersection_observer_ = nullptr;
+  }
 }
 
 void HTMLCapabilityElementBase::RemovedFrom(ContainerNode& insertion_point) {
diff --git a/third_party/blink/renderer/core/html/html_capability_element_base.h b/third_party/blink/renderer/core/html/html_capability_element_base.h
index 5e01e30..0c71c7f 100644
--- a/third_party/blink/renderer/core/html/html_capability_element_base.h
+++ b/third_party/blink/renderer/core/html/html_capability_element_base.h
@@ -211,6 +211,8 @@
   FRIEND_TEST_ALL_PREFIXES(HTMLCapabilityElementBaseIntersectionTest,
                            IntersectionChanged);
   FRIEND_TEST_ALL_PREFIXES(HTMLCapabilityElementBaseIntersectionTest,
+                           MovePEPCFromIframeAndDestroyIframe);
+  FRIEND_TEST_ALL_PREFIXES(HTMLCapabilityElementBaseIntersectionTest,
                            IntersectionChangedDisableEnableDisable);
   FRIEND_TEST_ALL_PREFIXES(HTMLCapabilityElementBaseIntersectionTest,
                            ContainerDivRotates);
diff --git a/third_party/blink/renderer/core/html/html_capability_element_base_test.cc b/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
index 2886eaf8..bcaca0a6 100644
--- a/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
+++ b/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
@@ -296,7 +296,6 @@
     }
 
     GetDocument().body()->AppendChild(permission_element);
-    GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
     GetDocument().View()->UpdateAllLifecyclePhasesForTest();
     return permission_element;
   }
@@ -1637,6 +1636,61 @@
   }
 };
 
+TEST_F(HTMLCapabilityElementBaseIntersectionTest,
+       MovePEPCFromIframeAndDestroyIframe) {
+  GetDocument().GetSettings()->SetDefaultFontSize(12);
+  SimRequest main_resource("https://example.test/", "text/html");
+  SimRequest iframe_resource("https://example.test/foo.html", "text/html");
+  LoadURL("https://example.test/");
+  main_resource.Complete(R"HTML(
+    <body>
+      <style>
+        #occluder {
+          position:absolute;
+          left:0;
+          top:0;
+          width:100px;
+          height:100px;
+          background:black;
+        }
+        #iframe {
+          position:absolute;
+          left:0;
+          top:150px;
+        }
+      </style>
+      <div id="occluder"></div>
+      <iframe id="iframe" src='https://example.test/foo.html' allow="camera *">
+      </iframe>
+    </body>
+  )HTML");
+  iframe_resource.Complete(R"HTML(
+    <!DOCTYPE html><usermedia id='camera' type='camera'></usermedia>
+  )HTML");
+
+  Compositor().BeginFrame();
+
+  auto* subframe = To<WebLocalFrameImpl>(MainFrame().FirstChild())->GetFrame();
+  auto* permission_element = static_cast<HTMLCapabilityElementBase*>(
+      subframe->GetDocument()->getElementById(AtomicString("camera")));
+  DeferredChecker checker1(permission_element);
+  checker1.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                          /*expected_enabled*/ true);
+
+  // Move PEPC to main document and remove iframe
+  auto& main_document = *MainFrame().GetFrame()->GetDocument();
+  main_document.body()->AppendChild(permission_element);
+  main_document.getElementById(AtomicString("iframe"))->remove();
+
+  Compositor().BeginFrame();
+
+  // Verify it is occluded and clicking is disabled.
+  WaitForIntersectionVisibilityChanged(
+      permission_element,
+      HTMLCapabilityElementBase::IntersectionVisibility::kOccludedOrDistorted);
+  EXPECT_FALSE(permission_element->IsClickingEnabled());
+}
+
 TEST_F(HTMLCapabilityElementBaseIntersectionTest, IntersectionChanged) {
   GetDocument().GetSettings()->SetDefaultFontSize(12);
   SimRequest main_resource("https://example.test/", "text/html");
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/renderer/core/html/html_capability_element_base_test.cc b/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
index 2886eaf8..bcaca0a6 100644
--- a/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
+++ b/third_party/blink/renderer/core/html/html_capability_element_base_test.cc
@@ -296,7 +296,6 @@
     }
 
     GetDocument().body()->AppendChild(permission_element);
-    GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
     GetDocument().View()->UpdateAllLifecyclePhasesForTest();
     return permission_element;
   }
@@ -1637,6 +1636,61 @@
   }
 };
 
+TEST_F(HTMLCapabilityElementBaseIntersectionTest,
+       MovePEPCFromIframeAndDestroyIframe) {
+  GetDocument().GetSettings()->SetDefaultFontSize(12);
+  SimRequest main_resource("https://example.test/", "text/html");
+  SimRequest iframe_resource("https://example.test/foo.html", "text/html");
+  LoadURL("https://example.test/");
+  main_resource.Complete(R"HTML(
+    <body>
+      <style>
+        #occluder {
+          position:absolute;
+          left:0;
+          top:0;
+          width:100px;
+          height:100px;
+          background:black;
+        }
+        #iframe {
+          position:absolute;
+          left:0;
+          top:150px;
+        }
+      </style>
+      <div id="occluder"></div>
+      <iframe id="iframe" src='https://example.test/foo.html' allow="camera *">
+      </iframe>
+    </body>
+  )HTML");
+  iframe_resource.Complete(R"HTML(
+    <!DOCTYPE html><usermedia id='camera' type='camera'></usermedia>
+  )HTML");
+
+  Compositor().BeginFrame();
+
+  auto* subframe = To<WebLocalFrameImpl>(MainFrame().FirstChild())->GetFrame();
+  auto* permission_element = static_cast<HTMLCapabilityElementBase*>(
+      subframe->GetDocument()->getElementById(AtomicString("camera")));
+  DeferredChecker checker1(permission_element);
+  checker1.CheckClickingEnabledAfterDelay(kDefaultTimeout,
+                                          /*expected_enabled*/ true);
+
+  // Move PEPC to main document and remove iframe
+  auto& main_document = *MainFrame().GetFrame()->GetDocument();
+  main_document.body()->AppendChild(permission_element);
+  main_document.getElementById(AtomicString("iframe"))->remove();
+
+  Compositor().BeginFrame();
+
+  // Verify it is occluded and clicking is disabled.
+  WaitForIntersectionVisibilityChanged(
+      permission_element,
+      HTMLCapabilityElementBase::IntersectionVisibility::kOccludedOrDistorted);
+  EXPECT_FALSE(permission_element->IsClickingEnabled());
+}
+
 TEST_F(HTMLCapabilityElementBaseIntersectionTest, IntersectionChanged) {
   GetDocument().GetSettings()->SetDefaultFontSize(12);
   SimRequest main_resource("https://example.test/", "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.