Tests: Import all WPT css/css-nesting tests

A few are skipped for now:
- A few ref tests fail
- Crash tests are not supported by our runner and time out
- top-level-is-scope.html crashes and needs further investigation
This commit is contained in:
Sam Atkins 2024-11-06 16:43:57 +00:00 committed by Andreas Kling
parent 6bb1ffbcd3
commit b0e79ce549
Notes: github-actions[bot] 2024-11-07 14:12:28 +00:00
62 changed files with 2098 additions and 0 deletions

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<title>Properties in nested conditional rules</title>
<link rel="author" title="Adam Argyle" href="mailto:argyle@google.com">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<style>
.test {
background-color: red;
width: 100px;
height: 100px;
display: grid;
}
@media (min-width: 50px) {
.test-5 > div {
background-color: green;
}
}
@supports (display: grid) {
.test-10 {
background-color: green;
}
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test test-5"><div></div></div>
<div class="test test-10"><div></div></div>
</body>

View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<title>Conditional rules with nesting</title>
<link rel="author" title="Adam Argyle" href="mailto:argyle@google.com">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<style>
.test {
background-color: red;
width: 30px;
height: 30px;
display: grid;
}
@media (min-width: 10px) {
.test-5 > div {
background-color: green;
}
}
@media (min-width: 10px) {
.test-6 > div {
background-color: green;
}
}
@supports (display: grid) {
.test-10 {
background-color: green;
}
}
@layer {
.test-11 {
background-color: green !important;
}
}
@scope (.test-12) {
:scope {
background-color: green;
}
}
div {
container-type: inline-size;
}
@container (width >= 0px) {
.test-13 {
background-color: green;
}
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test test-5"><div></div></div>
<div class="test test-6"><div></div></div>
<div class="test test-10"><div></div></div>
<div class="test test-11"><div></div></div>
<div class="test"><div class="test-12"></div></div>
<div class="test"><div class="test-13"></div></div>
</body>

View file

@ -0,0 +1,8 @@
<!doctype html>
<title>Nested has shouldn't match</title>
<style>
ul { background: green }
</style>
<ul>
<li>Bar</li>
</ul>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<title>Implicit nesting</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<style>
.test {
background-color: green;
width: 30px;
height: 30px;
display: grid;
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
</body>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<title>Nest-containing in forgiving parsing</title>
<style>
.test {
background-color: green;
width: 100px;
height: 100px;
display: grid;
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test"></div>
<div class="test"></div>
</body>

View file

@ -0,0 +1,5 @@
<!DOCTYPE html>
<title>@supports needs to be consistent with actual nesting support</title>
<body>
<p>Test passes if this text is not red</p>
</body>

View file

@ -0,0 +1,23 @@
<!DOCTYPE html>
<title>@supports with nesting</title>
<link rel="author" title="Matthieu Dubet" href="mailto:m_dubet@apple.com">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<style>
.test {
background-color: green;
width: 100px;
height: 100px;
display: grid;
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
<div class="test"></div>
</body>

View file

@ -0,0 +1,4 @@
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
<p>Test passes if there is a filled green square.</p>
<div style="width:100px; height:100px; background:green;"></div>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<title>Properties in nested conditional rules</title>
<link rel="author" title="Adam Argyle" href="mailto:argyle@google.com">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<link rel="match" href="../../../../expected/wpt-import/css/css-nesting/conditional-properties-ref.html">
<style>
.test {
background-color: red;
width: 100px;
height: 100px;
display: grid;
}
.test-5 {
@media (min-width: 50px) {
background-color: green;
}
}
.test-10 {
@supports (display: grid) {
background-color: green;
}
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test test-5"><div></div></div>
<div class="test test-10"><div></div></div>
</body>

View file

@ -0,0 +1,75 @@
<!DOCTYPE html>
<title>Conditional rules with nesting</title>
<link rel="author" title="Adam Argyle" href="mailto:argyle@google.com">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<link rel="match" href="../../../../expected/wpt-import/css/css-nesting/conditional-rules-ref.html">
<style>
.test {
background-color: red;
width: 30px;
height: 30px;
display: grid;
}
.test-5 {
@media (min-width: 10px) {
& {
background-color: green;
}
}
}
.test-6 {
@media (min-width: 10px) {
background-color: green;
}
}
.test-10 {
@supports (display: grid) {
& {
background-color: green;
}
}
}
.test-11 {
@layer {
& {
background-color: green !important;
}
}
}
.test-12 {
@scope (&) {
:scope {
background-color: green;
}
}
}
div {
container-type: inline-size;
}
.test-13 {
@container (width >= 0px) {
& {
background-color: green;
}
}
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test test-5"></div>
<div class="test test-6"></div>
<div class="test test-10"></div>
<div class="test test-11"></div>
<div class="test test-12"><div class="test-12"></div></div>
<div class="test"><div class="test-13"></div></div>
</body>

View file

@ -0,0 +1,21 @@
<!doctype html>
<title>Nested has shouldn't match</title>
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1864647">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9600">
<link rel="match" href="../../../../expected/wpt-import/css/css-nesting/has-nesting-ref.html">
<style>
ul { background: green }
li:has(strong) {
display: none;
:has(> &) {
background: red;
}
}
</style>
<ul>
<li><strong>Foo</strong></li>
<li>Bar</li>
</ul>

View file

@ -0,0 +1,23 @@
<!doctype html>
<meta charset="utf-8">
<title>:host and nesting (basic) </title>
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting/#nest-selector">
<link rel="match" href="../../../../expected/wpt-import//css/reference/ref-filled-green-100px-square-only.html">
<p>Test passes if there is a filled green square.</p>
<div id="host"></div>
<script>
host.attachShadow({mode: "open"}).innerHTML = `
<style>
:host {
.nested {
width: 100px;
height: 100px;
background-color: green;
}
}
</style>
<div class="nested"></div>
`;
</script>

View file

@ -0,0 +1,22 @@
<!doctype html>
<meta charset="utf-8">
<title>:host and nesting (bare declarations)</title>
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting/#nest-selector">
<link rel="match" href="../../../../expected/wpt-import//css/reference/ref-filled-green-100px-square-only.html">
<p>Test passes if there is a filled green square.</p>
<div id="host"></div>
<script>
host.attachShadow({mode: "open"}).innerHTML = `
<style>
:host {
@media (width >= 0) {
width: 100px;
height: 100px;
background-color: green;
}
}
</style>
`;
</script>

View file

@ -0,0 +1,26 @@
<!doctype html>
<meta charset="utf-8">
<title>:host and nesting (combined with something else)</title>
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting/#nest-selector">
<link rel="match" href="../../../../expected/wpt-import//css/reference/ref-filled-green-100px-square-only.html">
<p>Test passes if there is a filled green square.</p>
<div id="host"></div>
<script>
host.attachShadow({mode: "open"}).innerHTML = `
<style>
.nested {
width: 100px;
height: 100px;
background-color: green;
}
:host(#not-host), #host {
.nested {
background-color: red;
}
}
</style>
<div class="nested"></div>
`;
</script>

View file

@ -0,0 +1,25 @@
<!doctype html>
<meta charset="utf-8">
<title>:host and nesting (combined with something else, bare declarations)</title>
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting/#nest-selector">
<link rel="match" href="../../../../expected/wpt-import//css/reference/ref-filled-green-100px-square-only.html">
<p>Test passes if there is a filled green square.</p>
<div id="host"></div>
<script>
host.attachShadow({mode: "open"}).innerHTML = `
<style>
:host {
width: 100px;
height: 100px;
background-color: green;
}
:host(#not-host), #host {
@media (width >= 0) {
background-color: red;
}
}
</style>
`;
</script>

View file

@ -0,0 +1,24 @@
<!doctype html>
<meta charset="utf-8">
<title>:host and nesting (with pseudos)</title>
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting/#nest-selector">
<link rel="match" href="../../../../expected/wpt-import//css/reference/ref-filled-green-100px-square-only.html">
<p>Test passes if there is a filled green square.</p>
<div id="host"></div>
<script>
host.attachShadow({mode: "open"}).innerHTML = `
<style>
:host {
&::before {
display: block;
content: "";
width: 100px;
height: 100px;
background-color: green;
}
}
</style>
`;
</script>

View file

@ -0,0 +1,82 @@
<!DOCTYPE html>
<title>Implicit nesting</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<link rel="match" href="../../../../expected/wpt-import/css/css-nesting/implicit-nesting-ref.html">
<style>
.test {
background-color: red;
width: 30px;
height: 30px;
display: grid;
}
.test-1 {
> div {
background-color: green;
}
}
.test-2 {
.test-2-child {
background-color: green;
}
}
.test-2-child {
background-color: red;
}
.test-3-child {
background-color: red;
}
.test-3-child {
.test-3 & {
background-color: green;
}
}
.test-4 {
:is(&) {
background-color: green;
}
}
.test-5 {
:is(.test-5, &.does-not-exist) {
background-color: green;
}
}
.test-6 {
> .foo,.test-6-child,+ .bar {
background-color: green;
}
}
.test-7 {
> .foo, .bar, + .test-7-sibling {
background-color: green;
}
}
.test-8 {
> .foo, .test-8-child, + .bar {
background-color: green;
}
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test test-1"><div></div></div>
<div class="test test-2"><div class="test-2-child"></div></div>
<div class="test test-3"><div class="test-3-child"></div></div>
<div class="test test-4"></div>
<div class="test test-5"><div class="test-5"></div></div>
<div class="test test-6"><div class="test-6-child"></div></div>
<div class="test test-7" style="display:none"></div><div class="test test-7-sibling"></div>
<div class="test test-8"><div class="test-8-child"></div></div>
</body>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<title>Nest-containing in forgiving parsing</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<link rel="match" href="../../../../expected/wpt-import/css/css-nesting/nest-containing-forgiving-ref.html">
<style>
.test {
background-color: red;
width: 100px;
height: 100px;
display: grid;
}
.does-not-exist {
:is(.test-1, !&) {
background-color: green;
}
}
.does-not-exist {
:is(.test-2, :unknown(div,&)) {
background-color: green;
}
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test test-1"></div>
<div class="test test-2"></div>
</body>

View file

@ -0,0 +1,18 @@
<!doctype html>
<meta charset="utf-8">
<title>Nesting works with bare type selectors</title>
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
<link rel="author" title="Mozilla" href="https://mozilla.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<link rel="match" href="../../../../expected/wpt-import//css/reference/ref-filled-green-100px-square-only.html">
<style>
:root {
div {
width: 100px;
height: 100px;
background: green;
}
}
</style>
<p>Test passes if there is a filled green square.</p>
<div></div>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<title>@supports needs to be consistent with actual nesting support</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://crbug.com/1414012">
<link rel="match" href="../../../../expected/wpt-import/css/css-nesting/supports-is-consistent-ref.html">
<style>
/* This test is expected to pass even if the browser does not support nesting. */
@supports selector(&) {
p {
color: red;
& { color: inherit; }
}
}
</style>
<body>
<p>Test passes if this text is not red</p>
</body>

View file

@ -0,0 +1,55 @@
<!DOCTYPE html>
<title>@supports with nesting</title>
<link rel="author" title="Matthieu Dubet" href="mailto:m_dubet@apple.com">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<link rel="match" href="../../../../expected/wpt-import/css/css-nesting/supports-rule-ref.html">
<style>
.test {
background-color: red;
width: 100px;
height: 100px;
display: grid;
}
@supports(not selector(> .test-1)) {
.test-1 {
background-color: green;
}
}
.test {
> .test-2 {
background-color: green;
}
@supports (selector(> .test-2)) {
> .test-2 {
background-color: red;
}
}
}
.test-3 {
@supports (selector(&)) {
& {
background-color: green;
}
}
}
@supports(selector(&)) {
.test-4 {
background-color: green;
}
}
body * + * {
margin-top: 8px;
}
</style>
<body>
<p>Tests pass if <strong>block is green</strong></p>
<div class="test test-1"></div>
<div class="test"><div class="test-2"></div></div>
<div class="test test-3"></div>
<div class="test test-4"></div>
</body>

View file

@ -55,3 +55,18 @@ Text/input/wpt-import/html/infrastructure/safe-passing-of-structured-data/resour
; Flaky, apparently due to font loading
Text/input/wpt-import/css/css-flexbox/flex-item-compressible-001.html
; WPT ref-tests that currently fail
Ref/input/wpt-import/css/css-nesting/has-nesting.html
Ref/input/wpt-import/css/css-nesting/host-nesting-003.html
Ref/input/wpt-import/css/css-nesting/host-nesting-004.html
Ref/input/wpt-import/css/css-nesting/nest-containing-forgiving.html
; WPT crash tests are not supported yet - and probably should go in a separate directory
Text/input/wpt-import/css/css-nesting/delete-other-rule-crash.html
Text/input/wpt-import/css/css-nesting/implicit-parent-insertion-crash.html
Text/input/wpt-import/css/css-nesting/pseudo-part-crash.html
Text/input/wpt-import/css/css-nesting/pseudo-where-crash.html
; Currently crashes
Text/input/wpt-import/css/css-nesting/top-level-is-scope.html

View file

@ -0,0 +1,11 @@
Summary
Harness status: OK
Rerun
Found 1 tests
1 Pass
Details
Result Test Name MessagePass CSS Nesting bug: Block ignored after lack of whitespace

View file

@ -0,0 +1,12 @@
Summary
Harness status: OK
Rerun
Found 2 tests
2 Pass
Details
Result Test Name MessagePass Unknown declaration does not consume subsequent declaration
Pass Unknown declaration with blocks does not consume subsequent declaration

View file

@ -0,0 +1,11 @@
Summary
Harness status: OK
Rerun
Found 1 tests
1 Pass
Details
Result Test Name MessagePass Nested rule starting with tag

View file

@ -0,0 +1,12 @@
Summary
Harness status: OK
Rerun
Found 2 tests
2 Fail
Details
Result Test Name MessageFail Simple CSSOM manipulation of subrules
Fail Simple CSSOM manipulation of subrules 1

View file

@ -0,0 +1,11 @@
Summary
Harness status: OK
Rerun
Found 1 tests
1 Pass
Details
Result Test Name MessagePass CSS Selectors nested invalidation on changed parent

View file

@ -0,0 +1,11 @@
Summary
Harness status: OK
Rerun
Found 1 tests
1 Pass
Details
Result Test Name MessagePass CSS Selectors nested invalidation on changed child

View file

@ -0,0 +1,11 @@
Summary
Harness status: OK
Rerun
Found 1 tests
1 Pass
Details
Result Test Name MessagePass CSS Selectors nested invalidation with :has()

View file

@ -0,0 +1,11 @@
Summary
Harness status: OK
Rerun
Found 1 tests
1 Pass
Details
Result Test Name MessagePass CSS Selectors nested invalidation through @media by selectorText

View file

@ -0,0 +1,12 @@
Summary
Harness status: OK
Rerun
Found 2 tests
2 Fail
Details
Result Test Name MessageFail Empty CSSNestedDeclarations do not affect outer serialization
Fail Empty CSSNestedDeclarations do not affect outer serialization (nested grouping rule)

View file

@ -0,0 +1,23 @@
Summary
Harness status: OK
Rerun
Found 12 tests
6 Pass
6 Fail
Details
Result Test Name MessageFail Trailing declarations
Fail Mixed declarations
Fail CSSNestedDeclarations.style
Pass Nested group rule
Pass Nested @scope rule
Fail Inner rule starting with an ident
Fail Inserting a CSSNestedDeclaration rule into style rule Unable to parse CSS rule.
Fail Inserting a CSSNestedDeclaration rule into nested group rule Unable to parse CSS rule.
Pass Attempting to insert a CSSNestedDeclaration rule into top-level @media rule
Pass Attempting to insert a CSSNestedDeclaration rule into a stylesheet
Pass Attempting to insert a CSSNestedDeclaration rule, empty block
Pass Attempting to insert a CSSNestedDeclaration rule, all invalid declarations

View file

@ -0,0 +1,21 @@
Summary
Harness status: OK
Rerun
Found 11 tests
11 Fail
Details
Result Test Name MessageFail Trailing declarations apply after any preceding rules
Fail Trailing declarations apply after any preceding rules (no leading)
Fail Trailing declarations apply after any preceding rules (multiple)
Fail Nested declarations rule has same specificity as outer selector
Fail Nested declarations rule has top-level specificity behavior
Fail Nested declarations rule has top-level specificity behavior (max matching)
Fail Bare declartaion in nested grouping rule can match pseudo-element
Fail Nested group rules have top-level specificity behavior
Fail Nested @scope rules behave like :where(:scope)
Fail Nested @scope rules behave like :where(:scope) (trailing)
Fail Nested declarations rule responds to parent selector text change

View file

@ -0,0 +1,11 @@
Summary
Harness status: OK
Rerun
Found 1 tests
1 Fail
Details
Result Test Name MessageFail Nested rule responds to parent selector text change

View file

@ -0,0 +1,11 @@
Summary
Harness status: OK
Rerun
Found 1 tests
1 Pass
Details
Result Test Name MessagePass @layer can be nested

View file

@ -0,0 +1,43 @@
Summary
Harness status: OK
Rerun
Found 32 tests
31 Pass
1 Fail
Details
Result Test Name MessagePass .foo { & { color: green; }}
Pass .foo { &.bar { color: green; }}
Pass .foo { & .bar { color: green; }}
Pass .foo { & > .bar { color: green; }}
Pass .foo { > .bar { color: green; }}
Pass .foo { > & .bar { color: green; }}
Pass .foo { + .bar & { color: green; }}
Pass .foo { + .bar, .foo, > .baz { color: green; }}
Pass .foo { .foo { color: green; }}
Pass .foo { .test > & .bar { color: green; }}
Pass .foo { .foo, .foo & { color: green; }}
Pass .foo { .foo, .bar { color: green; }}
Pass .foo { :is(.bar, .baz) { color: green; }}
Pass .foo { &:is(.bar, .baz) { color: green; }}
Pass .foo { :is(.bar, &.baz) { color: green; }}
Pass .foo { &:is(.bar, &.baz) { color: green; }}
Pass .foo { div& { color: green; }}
Fail INVALID: .foo { &div { color: green; }}
Pass .foo { .class& { color: green; }}
Pass .foo { &.class { color: green; }}
Pass .foo { [attr]& { color: green; }}
Pass .foo { &[attr] { color: green; }}
Pass .foo { #id& { color: green; }}
Pass .foo { &#id { color: green; }}
Pass .foo { :hover& { color: green; }}
Pass .foo { &:hover { color: green; }}
Pass .foo { :is(div)& { color: green; }}
Pass .foo { &:is(div) { color: green; }}
Pass .foo { & .bar & .baz & .qux { color: green; }}
Pass .foo { && { color: green; }}
Pass .foo { & > section, & > article { color: green; }}
Pass .foo, .bar { & + .baz, &.qux { color: green; }}

View file

@ -0,0 +1,26 @@
Summary
Harness status: OK
Rerun
Found 15 tests
4 Pass
11 Fail
Details
Result Test Name MessageFail Declarations are serialized on one line, rules on two.
Fail Mixed declarations/rules are on two lines.
Fail Implicit rule is serialized
Fail Implicit rule not removed
Fail Implicit + empty hover rule
Fail Implicit like rule not in first position
Fail Two implicit-like rules
Fail Implicit like rule after decls
Fail Implicit like rule after decls, missing closing braces
Fail Implicit like rule with other selectors
Fail Implicit-like rule in style rule
Pass Empty conditional rule
Pass Empty style rule
Pass Empty conditional inside style rule
Pass Empty style inside conditional

View file

@ -0,0 +1,11 @@
Summary
Harness status: OK
Rerun
Found 1 tests
1 Fail
Details
Result Test Name MessageFail CSS Nesting: Specificity of top-level '&'

View file

@ -0,0 +1,52 @@
/**
* Does a best-effort attempt at invoking garbage collection. Attempts to use
* the standardized `TestUtils.gc()` function, but falls back to other
* environment-specific nonstandard functions, with a final result of just
* creating a lot of garbage (in which case you will get a console warning).
*
* This should generally only be used to attempt to trigger bugs and crashes
* inside tests, i.e. cases where if garbage collection happened, then this
* should not trigger some misbehavior. You cannot rely on garbage collection
* successfully trigger, or that any particular unreachable object will be
* collected.
*
* @returns {Promise<undefined>} A promise you should await to ensure garbage
* collection has had a chance to complete.
*/
self.garbageCollect = async () => {
// https://testutils.spec.whatwg.org/#the-testutils-namespace
if (self.TestUtils?.gc) {
return TestUtils.gc();
}
// Use --expose_gc for V8 (and Node.js)
// to pass this flag at chrome launch use: --js-flags="--expose-gc"
// Exposed in SpiderMonkey shell as well
if (self.gc) {
return self.gc();
}
// Present in some WebKit development environments
if (self.GCController) {
return GCController.collect();
}
console.warn(
'Tests are running without the ability to do manual garbage collection. ' +
'They will still work, but coverage will be suboptimal.');
for (var i = 0; i < 1000; i++) {
gcRec(10);
}
function gcRec(n) {
if (n < 1) {
return {};
}
let temp = { i: "ab" + i + i / 100000 };
temp += "foo";
gcRec(n - 1);
}
};

View file

@ -0,0 +1,6 @@
.test{span{color:red}span{color:green}}
/*
* There needs to be some text here, or the bug
* will not manifest for unrelated reasons.
*/

View file

@ -0,0 +1,17 @@
<!doctype html>
<title>CSS Nesting bug: Block ignored after lack of whitespace</title>
<link rel="help" href="https://crbug.com/362674384">
<link rel="stylesheet" href="block-skipping.css"> <!-- Bug only manifests in external stylesheets. -->
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<div class="test">
<span id="target">Test passes if this text is green.</span>
</div>
<script>
test(() => {
assert_equals(getComputedStyle(target).color, 'rgb(0, 128, 0)');
});
</script>

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<body>
<title>Crash with lazy parsing child rules and stylesheet copy-on-write</title>
<link rel="help" href="https://crbug.com/1404879">
<link href="../support/delete-other-rule-crash.css" rel="stylesheet">
<script src="../../common/gc.js"></script>
<script>
addEventListener('DOMContentLoaded', async () => {
requestAnimationFrame(async () => {
document.styleSheets[0].deleteRule(0);
await garbageCollect();
document.styleSheets[0].cssRules[0].cssText;
});
});
</script>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<title>CSS Nesting: Nesting, error recovery</title>
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
#target1 {
display:block;
display:new-block;
color:green;
}
#target2 {
display:block;
display:hover {};
color:green;
}
</style>
<div id=target1>Green</div>
<div id=target2>Green</div>
<script>
test(() => {
assert_equals(getComputedStyle(target1).color, 'rgb(0, 128, 0)');
}, 'Unknown declaration does not consume subsequent declaration');
</script>
<script>
test(() => {
assert_equals(getComputedStyle(target2).color, 'rgb(0, 128, 0)');
}, 'Unknown declaration with blocks does not consume subsequent declaration');
</script>

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<title>CSS Nesting: Implicit nesting (ident)</title>
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
#main {
div {
color: green;
}
}
</style>
<div id=main>
<div id=target>
Green
</div>
</main>
<script>
test(() => {
assert_equals(getComputedStyle(target).color, 'rgb(0, 128, 0)');
}, 'Nested rule starting with tag');
</script>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<body>
<title>Use-after-free when inserting implicit parent selector</title>
<link rel="help" href="https://crbug.com/1380313">
<style>
:root {
:lang(en), :lang(en) {
}
}
</style>
<div lang="en"></div>
<script>
// Allocate a large chunk of memory, to trigger a GC.
new Int32Array(536870911);
</script>

View file

@ -0,0 +1,56 @@
<!doctype html>
<title>Simple CSSOM manipulation of subrules</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style id="ss">
div {
/* This is not a conditional rule, and thus cannot be in nesting context. */
@font-face {
&.a { font-size: 10px; }
}
@media screen {
&.a { color: red; }
/* Same. */
@font-face {
&.a { font-size: 10px; }
}
}
}
</style>
<script>
test(() => {
let [ss] = document.styleSheets;
assert_equals(ss.cssRules.length, 1);
// The @font-face rule should be ignored.
assert_equals(ss.cssRules[0].cssText,
`div {
@media screen {
&.a { color: red; }
}
}`);
});
test(() => {
let [ss] = document.styleSheets;
assert_equals(ss.cssRules.length, 1);
assert_throws_dom('HierarchyRequestError',
() => { ss.cssRules[0].cssRules[0].insertRule('@font-face {}', 0); });
assert_throws_dom('HierarchyRequestError',
() => { ss.cssRules[0].insertRule('@font-face {}', 0); });
// The @font-face rules should be ignored (again).
assert_equals(ss.cssRules[0].cssText,
`div {
@media screen {
&.a { color: red; }
}
}`);
});
</script>

View file

@ -0,0 +1,33 @@
<!doctype html>
<title>CSS Selectors nested invalidation on changed parent</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
.b {
color: red;
}
.a {
& .b {
color: green;
}
}
</style>
<div id="container">
<div id="child" class="b">
Test passes if color is green.
</div>
</div>
<script>
test(() => {
let container = document.getElementById('container');
let child = document.getElementById('child');
assert_equals(getComputedStyle(child).color, 'rgb(255, 0, 0)');
container.classList.add('a');
assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
});
</script>

View file

@ -0,0 +1,33 @@
<!doctype html>
<title>CSS Selectors nested invalidation on changed child</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
.a {
color: green;
}
.a {
& .b {
color: red;
}
}
</style>
<div id="container" class="a">
<div id="child" class="b">
Test passes if color is green.
</div>
</div>
<script>
test(() => {
let container = document.getElementById('container');
let child = document.getElementById('child');
assert_equals(getComputedStyle(child).color, 'rgb(255, 0, 0)');
child.classList.remove('b');
assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
});
</script>

View file

@ -0,0 +1,34 @@
<!doctype html>
<title>CSS Selectors nested invalidation with :has()</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
.a {
color: red;
:has(&) {
color: green;
}
}
</style>
<div id="container">
Test passes if color is green.
<div>
<div id="child"></div>
</div>
</div>
<script>
test(() => {
let container = document.getElementById('container');
let child = document.getElementById('child');
assert_equals(getComputedStyle(container).color, 'rgb(0, 0, 0)');
assert_equals(getComputedStyle(child).color, 'rgb(0, 0, 0)');
child.classList.add('a');
assert_equals(getComputedStyle(container).color, 'rgb(0, 128, 0)');
assert_equals(getComputedStyle(child).color, 'rgb(255, 0, 0)');
});
</script>

View file

@ -0,0 +1,30 @@
<!doctype html>
<title>CSS Selectors nested invalidation through @media by selectorText</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
.b {
color: red;
}
& {
@media screen {
&.b { color: green; }
}
}
</style>
<div id="elem" class="a b">
Test passes if color is green.
</div>
<script>
test(() => {
let elem = document.getElementById('elem');
assert_equals(getComputedStyle(elem).color, 'rgb(255, 0, 0)');
document.styleSheets[0].rules[1].selectorText = '.a';
assert_equals(getComputedStyle(elem).color, 'rgb(0, 128, 0)');
});
</script>

View file

@ -0,0 +1,52 @@
<!DOCTYPE html>
<title>CSS Nesting: CSSNestedDeclarations CSSOM (whitespace)</title>
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
// https://drafts.csswg.org/cssom/#serialize-a-css-rule
test(() => {
let s = new CSSStyleSheet();
s.replaceSync(`
.a {
& { }
left: 1px;
& { }
right: 1px;
}
`);
assert_equals(s.cssRules.length, 1);
let a_rule = s.cssRules[0];
assert_equals(a_rule.cssRules.length, 4);
for (let child_rule of a_rule.cssRules) {
child_rule.style = '';
}
assert_equals(a_rule.cssText, '.a {\n & { }\n & { }\n}');
}, 'Empty CSSNestedDeclarations do not affect outer serialization');
// https://drafts.csswg.org/cssom/#serialize-a-css-rule
test(() => {
let s = new CSSStyleSheet();
s.replaceSync(`
.a {
@media (width > 1px) {
& { }
left: 1px;
& { }
right: 1px;
}
}
`);
assert_equals(s.cssRules.length, 1);
let outer = s.cssRules[0];
assert_equals(outer.cssRules.length, 1);
// @media
let media = outer.cssRules[0];
assert_equals(media.cssRules.length, 4);
for (let child_rule of media.cssRules) {
child_rule.style = '';
}
assert_equals(media.cssText, '@media (width > 1px) {\n & { }\n & { }\n}');
}, 'Empty CSSNestedDeclarations do not affect outer serialization (nested grouping rule)');
</script>

View file

@ -0,0 +1,237 @@
<!DOCTYPE html>
<title>CSS Nesting: CSSNestedDeclarations CSSOM</title>
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
test(() => {
let s = new CSSStyleSheet();
s.replaceSync(`
.a {
& { --x:1; }
--x:2;
}
`);
assert_equals(s.cssRules.length, 1);
let outer = s.cssRules[0];
assert_equals(outer.cssRules.length, 2);
assert_equals(outer.cssRules[0].cssText, `& { --x: 1; }`);
assert_equals(outer.cssRules[1].cssText, `--x: 2;`);
}, 'Trailing declarations');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync(`
.a {
--a:1;
--b:1;
& { --c:1; }
--d:1;
--e:1;
& { --f:1; }
--g:1;
--h:1;
--i:1;
& { --j:1; }
--k:1;
--l:1;
}
`);
assert_equals(s.cssRules.length, 1);
let outer = s.cssRules[0];
assert_equals(outer.cssRules.length, 6);
assert_equals(outer.cssRules[0].cssText, `& { --c: 1; }`);
assert_equals(outer.cssRules[1].cssText, `--d: 1; --e: 1;`);
assert_equals(outer.cssRules[2].cssText, `& { --f: 1; }`);
assert_equals(outer.cssRules[3].cssText, `--g: 1; --h: 1; --i: 1;`);
assert_equals(outer.cssRules[4].cssText, `& { --j: 1; }`);
assert_equals(outer.cssRules[5].cssText, `--k: 1; --l: 1;`);
}, 'Mixed declarations');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync(`
.a {
& { --x:1; }
--y:2;
--z:3;
}
`);
assert_equals(s.cssRules.length, 1);
let outer = s.cssRules[0];
assert_equals(outer.cssRules.length, 2);
let nested_declarations = outer.cssRules[1];
assert_true(nested_declarations instanceof CSSNestedDeclarations);
assert_equals(nested_declarations.style.length, 2);
assert_equals(nested_declarations.style.getPropertyValue('--x'), '');
assert_equals(nested_declarations.style.getPropertyValue('--y'), '2');
assert_equals(nested_declarations.style.getPropertyValue('--z'), '3');
}, 'CSSNestedDeclarations.style');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync(`
.a {
@media (width > 100px) {
--x:1;
--y:1;
.b { }
--z:1;
}
--w:1;
}
`);
assert_equals(s.cssRules.length, 1);
let outer = s.cssRules[0];
assert_equals(outer.cssRules.length, 2);
// @media
let media = outer.cssRules[0];
assert_equals(media.cssRules.length, 3);
assert_true(media.cssRules[0] instanceof CSSNestedDeclarations);
assert_equals(media.cssRules[0].cssText, `--x: 1; --y: 1;`);
assert_equals(media.cssRules[1].cssText, `& .b { }`);
assert_true(media.cssRules[2] instanceof CSSNestedDeclarations);
assert_equals(media.cssRules[2].cssText, `--z: 1;`);
assert_true(outer.cssRules[1] instanceof CSSNestedDeclarations);
assert_equals(outer.cssRules[1].cssText, `--w: 1;`);
}, 'Nested group rule');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync(`
.a {
@scope (.foo) {
--x:1;
--y:1;
.b { }
--z:1;
}
--w:1;
}
`);
assert_equals(s.cssRules.length, 1);
let outer = s.cssRules[0];
if (window.CSSScopeRule) {
assert_equals(outer.cssRules.length, 2);
// @scope
let scope = outer.cssRules[0];
assert_true(scope instanceof CSSScopeRule);
assert_equals(scope.cssRules.length, 3);
assert_true(scope.cssRules[0] instanceof CSSNestedDeclarations);
assert_equals(scope.cssRules[0].cssText, `--x: 1; --y: 1;`);
assert_equals(scope.cssRules[1].cssText, `.b { }`); // Implicit :scope here.
assert_true(scope.cssRules[2] instanceof CSSNestedDeclarations);
assert_equals(scope.cssRules[2].cssText, `--z: 1;`);
assert_true(outer.cssRules[1] instanceof CSSNestedDeclarations);
assert_equals(outer.cssRules[1].cssText, `--w: 1;`);
} else {
assert_equals(outer.cssRules.length, 0);
}
}, 'Nested @scope rule');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync(`
a {
& { --x:1; }
width: 100px;
height: 200px;
color:hover {}
--y: 2;
}
`);
assert_equals(s.cssRules.length, 1);
let outer = s.cssRules[0];
assert_equals(outer.cssRules.length, 4);
assert_equals(outer.cssRules[0].cssText, `& { --x: 1; }`);
assert_equals(outer.cssRules[1].cssText, `width: 100px; height: 200px;`);
assert_equals(outer.cssRules[2].cssText, `& color:hover { }`);
assert_equals(outer.cssRules[3].cssText, `--y: 2;`);
}, 'Inner rule starting with an ident');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync('.a {}');
assert_equals(s.cssRules.length, 1);
let a_rule = s.cssRules[0];
assert_equals(a_rule.cssRules.length, 0);
a_rule.insertRule(`
width: 100px;
height: 200px;
`);
assert_equals(a_rule.cssRules.length, 1);
assert_true(a_rule.cssRules[0] instanceof CSSNestedDeclarations);
assert_equals(a_rule.cssRules[0].cssText, `width: 100px; height: 200px;`);
}, 'Inserting a CSSNestedDeclaration rule into style rule');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync('.a { @media (width > 100px) {} }');
assert_equals(s.cssRules.length, 1);
assert_equals(s.cssRules[0].cssRules.length, 1);
let media_rule = s.cssRules[0].cssRules[0];
assert_true(media_rule instanceof CSSMediaRule);
assert_equals(media_rule.cssRules.length, 0);
media_rule.insertRule(`
width: 100px;
height: 200px;
`);
assert_equals(media_rule.cssRules.length, 1);
assert_true(media_rule.cssRules[0] instanceof CSSNestedDeclarations);
assert_equals(media_rule.cssRules[0].cssText, `width: 100px; height: 200px;`);
}, 'Inserting a CSSNestedDeclaration rule into nested group rule');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync('@media (width > 100px) {}');
assert_equals(s.cssRules.length, 1);
let media_rule = s.cssRules[0];
assert_true(media_rule instanceof CSSMediaRule);
assert_equals(media_rule.cssRules.length, 0);
assert_throws_dom('SyntaxError', () => {
media_rule.insertRule(`
width: 100px;
height: 200px;
`);
});
}, 'Attempting to insert a CSSNestedDeclaration rule into top-level @media rule');
test(() => {
let sheet = new CSSStyleSheet();
assert_throws_dom('SyntaxError', () => {
sheet.insertRule(`
width: 100px;
height: 200px;
`);
});
}, 'Attempting to insert a CSSNestedDeclaration rule into a stylesheet');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync('.a {}');
assert_equals(s.cssRules.length, 1);
let a_rule = s.cssRules[0];
assert_equals(a_rule.cssRules.length, 0);
assert_throws_dom('SyntaxError', () => {
a_rule.insertRule('');
});
}, 'Attempting to insert a CSSNestedDeclaration rule, empty block');
test(() => {
let s = new CSSStyleSheet();
s.replaceSync('.a {}');
assert_equals(s.cssRules.length, 1);
let a_rule = s.cssRules[0];
assert_equals(a_rule.cssRules.length, 0);
assert_throws_dom('SyntaxError', () => {
a_rule.insertRule(`
xwidth: 100px;
xheight: 200px;
`);
});
}, 'Attempting to insert a CSSNestedDeclaration rule, all invalid declarations');
</script>

