tor-browser

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

basic-dom-part-declarative-brace-syntax.tentative.html (9305B)


      1 <!DOCTYPE html>
      2 <title>DOM Parts: Basic object structure, {{}} declarative API</title>
      3 <link rel=author href="mailto:masonf@chromium.org">
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="./resources/domparts-utils.js"></script>
      7 
      8 <div>
      9  <!-- Note - the test will remove this chunk of DOM once the test completes -->
     10  <div id=target2>
     11    Declarative syntax - The *two* templates below should have IDENTICAL STRUCTURE
     12    to this one. There are four cases to test:
     13      1. Main document parsing (this chunk)
     14      2. Template parsing (the template below with id=declarative)
     15      3. Template/fragment cloning (a clone of the template with id=declarative)
     16      4. Declarative Shadow DOM parsing (template with id=declarative_shadow_dom and shadowrootmode attribute)
     17    <h1 id="name" parseparts>
     18      {{#}}
     19        First
     20        {{#}} <span {{}} id={{}}>Middle</span> {{/}}
     21        Last
     22      {{/}}
     23      <a foo {{}} id=nodepart1>content</a>
     24      <a {{}} id=nodepart2>content</a>
     25      <a {{}}id=nodepart3>content</a>
     26      <a id=nodepart4 {{}}>content</a>
     27      <a id=nodepart5 foo {{}}>content</a>
     28      <a id=nodepart6 foo {{}} >content</a>
     29    </h1>
     30  </div>
     31 </div>
     32 <template id=declarative>
     33  <div>
     34    <div id=target3>Declarative syntax
     35      <h1 id="name" parseparts>
     36        {{#}}
     37          First
     38          {{#}} <span {{}} id={{}}>Middle</span> {{/}}
     39          Last
     40        {{/}}
     41        <a foo {{}} id=nodepart1>content</a>
     42        <a {{}} id=nodepart2>content</a>
     43        <a {{}}id=nodepart3>content</a>
     44        <a id=nodepart4 {{}}>content</a>
     45        <a id=nodepart5 foo {{}}>content</a>
     46        <a id=nodepart6 foo {{}} >content</a>
     47      </h1>
     48    </div>
     49  </div>
     50 </template>
     51 
     52 <!-- TODO: This test should look at declarative shadow DOM behavior. -->
     53 
     54 <script> {
     55 function addPartsCleanup(t,partRoot) {
     56  t.add_cleanup(() => partRoot.getParts().forEach(part => part.disconnect()));
     57 }
     58 const kChildNodePartStartCommentData = "#";
     59 const kChildNodePartEndCommentData = "/";
     60 function assertIsComment(node, commentText) {
     61  assert_true(node instanceof Comment);
     62  // TODO(crbug.com/40271855): While developing alternative syntax, the comment might be empty or it might be "#" or "/".
     63  assert_true(node.textContent === '' || node.textContent === commentText);
     64 }
     65 
     66 const template = document.getElementById('declarative');
     67 ['Main Document','Template','Clone','PartClone'].forEach(testCase => {
     68  test((t) => {
     69    let doc,target,wrapper,cleanup;
     70    let expectDOMParts = true;
     71    switch (testCase) {
     72      case 'Main Document':
     73        doc = document;
     74        target = doc.querySelector('#target2');
     75        cleanup = [target.parentElement];
     76        break;
     77      case 'Template':
     78        doc = template.content;
     79        target = doc.querySelector('#target3');
     80        cleanup = [];
     81        break;
     82      case 'Clone':
     83        doc = document;
     84        wrapper = document.body.appendChild(document.createElement('div'));
     85        wrapper.appendChild(template.content.cloneNode(true));
     86        target = wrapper.querySelector('#target3');
     87        // A "normal" tree clone should not keep DOM Parts:
     88        expectDOMParts = false;
     89        cleanup = [wrapper];
     90        break;
     91      case 'PartClone':
     92        doc = document;
     93        wrapper = document.body.appendChild(document.createElement('div'));
     94        wrapper.appendChild(template.content.getPartRoot().clone().rootContainer);
     95        target = wrapper.querySelector('#target3');
     96        // Even a PartRoot clone should not add parts to the document, when that
     97        // clone is appendChilded to the document.
     98        expectDOMParts = false;
     99        cleanup = [wrapper];
    100        break;
    101      default:
    102        assert_unreached('Invalid test case');
    103    }
    104    assert_true(!!(doc && target && target.parentElement));
    105 
    106    const root = doc.getPartRoot();
    107    t.add_cleanup(() => cleanup.forEach(el => el.remove())); // Cleanup
    108    addPartsCleanup(t,root); // Clean up all Parts when this test ends.
    109 
    110    assert_true(root instanceof DocumentPartRoot);
    111    if (expectDOMParts) {
    112      let expectedRootParts = [{type:'ChildNodePart',metadata:[]}];
    113      for(let i=0;i<6;++i) {
    114        expectedRootParts.push({type:'NodePart',metadata:[]});
    115      }
    116      assertEqualParts(root.getParts(),expectedRootParts,0,'declarative root missing parts');
    117      for(let i=1;i<=6;++i) {
    118        assert_equals(root.getParts()[i].node,target.querySelector(`#nodepart${i}`));
    119      }
    120      const childPart1 = root.getParts()[0];
    121      assertIsComment(childPart1.previousSibling,kChildNodePartStartCommentData);
    122      assertIsComment(childPart1.nextSibling,kChildNodePartEndCommentData);
    123      const expectedChild1Parts = [{type:'ChildNodePart',metadata:[]}];
    124      assertEqualParts(childPart1.getParts(),expectedChild1Parts,0,'First level childpart should just have one child part');
    125      const childPart2 = childPart1.getParts()[0];
    126      assertIsComment(childPart2.previousSibling,kChildNodePartStartCommentData);
    127      assertIsComment(childPart2.nextSibling,kChildNodePartEndCommentData);
    128      const expectedChild2Parts = [{type:'NodePart',metadata:[]},{type:'AttributePart',metadata:[]}];
    129      assertEqualParts(childPart2.getParts(),expectedChild2Parts,0,'Second level childpart should have NodePart and AttributePart');
    130      assert_true(childPart2.getParts()[0].node instanceof HTMLSpanElement);
    131      assert_equals(childPart2.getParts()[0].node.textContent,'Middle');
    132      assert_true(childPart2.getParts()[1].node instanceof HTMLSpanElement);
    133      assert_equals(childPart2.getParts()[1].node.textContent,'Middle');
    134    } else {
    135      assertEqualParts(root.getParts(),[],[]);
    136    }
    137  }, `Basic declarative DOM Parts (${testCase})`);
    138 });
    139 
    140 }</script>
    141 
    142 <div parseparts>Before {{#}}Parts{{/}} After</div>
    143 <script>
    144  test((t) => {
    145    const target = document.currentScript.previousElementSibling;
    146    t.add_cleanup(() => target.remove());
    147    const root = document.getPartRoot();
    148    addPartsCleanup(t,root);
    149    assert_equals(root.getParts().length,1);
    150    assert_equals(target.innerHTML,'Before <!---->Parts<!----> After');
    151 
    152    // Verify that removing the parseparts attribute does nothing.
    153    target.removeAttribute('parseparts');
    154    assert_equals(root.getParts().length,1);
    155    assert_equals(target.innerHTML,'Before <!---->Parts<!----> After');
    156  }, 'Post-parsing structure of child parts, and stickiness');
    157 </script>
    158 
    159 <div>Before {{#}}Parts{{/}} After</div>
    160 <script>{
    161  test((t) => {
    162    const target = document.currentScript.previousElementSibling;
    163    t.add_cleanup(() => target.remove());
    164    const root = document.getPartRoot();
    165    addPartsCleanup(t,root);
    166    assert_equals(root.getParts().length,0);
    167    assert_equals(target.innerHTML,'Before {{#}}Parts{{/}} After');
    168    target.setAttribute('parseparts','');
    169    assert_equals(root.getParts().length,0);
    170    assert_equals(target.innerHTML,'Before {{#}}Parts{{/}} After');
    171  }, 'Parser only behavior - adding parseparts does nothing');
    172 }</script>
    173 
    174 <div parseparts>{{#}}{{/}}</div>
    175 <script>{
    176  test((t) => {
    177    const target = document.currentScript.previousElementSibling;
    178    t.add_cleanup(() => target.remove());
    179    const root = document.getPartRoot();
    180    addPartsCleanup(t,root);
    181    assert_equals(root.getParts().length,1);
    182    assert_equals(target.innerHTML,'<!----><!---->');
    183  }, 'Just parts, no text before');
    184 }</script>
    185 
    186 <div><input parseparts>{{#}}{{/}}</input></div>
    187 <script>{
    188  test((t) => {
    189    const target = document.currentScript.previousElementSibling;
    190    t.add_cleanup(() => target.remove());
    191    const root = document.getPartRoot();
    192    addPartsCleanup(t,root);
    193    assert_equals(root.getParts().length,0);
    194    assert_equals(target.innerHTML,'<input parseparts=\"\">{{#}}{{/}}');
    195  }, 'Self closing elements can\'t use parseparts');
    196 }</script>
    197 
    198 <div><head parseparts>{{#}}{{/}}</head></div>
    199 <script>{
    200  test((t) => {
    201    const target = document.currentScript.previousElementSibling;
    202    t.add_cleanup(() => target.remove());
    203    const root = document.getPartRoot();
    204    addPartsCleanup(t,root);
    205    assert_equals(root.getParts().length,0);
    206    assert_equals(target.innerHTML,'{{#}}{{/}}');
    207  }, 'Second head element can\'t use parseparts');
    208 }</script>
    209 
    210 <div parseparts><svg>{{#}}<circle/>{{/}}</svg></div>
    211 
    212 <script>{
    213  test((t) => {
    214    const target = document.currentScript.previousElementSibling;
    215    t.add_cleanup(() => target.remove());
    216    const root = document.getPartRoot();
    217    addPartsCleanup(t,root);
    218    assert_equals(root.getParts().length,1);
    219    assert_equals(target.innerHTML,'<svg><!----><circle/><!----></svg>');
    220  }, 'Foreign content should support Parts');
    221 }</script>
    222 
    223 <div>
    224  <div parseparts>{{}}{{ }}{{ #}}{{ /}}{{{}}}</div>
    225  <div>{{}}{{ }}{{ #}}{{ /}}{{{}}}</div>
    226 </div>
    227 <script>{
    228  test((t) => {
    229    const target = document.currentScript.previousElementSibling;
    230    t.add_cleanup(() => target.remove());
    231    const root = document.getPartRoot();
    232    addPartsCleanup(t,root);
    233    assert_equals(root.getParts().length,0);
    234    assert_equals(target.childElementCount,2);
    235    Array.from(target.children).forEach(el => {
    236      assert_equals(el.innerHTML,'{{}}{{ }}{{ #}}{{ /}}{{{}}}');
    237    })
    238  }, 'Not quite parts syntax - none should become parts, and nothing should crash');
    239 }</script>