<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[286433] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/286433">286433</a></dd>
<dt>Author</dt> <dd>antti@apple.com</dd>
<dt>Date</dt> <dd>2021-12-02 09:36:50 -0800 (Thu, 02 Dec 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>[:has() pseudo-class] Invalidation in non-subject position
https://bugs.webkit.org/show_bug.cgi?id=233758

Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

* web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt: Added.
* web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html: Added.
* web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt: Added.
* web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html: Added.
* web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt: Added.
* web-platform-tests/css/selectors/invalidation/has-in-parent-position.html: Added.
* web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt: Added.
* web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html: Added.

Source/WebCore:

Invalidation for selectors like '.ancestor:has(.foo) #target'.

Tests: imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html
       imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html
       imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html
       imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html

* style/ChildChangeInvalidation.cpp:
(WebCore::Style::ChildChangeInvalidation::invalidateForChangedElement):
(WebCore::Style::needsTraversal):
(WebCore::Style::needsDescendantTraversal):
* style/RuleFeature.cpp:
(WebCore::Style::isSiblingOrSubject):
(WebCore::Style::isHasPseudoClassMatchElement):
(WebCore::Style::computeHasPseudoClassMatchElement):
(WebCore::Style::computeSubSelectorMatchElement):

Use new MatchElement::HasNonSubject as a catch-all for cases where :has() is in a non-subject position.
In the future this can be optimized better by computing both the :has match element and the overall
match element separately.

* style/RuleFeature.h:
* style/StyleInvalidator.cpp:
(WebCore::Style::Invalidator::invalidateStyleWithMatchElement):

Worst-case invalidation.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsimportedw3cChangeLog">trunk/LayoutTests/imported/w3c/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorestyleChildChangeInvalidationcpp">trunk/Source/WebCore/style/ChildChangeInvalidation.cpp</a></li>
<li><a href="#trunkSourceWebCorestyleRuleFeaturecpp">trunk/Source/WebCore/style/RuleFeature.cpp</a></li>
<li><a href="#trunkSourceWebCorestyleRuleFeatureh">trunk/Source/WebCore/style/RuleFeature.h</a></li>
<li><a href="#trunkSourceWebCorestyleStyleInvalidatorcpp">trunk/Source/WebCore/style/StyleInvalidator.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinadjacentpositionexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinadjacentpositionhtml">trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinancestorpositionexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinancestorpositionhtml">trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinparentpositionexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinparentpositionhtml">trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinsiblingpositionexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinsiblingpositionhtml">trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsimportedw3cChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/ChangeLog (286432 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/ChangeLog 2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/LayoutTests/imported/w3c/ChangeLog    2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2021-12-02  Antti Koivisto  <antti@apple.com>
+
+        [:has() pseudo-class] Invalidation in non-subject position
+        https://bugs.webkit.org/show_bug.cgi?id=233758
+
+        Reviewed by Simon Fraser.
+
+        * web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt: Added.
+        * web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html: Added.
+        * web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt: Added.
+        * web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html: Added.
+        * web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt: Added.
+        * web-platform-tests/css/selectors/invalidation/has-in-parent-position.html: Added.
+        * web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt: Added.
+        * web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html: Added.
+
</ins><span class="cx"> 2021-12-02  Andreu Botella  <andreu@andreubotella.com>
</span><span class="cx"> 
</span><span class="cx">         File inputs in non-multipart form submissions show up as string values in the formdata event
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinadjacentpositionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt (0 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt                               (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position-expected.txt  2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -0,0 +1,73 @@
</span><ins>+
+PASS Initial color
+PASS add .test to previous_sibling
+PASS remove .test from previous_sibling
+PASS add .test to previous_sibling_child
+PASS remove .test from previous_sibling_child
+PASS add .test to previous_sibling_descendant
+PASS remove .test from previous_sibling_descendant
+PASS add .test to subject
+PASS remove .test from subject
+PASS add .test to next_sibling
+PASS remove .test from next_sibling
+PASS add .test to next_sibling_child
+PASS remove .test from next_sibling_child
+PASS add .test to next_sibling_descendant
+PASS remove .test from next_sibling_descendant
+PASS insert element div.test before previous_sibling
+PASS remove element div.test before previous_sibling
+PASS insert element div.test before previous_sibling_child
+PASS remove element div.test before previous_sibling_child
+PASS insert element div.test before previous_sibling_descendant
+PASS remove element div.test before previous_sibling_descendant
+PASS insert element div.test before subject
+PASS remove element div.test before subject
+PASS insert element div.test before next_sibling
+PASS remove element div.test before next_sibling
+PASS insert element div.test before next_sibling_child
+PASS remove element div.test before next_sibling_child
+PASS insert element div.test before next_sibling_descendant
+PASS remove element div.test before next_sibling_descendant
+PASS insert element div.test after previous_sibling
+PASS remove element div.test after previous_sibling
+PASS insert element div.test after previous_sibling_child
+PASS remove element div.test after previous_sibling_child
+PASS insert element div.test after previous_sibling_descendant
+PASS remove element div.test after previous_sibling_descendant
+PASS insert element div.test after subject
+PASS remove element div.test after subject
+PASS insert element div.test after next_sibling
+PASS remove element div.test after next_sibling
+PASS insert element div.test after next_sibling_child
+PASS remove element div.test after next_sibling_child
+PASS insert element div.test after next_sibling_descendant
+PASS remove element div.test after next_sibling_descendant
+PASS insert tree div>div.test before previous_sibling
+PASS remove tree div>div.test before previous_sibling
+PASS insert tree div>div.test before previous_sibling_child
+PASS remove tree div>div.test before previous_sibling_child
+PASS insert tree div>div.test before previous_sibling_descendant
+PASS remove tree div>div.test before previous_sibling_descendant
+PASS insert tree div>div.test before subject
+PASS remove tree div>div.test before subject
+PASS insert tree div>div.test before next_sibling
+PASS remove tree div>div.test before next_sibling
+PASS insert tree div>div.test before next_sibling_child
+PASS remove tree div>div.test before next_sibling_child
+PASS insert tree div>div.test before next_sibling_descendant
+PASS remove tree div>div.test before next_sibling_descendant
+PASS insert tree div>div.test after previous_sibling
+PASS remove tree div>div.test after previous_sibling
+PASS insert tree div>div.test after previous_sibling_child
+PASS remove tree div>div.test after previous_sibling_child
+PASS insert tree div>div.test after previous_sibling_descendant
+PASS remove tree div>div.test after previous_sibling_descendant
+PASS insert tree div>div.test after subject
+PASS remove tree div>div.test after subject
+PASS insert tree div>div.test after next_sibling
+PASS remove tree div>div.test after next_sibling
+PASS insert tree div>div.test after next_sibling_child
+PASS remove tree div>div.test after next_sibling_child
+PASS insert tree div>div.test after next_sibling_descendant
+PASS remove tree div>div.test after next_sibling_descendant
+
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinadjacentpositionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html (0 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html                               (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html  2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -0,0 +1,149 @@
</span><ins>+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Selector Invalidation: :has() in adjacent position</title>
+<link rel="author" title="Antti Koivisto" href="mailto:antti@apple.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
+<style>
+div, main { color: grey }
+div:has(.test) + #subject { color: red }
+div:has(> .test) + #subject { color: green }
+div:has(~ .test) + #subject { color: yellow }
+div:has(+ .test) + #subject { color: blue }
+div:has(~ div .test) + #subject { color: purple }
+div:has(+ div .test) + #subject { color: pink }
+</style>
+
+<main id=main>
+    <div id=previous_sibling>
+        <div id=previous_sibling_child>
+            <div id=previous_sibling_descendant></div>
+        </div>
+    </div>
+    <div id=subject></div>
+    <div id=next_sibling>
+        <div id=next_sibling_child>
+            <div id=next_sibling_descendant></div>
+        </div>
+    </div>
+</main>
+
+<script>
+const grey = 'rgb(128, 128, 128)';
+const red = 'rgb(255, 0, 0)';
+const green = 'rgb(0, 128, 0)';
+const blue = 'rgb(0, 0, 255)';
+const yellow = 'rgb(255, 255, 0)';
+const purple = 'rgb(128, 0, 128)';
+const pink = 'rgb(255, 192, 203)';
+
+function testColor(test_name, color) {
+    test(function() {
+        assert_equals(getComputedStyle(subject).color, color);
+    }, test_name);
+}
+
+function testClassChange(element, expectedColor)
+{
+    element.classList.add('test');
+    testColor(`add .test to ${element.id}`, expectedColor);
+    element.classList.remove('test');
+    testColor(`remove .test from ${element.id}`, grey);
+}
+
+function testElementInsertionBefore(beforeElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    newElement.classList.add('test')
+
+    beforeElement.before(newElement);
+    testColor(`insert element div.test before ${beforeElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove element div.test before ${beforeElement.id}`, grey);
+}
+
+function testElementInsertionAfter(afterElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    newElement.classList.add('test')
+
+    afterElement.after(newElement);
+    testColor(`insert element div.test after ${afterElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove element div.test after ${afterElement.id}`, grey);
+}
+
+function testTreeInsertionBefore(beforeElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    const newChild = document.createElement('div');
+    newChild.classList.add('test');
+    newElement.appendChild(newChild);
+
+    beforeElement.before(newElement);
+    testColor(`insert tree div>div.test before ${beforeElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove tree div>div.test before ${beforeElement.id}`, grey);
+}
+
+function testTreeInsertionAfter(afterElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    const newChild = document.createElement('div');
+    newChild.classList.add('test');
+    newElement.appendChild(newChild);
+
+    afterElement.after(newElement);
+    testColor(`insert tree div>div.test after ${afterElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove tree div>div.test after ${afterElement.id}`, grey);
+}
+
+testColor('Initial color', grey);
+
+testClassChange(previous_sibling, grey);
+testClassChange(previous_sibling_child, green);
+testClassChange(previous_sibling_descendant, red);
+testClassChange(subject, blue);
+testClassChange(next_sibling, yellow);
+testClassChange(next_sibling_child, purple);
+testClassChange(next_sibling_descendant, purple);
+
+testElementInsertionBefore(previous_sibling, grey);
+testElementInsertionBefore(previous_sibling_child, green);
+testElementInsertionBefore(previous_sibling_descendant, red);
+testElementInsertionBefore(subject, grey);
+testElementInsertionBefore(next_sibling, yellow);
+testElementInsertionBefore(next_sibling_child, purple);
+testElementInsertionBefore(next_sibling_descendant, purple);
+
+testElementInsertionAfter(previous_sibling, grey);
+testElementInsertionAfter(previous_sibling_child, green);
+testElementInsertionAfter(previous_sibling_descendant, red);
+testElementInsertionAfter(subject, yellow);
+testElementInsertionAfter(next_sibling, yellow);
+testElementInsertionAfter(next_sibling_child, purple);
+testElementInsertionAfter(next_sibling_descendant, purple);
+
+testTreeInsertionBefore(previous_sibling, grey);
+testTreeInsertionBefore(previous_sibling_child, red);
+testTreeInsertionBefore(previous_sibling_descendant, red);
+testTreeInsertionBefore(subject, green);
+testTreeInsertionBefore(next_sibling, purple);
+testTreeInsertionBefore(next_sibling_child, purple);
+testTreeInsertionBefore(next_sibling_descendant, purple);
+
+testTreeInsertionAfter(previous_sibling, green);
+testTreeInsertionAfter(previous_sibling_child, red);
+testTreeInsertionAfter(previous_sibling_descendant, red);
+testTreeInsertionAfter(subject, purple);
+testTreeInsertionAfter(next_sibling, purple);
+testTreeInsertionAfter(next_sibling_child, purple);
+testTreeInsertionAfter(next_sibling_descendant, purple);
+
+</script>
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinancestorpositionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt (0 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt                               (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position-expected.txt  2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -0,0 +1,83 @@
</span><ins>+
+PASS Initial color
+PASS add .test to subject_ancestor
+PASS remove .test from subject_ancestor
+PASS add .test to subject_parent
+PASS remove .test from subject_parent
+PASS add .test to subject
+PASS remove .test from subject
+PASS add .test to subject_child
+PASS remove .test from subject_child
+PASS add .test to subject_descendant
+PASS remove .test from subject_descendant
+PASS add .test to next_sibling
+PASS remove .test from next_sibling
+PASS add .test to next_sibling_child
+PASS remove .test from next_sibling_child
+PASS add .test to next_sibling_descendant
+PASS remove .test from next_sibling_descendant
+PASS insert element div.test before subject_ancestor
+PASS remove element div.test before subject_ancestor
+PASS insert element div.test before subject_parent
+PASS remove element div.test before subject_parent
+PASS insert element div.test before subject
+PASS remove element div.test before subject
+PASS insert element div.test before subject_child
+PASS remove element div.test before subject_child
+PASS insert element div.test before subject_descendant
+PASS remove element div.test before subject_descendant
+PASS insert element div.test before next_sibling
+PASS remove element div.test before next_sibling
+PASS insert element div.test before next_sibling_child
+PASS remove element div.test before next_sibling_child
+PASS insert element div.test before next_sibling_descendant
+PASS remove element div.test before next_sibling_descendant
+PASS insert element div.test after subject_ancestor
+PASS remove element div.test after subject_ancestor
+PASS insert element div.test after subject_parent
+PASS remove element div.test after subject_parent
+PASS insert element div.test after subject
+PASS remove element div.test after subject
+PASS insert element div.test after subject_child
+PASS remove element div.test after subject_child
+PASS insert element div.test after subject_descendant
+PASS remove element div.test after subject_descendant
+PASS insert element div.test after next_sibling
+PASS remove element div.test after next_sibling
+PASS insert element div.test after next_sibling_child
+PASS remove element div.test after next_sibling_child
+PASS insert element div.test after next_sibling_descendant
+PASS remove element div.test after next_sibling_descendant
+PASS insert tree div>div.test before subject_ancestor
+PASS remove tree div>div.test before subject_ancestor
+PASS insert tree div>div.test before subject_parent
+PASS remove tree div>div.test before subject_parent
+PASS insert tree div>div.test before subject
+PASS remove tree div>div.test before subject
+PASS insert tree div>div.test before subject_child
+PASS remove tree div>div.test before subject_child
+PASS insert tree div>div.test before subject_descendant
+PASS remove tree div>div.test before subject_descendant
+PASS insert tree div>div.test before next_sibling
+PASS remove tree div>div.test before next_sibling
+PASS insert tree div>div.test before next_sibling_child
+PASS remove tree div>div.test before next_sibling_child
+PASS insert tree div>div.test before next_sibling_descendant
+PASS remove tree div>div.test before next_sibling_descendant
+PASS insert tree div>div.test after subject_ancestor
+PASS remove tree div>div.test after subject_ancestor
+PASS insert tree div>div.test after subject_parent
+PASS remove tree div>div.test after subject_parent
+PASS insert tree div>div.test after subject
+PASS remove tree div>div.test after subject
+PASS insert tree div>div.test after subject_child
+PASS remove tree div>div.test after subject_child
+PASS insert tree div>div.test after subject_descendant
+PASS remove tree div>div.test after subject_descendant
+PASS insert tree div>div.test after next_sibling
+PASS remove tree div>div.test after next_sibling
+PASS insert tree div>div.test after next_sibling_child
+PASS remove tree div>div.test after next_sibling_child
+PASS insert tree div>div.test after next_sibling_descendant
+PASS remove tree div>div.test after next_sibling_descendant
+
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinancestorpositionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html (0 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html                               (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html  2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -0,0 +1,157 @@
</span><ins>+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Selector Invalidation: :has() in ancestor position</title>
+<link rel="author" title="Antti Koivisto" href="mailto:antti@apple.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
+<style>
+div, main { color: grey }
+div:has(.test) #subject { color: red }
+div:has(> .test) #subject { color: green }
+div:has(~ .test) #subject { color: yellow }
+div:has(+ .test) #subject { color: blue }
+div:has(~ div .test) #subject { color: purple }
+div:has(+ div .test) #subject { color: pink }
+</style>
+
+<main id=main>
+    <div id=subject_ancestor>
+        <div id=subject_parent>
+            <div id=subject>
+                <div id=subject_child>
+                    <div id=subject_descendant></div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div id=next_sibling>
+        <div id=next_sibling_child>
+            <div id=next_sibling_descendant></div>
+        </div>
+    </div>
+</main>
+
+<script>
+const grey = 'rgb(128, 128, 128)';
+const red = 'rgb(255, 0, 0)';
+const green = 'rgb(0, 128, 0)';
+const blue = 'rgb(0, 0, 255)';
+const yellow = 'rgb(255, 255, 0)';
+const purple = 'rgb(128, 0, 128)';
+const pink = 'rgb(255, 192, 203)';
+
+function testColor(test_name, color) {
+    test(function() {
+        assert_equals(getComputedStyle(subject).color, color);
+    }, test_name);
+}
+
+function testClassChange(element, expectedColor)
+{
+    element.classList.add('test');
+    testColor(`add .test to ${element.id}`, expectedColor);
+    element.classList.remove('test');
+    testColor(`remove .test from ${element.id}`, grey);
+}
+
+function testElementInsertionBefore(beforeElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    newElement.classList.add('test')
+
+    beforeElement.before(newElement);
+    testColor(`insert element div.test before ${beforeElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove element div.test before ${beforeElement.id}`, grey);
+}
+
+function testElementInsertionAfter(afterElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    newElement.classList.add('test')
+
+    afterElement.after(newElement);
+    testColor(`insert element div.test after ${afterElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove element div.test after ${afterElement.id}`, grey);
+}
+
+function testTreeInsertionBefore(beforeElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    const newChild = document.createElement('div');
+    newChild.classList.add('test');
+    newElement.appendChild(newChild);
+
+    beforeElement.before(newElement);
+    testColor(`insert tree div>div.test before ${beforeElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove tree div>div.test before ${beforeElement.id}`, grey);
+}
+
+function testTreeInsertionAfter(afterElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    const newChild = document.createElement('div');
+    newChild.classList.add('test');
+    newElement.appendChild(newChild);
+
+    afterElement.after(newElement);
+    testColor(`insert tree div>div.test after ${afterElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove tree div>div.test after ${afterElement.id}`, grey);
+}
+
+testColor('Initial color', grey);
+
+testClassChange(subject_ancestor, grey);
+testClassChange(subject_parent, green);
+testClassChange(subject, green);
+testClassChange(subject_child, red);
+testClassChange(subject_descendant, red);
+testClassChange(next_sibling, blue);
+testClassChange(next_sibling_child, pink);
+testClassChange(next_sibling_descendant, pink);
+
+testElementInsertionBefore(subject_ancestor, grey);
+testElementInsertionBefore(subject_parent, green);
+testElementInsertionBefore(subject, green);
+testElementInsertionBefore(subject_child, red);
+testElementInsertionBefore(subject_descendant, red);
+testElementInsertionBefore(next_sibling, blue);
+testElementInsertionBefore(next_sibling_child, pink);
+testElementInsertionBefore(next_sibling_descendant, pink);
+
+testElementInsertionAfter(subject_ancestor, blue);
+testElementInsertionAfter(subject_parent, blue);
+testElementInsertionAfter(subject, green);
+testElementInsertionAfter(subject_child, red);
+testElementInsertionAfter(subject_descendant, red);
+testElementInsertionAfter(next_sibling, yellow);
+testElementInsertionAfter(next_sibling_child, pink);
+testElementInsertionAfter(next_sibling_descendant, pink);
+
+testTreeInsertionBefore(subject_ancestor, grey);
+testTreeInsertionBefore(subject_parent, red);
+testTreeInsertionBefore(subject, red);
+testTreeInsertionBefore(subject_child, red);
+testTreeInsertionBefore(subject_descendant, red);
+testTreeInsertionBefore(next_sibling, pink);
+testTreeInsertionBefore(next_sibling_child, pink);
+testTreeInsertionBefore(next_sibling_descendant, pink);
+
+testTreeInsertionAfter(subject_ancestor, pink);
+testTreeInsertionAfter(subject_parent, pink);
+testTreeInsertionAfter(subject, red);
+testTreeInsertionAfter(subject_child, red);
+testTreeInsertionAfter(subject_descendant, red);
+testTreeInsertionAfter(next_sibling, purple);
+testTreeInsertionAfter(next_sibling_child, pink);
+testTreeInsertionAfter(next_sibling_descendant, pink);
+
+</script>
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinparentpositionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt (0 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt                         (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position-expected.txt    2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+
+PASS Initial color
+PASS add .test to subject_ancestor
+PASS remove .test from subject_ancestor
+PASS add .test to subject_parent
+PASS remove .test from subject_parent
+PASS add .test to subject
+PASS remove .test from subject
+PASS add .test to subject_child
+PASS remove .test from subject_child
+PASS add .test to subject_descendant
+PASS remove .test from subject_descendant
+PASS insert element div.test before subject_ancestor
+PASS remove element div.test before subject_ancestor
+PASS insert element div.test before subject_parent
+PASS remove element div.test before subject_parent
+PASS insert element div.test before subject
+PASS remove element div.test before subject
+PASS insert element div.test before subject_child
+PASS remove element div.test before subject_child
+PASS insert element div.test before subject_descendant
+PASS remove element div.test before subject_descendant
+PASS insert element div.test after subject_ancestor
+PASS remove element div.test after subject_ancestor
+PASS insert element div.test after subject_parent
+PASS remove element div.test after subject_parent
+PASS insert element div.test after subject
+PASS remove element div.test after subject
+PASS insert element div.test after subject_child
+PASS remove element div.test after subject_child
+PASS insert element div.test after subject_descendant
+PASS remove element div.test after subject_descendant
+PASS insert tree div>div.test before subject_ancestor
+PASS remove tree div>div.test before subject_ancestor
+PASS insert tree div>div.test before subject_parent
+PASS remove tree div>div.test before subject_parent
+PASS insert tree div>div.test before subject
+PASS remove tree div>div.test before subject
+PASS insert tree div>div.test before subject_child
+PASS remove tree div>div.test before subject_child
+PASS insert tree div>div.test before subject_descendant
+PASS remove tree div>div.test before subject_descendant
+PASS insert tree div>div.test after subject_ancestor
+PASS remove tree div>div.test after subject_ancestor
+PASS insert tree div>div.test after subject_parent
+PASS remove tree div>div.test after subject_parent
+PASS insert tree div>div.test after subject
+PASS remove tree div>div.test after subject
+PASS insert tree div>div.test after subject_child
+PASS remove tree div>div.test after subject_child
+PASS insert tree div>div.test after subject_descendant
+PASS remove tree div>div.test after subject_descendant
+
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinparentpositionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html (0 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html                         (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html    2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -0,0 +1,137 @@
</span><ins>+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Selector Invalidation: :has() in parent position</title>
+<link rel="author" title="Antti Koivisto" href="mailto:antti@apple.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
+<style>
+div, main { color: grey }
+div:has(.test) > #subject { color: red }
+div:has(> .test) > #subject { color: green }
+div:has(~ .test) >  #subject { color: yellow }
+div:has(+ .test) > #subject { color: blue }
+div:has(~ div .test) > #subject { color: purple }
+div:has(+ div .test) > #subject { color: pink }
+</style>
+
+<main id=main>
+    <div id=subject_ancestor>
+        <div id=subject_parent>
+            <div id=subject>
+                <div id=subject_child>
+                    <div id=subject_descendant></div>
+                </div>
+            </div>
+        </div>
+    </div>
+</main>
+
+<script>
+const grey = 'rgb(128, 128, 128)';
+const red = 'rgb(255, 0, 0)';
+const green = 'rgb(0, 128, 0)';
+const blue = 'rgb(0, 0, 255)';
+const yellow = 'rgb(255, 255, 0)';
+const purple = 'rgb(128, 0, 128)';
+const pink = 'rgb(255, 192, 203)';
+
+function testColor(test_name, color) {
+    test(function() {
+        assert_equals(getComputedStyle(subject).color, color);
+    }, test_name);
+}
+
+function testClassChange(element, expectedColor)
+{
+    element.classList.add('test');
+    testColor(`add .test to ${element.id}`, expectedColor);
+    element.classList.remove('test');
+    testColor(`remove .test from ${element.id}`, grey);
+}
+
+function testElementInsertionBefore(beforeElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    newElement.classList.add('test')
+
+    beforeElement.before(newElement);
+    testColor(`insert element div.test before ${beforeElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove element div.test before ${beforeElement.id}`, grey);
+}
+
+function testElementInsertionAfter(afterElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    newElement.classList.add('test')
+
+    afterElement.after(newElement);
+    testColor(`insert element div.test after ${afterElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove element div.test after ${afterElement.id}`, grey);
+}
+
+function testTreeInsertionBefore(beforeElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    const newChild = document.createElement('div');
+    newChild.classList.add('test');
+    newElement.appendChild(newChild);
+
+    beforeElement.before(newElement);
+    testColor(`insert tree div>div.test before ${beforeElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove tree div>div.test before ${beforeElement.id}`, grey);
+}
+
+function testTreeInsertionAfter(afterElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    const newChild = document.createElement('div');
+    newChild.classList.add('test');
+    newElement.appendChild(newChild);
+
+    afterElement.after(newElement);
+    testColor(`insert tree div>div.test after ${afterElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove tree div>div.test after ${afterElement.id}`, grey);
+}
+
+testColor('Initial color', grey);
+
+testClassChange(subject_ancestor, grey);
+testClassChange(subject_parent, grey);
+testClassChange(subject, green);
+testClassChange(subject_child, red);
+testClassChange(subject_descendant, red);
+
+testElementInsertionBefore(subject_ancestor, grey);
+testElementInsertionBefore(subject_parent, grey);
+testElementInsertionBefore(subject, green);
+testElementInsertionBefore(subject_child, red);
+testElementInsertionBefore(subject_descendant, red);
+
+testElementInsertionAfter(subject_ancestor, grey);
+testElementInsertionAfter(subject_parent, blue);
+testElementInsertionAfter(subject, green);
+testElementInsertionAfter(subject_child, red);
+testElementInsertionAfter(subject_descendant, red);
+
+testTreeInsertionBefore(subject_ancestor, grey);
+testTreeInsertionBefore(subject_parent, grey);
+testTreeInsertionBefore(subject, red);
+testTreeInsertionBefore(subject_child, red);
+testTreeInsertionBefore(subject_descendant, red);
+
+testTreeInsertionAfter(subject_ancestor, grey);
+testTreeInsertionAfter(subject_parent, pink);
+testTreeInsertionAfter(subject, red);
+testTreeInsertionAfter(subject_child, red);
+testTreeInsertionAfter(subject_descendant, red);
+
+</script>
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinsiblingpositionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt (0 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt                                (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position-expected.txt   2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -0,0 +1,73 @@
</span><ins>+
+PASS Initial color
+PASS add .test to previous_sibling
+PASS remove .test from previous_sibling
+PASS add .test to previous_sibling_child
+PASS remove .test from previous_sibling_child
+PASS add .test to previous_sibling_descendant
+PASS remove .test from previous_sibling_descendant
+PASS add .test to subject
+PASS remove .test from subject
+PASS add .test to next_sibling
+PASS remove .test from next_sibling
+PASS add .test to next_sibling_child
+PASS remove .test from next_sibling_child
+PASS add .test to next_sibling_descendant
+PASS remove .test from next_sibling_descendant
+PASS insert element div.test before previous_sibling
+PASS remove element div.test before previous_sibling
+PASS insert element div.test before previous_sibling_child
+PASS remove element div.test before previous_sibling_child
+PASS insert element div.test before previous_sibling_descendant
+PASS remove element div.test before previous_sibling_descendant
+PASS insert element div.test before subject
+PASS remove element div.test before subject
+PASS insert element div.test before next_sibling
+PASS remove element div.test before next_sibling
+PASS insert element div.test before next_sibling_child
+PASS remove element div.test before next_sibling_child
+PASS insert element div.test before next_sibling_descendant
+PASS remove element div.test before next_sibling_descendant
+PASS insert element div.test after previous_sibling
+PASS remove element div.test after previous_sibling
+PASS insert element div.test after previous_sibling_child
+PASS remove element div.test after previous_sibling_child
+PASS insert element div.test after previous_sibling_descendant
+PASS remove element div.test after previous_sibling_descendant
+PASS insert element div.test after subject
+PASS remove element div.test after subject
+PASS insert element div.test after next_sibling
+PASS remove element div.test after next_sibling
+PASS insert element div.test after next_sibling_child
+PASS remove element div.test after next_sibling_child
+PASS insert element div.test after next_sibling_descendant
+PASS remove element div.test after next_sibling_descendant
+PASS insert tree div>div.test before previous_sibling
+PASS remove tree div>div.test before previous_sibling
+PASS insert tree div>div.test before previous_sibling_child
+PASS remove tree div>div.test before previous_sibling_child
+PASS insert tree div>div.test before previous_sibling_descendant
+PASS remove tree div>div.test before previous_sibling_descendant
+PASS insert tree div>div.test before subject
+PASS remove tree div>div.test before subject
+PASS insert tree div>div.test before next_sibling
+PASS remove tree div>div.test before next_sibling
+PASS insert tree div>div.test before next_sibling_child
+PASS remove tree div>div.test before next_sibling_child
+PASS insert tree div>div.test before next_sibling_descendant
+PASS remove tree div>div.test before next_sibling_descendant
+PASS insert tree div>div.test after previous_sibling
+PASS remove tree div>div.test after previous_sibling
+PASS insert tree div>div.test after previous_sibling_child
+PASS remove tree div>div.test after previous_sibling_child
+PASS insert tree div>div.test after previous_sibling_descendant
+PASS remove tree div>div.test after previous_sibling_descendant
+PASS insert tree div>div.test after subject
+PASS remove tree div>div.test after subject
+PASS insert tree div>div.test after next_sibling
+PASS remove tree div>div.test after next_sibling
+PASS insert tree div>div.test after next_sibling_child
+PASS remove tree div>div.test after next_sibling_child
+PASS insert tree div>div.test after next_sibling_descendant
+PASS remove tree div>div.test after next_sibling_descendant
+
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestscssselectorsinvalidationhasinsiblingpositionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html (0 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html                                (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html   2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -0,0 +1,149 @@
</span><ins>+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Selector Invalidation: :has() in sibling position</title>
+<link rel="author" title="Antti Koivisto" href="mailto:antti@apple.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
+<style>
+div, main { color: grey }
+div:has(.test) ~ #subject { color: red }
+div:has(> .test) ~ #subject { color: green }
+div:has(~ .test) ~ #subject { color: yellow }
+div:has(+ .test) ~ #subject { color: blue }
+div:has(~ div .test) ~ #subject { color: purple }
+div:has(+ div .test) ~ #subject { color: pink }
+</style>
+
+<main id=main>
+    <div id=previous_sibling>
+        <div id=previous_sibling_child>
+            <div id=previous_sibling_descendant></div>
+        </div>
+    </div>
+    <div id=subject></div>
+    <div id=next_sibling>
+        <div id=next_sibling_child>
+            <div id=next_sibling_descendant></div>
+        </div>
+    </div>
+</main>
+
+<script>
+const grey = 'rgb(128, 128, 128)';
+const red = 'rgb(255, 0, 0)';
+const green = 'rgb(0, 128, 0)';
+const blue = 'rgb(0, 0, 255)';
+const yellow = 'rgb(255, 255, 0)';
+const purple = 'rgb(128, 0, 128)';
+const pink = 'rgb(255, 192, 203)';
+
+function testColor(test_name, color) {
+    test(function() {
+        assert_equals(getComputedStyle(subject).color, color);
+    }, test_name);
+}
+
+function testClassChange(element, expectedColor)
+{
+    element.classList.add('test');
+    testColor(`add .test to ${element.id}`, expectedColor);
+    element.classList.remove('test');
+    testColor(`remove .test from ${element.id}`, grey);
+}
+
+function testElementInsertionBefore(beforeElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    newElement.classList.add('test')
+
+    beforeElement.before(newElement);
+    testColor(`insert element div.test before ${beforeElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove element div.test before ${beforeElement.id}`, grey);
+}
+
+function testElementInsertionAfter(afterElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    newElement.classList.add('test')
+
+    afterElement.after(newElement);
+    testColor(`insert element div.test after ${afterElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove element div.test after ${afterElement.id}`, grey);
+}
+
+function testTreeInsertionBefore(beforeElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    const newChild = document.createElement('div');
+    newChild.classList.add('test');
+    newElement.appendChild(newChild);
+
+    beforeElement.before(newElement);
+    testColor(`insert tree div>div.test before ${beforeElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove tree div>div.test before ${beforeElement.id}`, grey);
+}
+
+function testTreeInsertionAfter(afterElement, expectedColor)
+{
+    const newElement = document.createElement('div');
+    const newChild = document.createElement('div');
+    newChild.classList.add('test');
+    newElement.appendChild(newChild);
+
+    afterElement.after(newElement);
+    testColor(`insert tree div>div.test after ${afterElement.id}`, expectedColor);
+
+    newElement.remove();
+    testColor(`remove tree div>div.test after ${afterElement.id}`, grey);
+}
+
+testColor('Initial color', grey);
+
+testClassChange(previous_sibling, grey);
+testClassChange(previous_sibling_child, green);
+testClassChange(previous_sibling_descendant, red);
+testClassChange(subject, blue);
+testClassChange(next_sibling, yellow);
+testClassChange(next_sibling_child, purple);
+testClassChange(next_sibling_descendant, purple);
+
+testElementInsertionBefore(previous_sibling, grey);
+testElementInsertionBefore(previous_sibling_child, green);
+testElementInsertionBefore(previous_sibling_descendant, red);
+testElementInsertionBefore(subject, blue);
+testElementInsertionBefore(next_sibling, yellow);
+testElementInsertionBefore(next_sibling_child, purple);
+testElementInsertionBefore(next_sibling_descendant, purple);
+
+testElementInsertionAfter(previous_sibling, blue);
+testElementInsertionAfter(previous_sibling_child, green);
+testElementInsertionAfter(previous_sibling_descendant, red);
+testElementInsertionAfter(subject, yellow);
+testElementInsertionAfter(next_sibling, yellow);
+testElementInsertionAfter(next_sibling_child, purple);
+testElementInsertionAfter(next_sibling_descendant, purple);
+
+testTreeInsertionBefore(previous_sibling, green);
+testTreeInsertionBefore(previous_sibling_child, red);
+testTreeInsertionBefore(previous_sibling_descendant, red);
+testTreeInsertionBefore(subject, pink);
+testTreeInsertionBefore(next_sibling, purple);
+testTreeInsertionBefore(next_sibling_child, purple);
+testTreeInsertionBefore(next_sibling_descendant, purple);
+
+testTreeInsertionAfter(previous_sibling, pink);
+testTreeInsertionAfter(previous_sibling_child, red);
+testTreeInsertionAfter(previous_sibling_descendant, red);
+testTreeInsertionAfter(subject, purple);
+testTreeInsertionAfter(next_sibling, purple);
+testTreeInsertionAfter(next_sibling_child, purple);
+testTreeInsertionAfter(next_sibling_descendant, purple);
+
+</script>
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (286432 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/ChangeLog      2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -1,3 +1,37 @@
</span><ins>+2021-12-02  Antti Koivisto  <antti@apple.com>
+
+        [:has() pseudo-class] Invalidation in non-subject position
+        https://bugs.webkit.org/show_bug.cgi?id=233758
+
+        Reviewed by Simon Fraser.
+
+        Invalidation for selectors like '.ancestor:has(.foo) #target'.
+
+        Tests: imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-adjacent-position.html
+               imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-ancestor-position.html
+               imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-parent-position.html
+               imported/w3c/web-platform-tests/css/selectors/invalidation/has-in-sibling-position.html
+
+        * style/ChildChangeInvalidation.cpp:
+        (WebCore::Style::ChildChangeInvalidation::invalidateForChangedElement):
+        (WebCore::Style::needsTraversal):
+        (WebCore::Style::needsDescendantTraversal):
+        * style/RuleFeature.cpp:
+        (WebCore::Style::isSiblingOrSubject):
+        (WebCore::Style::isHasPseudoClassMatchElement):
+        (WebCore::Style::computeHasPseudoClassMatchElement):
+        (WebCore::Style::computeSubSelectorMatchElement):
+
+        Use new MatchElement::HasNonSubject as a catch-all for cases where :has() is in a non-subject position.
+        In the future this can be optimized better by computing both the :has match element and the overall
+        match element separately.
+
+        * style/RuleFeature.h:
+        * style/StyleInvalidator.cpp:
+        (WebCore::Style::Invalidator::invalidateStyleWithMatchElement):
+
+        Worst-case invalidation.
+
</ins><span class="cx"> 2021-12-02  Andreu Botella  <andreu@andreubotella.com>
</span><span class="cx"> 
</span><span class="cx">         File inputs in non-multipart form submissions show up as string values in the formdata event
</span></span></pre></div>
<a id="trunkSourceWebCorestyleChildChangeInvalidationcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/style/ChildChangeInvalidation.cpp (286432 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/style/ChildChangeInvalidation.cpp   2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/style/ChildChangeInvalidation.cpp      2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -75,8 +75,11 @@
</span><span class="cx">         for (auto& invalidationRuleSet : *invalidationRuleSets) {
</span><span class="cx">             if (!isHasPseudoClassMatchElement(invalidationRuleSet.matchElement))
</span><span class="cx">                 continue;
</span><del>-            if (isDescendant && invalidationRuleSet.matchElement != MatchElement::HasDescendant)
-                continue;
</del><ins>+            if (isDescendant) {
+                // Elements deeper in the tree can't affect anything except when :has() selector uses descendant combinator.
+                if (invalidationRuleSet.matchElement != MatchElement::HasDescendant && invalidationRuleSet.matchElement != MatchElement::HasNonSubject)
+                    continue;
+            }
</ins><span class="cx">             Invalidator::addToMatchElementRuleSets(matchElementRuleSets, invalidationRuleSet);
</span><span class="cx">         }
</span><span class="cx">     };
</span><span class="lines">@@ -113,11 +116,15 @@
</span><span class="cx">         return true;
</span><span class="cx">     if (features.usesMatchElement(MatchElement::HasSiblingDescendant))
</span><span class="cx">         return true;
</span><ins>+    if (features.usesMatchElement(MatchElement::HasNonSubject))
+        return true;
</ins><span class="cx">     return features.usesMatchElement(MatchElement::HasSibling) && childChange.previousSiblingElement;
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> static bool needsDescendantTraversal(const RuleFeatureSet& features)
</span><span class="cx"> {
</span><ins>+    if (features.usesMatchElement(MatchElement::HasNonSubject))
+        return true;
</ins><span class="cx">     return features.usesMatchElement(MatchElement::HasDescendant) || features.usesMatchElement(MatchElement::HasSiblingDescendant);
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorestyleRuleFeaturecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/style/RuleFeature.cpp (286432 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/style/RuleFeature.cpp       2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/style/RuleFeature.cpp  2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -53,6 +53,7 @@
</span><span class="cx">     case MatchElement::HasChild:
</span><span class="cx">     case MatchElement::HasDescendant:
</span><span class="cx">     case MatchElement::HasSiblingDescendant:
</span><ins>+    case MatchElement::HasNonSubject:
</ins><span class="cx">         return false;
</span><span class="cx">     }
</span><span class="cx">     ASSERT_NOT_REACHED();
</span><span class="lines">@@ -66,6 +67,7 @@
</span><span class="cx">     case MatchElement::HasDescendant:
</span><span class="cx">     case MatchElement::HasSibling:
</span><span class="cx">     case MatchElement::HasSiblingDescendant:
</span><ins>+    case MatchElement::HasNonSubject:
</ins><span class="cx">         return true;
</span><span class="cx">     default:
</span><span class="cx">         return false;
</span><span class="lines">@@ -157,6 +159,7 @@
</span><span class="cx">     case MatchElement::HasDescendant:
</span><span class="cx">     case MatchElement::HasSibling:
</span><span class="cx">     case MatchElement::HasSiblingDescendant:
</span><ins>+    case MatchElement::HasNonSubject:
</ins><span class="cx">     case MatchElement::Host:
</span><span class="cx">         ASSERT_NOT_REACHED();
</span><span class="cx">         break;
</span><span class="lines">@@ -176,8 +179,11 @@
</span><span class="cx">         if (type == CSSSelector::PseudoClassHost)
</span><span class="cx">             return MatchElement::Host;
</span><span class="cx"> 
</span><del>-        if (type == CSSSelector::PseudoClassHas)
</del><ins>+        if (type == CSSSelector::PseudoClassHas) {
+            if (matchElement != MatchElement::Subject)
+                return MatchElement::HasNonSubject;
</ins><span class="cx">             return computeHasPseudoClassMatchElement(childSelector);
</span><ins>+        }
</ins><span class="cx">     }
</span><span class="cx">     if (selector.match() == CSSSelector::PseudoElement) {
</span><span class="cx">         // Similarly for ::slotted().
</span></span></pre></div>
<a id="trunkSourceWebCorestyleRuleFeatureh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/style/RuleFeature.h (286432 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/style/RuleFeature.h 2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/style/RuleFeature.h    2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx"> 
</span><span class="cx"> class RuleData;
</span><span class="cx"> 
</span><ins>+// FIXME: Has* values should be separated so we could describe both the :has() argument and its position in the selector.
</ins><span class="cx"> enum class MatchElement : uint8_t {
</span><span class="cx">     Subject,
</span><span class="cx">     Parent,
</span><span class="lines">@@ -49,6 +50,7 @@
</span><span class="cx">     HasDescendant,
</span><span class="cx">     HasSibling,
</span><span class="cx">     HasSiblingDescendant,
</span><ins>+    HasNonSubject, // FIXME: This is a catch-all for cases where :has() is in non-subject position.
</ins><span class="cx">     Host
</span><span class="cx"> };
</span><span class="cx"> constexpr unsigned matchElementCount = static_cast<unsigned>(MatchElement::Host) + 1;
</span></span></pre></div>
<a id="trunkSourceWebCorestyleStyleInvalidatorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/style/StyleInvalidator.cpp (286432 => 286433)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/style/StyleInvalidator.cpp  2021-12-02 16:44:31 UTC (rev 286432)
+++ trunk/Source/WebCore/style/StyleInvalidator.cpp     2021-12-02 17:36:50 UTC (rev 286433)
</span><span class="lines">@@ -346,6 +346,11 @@
</span><span class="cx">         }
</span><span class="cx">         break;
</span><span class="cx">     }
</span><ins>+    case MatchElement::HasNonSubject: {
+        SelectorMatchingState selectorMatchingState;
+        invalidateStyleForDescendants(*element.document().documentElement(), &selectorMatchingState);
+        break;
+    }
</ins><span class="cx">     case MatchElement::Host:
</span><span class="cx">         invalidateInShadowTreeIfNeeded(element);
</span><span class="cx">         break;
</span></span></pre>
</div>
</div>

</body>
</html>