tor-browser

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

imperative-slot-api-slotchange.html (11732B)


      1 <!DOCTYPE html>
      2 <title>Shadow DOM: Imperative Slot API slotchange event</title>
      3 <meta name="author" title="Yu Han" href="mailto:yuzhehan@chromium.org">
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <link rel="help" href="https://dom.spec.whatwg.org/#signaling-slot-change">
      7 <script src="resources/shadow-dom.js"></script>
      8 
      9 <div id="test_slotchange">
     10  <div id="host">
     11    <template id="shadow_root" data-mode="open" data-slot-assignment="manual">
     12      <slot id="s1"><div id="fb">fallback</div></slot>
     13      <slot id="s2"></slot>
     14      <div>
     15        <slot id="s2.5"></slot>
     16      </div>
     17      <slot id="s3"></slot>
     18    </template>
     19    <div id="c1"></div>
     20    <div id="c2"></div>
     21  </div>
     22  <div id="c4"></div>
     23 </div>
     24 
     25 <script>
     26 function getDataCollection() {
     27  return {
     28    s1EventCount: 0,
     29    s2EventCount: 0,
     30    s3EventCount: 0,
     31    s1ResolveFn: null,
     32    s2ResolveFn: null,
     33    s3ResolveFn: null,
     34  }
     35 }
     36 
     37 function setupShadowDOM(id, test, data) {
     38  let tTree = createTestTree(id);
     39  tTree.s1.addEventListener('slotchange', (event) => {
     40    if (!event.isFakeEvent) {
     41      test.step(function () {
     42          assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"');
     43          assert_equals(event.target, tTree.s1, 'slotchange event\'s target must be the slot element');
     44          assert_equals(event.relatedTarget, undefined, 'slotchange must not set relatedTarget');
     45      });
     46      data.s1EventCount++;
     47    }
     48    data.s1ResolveFn();
     49  });
     50  tTree.s2.addEventListener('slotchange', (event) => {
     51    if (!event.isFakeEvent) {
     52      test.step(function () {
     53          assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"');
     54          assert_equals(event.target, tTree.s2, 'slotchange event\'s target must be the slot element');
     55          assert_equals(event.relatedTarget, undefined, 'slotchange must not set relatedTarget');
     56      });
     57      data.s2EventCount++;
     58    }
     59    data.s2ResolveFn();
     60  });
     61  tTree.s3.addEventListener('slotchange', (event) => {
     62    if (!event.isFakeEvent) {
     63      test.step(function () {
     64        assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"');
     65        // listen to bubbling events.
     66        assert_equals(event.relatedTarget, undefined, 'slotchange must not set relatedTarget');
     67      });
     68      data.s3EventCount++;
     69    }
     70    data.s3ResolveFn();
     71  });
     72  return tTree;
     73 }
     74 
     75 function monitorSlots(data) {
     76    const s1Promise = new Promise((resolve, reject) => {
     77      data.s1ResolveFn = resolve;
     78    });
     79    const s2Promise = new Promise((resolve, reject) => {
     80      data.s2ResolveFn = resolve;
     81    });
     82    const s3Promise = new Promise((resolve, reject) => {
     83      data.s3ResolveFn = resolve;
     84    });
     85    return [s1Promise, s2Promise, s3Promise];
     86 }
     87 </script>
     88 
     89 <script>
     90 // Tests:
     91 async_test((test) => {
     92  const data = getDataCollection();
     93  let tTree = setupShadowDOM(test_slotchange, test, data);
     94  let [s1Promise, s2Promise] = monitorSlots(data);
     95 
     96  tTree.s1.assign(tTree.c1);
     97  tTree.s2.assign(tTree.c2);
     98 
     99  assert_equals(data.s1EventCount, 0, 'slotchange event must not be fired synchronously');
    100  assert_equals(data.s2EventCount, 0);
    101 
    102  Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => {
    103    assert_equals(data.s1EventCount, 1);
    104    assert_equals(data.s2EventCount, 1);
    105  }));
    106 }, 'slotchange event must not fire synchronously.');
    107 
    108 async_test((test) => {
    109  const data = getDataCollection();
    110  let tTree = setupShadowDOM(test_slotchange, test, data);
    111  let [s1Promise, s2Promise] = monitorSlots(data);
    112 
    113  tTree.s1.assign();;
    114  tTree.s2.assign();
    115  tTree.host.insertBefore(tTree.c4, tTree.c1);
    116 
    117  Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => {
    118    assert_equals(data.s1EventCount, 0);
    119    assert_equals(data.s2EventCount, 0);
    120  }));
    121 
    122  // use fake event to trigger event handler.
    123  let fakeEvent = new Event('slotchange');
    124  fakeEvent.isFakeEvent = true;
    125  tTree.s1.dispatchEvent(fakeEvent);
    126  tTree.s2.dispatchEvent(fakeEvent);
    127 }, 'slotchange event should not fire when assignments do not change assignedNodes.');
    128 
    129 async_test((test) => {
    130  const data = getDataCollection();
    131  let tTree = setupShadowDOM(test_slotchange,test, data);
    132  let [s1Promise] = monitorSlots(data);
    133 
    134  tTree.s1.assign(tTree.c1, tTree.c2);
    135 
    136  s1Promise.then(test.step_func(() => {
    137    assert_equals(data.s1EventCount, 1);
    138 
    139    [s1Promise] = monitorSlots(data);
    140    tTree.s1.assign(tTree.c1, tTree.c2);
    141    tTree.s1.assign(tTree.c1, tTree.c2, tTree.c1, tTree.c2, tTree.c2);
    142 
    143    s1Promise.then(test.step_func_done(() => {
    144      assert_equals(data.s1EventCount, 1);
    145    }));
    146 
    147    let fakeEvent = new Event('slotchange');
    148    fakeEvent.isFakeEvent = true;
    149    tTree.s1.dispatchEvent(fakeEvent);
    150  }));
    151 
    152 }, 'slotchange event should not fire when same node is assigned.');
    153 
    154 async_test((test) => {
    155  const data = getDataCollection();
    156  let tTree = setupShadowDOM(test_slotchange, test, data);
    157  let [s1Promise, s2Promise] = monitorSlots(data);
    158 
    159  tTree.s1.assign(tTree.c1);
    160  tTree.s2.assign(tTree.c2);
    161 
    162  Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => {
    163    assert_equals(data.s1EventCount, 1);
    164    assert_equals(data.s2EventCount, 1);
    165  }));
    166 }, "Fire slotchange event when slot's assigned nodes changes.");
    167 
    168 async_test((test) => {
    169  const data = getDataCollection();
    170  let tTree = setupShadowDOM(test_slotchange, test, data);
    171  let [s1Promise, s2Promise] = monitorSlots(data);
    172 
    173  tTree.s1.assign(tTree.c1);
    174 
    175  s1Promise.then(test.step_func(() => {
    176    assert_equals(data.s1EventCount, 1);
    177 
    178    [s1Promise, s2Promise] = monitorSlots(data);
    179    tTree.s2.assign(tTree.c1);
    180 
    181    Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => {
    182      assert_equals(data.s1EventCount, 2);
    183      assert_equals(data.s2EventCount, 1);
    184    }));
    185  }));
    186 }, "Fire slotchange event on previous slot and new slot when node is reassigned.");
    187 
    188 async_test((test) => {
    189  const data = getDataCollection();
    190  let tTree = setupShadowDOM(test_slotchange, test, data);
    191  let [s1Promise] = monitorSlots(data);
    192 
    193  tTree.s1.assign(tTree.c1);
    194 
    195  s1Promise.then(test.step_func(() => {
    196    assert_equals(data.s1EventCount, 1);
    197 
    198    [s1Promise] = monitorSlots(data);
    199    tTree.s1.assign();
    200 
    201    s1Promise.then(test.step_func_done(() => {
    202      assert_equals(data.s1EventCount, 2);
    203    }));
    204  }));
    205 }, "Fire slotchange event on node assignment and when assigned node is removed.");
    206 
    207 async_test((test) => {
    208  const data = getDataCollection();
    209  let tTree = setupShadowDOM(test_slotchange, test, data);
    210  let [s1Promise] = monitorSlots(data);
    211 
    212  tTree.s1.assign(tTree.c1, tTree.c2);
    213 
    214  s1Promise.then(test.step_func(() => {
    215    assert_equals(data.s1EventCount, 1);
    216 
    217    [s1Promise] = monitorSlots(data);
    218    tTree.s1.assign(tTree.c2, tTree.c1);
    219 
    220    s1Promise.then(test.step_func_done(() => {
    221      assert_equals(data.s1EventCount, 2);
    222    }));
    223  }));
    224 }, "Fire slotchange event when order of assigned nodes changes.");
    225 
    226 promise_test((test) => {
    227  const data = getDataCollection();
    228  let tTree = setupShadowDOM(test_slotchange, test, data);
    229  let [s1Promise] = monitorSlots(data);
    230 
    231  tTree.s1.assign(tTree.c1);
    232 
    233  return s1Promise.then(test.step_func(() => {
    234    assert_equals(data.s1EventCount, 1);
    235 
    236    [s1Promise] = monitorSlots(data);
    237    tTree.c1.remove();
    238 
    239    return s1Promise;
    240  }))
    241  .then(test.step_func(() => {
    242      assert_equals(data.s1EventCount, 2);
    243  }));
    244 }, "Fire slotchange event when assigned node is removed.");
    245 
    246 promise_test((test) => {
    247  const data = getDataCollection();
    248  let tTree = setupShadowDOM(test_slotchange, test, data);
    249  [s1Promise] = monitorSlots(data);
    250 
    251  tTree.s1.assign(tTree.c1);
    252 
    253  return s1Promise.then(test.step_func(() => {
    254    assert_equals(data.s1EventCount, 1);
    255 
    256    [s1Promise] = monitorSlots(data);
    257    tTree.s1.remove();
    258 
    259    return s1Promise;
    260  }))
    261  .then(test.step_func(() => {
    262      assert_equals(data.s1EventCount, 2);
    263  }));
    264 }, "Fire slotchange event when removing a slot from Shadows Root that changes its assigned nodes.");
    265 
    266 async_test((test) => {
    267  const data = getDataCollection();
    268  let tTree = setupShadowDOM(test_slotchange, test, data);
    269  let [s1Promise] = monitorSlots(data);
    270 
    271  tTree.s1.remove();
    272 
    273  let fakeEvent = new Event('slotchange');
    274  fakeEvent.isFakeEvent = true;
    275  tTree.s1.dispatchEvent(fakeEvent);
    276 
    277  s1Promise.then(test.step_func(() => {
    278    assert_equals(data.s2EventCount, 0);
    279 
    280    [s1Promise, s2Promise] = monitorSlots(data);
    281    tTree.shadow_root.insertBefore(tTree.s1, tTree.s2);
    282 
    283    tTree.s1.dispatchEvent(fakeEvent);
    284    tTree.s2.dispatchEvent(fakeEvent);
    285 
    286    Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => {
    287      assert_equals(data.s1EventCount, 0);
    288      assert_equals(data.s2EventCount, 0);
    289    }));
    290  }));
    291 
    292 }, "No slotchange event when adding or removing an empty slot.");
    293 
    294 async_test((test) => {
    295  const data = getDataCollection();
    296  let tTree = setupShadowDOM(test_slotchange, test, data);
    297  let [s1Promise, s2Promise] = monitorSlots(data);
    298 
    299  tTree.host.appendChild(document.createElement("div"));
    300 
    301  let fakeEvent = new Event('slotchange');
    302  fakeEvent.isFakeEvent = true;
    303  tTree.s1.dispatchEvent(fakeEvent);
    304  tTree.s2.dispatchEvent(fakeEvent);
    305 
    306  Promise.all([s1Promise, s2Promise]).then(test.step_func(() => {
    307    assert_equals(data.s1EventCount, 0);
    308    assert_equals(data.s2EventCount, 0);
    309 
    310    [s1Promise, s2Promise] = monitorSlots(data);
    311    tTree.shadow_root.insertBefore(document.createElement("div"), tTree.s2);
    312 
    313    tTree.s1.dispatchEvent(fakeEvent);
    314    tTree.s2.dispatchEvent(fakeEvent);
    315 
    316    Promise.all([s1Promise, s2Promise]).then(test.step_func_done(() => {
    317      assert_equals(data.s1EventCount, 0);
    318      assert_equals(data.s2EventCount, 0);
    319    }));
    320  }));
    321 
    322 }, "No slotchange event when adding another slotable.");
    323 
    324 </script>
    325 
    326 <div id="test_nested_slotchange">
    327  <div>
    328    <template data-mode="open" data-slot-assignment="manual">
    329      <div>
    330        <template data-mode="open" data-slot-assignment="manual">
    331          <slot id="s2"></slot>
    332          <slot id="s3"></slot>
    333        </template>
    334        <slot id="s1"></slot>
    335      </div>
    336    </template>
    337    <div id="c1"></div>
    338  </div>
    339 </div>
    340 
    341 <script>
    342 async_test((test) => {
    343  const data = getDataCollection();
    344  let tTree = setupShadowDOM(test_nested_slotchange, test, data);
    345  let [s1Promise, s2Promise, s3Promise] = monitorSlots(data);
    346 
    347  tTree.s3.assign(tTree.s1);
    348 
    349  s3Promise.then(test.step_func(() => {
    350    assert_equals(data.s3EventCount, 1);
    351    [s1Promise, s2Promise, s3Promise] = monitorSlots(data);
    352 
    353    tTree.s1.assign(tTree.c1);
    354 
    355    Promise.all([s1Promise, s3Promise]).then(test.step_func_done(() => {
    356      assert_equals(data.s1EventCount, 1);
    357      assert_equals(data.s3EventCount, 2);
    358    }));
    359  }));
    360 }, "Fire slotchange event when assign node to nested slot, ensure event bubbles ups.");
    361 
    362 promise_test(async t => {
    363  async function mutationObserversRun() {
    364    return new Promise(r => {
    365      t.step_timeout(r, 0);
    366    });
    367  }
    368  let tTree = createTestTree(test_slotchange);
    369 
    370  tTree.s1.assign(tTree.c1);
    371  tTree["s2.5"].assign(tTree.c2);
    372 
    373  let slotChangedOrder = [];
    374 
    375  // Clears out pending mutation observers
    376  await mutationObserversRun();
    377 
    378  tTree.s1.addEventListener("slotchange", function() {
    379    slotChangedOrder.push("s1");
    380  });
    381 
    382  tTree.s3.addEventListener("slotchange", function() {
    383    slotChangedOrder.push("s3");
    384  });
    385 
    386  tTree["s2.5"].addEventListener("slotchange", function() {
    387    slotChangedOrder.push("s2.5");
    388  });
    389 
    390  tTree.s3.assign(tTree.c2, tTree.c1);
    391  await mutationObserversRun();
    392  assert_array_equals(slotChangedOrder, ["s1", "s2.5", "s3"]);
    393 }, 'Signal a slot change should be done in tree order.');
    394 </script>