tor-browser

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

declarative-shadow-dom-attachment.html (4984B)


      1 <!DOCTYPE html>
      2 <title>Declarative Shadow DOM Element Attachment</title>
      3 <link rel='author' href='mailto:masonf@chromium.org'>
      4 <link rel='help' href='https://github.com/whatwg/dom/issues/831'>
      5 <script src='/resources/testharness.js'></script>
      6 <script src='/resources/testharnessreport.js'></script>
      7 <script src='../../html/resources/common.js'></script>
      8 
      9 <script>
     10 const shadowContent = '<span>Shadow tree</span><slot></slot>';
     11 function getDeclarativeContent(mode, delegatesFocus) {
     12  const delegatesFocusText = delegatesFocus ? ' shadowrootdelegatesfocus' : '';
     13  return `<template shadowrootmode=${mode}${delegatesFocusText}>${shadowContent}</template>`;
     14 }
     15 
     16 const lightDomTextContent = 'Light DOM';
     17 function addDeclarativeShadowRoot(elementType, mode, delegatesFocus) {
     18  const declarativeString = `<${elementType} id=theelement>${getDeclarativeContent(mode, delegatesFocus)}
     19     <span class='lightdom'>${lightDomTextContent}</span></${elementType}>`;
     20  const wrapper = document.createElement('div');
     21  wrapper.setHTMLUnsafe(declarativeString); // Should never throw
     22  const element = wrapper.querySelector('#theelement');
     23  return {wrapper, element};
     24 }
     25 
     26 function testElementType(allowed, nochildren, elementType, mode, delegatesFocus) {
     27  test((t) => {
     28    const nodes = addDeclarativeShadowRoot(elementType, mode, delegatesFocus);
     29    if (allowed) {
     30      const element = nodes.element;
     31      assert_true(!!element, 'Unable to locate the element');
     32      // Just one light DOM child, and no leftover template.
     33      assert_true(!nodes.wrapper.querySelector('template'));
     34      assert_equals(element.children.length, 1);
     35      assert_equals(element.children[0].textContent, lightDomTextContent);
     36      let originalShadowRoot = null;
     37      if (mode === 'open') {
     38        assert_true(!!element.shadowRoot, 'Shadow root should be present');
     39        assert_equals(element.shadowRoot.innerHTML, shadowContent, 'Correct shadow content');
     40        assert_equals(element.shadowRoot.delegatesFocus,delegatesFocus,'Correct delegatesFocus')
     41        originalShadowRoot = element.shadowRoot;
     42      }
     43 
     44      const oppositeMode = (mode === 'open') ? 'closed' : 'open';
     45      assert_throws_dom('NotSupportedError', () => {
     46        element.attachShadow({mode: oppositeMode});
     47      }, 'Calling attachShadow with a declarative shadow fails if the mode doesn\'t match');
     48 
     49      // Now, call attachShadow() and make sure we get back the same (original) shadowRoot, but empty.
     50      const newShadow = element.attachShadow({mode: mode, delegatesFocus: delegatesFocus});
     51      if (mode === 'open') {
     52        assert_equals(element.shadowRoot, originalShadowRoot, 'The same shadow root should be returned');
     53        assert_equals(element.shadowRoot.innerHTML, '', 'Empty shadow content');
     54        assert_equals(element.shadowRoot.mode, mode, 'Original shadow mode');
     55      }
     56 
     57      assert_throws_dom('NotSupportedError', () => {
     58        element.attachShadow({mode: mode});
     59      }, 'Calling attachShadow a second time on an element with a declarative shadow fails (same mode)');
     60 
     61      assert_throws_dom('NotSupportedError', () => {
     62        element.attachShadow({mode: oppositeMode});
     63      }, 'Calling attachShadow a second time on an element with a declarative shadow fails (opposite mode)');
     64    } else {
     65      if (!nochildren) {
     66        // Invalid elements should retain a <template> element child with a shadowrootmode attribute.
     67        const template = nodes.wrapper.querySelector('template[shadowrootmode]');
     68        assert_true(!!template);
     69        assert_equals(template.getAttribute('shadowrootmode'), mode, `Template with shadowrootmode=${mode} should be left over`);
     70        const span = nodes.wrapper.querySelector('span.lightdom');
     71        assert_true(!!span);
     72        assert_equals(span.textContent, lightDomTextContent);
     73        if (nodes.element) {
     74          // For some tags (e.g. <html>) there won't be an element inside wrapper.
     75          assert_true(!nodes.element.shadowRoot, 'Shadow root should not be present');
     76        }
     77      }
     78    }
     79  }, `Declarative Shadow DOM as a child of <${elementType}>, with mode=${mode}, delegatesFocus=${delegatesFocus}. Should be ${allowed ? 'safelisted' : 'disallowed'}.`);
     80 }
     81 
     82 function runAllTests() {
     83  const noCheck = ['body', 'template'];
     84  const safelisted = HTML5_SHADOW_ALLOWED_ELEMENTS.filter(el => !noCheck.includes(el));
     85  const disallowed = HTML5_SHADOW_DISALLOWED_ELEMENTS.filter(el => !noCheck.includes(el));
     86  const noChildElements = ['iframe','noscript','script','select','style','textarea','title'];
     87  for (let delegatesFocus of [false, true]) {
     88    for (let mode of ['open', 'closed', 'invalid']) {
     89      for (let elementName of safelisted) {
     90        testElementType(mode !== 'invalid', false, elementName, mode, delegatesFocus);
     91      }
     92      for (let elementName of disallowed) {
     93        testElementType(false, noChildElements.includes(elementName), elementName, mode, delegatesFocus);
     94      }
     95    }
     96  }
     97 }
     98 
     99 runAllTests();
    100 
    101 </script>