mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-12 02:59:45 +00: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:
parent
5387c923ca
commit
202c55bf28
Notes:
github-actions[bot]
2025-07-04 17:11:37 +00:00
Author: https://github.com/AtkinsSJ
Commit: 202c55bf28
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5302
Reviewed-by: https://github.com/tcl3 ✅
12 changed files with 503 additions and 2 deletions
|
@ -155,6 +155,9 @@
|
|||
"stalled": {
|
||||
"argument": ""
|
||||
},
|
||||
"state": {
|
||||
"argument": "<ident>"
|
||||
},
|
||||
"suboptimal-value": {
|
||||
"argument": ""
|
||||
},
|
||||
|
|
|
@ -61,6 +61,7 @@ static bool can_selector_use_fast_matches(Selector const& selector)
|
|||
PseudoClass::LocalLink,
|
||||
PseudoClass::OnlyChild,
|
||||
PseudoClass::Root,
|
||||
PseudoClass::State,
|
||||
PseudoClass::Visited))
|
||||
return false;
|
||||
} else if (!first_is_one_of(simple_selector.type,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <LibWeb/DOM/NamedNodeMap.h>
|
||||
#include <LibWeb/DOM/Text.h>
|
||||
#include <LibWeb/HTML/AttributeNames.h>
|
||||
#include <LibWeb/HTML/CustomElements/CustomStateSet.h>
|
||||
#include <LibWeb/HTML/HTMLAnchorElement.h>
|
||||
#include <LibWeb/HTML/HTMLDetailsElement.h>
|
||||
#include <LibWeb/HTML/HTMLDialogElement.h>
|
||||
|
@ -1059,6 +1060,15 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
|||
|
||||
return false;
|
||||
}
|
||||
case CSS::PseudoClass::State: {
|
||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-custom
|
||||
// The :state(identifier) pseudo-class must match all custom elements whose states set's set entries contains identifier.
|
||||
if (!element.is_custom())
|
||||
return false;
|
||||
if (auto* custom_state_set = element.custom_state_set())
|
||||
return custom_state_set->has_state(pseudo_class.ident->string_value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -2,5 +2,5 @@ Harness status: OK
|
|||
|
||||
Found 1 tests
|
||||
|
||||
1 Fail
|
||||
Fail Test :has() invalidation with :state() pseudo-classes
|
||||
1 Pass
|
||||
Pass Test :has() invalidation with :state() pseudo-classes
|
|
@ -0,0 +1,8 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 3 tests
|
||||
|
||||
3 Pass
|
||||
Pass state selector has influence on nth-of when state is applied
|
||||
Pass state selector only applies on given ident
|
||||
Pass style is invalided on clear()
|
|
@ -0,0 +1,8 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 3 tests
|
||||
|
||||
3 Pass
|
||||
Pass state selector has no influence when state is not applied
|
||||
Pass state selector has influence when state is applied
|
||||
Pass state selector only applies on given ident
|
|
@ -0,0 +1,15 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 10 tests
|
||||
|
||||
10 Pass
|
||||
Pass state selector has no influence when state is not applied
|
||||
Pass state selector has no influence on sibling selectors when not applied
|
||||
Pass state selector has influence when state is applied
|
||||
Pass state selector influences siblings when state is applied
|
||||
Pass state selector influences has() when state is applied
|
||||
Pass state selector only applies on given ident
|
||||
Pass state selector only applies to siblings on given ident
|
||||
Pass state selector only applies to has() on given ident
|
||||
Pass states added multiple times counts as one
|
||||
Pass style is invalided on clear()
|
|
@ -0,0 +1,14 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 8 tests
|
||||
|
||||
6 Pass
|
||||
2 Fail
|
||||
Pass :state() parsing passes
|
||||
Pass :state() parsing failures
|
||||
Pass deprecated :--state parsing failures
|
||||
Fail :state(foo) serialization
|
||||
Pass :state(foo) in simple cases
|
||||
Pass :state(foo) and other pseudo classes
|
||||
Fail :state(foo) and ::part()
|
||||
Pass :state(foo) and :host()
|
|
@ -0,0 +1,92 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" />
|
||||
<link rel="help" href="https://html.spec.whatwg.org/multipage/custom-elements.html#custom-state-pseudo-class" />
|
||||
<title>:state() css selector applies to nth-of selectors</title>
|
||||
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<custom-state id="myCE">First Element</custom-state>
|
||||
<p id="mySibling">First Sibling</p>
|
||||
<custom-state id="myCE2">Second Element</custom-state>
|
||||
<p id="mySibling2">Second Sibling</p>
|
||||
<style>
|
||||
:nth-child(1), :nth-child(2) {
|
||||
color: #f00;
|
||||
}
|
||||
:nth-child(2 of :state(--green)) {
|
||||
color: #0f0;
|
||||
}
|
||||
:nth-child(2 of :state(--green)) + p {
|
||||
color: #00f;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
customElements.define('custom-state', class extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.elementInternals = this.attachInternals();
|
||||
}
|
||||
});
|
||||
|
||||
test(function(t) {
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.delete('--green') });
|
||||
t.add_cleanup(() => { myCE2.elementInternals.states.delete('--green') });
|
||||
assert_false(myCE.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
myCE.elementInternals.states.add('--green');
|
||||
assert_true(myCE.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
myCE2.elementInternals.states.add('--green');
|
||||
assert_true(myCE2.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(0, 255, 0)');
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(0, 0, 255)');
|
||||
}, "state selector has influence on nth-of when state is applied");
|
||||
|
||||
test(function(t) {
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.delete('--foo') });
|
||||
t.add_cleanup(() => { myCE2.elementInternals.states.delete('--foo') });
|
||||
myCE.elementInternals.states.add('--foo');
|
||||
myCE2.elementInternals.states.add('--foo');
|
||||
assert_false(myCE.elementInternals.states.has('--green'));
|
||||
assert_true(myCE.elementInternals.states.has('--foo'));
|
||||
assert_false(myCE2.elementInternals.states.has('--green'));
|
||||
assert_true(myCE2.elementInternals.states.has('--foo'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
}, "state selector only applies on given ident");
|
||||
|
||||
test(function(t) {
|
||||
myCE.elementInternals.states.add('--green');
|
||||
myCE.elementInternals.states.add('--foo');
|
||||
myCE2.elementInternals.states.add('--green');
|
||||
myCE2.elementInternals.states.add('--foo');
|
||||
assert_true(myCE.elementInternals.states.has('--green'));
|
||||
assert_true(myCE.elementInternals.states.has('--foo'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(0, 255, 0)');
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(0, 0, 255)');
|
||||
myCE.elementInternals.states.clear();
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
}, "style is invalided on clear()");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,74 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="timeout" content="long" />
|
||||
<meta
|
||||
name="author"
|
||||
title="Keith Cirkel"
|
||||
href="mailto:wpt@keithcirkel.co.uk"
|
||||
/>
|
||||
<link rel="help" href="https://html.spec.whatwg.org/multipage/custom-elements.html#custom-state-pseudo-class" />
|
||||
<title>:state() css selector applies in shadow roots</title>
|
||||
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<custom-state id="myCE"> I should be green </custom-state>
|
||||
<style></style>
|
||||
<script>
|
||||
customElements.define(
|
||||
"custom-state",
|
||||
class extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.elementInternals = this.attachInternals();
|
||||
const css = new CSSStyleSheet();
|
||||
css.replaceSync(`
|
||||
:host {
|
||||
color: #f00;
|
||||
}
|
||||
:host(:state(green)) {
|
||||
color: #0f0;
|
||||
}
|
||||
`);
|
||||
this.attachShadow({ mode: "open" }).adoptedStyleSheets.push(css);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test(function () {
|
||||
assert_false(myCE.elementInternals.states.has("green"));
|
||||
assert_equals(
|
||||
getComputedStyle(myCE).getPropertyValue("color"),
|
||||
"rgb(255, 0, 0)",
|
||||
);
|
||||
}, "state selector has no influence when state is not applied");
|
||||
|
||||
test(function (t) {
|
||||
myCE.elementInternals.states.add("green");
|
||||
t.add_cleanup(() => {
|
||||
myCE.elementInternals.states.delete("green");
|
||||
});
|
||||
assert_true(myCE.elementInternals.states.has("green"));
|
||||
assert_equals(
|
||||
getComputedStyle(myCE).getPropertyValue("color"),
|
||||
"rgb(0, 255, 0)",
|
||||
);
|
||||
}, "state selector has influence when state is applied");
|
||||
|
||||
test(function (t) {
|
||||
myCE.elementInternals.states.add("foo");
|
||||
t.add_cleanup(() => {
|
||||
myCE.elementInternals.states.delete("foo");
|
||||
});
|
||||
assert_false(myCE.elementInternals.states.has("green"));
|
||||
assert_true(myCE.elementInternals.states.has("foo"));
|
||||
assert_equals(
|
||||
getComputedStyle(myCE).getPropertyValue("color"),
|
||||
"rgb(255, 0, 0)",
|
||||
);
|
||||
}, "state selector only applies on given ident");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,130 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="timeout" content="long">
|
||||
<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" />
|
||||
<link rel="help" href="https://html.spec.whatwg.org/multipage/custom-elements.html#custom-state-pseudo-class" />
|
||||
<title>:state() css selector applies</title>
|
||||
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<custom-state id="myCE">I should be green</custom-state>
|
||||
<p id="mySibling">I should be blue</p>
|
||||
<p id="myHas">I should be blue</p>
|
||||
<style>
|
||||
custom-state {
|
||||
color: #f00;
|
||||
}
|
||||
custom-state + p {
|
||||
color: #f00;
|
||||
}
|
||||
custom-state:state(--green) {
|
||||
color: #0f0;
|
||||
}
|
||||
custom-state:--green {
|
||||
color: #0f0;
|
||||
}
|
||||
body:has(custom-state:state(--green)) p {
|
||||
color: #0ff;
|
||||
}
|
||||
custom-state:state(--green) + p[id] {
|
||||
color: #00f;
|
||||
}
|
||||
custom-state:--green + p {
|
||||
color: #00f;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
customElements.define('custom-state', class extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.elementInternals = this.attachInternals();
|
||||
}
|
||||
});
|
||||
|
||||
test(function() {
|
||||
assert_false(myCE.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
}, "state selector has no influence when state is not applied");
|
||||
|
||||
test(function() {
|
||||
assert_false(myCE.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
}, "state selector has no influence on sibling selectors when not applied");
|
||||
|
||||
test(function(t) {
|
||||
myCE.elementInternals.states.add('--green');
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.delete('--green') });
|
||||
assert_true(myCE.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(0, 255, 0)');
|
||||
}, "state selector has influence when state is applied");
|
||||
|
||||
test(function(t) {
|
||||
myCE.elementInternals.states.add('--green');
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.delete('--green') });
|
||||
assert_true(myCE.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(0, 0, 255)');
|
||||
}, "state selector influences siblings when state is applied");
|
||||
|
||||
test(function(t) {
|
||||
myCE.elementInternals.states.add('--green');
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.delete('--green') });
|
||||
assert_true(myCE.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(myHas).getPropertyValue('color'), 'rgb(0, 255, 255)');
|
||||
}, "state selector influences has() when state is applied");
|
||||
|
||||
test(function(t) {
|
||||
myCE.elementInternals.states.add('--foo');
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.delete('--foo') });
|
||||
assert_false(myCE.elementInternals.states.has('--green'));
|
||||
assert_true(myCE.elementInternals.states.has('--foo'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
}, "state selector only applies on given ident");
|
||||
|
||||
test(function(t) {
|
||||
myCE.elementInternals.states.add('--foo');
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.delete('--foo') });
|
||||
assert_false(myCE.elementInternals.states.has('--green'));
|
||||
assert_true(myCE.elementInternals.states.has('--foo'));
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
}, "state selector only applies to siblings on given ident");
|
||||
|
||||
test(function(t) {
|
||||
myCE.elementInternals.states.add('--foo');
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.delete('--foo') });
|
||||
assert_false(myCE.elementInternals.states.has('--green'));
|
||||
assert_true(myCE.elementInternals.states.has('--foo'));
|
||||
assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
}, "state selector only applies to has() on given ident");
|
||||
|
||||
test(function(t) {
|
||||
myCE.elementInternals.states.add('--green');
|
||||
myCE.elementInternals.states.add('--green');
|
||||
myCE.elementInternals.states.add('--green');
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.delete('--green') });
|
||||
assert_true(myCE.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(0, 255, 0)');
|
||||
assert_true(myCE.elementInternals.states.delete('--green'));
|
||||
assert_false(myCE.elementInternals.states.has('--green'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
assert_false(myCE.elementInternals.states.delete('--green'));
|
||||
}, "states added multiple times counts as one");
|
||||
|
||||
test(function(t) {
|
||||
myCE.elementInternals.states.add('--green');
|
||||
myCE.elementInternals.states.add('--foo');
|
||||
t.add_cleanup(() => { myCE.elementInternals.states.clear() });
|
||||
assert_true(myCE.elementInternals.states.has('--green'));
|
||||
assert_true(myCE.elementInternals.states.has('--foo'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(0, 255, 0)');
|
||||
myCE.elementInternals.states.clear();
|
||||
assert_false(myCE.elementInternals.states.has('--green'));
|
||||
assert_false(myCE.elementInternals.states.has('--foo'));
|
||||
assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)');
|
||||
}, "style is invalided on clear()");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue