| <!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> |