tor-browser

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

shadow-host-style-sharing.html (7075B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <title>Shadow Host Style Sharing Tests</title>
      4 <link rel="help" href="https://drafts.csswg.org/css-scoping/#host-selector">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 
      8 <div id="container"></div>
      9 
     10 <script>
     11 const container = document.getElementById('container');
     12 
     13 function cleanup() {
     14  container.innerHTML = '';
     15 }
     16 
     17 // Test 1: Different :host rules in different stylesheets
     18 test(() => {
     19  cleanup();
     20 
     21  const redSheet = new CSSStyleSheet();
     22  redSheet.replaceSync(':host { color: rgb(255, 0, 0); }');
     23 
     24  const blueSheet = new CSSStyleSheet();
     25  blueSheet.replaceSync(':host { color: rgb(0, 0, 255); }');
     26 
     27  let useRed = true;
     28 
     29  class DynamicHost extends HTMLElement {
     30    constructor() {
     31      super();
     32      const shadow = this.attachShadow({ mode: 'open' });
     33      shadow.innerHTML = '<div>content</div>';
     34      shadow.adoptedStyleSheets = [useRed ? redSheet : blueSheet];
     35    }
     36  }
     37  customElements.define('dynamic-host-1', DynamicHost);
     38 
     39  const parent = document.createElement('div');
     40 
     41  // Create siblings: first 10 with red sheet
     42  useRed = true;
     43  const redHosts = [];
     44  for (let i = 0; i < 10; i++) {
     45    const host = document.createElement('dynamic-host-1');
     46    redHosts.push(host);
     47    parent.appendChild(host);
     48  }
     49 
     50  // Then 1 with blue sheet as a sibling
     51  useRed = false;
     52  const blueHost = document.createElement('dynamic-host-1');
     53  parent.appendChild(blueHost);
     54 
     55  // Add to document and force style computation
     56  container.appendChild(parent);
     57  container.offsetHeight;
     58 
     59  assert_equals(getComputedStyle(blueHost).color, 'rgb(0, 0, 255)', 'Must be blue, not red');
     60 
     61  for (const host of redHosts) {
     62    assert_equals(getComputedStyle(host).color, 'rgb(255, 0, 0)', 'Must be red');
     63  }
     64 }, 'Different CascadeData must block sharing despite being same element type');
     65 
     66 // Test 2: Same :host rules, different inherited styles
     67 test(() => {
     68  cleanup();
     69 
     70  const sharedSheet = new CSSStyleSheet();
     71  sharedSheet.replaceSync(':host { font-size: 20px; }');
     72 
     73  class SharedHost extends HTMLElement {
     74    constructor() {
     75      super();
     76      const shadow = this.attachShadow({ mode: 'open' });
     77      shadow.adoptedStyleSheets = [sharedSheet];
     78      shadow.innerHTML = '<span>content</span>';
     79    }
     80  }
     81  customElements.define('shared-host-2', SharedHost);
     82 
     83  const parent1 = document.createElement('div');
     84  parent1.style.color = 'rgb(255, 0, 0)';
     85  const host1 = document.createElement('shared-host-2');
     86  parent1.appendChild(host1);
     87  container.appendChild(parent1);
     88 
     89  const parent2 = document.createElement('div');
     90  parent2.style.color = 'rgb(0, 255, 0)';
     91  const host2 = document.createElement('shared-host-2');
     92  parent2.appendChild(host2);
     93  container.appendChild(parent2);
     94 
     95  assert_equals(getComputedStyle(host1).color, 'rgb(255, 0, 0)');
     96  assert_equals(getComputedStyle(host2).color, 'rgb(0, 255, 0)');
     97 }, 'Same :host rules but different inherited styles');
     98 
     99 // Test 3: :host with class selector
    100 test(() => {
    101  cleanup();
    102 
    103  const classSheet = new CSSStyleSheet();
    104  classSheet.replaceSync(`
    105    :host(.active) { background-color: rgb(255, 0, 0); }
    106    :host(.inactive) { background-color: rgb(0, 0, 255); }
    107  `);
    108 
    109  class ClassHost extends HTMLElement {
    110    constructor() {
    111      super();
    112      const shadow = this.attachShadow({ mode: 'open' });
    113      shadow.adoptedStyleSheets = [classSheet];
    114      shadow.innerHTML = '<span>content</span>';
    115    }
    116  }
    117  customElements.define('class-host-3', ClassHost);
    118 
    119  const active = document.createElement('class-host-3');
    120  active.className = 'active';
    121  const inactive = document.createElement('class-host-3');
    122  inactive.className = 'inactive';
    123 
    124  container.appendChild(active);
    125  container.appendChild(inactive);
    126 
    127  assert_equals(getComputedStyle(active).backgroundColor, 'rgb(255, 0, 0)');
    128  assert_equals(getComputedStyle(inactive).backgroundColor, 'rgb(0, 0, 255)');
    129 }, ':host with class selector should not share across different classes');
    130 
    131 // Test 4: :host with attribute selector
    132 test(() => {
    133  cleanup();
    134 
    135  const attrSheet = new CSSStyleSheet();
    136  attrSheet.replaceSync(`
    137    :host([data-theme="dark"]) { color: rgb(0, 0, 0); }
    138    :host([data-theme="light"]) { color: rgb(255, 255, 255); }
    139  `);
    140 
    141  class AttrHost extends HTMLElement {
    142    constructor() {
    143      super();
    144      const shadow = this.attachShadow({ mode: 'open' });
    145      shadow.adoptedStyleSheets = [attrSheet];
    146      shadow.innerHTML = '<span>content</span>';
    147    }
    148  }
    149  customElements.define('attr-host-4', AttrHost);
    150 
    151  const dark = document.createElement('attr-host-4');
    152  dark.setAttribute('data-theme', 'dark');
    153  const light = document.createElement('attr-host-4');
    154  light.setAttribute('data-theme', 'light');
    155 
    156  container.appendChild(dark);
    157  container.appendChild(light);
    158 
    159  assert_equals(getComputedStyle(dark).color, 'rgb(0, 0, 0)');
    160  assert_equals(getComputedStyle(light).color, 'rgb(255, 255, 255)');
    161 }, ':host with attribute selector should not share across different attributes');
    162 
    163 // Test 5: Same stylesheet, different inline styles
    164 test(() => {
    165  cleanup();
    166 
    167  const sharedSheet = new CSSStyleSheet();
    168  sharedSheet.replaceSync(':host { display: block; }');
    169 
    170  class InlineHost extends HTMLElement {
    171    constructor() {
    172      super();
    173      const shadow = this.attachShadow({ mode: 'open' });
    174      shadow.adoptedStyleSheets = [sharedSheet];
    175      shadow.innerHTML = '<span>content</span>';
    176    }
    177  }
    178  customElements.define('inline-host-5', InlineHost);
    179 
    180  const host1 = document.createElement('inline-host-5');
    181  host1.style.color = 'rgb(255, 0, 0)';
    182 
    183  const host2 = document.createElement('inline-host-5');
    184  host2.style.color = 'rgb(0, 255, 0)';
    185 
    186  container.appendChild(host1);
    187  container.appendChild(host2);
    188 
    189  assert_equals(getComputedStyle(host1).color, 'rgb(255, 0, 0)');
    190  assert_equals(getComputedStyle(host2).color, 'rgb(0, 255, 0)');
    191 }, 'Same CascadeData but different inline styles should not share');
    192 
    193 // Test 6: Same :host rules but different shadow stylesheets
    194 test(() => {
    195  cleanup();
    196 
    197  const sheetA = new CSSStyleSheet();
    198  sheetA.replaceSync(':host { color: rgb(255, 0, 0); }');
    199 
    200  const sheetB = new CSSStyleSheet();
    201  sheetB.replaceSync(':host { color: rgb(255, 0, 0); } span { font-weight: bold; }');
    202 
    203  let whichSheet = 'a';
    204 
    205  class SubtleHost extends HTMLElement {
    206    constructor() {
    207      super();
    208      const shadow = this.attachShadow({ mode: 'open' });
    209      shadow.adoptedStyleSheets = [whichSheet === 'a' ? sheetA : sheetB];
    210      shadow.innerHTML = '<span>content</span>';
    211    }
    212  }
    213  customElements.define('subtle-host-6', SubtleHost);
    214 
    215  whichSheet = 'a';
    216  const hostA = document.createElement('subtle-host-6');
    217 
    218  whichSheet = 'b';
    219  const hostB = document.createElement('subtle-host-6');
    220 
    221  container.appendChild(hostA);
    222  container.appendChild(hostB);
    223 
    224  assert_equals(getComputedStyle(hostA).color, 'rgb(255, 0, 0)');
    225  assert_equals(getComputedStyle(hostB).color, 'rgb(255, 0, 0)');
    226 }, 'Same :host rules but different CascadeData should prevent sharing');
    227 </script>