tor-browser

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

scope-specificity.html (3903B)


      1 <!DOCTYPE html>
      2 <title>@scope - specificity</title>
      3 <link rel="help" href="https://drafts.csswg.org/css-cascade-6/#scope-atrule">
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <style id=style>
      7 </style>
      8 <main id=main>
      9  <style id=styleImplicit></style>
     10  <div id=a class=a>
     11    <div id=b class=b>
     12    </div>
     13  </div>
     14 </main>
     15 <script>
     16 
     17 // Format a scoped style rule using the selector at scoped_selector's last element,
     18 // with each preceding array item representing an enclosing @scope rule.
     19 //
     20 // Example:
     21 //
     22 //  scoped_selector=['@scope (foo)', '@scope (bar)', 'div']
     23 //  declarations='z-index:42'
     24 //    => '@scope (foo) { @scope (bar) { div { z-index:42 } } }'
     25 function format_scoped_rule(scoped_selector, declarations) {
     26  if (scoped_selector.length < 2) {
     27    throw "Fail";
     28  }
     29  let scope_prelude = scoped_selector[0];
     30  let remainder = scoped_selector.slice(1);
     31  let content = remainder.length == 1
     32                  ? `${remainder[0]} { ${declarations} }`
     33                  : format_scoped_rule(remainder, declarations);
     34  return `${scope_prelude} { ${content} }`;
     35 }
     36 
     37 // Verify that the specificity of 'scoped_selector' is the same
     38 // as the specificity of 'ref_selector'. Both selectors must select
     39 // an element within #main.
     40 function test_scope_specificity(scoped_selector, ref_selector, style) {
     41  if (style === undefined) {
     42    style = document.getElementById("style");
     43  }
     44  test(t => {
     45    t.add_cleanup(() => { style.textContent = ''; });
     46 
     47    let element = main.querySelector(ref_selector);
     48    assert_not_equals(element, null);
     49 
     50    let scoped_rule = format_scoped_rule(scoped_selector, 'z-index:1');
     51    let ref_rule = `:is(${ref_selector}) { z-index:2 }`;
     52 
     53    style.textContent = `${scoped_rule}`;
     54    assert_equals(getComputedStyle(element).zIndex, '1', 'scoped rule');
     55 
     56    style.textContent = `${ref_rule}`;
     57    assert_equals(getComputedStyle(element).zIndex, '2', 'unscoped rule');
     58 
     59    // The scoped rule should win due to proximity.
     60    style.textContent = `${scoped_rule} ${ref_rule}`;
     61    assert_equals(getComputedStyle(element).zIndex, '1', 'scoped + unscoped');
     62 
     63    // The scoped rule should win due to proximity (reverse).
     64    style.textContent = `${ref_rule} ${scoped_rule}`;
     65    assert_equals(getComputedStyle(element).zIndex, '1', 'unscoped + scoped');
     66 
     67    // Add one (1) to the specificty of the unscoped rule. This should
     68    // cause the unscoped rule to win instead.
     69    style.textContent = `div${ref_rule} ${scoped_rule}`;
     70    assert_equals(getComputedStyle(element).zIndex, '2', 'unscoped + scoped');
     71  }, format_scoped_rule(scoped_selector, '') + ' and ' + ref_selector);
     72 }
     73 
     74 // Selectors within @scope implicitly have `:scope <descendant-combinator>`
     75 // added, but no specificity associated with it is added.
     76 // See https://github.com/w3c/csswg-drafts/issues/10196
     77 test_scope_specificity(['@scope (#main)', '.b'], '.b');
     78 test_scope_specificity(['@scope (#main) to (.b)', '.a'], '.a');
     79 test_scope_specificity(['@scope (#main, .foo, .bar)', '#a'], '#a');
     80 test_scope_specificity(['@scope (#main)', 'div.b'], 'div.b');
     81 test_scope_specificity(['@scope (#main)', ':scope .b'], '.a .b');
     82 // & behaves like :where(:scope) - No specificity is added.
     83 // See https://github.com/w3c/csswg-drafts/issues/9740
     84 test_scope_specificity(['@scope (#main)', '& .b'], ':where(#main) .b');
     85 test_scope_specificity(['@scope (#main)', 'div .b'], 'div .b');
     86 test_scope_specificity(['@scope (#main)', '@scope (.a)', '.b'], '.b');
     87 // Explicit `:scope` adds specficity.
     88 test_scope_specificity(['@scope (#main)', ':scope .b'], ':scope .b');
     89 // & behaves like :where(:scope), even for implicit scope roots.
     90 test_scope_specificity(['@scope', '& .b'], ':where(:scope) .b', styleImplicit);
     91 // Using relative selector syntax does not add specificity
     92 test_scope_specificity(['@scope (#main)', '> .a'], ':where(#main) > .a');
     93 </script>