tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cssom.html (8076B)


      1 <!doctype html>
      2 <title>Simple CSSOM manipulation of subrules</title>
      3 <link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
      4 <link rel="help" href="https://drafts.csswg.org/css-nesting-1/">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 
      8 <style id="ss"></style>
      9 
     10 <script>
     11  test(() => {
     12    assert_equals(CSSStyleRule.__proto__, CSSGroupingRule);
     13  }, "CSSStyleRule is a CSSGroupingRule");
     14 
     15  test(() => {
     16    let [ss] = document.styleSheets;
     17    assert_equals(ss.cssRules.length, 0);
     18    ss.insertRule('.a { color: red; }');
     19    assert_equals(ss.cssRules.length, 1);
     20    assert_equals(ss.cssRules[0].cssText, '.a { color: red; }');
     21 
     22    // Test inserting sub-cssRules, at various positions.
     23    ss.cssRules[0].insertRule('& .b { color: green; }');
     24    ss.cssRules[0].insertRule('& .c { color: blue; }', 1);
     25    ss.cssRules[0].insertRule('& .d { color: hotpink; }', 1);
     26    assert_equals(ss.cssRules[0].cssText,
     27 `.a {
     28  color: red;
     29  & .b { color: green; }
     30  & .d { color: hotpink; }
     31  & .c { color: blue; }
     32 }`, 'inserting should work');
     33 
     34    // Test deleting a rule.
     35    ss.cssRules[0].deleteRule(1);
     36    assert_equals(ss.cssRules[0].cssText,
     37 `.a {
     38  color: red;
     39  & .b { color: green; }
     40  & .c { color: blue; }
     41 }`, 'deleting should work');
     42  });
     43 
     44  // Test that out-of-bounds throws exceptions and does not affect the stylesheet.
     45  const sampleSheetText =
     46 `.a {
     47  color: red;
     48  & .b { color: green; }
     49  & .c { color: blue; }
     50 }`;
     51 
     52  test(() => {
     53    document.getElementById('ss').innerHTML = sampleSheetText;
     54    let [ss] = document.styleSheets;
     55    assert_throws_dom('IndexSizeError', () => { ss.cssRules[0].insertRule('& .broken {}', 3); });
     56    assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after no-insert');
     57  });
     58 
     59  test(() => {
     60    document.getElementById('ss').innerHTML = sampleSheetText;
     61    let [ss] = document.styleSheets;
     62    assert_throws_dom('IndexSizeError', () => { ss.cssRules[0].insertRule('& .broken {}', -1); });
     63    assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after no-insert');
     64  });
     65 
     66  test(() => {
     67    document.getElementById('ss').innerHTML = sampleSheetText;
     68    let [ss] = document.styleSheets;
     69    assert_throws_dom('IndexSizeError', () => { ss.cssRules[0].deleteRule(5); });
     70    assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after no-delete');
     71  });
     72 
     73  test(() => {
     74    document.getElementById('ss').innerHTML = sampleSheetText;
     75    let [ss] = document.styleSheets;
     76    assert_equals(ss.cssRules[0].cssRules[2], undefined, 'subscript out-of-bounds returns undefined');
     77    assert_equals(ss.cssRules[0].cssRules.item(2), null, 'item() out-of-bounds returns null');
     78    assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after no-access');
     79  });
     80 
     81  // Test that inserting an invalid rule throws an exception.
     82  test(() => {
     83    document.getElementById('ss').innerHTML = sampleSheetText;
     84    let [ss] = document.styleSheets;
     85    let exception;
     86    assert_throws_dom('SyntaxError', () => { ss.cssRules[0].insertRule('% {}'); });
     87    assert_equals(ss.cssRules[0].cssText, sampleSheetText, 'unchanged after invalid rule');
     88  });
     89 
     90  // Test that we can get out single rule through .cssRules.
     91  test(() => {
     92    document.getElementById('ss').innerHTML = sampleSheetText;
     93    let [ss] = document.styleSheets;
     94    assert_equals(ss.cssRules[0].cssRules[1].cssText, '& .c { color: blue; }');
     95  });
     96 
     97  // Test that we can insert a @supports rule, that it serializes in the right place
     98  // and has the right parent. Note that the indentation is broken per-spec.
     99  test(() => {
    100    document.getElementById('ss').innerHTML = sampleSheetText;
    101    let [ss] = document.styleSheets;
    102    ss.cssRules[0].insertRule('@supports selector(&) { & div { font-size: 10px; }}', 1);
    103    assert_equals(ss.cssRules[0].cssText,
    104 `.a {
    105  color: red;
    106  & .b { color: green; }
    107  @supports selector(&) {
    108  & div { font-size: 10px; }
    109 }
    110  & .c { color: blue; }
    111 }`, '@supports is added');
    112 
    113    assert_equals(ss.cssRules[0].cssRules[1].parentRule, ss.cssRules[0]);
    114    ss.cssRules[0].deleteRule(1);
    115    assert_equals(ss.cssRules[0].cssText, sampleSheetText);
    116  });
    117 
    118  // Nested rules are not part of declaration lists, and thus should not
    119  // be possible to insert with .style.
    120  test(() => {
    121    document.getElementById('ss').innerHTML = sampleSheetText;
    122    let [ss] = document.styleSheets;
    123    ss.cssRules[0].style = 'color: olivedrab; &.d { color: peru; }';
    124    assert_equals(ss.cssRules[0].cssText,
    125 `.a {
    126  color: olivedrab;
    127  & .b { color: green; }
    128  & .c { color: blue; }
    129 }`, 'color is changed, new rule is ignored');
    130  });
    131 
    132  test(() => {
    133    document.getElementById('ss').innerHTML = sampleSheetText;
    134    let [ss] = document.styleSheets;
    135    ss.cssRules[0].cssRules[0].selectorText = 'div.b .c &';  // Allowed
    136    ss.cssRules[0].cssRules[1].selectorText = '.c div.b &, div &';  // Allowed.
    137    ss.cssRules[0].insertRule('div & {}'); // Allowed.
    138    assert_equals(ss.cssRules[0].cssText,
    139 `.a {
    140  color: red;
    141  div & { }
    142  div.b .c & { color: green; }
    143  .c div.b &, div & { color: blue; }
    144 }`, 'selectorText and insertRule');
    145  });
    146 
    147  // Rules that are dropped in forgiving parsing but that contain &,
    148  // must still be serialized out as they were.
    149  test(() => {
    150    const text = '.a { :is(!& .foo, .b) { color: green; } }';
    151    document.getElementById('ss').innerHTML = text;
    152    let [ss] = document.styleSheets;
    153    assert_equals(ss.cssRules[0].cssText,
    154 `.a {
    155  :is(!& .foo, .b) { color: green; }
    156 }`, 'invalid rule containing ampersand is kept in serialization');
    157  });
    158 
    159  test((t) => {
    160    let main = document.createElement('main');
    161    main.innerHTML = `
    162      <style>
    163        .a {
    164          & { z-index:1; }
    165          & #inner1 { z-index:1; }
    166          .stuff, :is(&) #inner2 { z-index:1; }
    167        }
    168      </style>
    169      <div id="outer" class="b">
    170        <div id="inner1"></div>
    171        <div id="inner2"></div>
    172      </div>
    173    `;
    174    document.documentElement.append(main);
    175    t.add_cleanup(() => main.remove());
    176 
    177    assert_equals(getComputedStyle(outer).zIndex, 'auto');
    178    assert_equals(getComputedStyle(inner1).zIndex, 'auto');
    179    assert_equals(getComputedStyle(inner2).zIndex, 'auto');
    180 
    181    // .a => .b
    182    main.firstElementChild.sheet.cssRules[0].selectorText = '.b';
    183 
    184    assert_equals(getComputedStyle(outer).zIndex, '1');
    185    assert_equals(getComputedStyle(inner1).zIndex, '1');
    186    assert_equals(getComputedStyle(inner2).zIndex, '1');
    187  }, 'Mutating the selectorText of outer rule invalidates inner rules');
    188 
    189  // CSSNestedDeclarations
    190  test((t) => {
    191    const main = document.createElement('main');
    192    main.innerHTML = `
    193      <style id="main_ss">
    194        div {
    195          z-index: 1;
    196          &.test { foo:bar; }
    197        }
    198      </style>
    199      <div id="outer" class="test">
    200      </div>
    201    `;
    202    document.documentElement.append(main);
    203    t.add_cleanup(() => main.remove());
    204    assert_equals(getComputedStyle(outer).zIndex, '1');
    205    const main_ss = document.getElementById("main_ss").sheet;
    206    const rule = main_ss.cssRules[0];
    207    assert_equals(rule.cssRules.length, 1);
    208    rule.insertRule('z-index: 3;');
    209    assert_equals(rule.cssRules.length, 2);
    210    assert_equals(getComputedStyle(outer).zIndex, '3');
    211 
    212    // Throw only when no valid declaration  https://github.com/w3c/csswg-drafts/issues/10520
    213    assert_throws_dom('SyntaxError', () => { rule.insertRule('nothing-to-insert-because-invalid-property-should-throw: 2;'); });
    214    assert_equals(rule.cssRules.length, 2);
    215 
    216    // Test the insertion of nested declarations inside grouping rule
    217    rule.insertRule('@media screen { a { color: blue; }}',2);
    218    assert_equals(rule.cssRules.length, 3);
    219    const mediaRule = rule.cssRules[2];
    220    mediaRule.insertRule('z-index: 3;');
    221    assert_equals(mediaRule.cssRules.length, 2);
    222    assert_throws_dom('SyntaxError', () => { mediaRule.insertRule('nothing-to-insert-because-invalid-property-should-throw: 2;'); });
    223  }, 'Manipulation of nested declarations through CSSOM');
    224 
    225 </script>