View file

@ -0,0 +1,221 @@
<!DOCTYPE html>
<title>CSS Nesting: CSSNestedDeclarations matching</title>
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
.trailing {
--x: FAIL;
& { --x: FAIL; }
--x: PASS;
}
</style>
<div class=trailing></div>
<script>
test(() => {
let e = document.querySelector('.trailing');
assert_equals(getComputedStyle(e).getPropertyValue('--x'), 'PASS');
}, 'Trailing declarations apply after any preceding rules');
</script>
<style>
.trailing_no_leading {
& { --x: FAIL; }
--x: PASS;
}
</style>
<div class=trailing_no_leading></div>
<script>
test(() => {
let e = document.querySelector('.trailing_no_leading');
assert_equals(getComputedStyle(e).getPropertyValue('--x'), 'PASS');
}, 'Trailing declarations apply after any preceding rules (no leading)');
</script>
<style>
.trailing_multiple {
--x: FAIL;
--y: FAIL;
--z: FAIL;
--w: FAIL;
& { --x: FAIL; }
--x: PASS;
--y: PASS;
& { --z: FAIL; }
--z: PASS;
--w: PASS;
}
</style>
<div class=trailing_multiple></div>
<script>
test(() => {
let e = document.querySelector('.trailing_multiple');
let s = getComputedStyle(e);
assert_equals(s.getPropertyValue('--x'), 'PASS');
assert_equals(s.getPropertyValue('--y'), 'PASS');
assert_equals(s.getPropertyValue('--z'), 'PASS');
assert_equals(s.getPropertyValue('--w'), 'PASS');
}, 'Trailing declarations apply after any preceding rules (multiple)');
</script>
<style>
.trailing_specificity {
--x: FAIL;
:is(&, div.nomatch2) { --x: PASS; } /* Specificity: (0, 1, 1) */
--x: FAIL; /* Specificity: (0, 1, 0) */
}
</style>
<div class=trailing_specificity></div>
<script>
test(() => {
let e = document.querySelector('.trailing_specificity');
assert_equals(getComputedStyle(e).getPropertyValue('--x'), 'PASS');
}, 'Nested declarations rule has same specificity as outer selector');
</script>
<style>
#nomatch, .specificity_top_level {
--x: FAIL;
:is(&, div.nomatch2) { --x: PASS; } /* Specificity: (0, 1, 1) */
--x: FAIL; /* Specificity: (0, 1, 0). In particular, this does not have
specificity like :is(#nomatch, .specificity_top_level). */
}
</style>
<div class=specificity_top_level></div>
<script>
test(() => {
let e = document.querySelector('.specificity_top_level');
assert_equals(getComputedStyle(e).getPropertyValue('--x'), 'PASS');
}, 'Nested declarations rule has top-level specificity behavior');
</script>
<style>
#nomatch, .specificity_top_level_max, div.specificity_top_level_max {
--x: FAIL;
:is(:where(&), div.nomatch2) { --x: FAIL; } /* Specificity: (0, 1, 1) */
--x: PASS; /* Specificity: (0, 1, 1) (for div.specificity_top_level_max) */
}
</style>
<div class=specificity_top_level_max></div>
<script>
test(() => {
let e = document.querySelector('.specificity_top_level_max');
assert_equals(getComputedStyle(e).getPropertyValue('--x'), 'PASS');
}, 'Nested declarations rule has top-level specificity behavior (max matching)');
</script>
<style>
.nested_pseudo::after {
--x: FAIL;
@media (width > 0px) {
--x: PASS;
}
}
</style>
<div class=nested_pseudo></div>
<script>
test(() => {
let e = document.querySelector('.nested_pseudo');
assert_equals(getComputedStyle(e, '::after').getPropertyValue('--x'), 'PASS');
}, 'Bare declartaion in nested grouping rule can match pseudo-element');
</script>
<style>
#nomatch, .nested_group_rule {
--x: FAIL;
@media (width > 0px) {
--x: FAIL; /* Specificity: (0, 1, 0) */
}
--x: PASS;
}
</style>
<div class=nested_group_rule></div>
<script>
test(() => {
let e = document.querySelector('.nested_group_rule');
assert_equals(getComputedStyle(e).getPropertyValue('--x'), 'PASS');
}, 'Nested group rules have top-level specificity behavior');
</script>
<style>
.nested_scope_rule {
div:where(&) { /* Specificity: (0, 0, 1) */
--x: PASS;
}
@scope (&) {
--x: FAIL; /* Specificity: (0, 0, 0) */
}
}
</style>
<div class=nested_scope_rule></div>
<script>
test(() => {
let e = document.querySelector('.nested_scope_rule');
assert_equals(getComputedStyle(e).getPropertyValue('--x'), 'PASS');
}, 'Nested @scope rules behave like :where(:scope)');
</script>
<style>
.nested_scope_rule_trailing {
div:where(&) { /* Specificity: (0, 0, 1) */
--x: PASS;
}
@scope (&) {
--ignored: 1;
.ignored {}
--x: FAIL; /* Specificity: (0, 0, 0) */
}
}
</style>
<div class=nested_scope_rule_trailing></div>
<script>
test(() => {
let e = document.querySelector('.nested_scope_rule_trailing');
assert_equals(getComputedStyle(e).getPropertyValue('--x'), 'PASS');
}, 'Nested @scope rules behave like :where(:scope) (trailing)');
</script>
<style id=set_parent_selector_text_style>
.set_parent_selector_text {
div {
color: red;
}
.a1 {
.ignored {}
color: green;
}
}
</style>
<div class=set_parent_selector_text>
<div class=a1>A1</div>
<div class=a2>A2</div>
</div>
<script>
test(() => {
let a1 = document.querySelector('.set_parent_selector_text > .a1');
let a2 = document.querySelector('.set_parent_selector_text > .a2');
assert_equals(getComputedStyle(a1).color, 'rgb(0, 128, 0)');
assert_equals(getComputedStyle(a2).color, 'rgb(255, 0, 0)');
let rules = set_parent_selector_text_style.sheet.cssRules;
assert_equals(rules.length, 1);
assert_equals(rules[0].cssRules.length, 2);
let a_rule = rules[0].cssRules[1];
assert_equals(a_rule.selectorText, '& .a1');
a_rule.selectorText = '.a2';
assert_equals(a_rule.selectorText, '& .a2');
assert_equals(getComputedStyle(a1).color, 'rgb(255, 0, 0)');
assert_equals(getComputedStyle(a2).color, 'rgb(0, 128, 0)');
}, 'Nested declarations rule responds to parent selector text change');
</script>

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<title>CSS Nesting: Style invalidates after CSSOM mutations to nested rules</title>
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/#nested-style-rule">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/#nested-group-rules">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style id=set_parent_selector_text_style>
.set_parent_selector_text {
div {
color: red;
}
.a1 {
color: green;
}
}
</style>
<div class=set_parent_selector_text>
<div class=a1>A1</div>
<div class=a2>A2</div>
</div>
<script>
test(() => {
let a1 = document.querySelector('.set_parent_selector_text > .a1');
let a2 = document.querySelector('.set_parent_selector_text > .a2');
assert_equals(getComputedStyle(a1).color, 'rgb(0, 128, 0)');
assert_equals(getComputedStyle(a2).color, 'rgb(255, 0, 0)');
let rules = set_parent_selector_text_style.sheet.cssRules;
assert_equals(rules.length, 1);
assert_equals(rules[0].cssRules.length, 2);
let a_rule = rules[0].cssRules[1];
assert_equals(a_rule.selectorText, '& .a1');
a_rule.selectorText = '.a2';
assert_equals(getComputedStyle(a1).color, 'rgb(255, 0, 0)');
assert_equals(getComputedStyle(a2).color, 'rgb(0, 128, 0)');
}, 'Nested rule responds to parent selector text change');
</script>

