summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-nesting
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /testing/web-platform/tests/css/css-nesting
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/css-nesting')
-rw-r--r--testing/web-platform/tests/css/css-nesting/conditional-properties-ref.html33
-rw-r--r--testing/web-platform/tests/css/css-nesting/conditional-properties.html34
-rw-r--r--testing/web-platform/tests/css/css-nesting/conditional-rules-ref.html64
-rw-r--r--testing/web-platform/tests/css/css-nesting/conditional-rules.html75
-rw-r--r--testing/web-platform/tests/css/css-nesting/cssom.html187
-rw-r--r--testing/web-platform/tests/css/css-nesting/delete-other-rule-crash.html15
-rw-r--r--testing/web-platform/tests/css/css-nesting/implicit-nesting-ref.html27
-rw-r--r--testing/web-platform/tests/css/css-nesting/implicit-nesting.html82
-rw-r--r--testing/web-platform/tests/css/css-nesting/implicit-parent-insertion-crash.html16
-rw-r--r--testing/web-platform/tests/css/css-nesting/invalid-inner-rules.html56
-rw-r--r--testing/web-platform/tests/css/css-nesting/invalidation-001.html33
-rw-r--r--testing/web-platform/tests/css/css-nesting/invalidation-002.html33
-rw-r--r--testing/web-platform/tests/css/css-nesting/invalidation-003.html34
-rw-r--r--testing/web-platform/tests/css/css-nesting/invalidation-004.html30
-rw-r--r--testing/web-platform/tests/css/css-nesting/nest-containing-forgiving-ref.html18
-rw-r--r--testing/web-platform/tests/css/css-nesting/nest-containing-forgiving.html27
-rw-r--r--testing/web-platform/tests/css/css-nesting/nesting-basic-ref.html30
-rw-r--r--testing/web-platform/tests/css/css-nesting/nesting-basic.html112
-rw-r--r--testing/web-platform/tests/css/css-nesting/nesting-type-selector.html18
-rw-r--r--testing/web-platform/tests/css/css-nesting/parsing.html47
-rw-r--r--testing/web-platform/tests/css/css-nesting/pseudo-part-crash.html12
-rw-r--r--testing/web-platform/tests/css/css-nesting/serialize-group-rules-with-decls.tentative.html69
-rw-r--r--testing/web-platform/tests/css/css-nesting/supports-is-consistent-ref.html5
-rw-r--r--testing/web-platform/tests/css/css-nesting/supports-is-consistent.html17
-rw-r--r--testing/web-platform/tests/css/css-nesting/top-level-is-scope.html30
25 files changed, 1104 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-nesting/conditional-properties-ref.html b/testing/web-platform/tests/css/css-nesting/conditional-properties-ref.html
new file mode 100644
index 0000000000..0285acbf33
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/conditional-properties-ref.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/conditional-properties.html b/testing/web-platform/tests/css/css-nesting/conditional-properties.html
new file mode 100644
index 0000000000..122b8635c1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/conditional-properties.html
@@ -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="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>
diff --git a/testing/web-platform/tests/css/css-nesting/conditional-rules-ref.html b/testing/web-platform/tests/css/css-nesting/conditional-rules-ref.html
new file mode 100644
index 0000000000..c4fabd672a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/conditional-rules-ref.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/conditional-rules.html b/testing/web-platform/tests/css/css-nesting/conditional-rules.html
new file mode 100644
index 0000000000..55ecc1f9e4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/conditional-rules.html
@@ -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="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 (.test-12) {
+ :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>
+ <div class="test"><div class="test-13"></div></div>
+</body>
diff --git a/testing/web-platform/tests/css/css-nesting/cssom.html b/testing/web-platform/tests/css/css-nesting/cssom.html
new file mode 100644
index 0000000000..b5bd80d1b4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/cssom.html
@@ -0,0 +1,187 @@
+<!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"></style>
+
+<script>
+ test(() => {
+ let [ss] = document.styleSheets;
+ assert_equals(ss.cssRules.length, 0);
+ ss.insertRule('.a { color: red; }');
+ assert_equals(ss.cssRules.length, 1);
+ assert_equals(ss.cssRules[0].cssText, '.a { color: red; }');
+
+ // Test inserting sub-cssRules, at various positions.
+ ss.cssRules[0].insertRule('& .b { color: green; }');
+ ss.cssRules[0].insertRule('& .c { color: blue; }', 1);
+ ss.cssRules[0].insertRule('& .d { color: hotpink; }', 1);
+ assert_equals(ss.cssRules[0].cssText,
+`.a {
+ color: red;
+ & .b { color: green; }
+ & .d { color: hotpink; }
+ & .c { color: blue; }
+}`, 'inserting should work');
+
+ // Test deleting a rule.
+ ss.cssRules[0].deleteRule(1);
+ assert_equals(ss.cssRules[0].cssText,
+`.a {
+ color: red;
+ & .b { color: green; }
+ & .c { color: blue; }
+}`, 'deleting should work');
+ });
+
+ // Test that out-of-bounds throws exceptions and does not affect the stylesheet.
+ const sampleSheetText =
+`.a {
+ color: red;
+ & .b { color: green; }
+ & .c { color: blue; }
+}`;
+
+ test(() => {
+ document.getElementById('ss').innerHTML = sampleSheetText;
+ let [ss] = document.styleSheets;
+ assert_throws_dom('IndexSizeError', () => { ss.cssRules[0].insertRule('& .broken {}', 3); });
+ assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after no-insert');
+ });
+
+ test(() => {
+ document.getElementById('ss').innerHTML = sampleSheetText;
+ let [ss] = document.styleSheets;
+ assert_throws_dom('IndexSizeError', () => { ss.cssRules[0].insertRule('& .broken {}', -1); });
+ assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after no-insert');
+ });
+
+ test(() => {
+ document.getElementById('ss').innerHTML = sampleSheetText;
+ let [ss] = document.styleSheets;
+ assert_throws_dom('IndexSizeError', () => { ss.cssRules[0].deleteRule(5); });
+ assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after no-delete');
+ });
+
+ test(() => {
+ document.getElementById('ss').innerHTML = sampleSheetText;
+ let [ss] = document.styleSheets;
+ assert_equals(ss.cssRules[0].cssRules[2], undefined, 'subscript out-of-bounds returns undefined');
+ assert_equals(ss.cssRules[0].cssRules.item(2), null, 'item() out-of-bounds returns null');
+ assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after no-access');
+ });
+
+ // Test that inserting an invalid rule throws an exception.
+ test(() => {
+ document.getElementById('ss').innerHTML = sampleSheetText;
+ let [ss] = document.styleSheets;
+ let exception;
+ assert_throws_dom('SyntaxError', () => { ss.cssRules[0].insertRule('% {}'); });
+ assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after invalid rule');
+ });
+
+ // Test that we can get out single rule through .cssRules.
+ test(() => {
+ document.getElementById('ss').innerHTML = sampleSheetText;
+ let [ss] = document.styleSheets;
+ assert_equals(ss.cssRules[0].cssRules[1].cssText, '& .c { color: blue; }');
+ });
+
+ // Test that we can insert a @supports rule, that it serializes in the right place
+ // and has the right parent. Note that the indentation is broken per-spec.
+ test(() => {
+ document.getElementById('ss').innerHTML = sampleSheetText;
+ let [ss] = document.styleSheets;
+ ss.cssRules[0].insertRule('@supports selector(&) { & div { font-size: 10px; }}', 1);
+ assert_equals(ss.cssRules[0].cssText,
+`.a {
+ color: red;
+ & .b { color: green; }
+ @supports selector(&) {
+ & div { font-size: 10px; }
+}
+ & .c { color: blue; }
+}`, '@supports is added');
+
+ assert_equals(ss.cssRules[0].cssRules[1].parentRule, ss.cssRules[0]);
+ ss.cssRules[0].deleteRule(1);
+ assert_equals(ss.cssRules[0].cssText, sampleSheetText);
+ });
+
+ // Nested rules are not part of declaration lists, and thus should not
+ // be possible to insert with .style.
+ test(() => {
+ document.getElementById('ss').innerHTML = sampleSheetText;
+ let [ss] = document.styleSheets;
+ ss.cssRules[0].style = 'color: olivedrab; &.d { color: peru; }';
+ assert_equals(ss.cssRules[0].cssText,
+`.a {
+ color: olivedrab;
+ & .b { color: green; }
+ & .c { color: blue; }
+}`, 'color is changed, new rule is ignored');
+ });
+
+ // We cannot insert anything starting with an tag, as that would cause
+ // the serialized rule not to parse back. Compounds starting with a tag
+ // that are _not_ the first compound in a complex selector are OK, though,
+ // as are complex selectors that are not the first in the list.
+ test(() => {
+ document.getElementById('ss').innerHTML = sampleSheetText;
+ let [ss] = document.styleSheets;
+ ss.cssRules[0].cssRules[0].selectorText = 'div.b .c &'; // Ignored.
+ ss.cssRules[0].cssRules[1].selectorText = '.c div.b &, div &'; // Allowed.
+ assert_throws_dom('SyntaxError', () => { ss.cssRules[0].insertRule('div & {}'); });
+ assert_equals(ss.cssRules[0].cssText,
+`.a {
+ color: red;
+ & .b { color: green; }
+ .c div.b &, div & { color: blue; }
+}`, 'one rule is kept unchanged, the other is changed');
+ });
+
+ // Rules that are dropped in forgiving parsing but that contain &,
+ // must still be serialized out as they were.
+ test(() => {
+ const text = '.a { :is(!& .foo, .b) { color: green; } }';
+ document.getElementById('ss').innerHTML = text;
+ let [ss] = document.styleSheets;
+ assert_equals(ss.cssRules[0].cssText,
+`.a {
+ :is(!& .foo, .b) { color: green; }
+}`, 'invalid rule containing ampersand is kept in serialization');
+ });
+
+ test((t) => {
+ let main = document.createElement('main');
+ main.innerHTML = `
+ <style>
+ .a {
+ & { z-index:1; }
+ & #inner1 { z-index:1; }
+ .stuff, :is(&) #inner2 { z-index:1; }
+ }
+ </style>
+ <div id="outer" class="b">
+ <div id="inner1"></div>
+ <div id="inner2"></div>
+ </div>
+ `;
+ document.documentElement.append(main);
+ t.add_cleanup(() => main.remove());
+
+ assert_equals(getComputedStyle(outer).zIndex, 'auto');
+ assert_equals(getComputedStyle(inner1).zIndex, 'auto');
+ assert_equals(getComputedStyle(inner2).zIndex, 'auto');
+
+ // .a => .b
+ main.firstElementChild.sheet.cssRules[0].selectorText = '.b';
+
+ assert_equals(getComputedStyle(outer).zIndex, '1');
+ assert_equals(getComputedStyle(inner1).zIndex, '1');
+ assert_equals(getComputedStyle(inner2).zIndex, '1');
+ }, 'Mutating the selectorText of outer rule invalidates inner rules');
+</script>
diff --git a/testing/web-platform/tests/css/css-nesting/delete-other-rule-crash.html b/testing/web-platform/tests/css/css-nesting/delete-other-rule-crash.html
new file mode 100644
index 0000000000..bde7c554e8
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/delete-other-rule-crash.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/implicit-nesting-ref.html b/testing/web-platform/tests/css/css-nesting/implicit-nesting-ref.html
new file mode 100644
index 0000000000..0057a67fd0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/implicit-nesting-ref.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/implicit-nesting.html b/testing/web-platform/tests/css/css-nesting/implicit-nesting.html
new file mode 100644
index 0000000000..0a76dedc5b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/implicit-nesting.html
@@ -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="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>
diff --git a/testing/web-platform/tests/css/css-nesting/implicit-parent-insertion-crash.html b/testing/web-platform/tests/css/css-nesting/implicit-parent-insertion-crash.html
new file mode 100644
index 0000000000..4be1e1c8f9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/implicit-parent-insertion-crash.html
@@ -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>
+
diff --git a/testing/web-platform/tests/css/css-nesting/invalid-inner-rules.html b/testing/web-platform/tests/css/css-nesting/invalid-inner-rules.html
new file mode 100644
index 0000000000..ffb53420e2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/invalid-inner-rules.html
@@ -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 @layer 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>
diff --git a/testing/web-platform/tests/css/css-nesting/invalidation-001.html b/testing/web-platform/tests/css/css-nesting/invalidation-001.html
new file mode 100644
index 0000000000..a9a4284cc3
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/invalidation-001.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/invalidation-002.html b/testing/web-platform/tests/css/css-nesting/invalidation-002.html
new file mode 100644
index 0000000000..8419c4526e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/invalidation-002.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/invalidation-003.html b/testing/web-platform/tests/css/css-nesting/invalidation-003.html
new file mode 100644
index 0000000000..d1d6d4b9ae
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/invalidation-003.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/invalidation-004.html b/testing/web-platform/tests/css/css-nesting/invalidation-004.html
new file mode 100644
index 0000000000..a66c47cf16
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/invalidation-004.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/nest-containing-forgiving-ref.html b/testing/web-platform/tests/css/css-nesting/nest-containing-forgiving-ref.html
new file mode 100644
index 0000000000..36b07c92b6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/nest-containing-forgiving-ref.html
@@ -0,0 +1,18 @@
+<!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>
+</body>
diff --git a/testing/web-platform/tests/css/css-nesting/nest-containing-forgiving.html b/testing/web-platform/tests/css/css-nesting/nest-containing-forgiving.html
new file mode 100644
index 0000000000..d399142f7e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/nest-containing-forgiving.html
@@ -0,0 +1,27 @@
+<!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="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;
+ }
+ }
+
+ body * + * {
+ margin-top: 8px;
+ }
+</style>
+<body>
+ <p>Tests pass if <strong>block is green</strong></p>
+ <div class="test test-1"></div>
+</body>
diff --git a/testing/web-platform/tests/css/css-nesting/nesting-basic-ref.html b/testing/web-platform/tests/css/css-nesting/nesting-basic-ref.html
new file mode 100644
index 0000000000..8038f369cd
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/nesting-basic-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>Basic 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: 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>
+ <div class="test"></div>
+ <div class="test"></div>
+ <div class="test"></div>
+</body>
diff --git a/testing/web-platform/tests/css/css-nesting/nesting-basic.html b/testing/web-platform/tests/css/css-nesting/nesting-basic.html
new file mode 100644
index 0000000000..19ff48e4a2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/nesting-basic.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<title>Basic 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="nesting-basic-ref.html">
+<style>
+ .test {
+ background-color: red;
+ width: 30px;
+ height: 30px;
+ display: grid;
+ }
+
+ .test-1 {
+ & > div {
+ background-color: green;
+ }
+ }
+
+ .test-2 {
+ & > div {
+ background-color: green;
+ }
+ }
+
+ .test-3 {
+ & .test-3-child {
+ background-color: green;
+ }
+ }
+
+ span > b {
+ .test-4 section & {
+ display: inline-block;
+ background-color: green;
+ width: 100%;
+ height: 100%;
+ }
+
+ .test-4 section > & {
+ background-color: red;
+ }
+ }
+
+ .test-6 {
+ &.test {
+ background-color: green;
+ }
+ }
+
+ .test-7, .t7- {
+ & + .test-7-child, &.t7-- {
+ background-color: green;
+ }
+ }
+
+ .test-8 {
+ & {
+ background-color: green;
+ }
+ }
+
+ .test-9 {
+ &:is(.t9-, &.t9--) {
+ background-color: green;
+ }
+ }
+
+ .test-10 {
+ & {
+ background-color: green;
+ }
+ background-color: red;
+ }
+
+ .test-11 {
+ & {
+ background-color: red;
+ }
+ background-color: green !important;
+ }
+
+ /* & at top level counts as :scope, i.e. the root element here */
+ & .test-12 {
+ background-color: green;
+ }
+ & > .test-12 {
+ background-color: red !important;
+ }
+
+ 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></div></div>
+ <div class="test test-3"><div class="test-3-child"></div></div>
+ <div class="test test-4">
+ <section>
+ <span><b></b></span>
+ </section>
+ </div>
+ <div class="test test-6"><div></div></div>
+ <div class="test t7- t7--"><div class="test-7-child"></div></div>
+ <div class="test test-8"><div></div></div>
+ <div class="test test-9 t9-- t9-"><div></div></div>
+ <div class="test test-10"><div></div></div>
+ <div class="test test-11"><div></div></div>
+ <div class="test test-12"></div>
+</body>
diff --git a/testing/web-platform/tests/css/css-nesting/nesting-type-selector.html b/testing/web-platform/tests/css/css-nesting/nesting-type-selector.html
new file mode 100644
index 0000000000..1805896b8d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/nesting-type-selector.html
@@ -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="/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>
diff --git a/testing/web-platform/tests/css/css-nesting/parsing.html b/testing/web-platform/tests/css/css-nesting/parsing.html
new file mode 100644
index 0000000000..c22eaada3c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/parsing.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<title>CSS Selectors parsing</title>
+<link rel="author" title="Adam Argyle" href="mailto:argyle@google.com">
+<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
+
+ const beforeEach = () => {
+ while (ss.rules.length)
+ ss.removeRule(0)
+ }
+
+ const testRules = [
+ `.foo {\n & { color: green; }\n}`, // 🐰
+ `.foo {\n &.bar { color: green; }\n}`,
+ `.foo {\n & .bar { color: green; }\n}`,
+ `.foo {\n & > .bar { color: green; }\n}`,
+ `.foo {\n > .bar { color: green; }\n}`,
+ `.foo {\n > & .bar { color: green; }\n}`,
+ `.foo {\n + .bar & { color: green; }\n}`,
+ `.foo {\n .test > & .bar { color: green; }\n}`,
+ `.foo {\n + .bar, .foo, > .lol { color: green; }\n}`,
+ `.foo {\n &:is(.bar, &.baz) { color: green; }\n}`,
+ `.foo {\n .bar& { color: green; }\n}`,
+ `.foo {\n .bar & { color: green; }\n}`,
+ `.foo {\n .bar > & { color: green; }\n}`,
+ `.foo, .bar {\n & + .baz, &.qux { color: green; }\n}`,
+ `.foo {\n & .bar & .baz & .qux { color: green; }\n}`,
+ `.foo {\n @media (min-width: 50px) { color: green; }\n}`,
+ `main {\n & > section, & > article {\n & > header { color: green; }\n}\n}`,
+ ]
+
+ testRules.forEach(testRule => {
+ test(function() {
+ beforeEach()
+ ss.insertRule(testRule)
+ // todo?
+ // when parsing is being ready/prototyped,
+ // switch to crawling nested rules instead of comparing text
+ assert_equals(ss.rules[0].cssText, testRule)
+ }, testRule)
+ })
+</script>
diff --git a/testing/web-platform/tests/css/css-nesting/pseudo-part-crash.html b/testing/web-platform/tests/css/css-nesting/pseudo-part-crash.html
new file mode 100644
index 0000000000..3ab521d71c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/pseudo-part-crash.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/serialize-group-rules-with-decls.tentative.html b/testing/web-platform/tests/css/css-nesting/serialize-group-rules-with-decls.tentative.html
new file mode 100644
index 0000000000..c3b6bb7be7
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/serialize-group-rules-with-decls.tentative.html
@@ -0,0 +1,69 @@
+<!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) {
+ assert_equals(serialize(cssText), cssText);
+ }
+
+ function assert_becomes(cssText, serializedCssText) {
+ assert_equals(serialize(cssText), serializedCssText);
+ }
+
+ // Declarations are serialized on one line, rules on two.
+ test(() => {
+ assert_unchanged("@media screen {\n div { color: red; background-color: green; }\n}");
+ assert_unchanged("div {\n @media screen { color: red; background-color: green; }\n}");
+ });
+
+ // Mixed declarations/rules are on two lines.
+ test(() => {
+ assert_unchanged("div {\n @supports selector(&) {\n color: red; background-color: green;\n &:hover { color: navy; }\n}\n}");
+ });
+
+ // & {} rules are removed if and only if they are first, and they have no children.
+ test(() => {
+ assert_becomes("div { @media screen { & { color: red; } }",
+ "div {\n @media screen { color: red; }\n}");
+ assert_becomes("div { @media screen { & { color: red; &:hover { } } }",
+ "div {\n @media screen {\n & {\n color: red;\n &:hover { }\n}\n}\n}");
+ assert_becomes("div { @media screen { &.cls { color: red; } & { color: red; }",
+ "div {\n @media screen {\n &.cls { color: red; }\n & { color: red; }\n}\n}");
+ assert_becomes("div { @media screen { & { color: red; } & { color: red; }",
+ "div {\n @media screen {\n color: red;\n & { color: red; }\n}\n}");
+ assert_becomes("div { @media screen { color: red; & { color: red; }",
+ "div {\n @media screen {\n color: red;\n & { color: red; }\n}\n}");
+ assert_becomes("div { @media screen { color: red; & { color: blue; }",
+ "div {\n @media screen {\n color: red;\n & { color: blue; }\n}\n}");
+ assert_becomes("div { @media screen { &, p > & { color: blue; }",
+ "div {\n @media screen {\n &, p > & { color: blue; }\n}\n}");
+ });
+
+ // They are not removed from regular rules.
+ test(() => {
+ assert_becomes("div { & { color: red; } }", "div {\n & { color: red; }\n}");
+ });
+
+ // Empty rules (confusingly?) serialize different between style rules
+ // and conditional group rules.
+ test(() => {
+ assert_unchanged("@media screen {\n}");
+ assert_unchanged("div { }");
+ assert_unchanged("div {\n @media screen {\n}\n}");
+ assert_unchanged("@media screen {\n div { }\n}");
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-nesting/supports-is-consistent-ref.html b/testing/web-platform/tests/css/css-nesting/supports-is-consistent-ref.html
new file mode 100644
index 0000000000..0eb8cd16a1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/supports-is-consistent-ref.html
@@ -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>
diff --git a/testing/web-platform/tests/css/css-nesting/supports-is-consistent.html b/testing/web-platform/tests/css/css-nesting/supports-is-consistent.html
new file mode 100644
index 0000000000..8be0d666d6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/supports-is-consistent.html
@@ -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="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>
diff --git a/testing/web-platform/tests/css/css-nesting/top-level-is-scope.html b/testing/web-platform/tests/css/css-nesting/top-level-is-scope.html
new file mode 100644
index 0000000000..fefc77d5d0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-nesting/top-level-is-scope.html
@@ -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>