mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 17:55:07 +02:00
LibWeb/CSS: Implement the :state(foo) pseudo-class
This matches custom elements that have `foo` in their custom states set. The 2 test failures here are because we don't support `::part()` yet.
This commit is contained in:
committed by
Tim Ledbetter
parent
5387c923ca
commit
202c55bf28
Notes:
github-actions[bot]
2025-07-04 17:11:37 +00:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/LadybirdBrowser/ladybird/commit/202c55bf286 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5302 Reviewed-by: https://github.com/tcl3 ✅
@@ -0,0 +1,146 @@
|
||||
<!DOCTYPE html>
|
||||
<link rel=help href="https://html.spec.whatwg.org/multipage/custom-elements.html#custom-state-pseudo-class">
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<style>
|
||||
#state-and-part::part(inner) {
|
||||
opacity: 0;
|
||||
}
|
||||
#state-and-part::part(inner):state(innerFoo) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
#state-and-part:state(outerFoo)::part(inner) {
|
||||
opacity: 0.25;
|
||||
}
|
||||
:state( \(escaped\ state ) {}
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
class TestElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this._internals = this.attachInternals();
|
||||
}
|
||||
|
||||
get i() {
|
||||
return this._internals;
|
||||
}
|
||||
}
|
||||
customElements.define('test-element', TestElement);
|
||||
|
||||
class ContainerElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this._internals = this.attachInternals();
|
||||
this._shadow = this.attachShadow({mode:'open'});
|
||||
this._shadow.innerHTML = `
|
||||
<style>
|
||||
:host {
|
||||
border-style: solid;
|
||||
}
|
||||
:host(:state(dotted)) {
|
||||
border-style: dotted;
|
||||
}
|
||||
</style>
|
||||
<test-element part="inner"></test-element>`;
|
||||
}
|
||||
|
||||
get i() {
|
||||
return this._internals;
|
||||
}
|
||||
get innerElement() {
|
||||
return this._shadow.querySelector('test-element');
|
||||
}
|
||||
}
|
||||
customElements.define('container-element', ContainerElement);
|
||||
|
||||
test(() => {
|
||||
document.querySelector(':state(foo)');
|
||||
document.querySelector(':state(--foo)');
|
||||
document.querySelector(':state(--)');
|
||||
document.querySelector(':state(--16px)');
|
||||
}, ':state() parsing passes');
|
||||
|
||||
test(() => {
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':state'); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':state('); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':state()'); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':state(=)'); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':state(name=value)'); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':state( foo bar)'); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':state(16px)'); });
|
||||
}, ':state() parsing failures');
|
||||
|
||||
test(() => {
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':--('); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':--()'); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':--)'); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':--='); });
|
||||
assert_throws_dom('SyntaxError', () => { document.querySelector(':--name=value'); });
|
||||
}, 'deprecated :--state parsing failures');
|
||||
|
||||
test(() => {
|
||||
assert_equals(document.styleSheets[0].cssRules[1].cssText,
|
||||
'#state-and-part::part(inner):state(innerFoo) { opacity: 0.5; }');
|
||||
assert_equals(document.styleSheets[0].cssRules[3].selectorText,
|
||||
':state(\\(escaped\\ state)');
|
||||
}, ':state(foo) serialization');
|
||||
|
||||
test(() => {
|
||||
let element = new TestElement();
|
||||
let states = element.i.states;
|
||||
|
||||
assert_false(element.matches(':state(foo)'));
|
||||
assert_true(element.matches(':not(:state(foo))'));
|
||||
states.add('foo');
|
||||
assert_true(element.matches(':state(foo)'));
|
||||
assert_true(element.matches(':is(:state(foo))'));
|
||||
element.classList.add('c1', 'c2');
|
||||
assert_true(element.matches('.c1:state(foo)'));
|
||||
assert_true(element.matches(':state(foo).c1'));
|
||||
assert_true(element.matches('.c2:state(foo).c1'));
|
||||
}, ':state(foo) in simple cases');
|
||||
|
||||
test(() => {
|
||||
let element = new TestElement();
|
||||
element.tabIndex = 0;
|
||||
document.body.appendChild(element);
|
||||
element.focus();
|
||||
let states = element.i.states;
|
||||
|
||||
states.add('foo');
|
||||
assert_true(element.matches(':focus:state(foo)'));
|
||||
assert_true(element.matches(':state(foo):focus'));
|
||||
}, ':state(foo) and other pseudo classes');
|
||||
|
||||
test(() => {
|
||||
let outer = new ContainerElement();
|
||||
outer.id = 'state-and-part';
|
||||
document.body.appendChild(outer);
|
||||
let inner = outer.innerElement;
|
||||
let innerStates = inner.i.states;
|
||||
|
||||
innerStates.add('innerFoo');
|
||||
assert_equals(getComputedStyle(inner).opacity, '0.5',
|
||||
'::part() followed by :state()');
|
||||
innerStates.delete('innerFoo');
|
||||
innerStates.add('innerfoo');
|
||||
assert_equals(getComputedStyle(inner).opacity, '0',
|
||||
':state() matching should be case-sensitive');
|
||||
innerStates.delete('innerfoo');
|
||||
|
||||
outer.i.states.add('outerFoo');
|
||||
assert_equals(getComputedStyle(inner).opacity, '0.25',
|
||||
':state(foo) followed by ::part()');
|
||||
}, ':state(foo) and ::part()');
|
||||
|
||||
test(() => {
|
||||
let outer = new ContainerElement();
|
||||
document.body.appendChild(outer);
|
||||
|
||||
assert_equals(getComputedStyle(outer).borderStyle, 'solid');
|
||||
outer.i.states.add('dotted');
|
||||
assert_equals(getComputedStyle(outer).borderStyle, 'dotted');
|
||||
}, ':state(foo) and :host()');
|
||||
</script>
|
||||
</body>
|
||||
Reference in New Issue
Block a user