Bug 1739391 [wpt PR 31504] - Support style invalidation of ':has()' in rightmost compound, a=testonly

Automatic update from web-platform-tests
Support style invalidation of ':has()' in rightmost compound

This is the initial CL of supporting style invalidation with ':has()'.

To minimize complexity, we will focus on the following limited usage.
- Supports ':has()' in a rightmost, top-level compound
- Supports ':has()' argument starts with child or descendant combinator
- Only allow attribute or elemental selectors in argument

So the following will be ignored for now.
- Ignore ':has()' in a non-rightmost compound
- Ignore ':has()' in a logical combinations
- Ignore ':has()' argument starts with '~' or '+'
- Ignore all pseudos in ':has()' argument

To support this case, for a change on an element, we need to invalidate
ancestors.

Invalidate every ancestors for every mutations on every element is very
bad for performance.

So we need a way to skip these.
- irrelevant element
- irrelevant ancestor
- irrelevant mutation

To skip the 'irrelevant element' and 'irrelevant ancestor', this CL
introduces two flags in the ComputedStyle.
- AncestorsAffectedByHas   : Indicate that the element has an ancestor
                             affected by ':has()' state change
- AffectedByHas  : Indicate that the element can be affected by ':has()'
                   state change

During style recalculation, 'AffectedByHas' and 'AncestorAffectedByHas'
flag will be set when SelectorChecker tries to match any ':has()'
selector on an element. The 'AncestorAffectedByHas' flag is inherited
by children of the element.

<style> .a:has(.b) { color: red }</style>
<div>
  <div class="a">    <!-- AffectedByHas, AncestorAffectedByHas  -->
    <div>            <!-- AncestorAffectedByHas  -->
      <div>          <!-- AncestorAffectedByHas  -->
      </div>
    </div>
  </div>
  <div>
    <div>
    </div>
  </div>
</div>

Only for a mutation on the element with 'AncestorAffectedByHas', style
engine will walk up ancestors. And it invalidates style only when an
ancestor is 'AffectedByHas'.

Still we have invalidation for irrelevant mutation and invalidation on
irrelevant ancestors. We will handle those by separate CLs.

Bug: 669058
Change-Id: Ia7e607e01f9a070c0d69d89679b99efa0a104797
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3260677
Commit-Queue: Byungwoo Lee <blee@igalia.com>
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#940274}

--

wpt-commits: e29b779bac49b4976b7fad8c10b8c9e762018cf1
wpt-pr: 31504
This commit is contained in:
Byungwoo Lee 2021-11-13 10:07:58 +00:00 коммит произвёл moz-wptsync-bot
Родитель 5756622db3
Коммит e8b96dbbd6
1 изменённых файлов: 84 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,84 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Selectors Invalidation: :has() invalidation basic</title>
<link rel="author" title="Byungwoo Lee" href="mailto:blee@igalia.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 }
.subject:has(> .child) { color: red }
.subject:has(.descendant) { color: green }
.subject:has([attrname=descendant]) { color: blue }
.subject:has(#div_descendant) { color: yellow }
.subject:has(descendant) { color: yellowgreen }
</style>
<main id=main>
<div id=div_subject class="subject">
<div id=div_child>
<div id=div_grandchild></div>
</div>
</div>
</main>
<script>
let grey = 'rgb(128, 128, 128)';
let red = 'rgb(255, 0, 0)';
let green = 'rgb(0, 128, 0)';
let blue = 'rgb(0, 0, 255)';
let yellow = 'rgb(255, 255, 0)';
let yellowgreen = 'rgb(154, 205, 50)';
function test_div(test_name, el, color) {
test(function() {
assert_equals(getComputedStyle(el).color, color);
}, test_name + ': div#' + el.id + '.color');
}
test_div('initial_color', div_subject, grey);
test_div('initial_color', div_child, grey);
test_div('initial_color', div_grandchild, grey);
div_child.classList.add('child');
test_div('add .child to #div_child', div_subject, red);
div_child.classList.remove('child');
test_div('remove .child from #div_child', div_subject, grey);
div_grandchild.classList.add('child');
test_div('add .child to #div_grandchild', div_subject, grey);
div_grandchild.classList.remove('child');
test_div('remove .child from #div_grandchild', div_subject, grey);
div_child.classList.add('descendant');
test_div('add .descendant to #div_child', div_subject, green);
div_child.classList.remove('descendant');
test_div('remove .descendant from #div_child', div_subject, grey);
div_grandchild.classList.add('descendant');
test_div('add .descendant to #div_grandchild', div_subject, green);
div_grandchild.classList.remove('descendant');
test_div('remove .descendant from #div_grandchild', div_subject, grey);
div_grandchild.setAttribute('attrname', 'descendant');
test_div('set descendant to #div_grandchild[attrname]', div_subject, blue);
div_grandchild.setAttribute('attrname', '');
test_div('clear #div_grandchild[attrname]', div_subject, grey);
div_grandchild.id = 'div_descendant';
test_div('change #div_grandchild to #div_descendant', div_subject, yellow);
div_descendant.id = 'div_grandchild';
test_div('change #div_descendant to #div_grandchild', div_subject, grey);
descendant = document.createElement('descendant');
div_subject.appendChild(descendant);
test_div('add descendant to #div_subject', div_subject, yellowgreen);
div_subject.removeChild(descendant);
test_div('remove descendant from #div_subject', div_subject, grey);
div = document.createElement('div');
div.appendChild(descendant);
div_subject.appendChild(div);
test_div('add "div > descendant" to #div_subject', div_subject, yellowgreen);
div_subject.removeChild(div);
test_div('remove "div > descendant" from #div_subject', div_subject, grey);
</script>