-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support `:host:has()` case to check whether a shadow host element has a relationship between its shadow root node and shadow tree element: - w3c/csswg-drafts#10693 Normally, `:has()` checks relationship between its anchor element and the other elements in the same tree. But in `:host:has()` case, `:has()` checks relationship in the shadow tree of the anchor element. For example, `:host(.a):has(> div)` matches a shadow host element if the host has `a` class value and the shadow root of the host has a child div element. To cross tree boundary for testing selector and invalidating styles, this CL adds 'HasArgumentMatchInShadowTree' flag to the CSSSelector and sets the flag while parsing selectors. SelectorChecker and CheckPseudoHasArgumentTraversalIterator cross tree boundary for `:has()` argument test traversal if the flag is set. RuleInvalidationDataVisitor sets 'TreeBoundaryCrossing` invalidation-set flag for non-subject `:has()` if the flag is set. If StyleEngine reaches to a shadow host element while performing `:has()` invalidation, it invalidates the host element if the host is affected by `:has()` state change. Bug: 359758910 Change-Id: I69f0813deca4caefcff1f0b5ff8181ba67967a40 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5839398 Commit-Queue: Byungwoo Lee <[email protected]> Reviewed-by: Rune Lillesveen <[email protected]> Cr-Commit-Position: refs/heads/main@{#1362877}
- Loading branch information
1 parent
a1f4bbf
commit 2d2ea05
Showing
2 changed files
with
290 additions
and
0 deletions.
There are no files selected for viewing
147 changes: 147 additions & 0 deletions
147
css/selectors/invalidation/host-has-shadow-tree-element-at-nonsubject-position.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"> | ||
<title>:host:has(...) to check whether a shadow host has a shadow tree element (nonsubject position)</title> | ||
<link rel="author" title="Byungwoo Lee" href="mailto:[email protected]"> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<link rel="help" href="https://drafts.csswg.org/selectors/#relational"> | ||
<div class="ancestor host_context"> | ||
<div id="host" class="ancestor"> | ||
<template shadowrootmode="open"> | ||
<style> | ||
div { color: red; } | ||
:host:has(.descendant) .subject { color: green; } | ||
:host:has(> .child) .subject { color: blue; } | ||
:host:has(~ .sibling) .subject { color: yellow; } | ||
:host:has(:is(.ancestor .descendant)) .subject { color: purple; } | ||
:host:has(.descendant):has(> .child) .subject { color: pink; } | ||
:host-context(.host_context):has(> .child > .grand_child) .subject { color: ivory; } | ||
:host(.host_context):has(> .child > .grand_child) .subject { color: skyblue; } | ||
:host:has(> .child > .grand_child):host(.host_context):has(> .child > .descendant) .subject { color: lightgreen; } | ||
</style> | ||
<div id="subject" class="subject"></div> | ||
<div id="shadow_child"> | ||
<div id="shadow_descendant"></div> | ||
</div> | ||
</template> | ||
<div class="child"> | ||
<div class="descendant"></div> | ||
</div> | ||
</div> | ||
<div class="sibling"></div> | ||
</div> | ||
|
||
<script> | ||
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)'; | ||
const ivory = 'rgb(255, 255, 240)'; | ||
const skyblue = 'rgb(135, 206, 235)'; | ||
const lightgreen = 'rgb(144, 238, 144)'; | ||
|
||
var shadow_root = host.shadowRoot; | ||
|
||
function element(id) { | ||
return document.getElementById(id); | ||
} | ||
|
||
function shadow_element(id) { | ||
return shadow_root.getElementById(id); | ||
} | ||
|
||
var subject = shadow_element('subject'); | ||
|
||
function test_color(test_name, color) { | ||
test(function() { | ||
assert_equals(getComputedStyle(subject).color, color); | ||
}, test_name); | ||
} | ||
|
||
function create_div(id, class_name) { | ||
let div = document.createElement('div'); | ||
div.id = id; | ||
div.classList.add(class_name); | ||
return div | ||
} | ||
|
||
test_color('Initial color', red); | ||
|
||
shadow_element('shadow_child').classList.add('descendant'); | ||
test_color(`Add .descendant to #shadow_child`, green); | ||
|
||
shadow_element('shadow_child').classList.remove('descendant'); | ||
test_color(`Remove .descendant from #shadow_child`, red); | ||
|
||
shadow_element('shadow_descendant').classList.add('descendant'); | ||
test_color(`Add .descendant to #shadow_descendant`, green); | ||
|
||
shadow_element('shadow_child').classList.add('ancestor'); | ||
test_color(`Add .ancestor to #shadow_child:has(.descendant)`, purple); | ||
|
||
shadow_element('shadow_child').classList.remove('ancestor'); | ||
test_color(`Remove .ancestor from #shadow_child:has(.descendant)`, green); | ||
|
||
shadow_element('shadow_child').classList.add('child'); | ||
test_color(`Add .child to #shadow_child:has(.descendant)`, pink); | ||
|
||
shadow_element('shadow_child').classList.remove('child'); | ||
test_color(`Remove .child from #shadow_child:has(.descendant)`, green); | ||
|
||
shadow_element('shadow_descendant').classList.remove('descendant'); | ||
test_color(`Remove .descendant from #shadow_descendant`, red); | ||
|
||
shadow_element('shadow_child').classList.add('child'); | ||
test_color(`Add .child to #shadow_child`, blue); | ||
|
||
shadow_element('shadow_descendant').classList.add('grand_child'); | ||
test_color(`Add .grand_child to #shadow_descendant`, ivory); | ||
|
||
element('host').classList.add('host_context'); | ||
test_color(`Add .host_context to #host`, skyblue); | ||
|
||
shadow_element('shadow_descendant').classList.add('descendant'); | ||
test_color(`Add .descendant to #shadow_descendant.grand_child`, lightgreen); | ||
|
||
shadow_element('shadow_descendant').classList.remove('descendant'); | ||
test_color(`Remove .descendant from #shadow_descendant.grand_child`, skyblue); | ||
|
||
shadow_element('shadow_descendant').classList.remove('grand_child'); | ||
test_color(`Remove .grand_child from #shadow_descendant`, blue); | ||
|
||
shadow_element('shadow_child').classList.remove('child'); | ||
test_color(`Remove .child from #shadow_child`, red); | ||
|
||
shadow_element('shadow_descendant').classList.add('child'); | ||
test_color(`Add .child to #shadow_descendant`, red); | ||
|
||
shadow_element('shadow_descendant').classList.remove('child'); | ||
test_color(`Remove .child from #shadow_descendant`, red); | ||
|
||
div = shadow_root.insertBefore(create_div('first_child', 'descendant'), | ||
shadow_root.firstChild); | ||
test_color(`Insert #first_child.descendant to shadow root`, green); | ||
div.remove(); | ||
test_color(`Remove #first_child.descendant from shadow root`, red); | ||
|
||
div = shadow_root.insertBefore(create_div('last_child', 'descendant'), null); | ||
test_color(`Insert #last_child.descendant to shadow root`, green); | ||
div.remove(); | ||
test_color(`Remove #last_child.descendant from shadow root`, red); | ||
|
||
div = shadow_root.insertBefore(create_div('child_in_middle','descendant'), | ||
shadow_element('shadow_child')); | ||
test_color(`Insert #child_in_middle.descendant before #shadow_child`, green); | ||
div.remove(); | ||
test_color(`Remove #child_in_middle.descendant from shadow root`, red); | ||
|
||
div = shadow_element('shadow_child') | ||
.insertBefore(create_div('grand_child','descendant'), | ||
shadow_element('shadow_descendant')); | ||
test_color(`Insert #grand_child.descendant before #shadow_descendant`, green); | ||
div.remove(); | ||
test_color(`Remove #grand_child.descendant from shadow tree`, red); | ||
|
||
</script> |
143 changes: 143 additions & 0 deletions
143
css/selectors/invalidation/host-has-shadow-tree-element-at-subject-position.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"> | ||
<title>:host:has(...) to check whether a shadow host has a shadow tree element (subject position)</title> | ||
<link rel="author" title="Byungwoo Lee" href="mailto:[email protected]"> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<link rel="help" href="https://drafts.csswg.org/selectors/#relational"> | ||
<div class="ancestor host_context"> | ||
<div id="host" class="ancestor"> | ||
<template shadowrootmode="open"> | ||
<style> | ||
:host:has(.descendant) { color: green; } | ||
:host:has(> .child) { color: blue; } | ||
:host:has(~ .sibling) { color: yellow; } | ||
:host:has(:is(.ancestor .descendant)) { color: purple; } | ||
:host:has(.descendant):has(> .child) { color: pink; } | ||
:host-context(.host_context):has(> .child > .grand_child) { color: ivory; } | ||
:host(.host_context):has(> .child > .grand_child) { color: skyblue; } | ||
:host:has(> .child > .grand_child):host(.host_context):has(> .child > .descendant) { color: lightgreen; } | ||
</style> | ||
<div id="shadow_child"> | ||
<div id="shadow_descendant"></div> | ||
</div> | ||
</template> | ||
<div class="child"> | ||
<div class="descendant"></div> | ||
</div> | ||
</div> | ||
<div class="sibling"></div> | ||
</div> | ||
|
||
<script> | ||
const black = 'rgb(0, 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)'; | ||
const ivory = 'rgb(255, 255, 240)'; | ||
const skyblue = 'rgb(135, 206, 235)'; | ||
const lightgreen = 'rgb(144, 238, 144)'; | ||
|
||
var shadow_root = host.shadowRoot; | ||
|
||
function element(id) { | ||
return document.getElementById(id); | ||
} | ||
|
||
function shadow_element(id) { | ||
return shadow_root.getElementById(id); | ||
} | ||
|
||
function test_color(test_name, color) { | ||
test(function() { | ||
assert_equals(getComputedStyle(host).color, color); | ||
}, test_name); | ||
} | ||
|
||
function create_div(id, class_name) { | ||
let div = document.createElement('div'); | ||
div.id = id; | ||
div.classList.add(class_name); | ||
return div | ||
} | ||
|
||
test_color('Initial color', black); | ||
|
||
shadow_element('shadow_child').classList.add('descendant'); | ||
test_color(`Add .descendant to #shadow_child`, green); | ||
|
||
shadow_element('shadow_child').classList.remove('descendant'); | ||
test_color(`Remove .descendant from #shadow_child`, black); | ||
|
||
shadow_element('shadow_descendant').classList.add('descendant'); | ||
test_color(`Add .descendant to #shadow_descendant`, green); | ||
|
||
shadow_element('shadow_child').classList.add('ancestor'); | ||
test_color(`Add .ancestor to #shadow_child:has(.descendant)`, purple); | ||
|
||
shadow_element('shadow_child').classList.remove('ancestor'); | ||
test_color(`Remove .ancestor from #shadow_child:has(.descendant)`, green); | ||
|
||
shadow_element('shadow_child').classList.add('child'); | ||
test_color(`Add .child to #shadow_child:has(.descendant)`, pink); | ||
|
||
shadow_element('shadow_child').classList.remove('child'); | ||
test_color(`Remove .child from #shadow_child:has(.descendant)`, green); | ||
|
||
shadow_element('shadow_descendant').classList.remove('descendant'); | ||
test_color(`Remove .descendant from #shadow_descendant`, black); | ||
|
||
shadow_element('shadow_child').classList.add('child'); | ||
test_color(`Add .child to #shadow_child`, blue); | ||
|
||
shadow_element('shadow_descendant').classList.add('grand_child'); | ||
test_color(`Add .grand_child to #shadow_descendant`, ivory); | ||
|
||
element('host').classList.add('host_context'); | ||
test_color(`Add .host_context to #host`, skyblue); | ||
|
||
shadow_element('shadow_descendant').classList.add('descendant'); | ||
test_color(`Add .descendant to #shadow_descendant.grand_child`, lightgreen); | ||
|
||
shadow_element('shadow_descendant').classList.remove('descendant'); | ||
test_color(`Remove .descendant from #shadow_descendant.grand_child`, skyblue); | ||
|
||
shadow_element('shadow_descendant').classList.remove('grand_child'); | ||
test_color(`Remove .grand_child from #shadow_descendant`, blue); | ||
|
||
shadow_element('shadow_child').classList.remove('child'); | ||
test_color(`Remove .child from #shadow_child`, black); | ||
|
||
shadow_element('shadow_descendant').classList.add('child'); | ||
test_color(`Add .child to #shadow_descendant`, black); | ||
|
||
shadow_element('shadow_descendant').classList.remove('child'); | ||
test_color(`Remove .child from #shadow_descendant`, black); | ||
|
||
div = shadow_root.insertBefore(create_div('first_child', 'descendant'), | ||
shadow_root.firstChild); | ||
test_color(`Insert #first_child.descendant to shadow root`, green); | ||
div.remove(); | ||
test_color(`Remove #first_child.descendant from shadow root`, black); | ||
|
||
div = shadow_root.insertBefore(create_div('last_child', 'descendant'), null); | ||
test_color(`Insert #last_child.descendant to shadow root`, green); | ||
div.remove(); | ||
test_color(`Remove #last_child.descendant from shadow root`, black); | ||
|
||
div = shadow_root.insertBefore(create_div('child_in_middle','descendant'), | ||
shadow_element('shadow_child')); | ||
test_color(`Insert #child_in_middle.descendant before #shadow_child`, green); | ||
div.remove(); | ||
test_color(`Remove #child_in_middle.descendant from shadow root`, black); | ||
|
||
div = shadow_element('shadow_child') | ||
.insertBefore(create_div('grand_child','descendant'), | ||
shadow_element('shadow_descendant')); | ||
test_color(`Insert #grand_child.descendant before #shadow_descendant`, green); | ||
div.remove(); | ||
test_color(`Remove #grand_child.descendant from shadow tree`, black); | ||
|
||
</script> |