tor-browser

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

Selection-getComposedRanges-dom-mutations-removal.html (5653B)


      1 <!DOCTYPE html>
      2 <html>
      3 <body>
      4 <meta name="author" href="mailto:dizhangg@chromium.org">
      5 <link rel="help" href="https://w3c.github.io/selection-api/#dom-selection-getcomposedranges">
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 <meta name="variant" content="?mode=closed">
      9 <meta name="variant" content="?mode=open">
     10 
     11 <div id="container"></div>
     12 
     13 <script>
     14 
     15 const mode = (new URLSearchParams(document.location.search)).get("mode");
     16 
     17 test(() => {
     18  const sel = getSelection();
     19 
     20  container.innerHTML = 'a<div id="host"></div>b';
     21  const host = container.querySelector('#host');
     22  const shadowRoot = host.attachShadow({ mode });
     23  shadowRoot.innerHTML = 'hello, world';
     24  sel.setBaseAndExtent(shadowRoot.firstChild, 7, container, 2);
     25  const rangeBefore = sel.getComposedRanges({ shadowRoots: [shadowRoot] })[0];
     26  host.remove();
     27  const rangeAfter = sel.getComposedRanges({ shadowRoots: [shadowRoot] })[0];
     28 
     29  assert_equals(rangeBefore.startContainer, shadowRoot.firstChild, 'StaticRange does not update on new mutation.');
     30  assert_equals(rangeBefore.startOffset, 7);
     31  assert_equals(rangeBefore.endContainer, container);
     32  assert_equals(rangeBefore.endOffset, 2);
     33 
     34  assert_equals(rangeAfter.startContainer, container, 'collapsed to the host parent: container');
     35  assert_equals(rangeAfter.startOffset, 1);
     36  assert_equals(rangeAfter.endContainer, container);
     37  assert_equals(rangeAfter.endOffset, 1);
     38 }, 'Range is fully in shadow tree. Removing shadow host collapses composed StaticRange. Note it does not update previously returned composed StaticRange.');
     39 
     40 test(() => {
     41  const sel = getSelection();
     42 
     43  container.innerHTML = '<div id="wrapper">a<div id="host"></div>b</div>';
     44  const wrapper = container.querySelector('#wrapper');
     45  const host = container.querySelector('#host');
     46  const shadowRoot = host.attachShadow({ mode });
     47  shadowRoot.innerHTML = 'hello, world';
     48  sel.setBaseAndExtent(shadowRoot.firstChild, 4, shadowRoot.firstChild, 7);
     49  wrapper.remove();
     50 
     51  const rangeAfter = sel.getComposedRanges({ shadowRoots: [shadowRoot] })[0];
     52  assert_equals(rangeAfter.startContainer, container, 'collapsed to parent of removed node');
     53  assert_equals(rangeAfter.startOffset, 0);
     54  assert_equals(rangeAfter.endContainer, container);
     55  assert_equals(rangeAfter.endOffset, 0);
     56 }, 'Range is fully in shadow tree. Removing parent of shadow host collapses composed StaticRange.');
     57 
     58 test(() => {
     59 const sel = getSelection();
     60 
     61  container.innerHTML = '<div id="hello">Hello,</div><div id="world"> World</div>';
     62  sel.setBaseAndExtent(hello.firstChild, 1, world.firstChild, 3);
     63  hello.firstChild.remove();
     64  const rangeAfter = sel.getComposedRanges()[0];
     65 
     66  assert_equals(rangeAfter.startContainer, hello);
     67  assert_equals(rangeAfter.startOffset, 0);
     68  assert_equals(rangeAfter.endContainer, world.firstChild);
     69  assert_equals(rangeAfter.endOffset, 3);
     70 }, 'Range is in light DOM. Removing startContainer rescopes new composed range to its parent.');
     71 
     72 test(() => {
     73 const sel = getSelection();
     74 
     75 container.innerHTML = 'a<div id="host"></div>b';
     76  const host = container.querySelector('#host');
     77  const shadowRoot = host.attachShadow({ mode });
     78  shadowRoot.innerHTML = 'hello, world';
     79  sel.setBaseAndExtent(shadowRoot.firstChild, 7, container, 2);
     80  shadowRoot.innerHTML = '';
     81  const rangeAfter = sel.getComposedRanges({ shadowRoots: [shadowRoot] })[0];
     82 
     83  assert_equals(rangeAfter.startContainer, shadowRoot, 'collapsed to be at the parent shadow root');
     84  assert_equals(rangeAfter.startOffset, 0);
     85  assert_equals(rangeAfter.endContainer, container);
     86  assert_equals(rangeAfter.endOffset, 2);
     87 }, 'Range is across shadow trees. Replacing shadowRoot content rescopes new composed range to the shadowRoot.');
     88 
     89 test(() => {
     90  const sel = getSelection();
     91 
     92  container.innerHTML = 'a<div id="outerhost"></div>b';
     93  const outerHost = container.querySelector('#outerhost');
     94  const outerRoot = outerHost.attachShadow({ mode });
     95  outerRoot.innerHTML = 'c<div id="innerHost"></div>d';
     96  const innerHost = outerRoot.querySelector('#innerHost');
     97  const innerRoot = innerHost.attachShadow({ mode });
     98  innerRoot.innerHTML = 'hello, world';
     99  sel.setBaseAndExtent(container.firstChild, 0, innerRoot.firstChild, 4);
    100  outerHost.remove();
    101  const rangeAfter = sel.getComposedRanges({ shadowRoots: [innerRoot, outerRoot] })[0];
    102 
    103  assert_equals(rangeAfter.startContainer, container.firstChild);
    104  assert_equals(rangeAfter.startOffset, 0);
    105  assert_equals(rangeAfter.endContainer, container);
    106  assert_equals(rangeAfter.endOffset, 1);
    107 }, 'Range is across shadow trees. Removing ancestor shadow host rescopes composed range end to parent.');
    108 
    109 test(() => {
    110  container.innerHTML = [
    111    '<div id=host>',
    112    '<div id=div1 slot=slot2>slotted content 1</div>',
    113    '<div id=div2 slot=slot1>slotted content 2</div>',
    114    '</div>'
    115  ].join('');
    116  const shadowRoot = host.attachShadow({mode: 'open'});
    117  shadowRoot.innerHTML = [
    118    '<span>before</span>',
    119    '<slot name=slot1></slot>',
    120    '<span>between</span>',
    121    '<slot name=slot2></slot>',
    122    '<span>after</span>',
    123  ].join('');
    124 
    125  const sel = getSelection();
    126  sel.setBaseAndExtent(div1.firstChild, 2, div2.firstChild, 2);
    127  div1.remove();
    128 
    129  const rangeAfter = sel.getComposedRanges({ shadowRoots: [shadowRoot] })[0];
    130  assert_equals(rangeAfter.startContainer, host);
    131  assert_equals(rangeAfter.startOffset, 0);
    132  assert_equals(rangeAfter.endContainer, div2.firstChild);
    133  assert_equals(rangeAfter.endOffset, 2);
    134 }, 'Range is between two light slotted contents. Removing start container rescopes to its parent in light tree.');
    135 
    136 </script>
    137 </body>
    138 </html>