tor-browser

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

declarative-shadow-dom-basic.html (16529B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <title>Declarative Shadow DOM</title>
      4 <link rel="author" href="mailto:masonf@chromium.org">
      5 <link rel="help" href="https://github.com/whatwg/dom/issues/831">
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 
      9 <div id="host" style="display:none">
     10  <template shadowrootmode="open">
     11    <slot id="s1" name="slot1"></slot>
     12  </template>
     13  <div id="c1" slot="slot1"></div>
     14 </div>
     15 
     16 <script>
     17 
     18 // Uncaught exceptions (which will manifest as test harness
     19 // errors) constitute a failure of this test. No parsing
     20 // operations, whether imperative (setHTMLUnsafe) or declarative
     21 // (main document parsing) should ever throw exceptions.
     22 // More context:
     23 // https://github.com/whatwg/html/issues/10527#issuecomment-2275253439
     24 
     25 test(() => {
     26  const host = document.querySelector('#host');
     27  const c1 = host.querySelector('#c1');
     28  assert_true(!!c1);
     29  assert_equals(host.querySelector('#s1'), null, "Should be inside shadow root");
     30  assert_equals(host.querySelector('template'), null, "No leftover template node");
     31  assert_true(!!host.shadowRoot,"No shadow root found");
     32  const s1 = host.shadowRoot.querySelector('#s1');
     33  assert_equals(c1.assignedSlot, s1);
     34  assert_array_equals(s1.assignedNodes(), [c1]);
     35 }, 'Declarative Shadow DOM: Basic test');
     36 
     37 test(() => {
     38  assert_true(HTMLTemplateElement.prototype.hasOwnProperty("shadowRootMode"),'Unable to feature detect');
     39 }, 'Declarative Shadow DOM: Feature detection');
     40 
     41 test(() => {
     42  const t = document.createElement('template');
     43  t.setAttribute('shadowrootmode','open');
     44  assert_equals(t.shadowRootMode,'open','The shadowRootMode IDL should reflect the content attribute');
     45  t.setAttribute('shadowrootmode','closed');
     46  assert_equals(t.shadowRootMode,'closed','"open" and "closed" should both be valid values');
     47  t.setAttribute('shadowrootmode','OpEn');
     48  assert_equals(t.shadowRootMode,'open','Case insensitive');
     49  t.setAttribute('shadowrootmode','INVALID');
     50  assert_equals(t.shadowRootMode,'','Invalid values map to empty string');
     51  t.removeAttribute('shadowrootmode');
     52  assert_equals(t.shadowRootMode,'','No shadowrootmode attribute maps to empty string');
     53 }, 'Shadowrootmode reflection');
     54 
     55 test(() => {
     56  const t = document.createElement('template');
     57  t.shadowRootMode = 'blah';
     58  assert_equals(t.shadowRootMode, '');
     59  t.getAttribute('shadowrootmode', 'blah');
     60  t.shadowRootMode = 'CLOSED';
     61  assert_equals(t.shadowRootMode, 'closed');
     62  t.getAttribute('shadowrootmode', 'CLOSED');
     63 }, 'Shadowrootmode reflection, setter');
     64 
     65 test(() => {
     66  const t = document.createElement('template');
     67  t.setAttribute('shadowrootdelegatesfocus','');
     68  assert_equals(t.shadowRootDelegatesFocus,true,'The shadowRootDelegatesFocus IDL should reflect the content attribute');
     69  t.setAttribute('shadowrootdelegatesfocus','foobar');
     70  assert_equals(t.shadowRootDelegatesFocus,true,'The value doesn\'t matter');
     71  t.removeAttribute('shadowrootdelegatesfocus');
     72  assert_equals(t.shadowRootDelegatesFocus,false,'No shadowRootDelegatesFocus attribute maps to false');
     73 }, 'Shadowrootdelegatesfocus reflection');
     74 
     75 test(() => {
     76  const t = document.createElement('template');
     77  assert_equals(t.getAttribute('shadowrootdelegatesfocus'), null);
     78  t.shadowRootDelegatesFocus = true;
     79  assert_equals(t.getAttribute('shadowrootdelegatesfocus'), '');
     80  assert_equals(t.shadowRootDelegatesFocus, true);
     81  t.shadowRootDelegatesFocus = false;
     82  assert_equals(t.getAttribute('shadowrootdelegatesfocus'), null);
     83  assert_equals(t.shadowRootDelegatesFocus, false);
     84 }, 'Shadowrootdelegatesfocus reflection, setter');
     85 
     86 
     87 test(() => {
     88  const t = document.createElement('template');
     89  t.setAttribute('shadowrootclonable','');
     90  assert_equals(t.shadowRootClonable,true,'The shadowRootClonable IDL should reflect the content attribute');
     91  t.setAttribute('shadowrootclonable','foobar');
     92  assert_equals(t.shadowRootClonable,true,'The value doesn\'t matter');
     93  t.removeAttribute('shadowrootclonable');
     94  assert_equals(t.shadowRootClonable,false,'No shadowRootClonable attribute maps to false');
     95 }, 'Shadowrootclonable reflection');
     96 
     97 test(() => {
     98  const t = document.createElement('template');
     99  assert_equals(t.getAttribute('shadowrootclonable'), null);
    100  t.shadowRootClonable = true;
    101  assert_equals(t.getAttribute('shadowrootclonable'), '');
    102  assert_equals(t.shadowRootClonable, true);
    103  t.shadowRootClonable = false;
    104  assert_equals(t.getAttribute('shadowrootclonable'), null);
    105  assert_equals(t.shadowRootClonable, false);
    106 }, 'Shadowrootclonable reflection, setter');
    107 
    108 test(() => {
    109  const div = document.createElement('div');
    110  div.setHTMLUnsafe(`
    111    <div id="host">
    112      <template shadowrootmode="open">
    113        <slot id="s1" name="slot1"></slot>
    114      </template>
    115      <div id="c1" slot="slot1"></div>
    116    </div>
    117    `);
    118  const host = div.querySelector('#host');
    119  const c1 = host.querySelector('#c1');
    120  assert_true(!!c1);
    121  assert_equals(host.querySelector('#s1'), null, "Should be inside shadow root");
    122  assert_equals(host.querySelector('template'), null, "No leftover template node");
    123  assert_true(!!host.shadowRoot,"No shadow root found");
    124  const s1 = host.shadowRoot.querySelector('#s1');
    125  assert_equals(c1.assignedSlot, s1);
    126  assert_array_equals(s1.assignedNodes(), [c1]);
    127 }, 'Declarative Shadow DOM: Fragment parser basic test');
    128 
    129 test(() => {
    130  const div = document.createElement('div');
    131  div.setHTMLUnsafe(`
    132    <div id="host">
    133      <template shadowrootmode="invalid">
    134      </template>
    135    </div>
    136    `);
    137  const host = div.querySelector('#host');
    138  assert_equals(host.shadowRoot, null, "Shadow root was found");
    139  const tmpl = host.querySelector('template');
    140  assert_true(!!tmpl,"Template should still be present");
    141  const shadowrootAttr = tmpl.getAttribute('shadowrootmode');
    142  assert_equals(shadowrootAttr,"invalid","'shadowrootmode' attribute should still be present");
    143 }, 'Declarative Shadow DOM: Invalid shadow root attribute');
    144 
    145 test(() => {
    146  const div = document.createElement('div');
    147  div.setHTMLUnsafe(`
    148    <div id="host">
    149      <template shadowrootmode="closed">
    150      </template>
    151    </div>
    152    `);
    153  const host = div.querySelector('#host');
    154  assert_equals(host.shadowRoot, null, "Closed shadow root");
    155  assert_equals(host.querySelector('template'), null, "No template - converted to shadow root");
    156 }, 'Declarative Shadow DOM: Closed shadow root attribute');
    157 
    158 test(() => {
    159  const div = document.createElement('div');
    160  div.setHTMLUnsafe(`
    161    <div id="host">
    162      <template shadowrootmode="open">
    163        <slot id="s1" name="slot1"></slot>
    164    </div>
    165    `);
    166  const host = div.querySelector('#host');
    167  assert_equals(host.querySelector('#s1'), null, "Should be inside shadow root");
    168  assert_equals(host.querySelector('template'), null, "No leftover template node");
    169  assert_true(!!host.shadowRoot,"No shadow root found");
    170  const s1 = host.shadowRoot.querySelector('#s1');
    171  assert_true(!!s1,"Slot should be inside the shadow root");
    172 }, 'Declarative Shadow DOM: Missing closing tag');
    173 
    174 test(() => {
    175  const div = document.createElement('div');
    176  div.setHTMLUnsafe(`
    177    <div id="host">
    178      <template shadowrootmode="open" shadowrootdelegatesfocus>
    179      </template>
    180    </div>
    181    `);
    182  var host = div.querySelector('#host');
    183  assert_true(!!host.shadowRoot,"No shadow root found");
    184  assert_true(host.shadowRoot.delegatesFocus,"delegatesFocus should be true");
    185  div.setHTMLUnsafe(`
    186    <div id="host">
    187      <template shadowrootmode="open">
    188      </template>
    189    </div>
    190    `);
    191  host = div.querySelector('#host');
    192  assert_true(!!host.shadowRoot,"No shadow root found");
    193  assert_false(host.shadowRoot.delegatesFocus,"delegatesFocus should be false without the shadowrootdelegatesfocus attribute");
    194 }, 'Declarative Shadow DOM: delegates focus attribute');
    195 
    196 test(() => {
    197  const div = document.createElement('div');
    198  div.setHTMLUnsafe(`
    199    <div id="host">
    200      <template shadowrootmode="open" shadowrootclonable>
    201      </template>
    202    </div>
    203    `);
    204  var host = div.querySelector('#host');
    205  assert_true(!!host.shadowRoot,"No shadow root found");
    206  assert_true(host.shadowRoot.clonable,"clonable should be true");
    207  div.setHTMLUnsafe(`
    208    <div id="host">
    209      <template shadowrootmode="open">
    210      </template>
    211    </div>
    212    `);
    213  host = div.querySelector('#host');
    214  assert_true(!!host.shadowRoot,"No shadow root found");
    215  assert_false(host.shadowRoot.clonable,"clonable should be false without the shadowrootclonable attribute");
    216 }, 'Declarative Shadow DOM: clonable attribute');
    217 </script>
    218 
    219 <div id="multi-host" style="display:none">
    220  <template shadowrootmode="open">
    221    <span>root 1</span>
    222  </template>
    223  <template shadowrootmode="closed">
    224    <span>root 2</span>
    225  </template>
    226 </div>
    227 <script>
    228 test(() => {
    229  const host = document.querySelector('#multi-host');
    230  const leftover = host.querySelector('template');
    231  assert_true(!!leftover, "The second (duplicate) template should be left in the DOM");
    232  assert_true(leftover instanceof HTMLTemplateElement);
    233  assert_equals(leftover.getAttribute('shadowrootmode'),"closed");
    234  assert_equals(leftover.shadowRootMode,"closed");
    235  assert_true(!!host.shadowRoot,"No open shadow root found - first root should remain");
    236  const innerSpan = host.shadowRoot.querySelector('span');
    237  assert_equals(innerSpan.textContent, 'root 1', "Content should come from first declarative shadow root");
    238 }, 'Declarative Shadow DOM: Multiple roots');
    239 
    240 </script>
    241 
    242 <template id="template-containing-shadow">
    243  <div class="innerdiv">
    244    <template shadowrootmode=open shadowrootclonable>Content</template>
    245  </div>
    246 </template>
    247 <script>
    248 test(() => {
    249  const template = document.querySelector('#template-containing-shadow');
    250  const container1 = document.createElement('div');
    251  container1.style.display = 'none';
    252  document.body.appendChild(container1);
    253  container1.appendChild(template.content.cloneNode(true));
    254  let innerDiv = container1.querySelector('div.innerdiv');
    255  const shadowRoot1 = innerDiv.shadowRoot;
    256  assert_true(!!shadowRoot1,"Inner div should have a shadow root");
    257  assert_equals(innerDiv.querySelector('template'), null, "No leftover template node");
    258 
    259  const container2 = document.createElement('div');
    260  container2.style.display = 'none';
    261  document.body.appendChild(container2);
    262  container2.appendChild(template.content.cloneNode(true));
    263  innerDiv = container2.querySelector('div.innerdiv');
    264  const shadowRoot2 = innerDiv.shadowRoot;
    265  assert_true(!!shadowRoot2,"Inner div should have a shadow root");
    266  assert_equals(innerDiv.querySelector('template'), null, "No leftover template node");
    267 
    268  assert_not_equals(shadowRoot1,shadowRoot2,'Should not get back the same shadow root');
    269 
    270  // Make sure importNode also works.
    271  const container3 = document.createElement('div');
    272  container3.style.display = 'none';
    273  document.body.appendChild(container3);
    274  container3.appendChild(document.importNode(template.content,true));
    275  innerDiv = container3.querySelector('div.innerdiv');
    276  const shadowRoot3 = innerDiv.shadowRoot;
    277  assert_true(!!shadowRoot3,"Inner div should have a shadow root");
    278  assert_equals(innerDiv.querySelector('template'), null, "No leftover template node");
    279  assert_not_equals(shadowRoot1,shadowRoot3,'Should not get back the same shadow root');
    280 
    281 }, 'Declarative Shadow DOM: template containing declarative shadow root (with shadowrootclonable)');
    282 </script>
    283 
    284 <template id="template-containing-deep-shadow">
    285  <div><div><div><div><div>
    286    <div class="innerdiv">
    287      <template shadowrootmode=open shadowrootclonable>Content</template>
    288    </div>
    289  </div></div></div></div></div>
    290 </template>
    291 <script>
    292 test(() => {
    293  const template = document.querySelector('#template-containing-deep-shadow');
    294  const host = document.createElement('div');
    295  host.style.display = 'none';
    296  document.body.appendChild(host);
    297  host.appendChild(template.content.cloneNode(true));
    298  assert_true(!!host.querySelector('div.innerdiv').shadowRoot,"Inner div should have a shadow root");
    299 }, 'Declarative Shadow DOM: template containing (deeply nested) declarative shadow root');
    300 </script>
    301 
    302 <template id="template-containing-template">
    303  <div>
    304    <template id="inner-template">
    305      <div class="innerdiv">
    306        <template shadowrootmode=open shadowrootclonable>Content</template>
    307      </div>
    308    </template>
    309  </div>
    310 </template>
    311 <script>
    312 test(() => {
    313  const template = document.querySelector('#template-containing-template');
    314  const host = document.createElement('div');
    315  host.style.display = 'none';
    316  document.body.appendChild(host);
    317  host.appendChild(template.content.cloneNode(true));
    318  const innerTemplate = host.querySelector('#inner-template');
    319  assert_true(!!innerTemplate.content.querySelector('div.innerdiv').shadowRoot,"Inner div should have a shadow root");
    320 }, 'Declarative Shadow DOM: template containing a template containing declarative shadow root');
    321 </script>
    322 
    323 <template id="template-containing-ua-shadow">
    324  <div class="innerdiv">
    325    <template shadowrootmode=open shadowrootclonable>
    326      <video></video> <!--Assumed to have UA shadow root-->
    327    </template>
    328  </div>
    329 </template>
    330 <script>
    331 test(() => {
    332  const template = document.querySelector('#template-containing-ua-shadow');
    333  const host = document.createElement('div');
    334  host.style.display = 'none';
    335  document.body.appendChild(host);
    336  // Mostly make sure clone of template *does* clone the
    337  // shadow root, and doesn't crash on cloning the <video>.
    338  host.appendChild(template.content.cloneNode(true));
    339  let innerDiv = host.querySelector('div.innerdiv');
    340  const shadowRoot = innerDiv.shadowRoot;
    341  assert_true(!!shadowRoot,"Inner div should have a shadow root");
    342  assert_equals(innerDiv.querySelector('template'), null, "No leftover template node");
    343  assert_true(!!innerDiv.shadowRoot.querySelector('video'),'Video element should be present');
    344 }, 'Declarative Shadow DOM: template containing declarative shadow root and UA shadow root');
    345 </script>
    346 
    347 <template id="template-containing-ua-shadow-closed">
    348  <div class="innerdiv">
    349    <template shadowrootmode=closed>
    350      <video></video> <!--Assumed to have UA shadow root-->
    351    </template>
    352  </div>
    353 </template>
    354 <script>
    355 test(() => {
    356  const template = document.querySelector('#template-containing-ua-shadow-closed');
    357  const host = document.createElement('div');
    358  host.style.display = 'none';
    359  document.body.appendChild(host);
    360  host.appendChild(template.content.cloneNode(true));
    361  let innerDiv = host.querySelector('div.innerdiv');
    362  assert_true(!innerDiv.shadowRoot,"Inner div should have a closed shadow root");
    363 }, 'Declarative Shadow DOM: template containing closed declarative shadow root and UA shadow root');
    364 </script>
    365 
    366 <template id="root-element-shadow">
    367    <template shadowrootmode=open>Content</template>
    368 </template>
    369 <script>
    370 test(() => {
    371  // Root element of this template is a <template shadowroot>:
    372  const template = document.querySelector('#root-element-shadow');
    373  const host = document.createElement('div');
    374  host.appendChild(template.content.cloneNode(true));
    375  assert_equals(host.shadowRoot, null, "Shadow root should not be present");
    376  const tmpl = host.querySelector('template');
    377  assert_true(!!tmpl,"Template should still be present");
    378  assert_equals(tmpl.getAttribute('shadowrootmode'),"open","'shadowrootmode' attribute should still be present");
    379 }, 'Declarative Shadow DOM: declarative shadow roots are not supported by the template element');
    380 </script>
    381 
    382 <script>
    383  let gotError = false;
    384  window.addEventListener('error',() => {gotError = true;});
    385 </script>
    386 <progress id="invalid-element-exception">
    387  <template shadowrootmode=open>Content</template>
    388 </progress>
    389 <script>
    390 test(() => {
    391  assert_false(gotError,'Exceptions should not be thrown by the parser');
    392  const host = document.querySelector('#invalid-element-exception');
    393  const leftover = host.querySelector('template');
    394  assert_true(!host.shadowRoot,"Progress elements don't allow shadow roots");
    395  assert_true(!!leftover, "The template should be left in the DOM");
    396  // This also should not throw exceptions:
    397  const div = document.createElement('div');
    398  document.body.appendChild(div);
    399  div.setHTMLUnsafe('<progress><template shadowrootmode=open></template></progress>');
    400  assert_false(gotError,'Exceptions should not be thrown by the parser');
    401  assert_true(!!div.querySelector('template'),'parsing should have succeeded and left the template child');
    402  host.remove();
    403  div.remove();
    404 }, 'Declarative Shadow DOM: explicit test that exceptions are not thrown');
    405 </script>