wpt / css / css-nesting / cssom.html

Status: FAIL | Duration: 53ms | Subtests: 0/14 | Open test on wpt.live | wpt.fyi

Data from commit:
a50cb89 wpt: run reference-named files which are themselves tests (#836) (2026-09-03)

SubtestStatusMessage
CSSStyleRule is a CSSGroupingRuleFAILCSSStyleRule is not defined
Simple CSSOM manipulation of subrulesFAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 1FAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 2FAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 3FAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 4FAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 5FAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 6FAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 7FAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 8FAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 9FAILCannot destructure 'undefined' value
Simple CSSOM manipulation of subrules 10FAILCannot destructure 'undefined' value
Mutating the selectorText of outer rule invalidates inner rulesFAILouter is not defined
Manipulation of nested declarations through CSSOMFAILouter is not defined

/css/css-nesting/cssom.html

<!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(() => {
    assert_equals(CSSStyleRule.__proto__, CSSGroupingRule);
  }, "CSSStyleRule is a CSSGroupingRule");

  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');
  });

  test(() => {
    document.getElementById('ss').innerHTML = sampleSheetText;
    let [ss] = document.styleSheets;
    ss.cssRules[0].cssRules[0].selectorText = 'div.b .c &';  // Allowed
    ss.cssRules[0].cssRules[1].selectorText = '.c div.b &, div &';  // Allowed.
    ss.cssRules[0].insertRule('div & {}'); // Allowed.
    assert_equals(ss.cssRules[0].cssText,
`.a {
  color: red;
  div & { }
  div.b .c & { color: green; }
  .c div.b &, div & { color: blue; }
}`, 'selectorText and insertRule');
  });

  // 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');

  // CSSNestedDeclarations
  test((t) => {
    const main = document.createElement('main');
    main.innerHTML = `
      <style id="main_ss">
        div {
          z-index: 1;
          &.test { foo:bar; }
        }
      </style>
      <div id="outer" class="test">
      </div>
    `;
    document.documentElement.append(main);
    t.add_cleanup(() => main.remove());
    assert_equals(getComputedStyle(outer).zIndex, '1');
    const main_ss = document.getElementById("main_ss").sheet;
    const rule = main_ss.cssRules[0];
    assert_equals(rule.cssRules.length, 1);
    rule.insertRule('z-index: 3;');
    assert_equals(rule.cssRules.length, 2);
    assert_equals(getComputedStyle(outer).zIndex, '3');

    // Throw only when no valid declaration  https://github.com/w3c/csswg-drafts/issues/10520
    assert_throws_dom('SyntaxError', () => { rule.insertRule('nothing-to-insert-because-invalid-property-should-throw: 2;'); });
    assert_equals(rule.cssRules.length, 2);

    // Test the insertion of nested declarations inside grouping rule
    rule.insertRule('@media screen { a { color: blue; }}',2);
    assert_equals(rule.cssRules.length, 3);
    const mediaRule = rule.cssRules[2];
    mediaRule.insertRule('z-index: 3;');
    assert_equals(mediaRule.cssRules.length, 2);
    assert_throws_dom('SyntaxError', () => { mediaRule.insertRule('nothing-to-insert-because-invalid-property-should-throw: 2;'); });
  }, 'Manipulation of nested declarations through CSSOM');

</script>