View file

@ -0,0 +1,52 @@
<!DOCTYPE html>
<title>Nested @layers</title>
<link rel="help" href="https://drafts.csswg.org/css-nesting/#nested-group-rules">
<link rel="help" href="https://drafts.csswg.org/css-cascade-5/#layering">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
.a {
/* This should have no effect. Only at-rules containing style rules
are vaild when nested. */
@layer theme, base;
}
/* The theme layer wins over the base layer. */
@layer base, theme;
.a {
@layer theme {
& {
z-index: 1;
}
.b {
background-color: green;
}
}
}
@layer base {
.a {
z-index: 0;
}
.a .b {
background-color: red;
}
}
</style>
<main>
<div class="a">
<div class="b">
</div>
</div>
</main>
<script>
test(() => {
let a = document.querySelector("main > .a");
let b = document.querySelector("main > .a > .b");
assert_equals(getComputedStyle(a).zIndex, "1");
assert_equals(getComputedStyle(b).backgroundColor, "rgb(0, 128, 0)");
}, '@layer can be nested');
</script>

View file

@ -0,0 +1,87 @@
<!doctype html>
<title>CSS Selectors parsing</title>
<link rel="author" title="Adam Argyle" href="mailto:argyle@google.com">
<link rel="author" title="Tab Atkins-Bittner" href="https://tabatkins.com/contact/">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style id="test-sheet"></style>
<script>
let [ss] = document.styleSheets
function resetStylesheet() {
while (ss.rules.length)
ss.removeRule(0)
}
function testNestedSelector(sel, {expected=sel, parent=".foo"}={}) {
resetStylesheet();
const ruleText = `${parent} { ${sel} { color: green; }}`
test(()=>{
ss.insertRule(ruleText);
assert_equals(ss.rules.length, 1, "Outer rule should exist.");
const rule = ss.rules[0];
assert_equals(rule.cssRules.length, 1, "Inner rule should exist.");
const innerRule = rule.cssRules[0];
assert_equals(innerRule.selectorText, expected, `Inner rule's selector should be "${expected}".`);
}, ruleText);
}
function testInvalidNestingSelector(sel, {parent=".foo"}={}) {
resetStylesheet();
const ruleText = `${parent} { ${sel} { color: green; }}`
test(()=>{
ss.insertRule(ruleText);
assert_equals(ss.rules.length, 1, "Outer rule should exist.");
const rule = ss.rules[0];
assert_equals(rule.cssRules.length, 0, "Inner rule should not exist.");
}, "INVALID: " + ruleText);
}
// basic usage
testNestedSelector("&");
testNestedSelector("&.bar");
testNestedSelector("& .bar");
testNestedSelector("& > .bar");
// relative selector
testNestedSelector("> .bar", {expected:"& > .bar"});
testNestedSelector("> & .bar", {expected:"& > & .bar"});
testNestedSelector("+ .bar &", {expected:"& + .bar &"});
testNestedSelector("+ .bar, .foo, > .baz", {expected:"& + .bar, & .foo, & > .baz"});
// implicit relative (and not)
testNestedSelector(".foo", {expected:"& .foo"});
testNestedSelector(".test > & .bar");
testNestedSelector(".foo, .foo &", {expected:"& .foo, .foo &"});
testNestedSelector(".foo, .bar", {expected:"& .foo, & .bar"});
testNestedSelector(":is(.bar, .baz)", {expected:"& :is(.bar, .baz)"});
testNestedSelector("&:is(.bar, .baz)");
testNestedSelector(":is(.bar, &.baz)");
testNestedSelector("&:is(.bar, &.baz)");
// Mixing nesting selector with other simple selectors
testNestedSelector("div&");
testInvalidNestingSelector("&div"); // type selector must be first
testNestedSelector(".class&");
testNestedSelector("&.class");
testNestedSelector("[attr]&");
testNestedSelector("&[attr]");
testNestedSelector("#id&");
testNestedSelector("&#id");
testNestedSelector(":hover&");
testNestedSelector("&:hover");
testNestedSelector(":is(div)&");
testNestedSelector("&:is(div)");
// Multiple nesting selectors
testNestedSelector("& .bar & .baz & .qux");
testNestedSelector("&&");
// Selector list in inner rule
testNestedSelector("& > section, & > article");
// Selector list in both inner and outer rule.
testNestedSelector("& + .baz, &.qux", {parent:".foo, .bar"});
</script>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<meta charset="UTF-8">
<title>Nesting pseudo element selectors should not crash</title>
<link rel="help" href="https://crbug.com/1376227">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7912">
<style>
div::part(x) {
& {
color: red;
}
}
</style>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<style>
.foo {
::before:where(&) {
color: red;
}
}
</style>
<div class=foo></div>

View file

@ -0,0 +1,91 @@
<!doctype html>
<title>Serialization of declarations in group rules</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7850">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style id="test-sheet"></style>
<script>
function serialize(cssText) {
let [ss] = document.styleSheets;
while (ss.rules.length) {
ss.removeRule(0)
}
ss.insertRule(cssText);
return ss.rules[0].cssText;
}
function assert_unchanged(cssText, description) {
test(() => {
assert_equals(serialize(cssText), cssText, description);
}, description);
}
function assert_becomes(cssText, serializedCssText, description) {
test(() => {
assert_equals(serialize(cssText), serializedCssText, description);
}, description);
}
assert_unchanged(
"@media screen {\n div { color: red; background-color: green; }\n}",
"Declarations are serialized on one line, rules on two."
)
assert_becomes(
"div { @media screen { color: red; background-color: green; } }",
"div {\n @media screen {\n color: red; background-color: green;\n}\n}",
"Mixed declarations/rules are on two lines."
);
assert_becomes(
"div {\n @supports selector(&) { color: red; background-color: green; } &:hover { color: navy; } }",
"div {\n @supports selector(&) {\n color: red; background-color: green;\n}\n &:hover { color: navy; }\n}",
"Implicit rule is serialized",
);
assert_unchanged("div {\n @media screen {\n & { color: red; }\n}\n}", "Implicit rule not removed");
assert_becomes(
"div { @media screen { & { color: red; &:hover { } } }",
"div {\n @media screen {\n & {\n color: red;\n &:hover { }\n}\n}\n}",
"Implicit + empty hover rule"
);
assert_becomes(
"div { @media screen { &.cls { color: red; } & { color: red; }",
"div {\n @media screen {\n &.cls { color: red; }\n & { color: red; }\n}\n}",
"Implicit like rule not in first position"
);
assert_becomes(
"div { @media screen { & { color: red; } & { color: red; }",
"div {\n @media screen {\n & { color: red; }\n & { color: red; }\n}\n}",
"Two implicit-like rules"
);
assert_becomes(
"div { @media screen { color: red; & { color: red; }",
"div {\n @media screen {\n color: red;\n & { color: red; }\n}\n}",
"Implicit like rule after decls"
);
assert_becomes(
"div { @media screen { color: red; & { color: blue; }",
"div {\n @media screen {\n color: red;\n & { color: blue; }\n}\n}",
"Implicit like rule after decls, missing closing braces"
);
assert_becomes(
"div { @media screen { &, p > & { color: blue; }",
"div {\n @media screen {\n &, p > & { color: blue; }\n}\n}",
"Implicit like rule with other selectors"
);
assert_becomes(
"div { & { color: red; } }",
"div {\n & { color: red; }\n}",
"Implicit-like rule in style rule"
);
// Empty rules (confusingly?) serialize different between style rules
// and conditional group rules.
assert_unchanged("@media screen {\n}", "Empty conditional rule");
assert_unchanged("div { }", "Empty style rule");
assert_unchanged("div {\n @media screen {\n}\n}", "Empty conditional inside style rule");
assert_unchanged("@media screen {\n div { }\n}", "Empty style inside conditional");
</script>

View file

@ -0,0 +1,30 @@
<!doctype html>
<title>Top-level & is treated like :scope</title>
<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<div id="p">
<div class="match" id="level1">
<div class="match" id="level2"></div>
</div>
</div>
<script>
test(() => {
let matched = [];
for (const elem of p.querySelectorAll('& .match')) {
matched.push(elem.getAttribute('id'));
}
assert_array_equals(matched, ['level1', 'level2']);
}, '& as direct ancestor');
test(() => {
let matched = [];
for (const elem of p.querySelectorAll('& > .match')) {
matched.push(elem.getAttribute('id'));
}
assert_array_equals(matched, ['level1']);
}, '& matches scoped element only, not everything');
</script>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<title>CSS Nesting: Specificity of top-level '&'</title>
<link rel="help" href="https://drafts.csswg.org/css-nesting-1">
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/10196">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<style>
/* Note: at the top level, '&' matches like ':root'. */
/* Should have zero specificity: */
& { color: red; }
/* Should also have zero specificity: */
:where(&) { color: green; }
</style>
<script>
test(() => {
assert_equals(getComputedStyle(document.documentElement).color, 'rgb(0, 128, 0)');
});
</script>

View file

@ -0,0 +1,2 @@
.a {}
.b { .c {} }