tor-browser

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

Event-dispatch-on-disabled-elements.html (7727B)


      1 <!doctype html>
      2 <meta charset="utf8">
      3 <meta name="timeout" content="long">
      4 <title>Events must dispatch on disabled elements</title>
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="/resources/testdriver.js"></script>
      8 <script src="/resources/testdriver-vendor.js"></script>
      9 <style>
     10  @keyframes fade {
     11    0% {
     12      opacity: 1;
     13    }
     14    100% {
     15      opacity: 0;
     16    }
     17  }
     18 </style>
     19 <body>
     20 <script>
     21 // HTML elements that can be disabled
     22 const formElements = ["button", "input", "select", "textarea"];
     23 
     24 test(() => {
     25  for (const localName of formElements) {
     26    const elem = document.createElement(localName);
     27    elem.disabled = true;
     28    // pass becomes true if the event is called and it's the right type.
     29    let pass = false;
     30    const listener = ({ type }) => {
     31      pass = type === "click";
     32    };
     33    elem.addEventListener("click", listener, { once: true });
     34    elem.dispatchEvent(new Event("click"));
     35    assert_true(
     36      pass,
     37      `Untrusted "click" Event didn't dispatch on ${elem.constructor.name}.`
     38    );
     39  }
     40 }, "Can dispatch untrusted 'click' Events at disabled HTML elements.");
     41 
     42 test(() => {
     43  for (const localName of formElements) {
     44    const elem = document.createElement(localName);
     45    elem.disabled = true;
     46    // pass becomes true if the event is called and it's the right type.
     47    let pass = false;
     48    const listener = ({ type }) => {
     49      pass = type === "pass";
     50    };
     51    elem.addEventListener("pass", listener, { once: true });
     52    elem.dispatchEvent(new Event("pass"));
     53    assert_true(
     54      pass,
     55      `Untrusted "pass" Event didn't dispatch on ${elem.constructor.name}`
     56    );
     57  }
     58 }, "Can dispatch untrusted Events at disabled HTML elements.");
     59 
     60 test(() => {
     61  for (const localName of formElements) {
     62    const elem = document.createElement(localName);
     63    elem.disabled = true;
     64    // pass becomes true if the event is called and it's the right type.
     65    let pass = false;
     66    const listener = ({ type }) => {
     67      pass = type === "custom-pass";
     68    };
     69    elem.addEventListener("custom-pass", listener, { once: true });
     70    elem.dispatchEvent(new CustomEvent("custom-pass"));
     71    assert_true(
     72      pass,
     73      `CustomEvent "custom-pass" didn't dispatch on ${elem.constructor.name}`
     74    );
     75  }
     76 }, "Can dispatch CustomEvents at disabled HTML elements.");
     77 
     78 test(() => {
     79  for (const localName of formElements) {
     80    const elem = document.createElement(localName);
     81 
     82    // Element is disabled... so this click() MUST NOT fire an event.
     83    elem.disabled = true;
     84    let pass = true;
     85    elem.onclick = e => {
     86      pass = false;
     87    };
     88    elem.click();
     89    assert_true(
     90      pass,
     91      `.click() must not dispatch "click" event on disabled ${
     92        elem.constructor.name
     93      }.`
     94    );
     95 
     96    // Element is (re)enabled... so this click() fires an event.
     97    elem.disabled = false;
     98    pass = false;
     99    elem.onclick = e => {
    100      pass = true;
    101    };
    102    elem.click();
    103    assert_true(
    104      pass,
    105      `.click() must dispatch "click" event on enabled ${
    106        elem.constructor.name
    107      }.`
    108    );
    109  }
    110 }, "Calling click() on disabled elements must not dispatch events.");
    111 
    112 promise_test(async () => {
    113  // For each form element type, set up transition event handlers.
    114  for (const localName of formElements) {
    115    const elem = document.createElement(localName);
    116    elem.disabled = true;
    117    document.body.appendChild(elem);
    118    const eventPromises = [
    119      "transitionrun",
    120      "transitionstart",
    121      "transitionend",
    122    ].map(eventType => {
    123      return new Promise(r => {
    124        elem.addEventListener(eventType, r);
    125      });
    126    });
    127    // Flushing style triggers transition.
    128    getComputedStyle(elem).opacity;
    129    elem.style.transition = "opacity .1s";
    130    elem.style.opacity = 0;
    131    getComputedStyle(elem).opacity;
    132    // All the events fire...
    133    await Promise.all(eventPromises);
    134    elem.remove();
    135  }
    136 }, "CSS Transitions transitionrun, transitionstart, transitionend events fire on disabled form elements");
    137 
    138 promise_test(async () => {
    139  // For each form element type, set up transition event handlers.
    140  for (const localName of formElements) {
    141    const elem = document.createElement(localName);
    142    elem.disabled = true;
    143    document.body.appendChild(elem);
    144    getComputedStyle(elem).opacity;
    145    elem.style.transition = "opacity 100s";
    146    // We use ontransitionstart to cancel the event.
    147    elem.ontransitionstart = () => {
    148      elem.style.display = "none";
    149    };
    150    const promiseToCancel = new Promise(r => {
    151      elem.ontransitioncancel = r;
    152    });
    153    // Flushing style triggers the transition.
    154    elem.style.opacity = 0;
    155    getComputedStyle(elem).opacity;
    156    await promiseToCancel;
    157    // And we are done with this element.
    158    elem.remove();
    159  }
    160 }, "CSS Transitions transitioncancel event fires on disabled form elements");
    161 
    162 promise_test(async () => {
    163  // For each form element type, set up transition event handlers.
    164  for (const localName of formElements) {
    165    const elem = document.createElement(localName);
    166    document.body.appendChild(elem);
    167    elem.disabled = true;
    168    const animationStartPromise = new Promise(r => {
    169      elem.addEventListener("animationstart", () => {
    170        // Seek to the second iteration to trigger the animationiteration event
    171        elem.style.animationDelay = "-100s"
    172        r();
    173      });
    174    });
    175    const animationIterationPromise = new Promise(r => {
    176      elem.addEventListener("animationiteration", ()=>{
    177        elem.style.animationDelay = "-200s"
    178        r();
    179      });
    180    });
    181    const animationEndPromise = new Promise(r => {
    182      elem.addEventListener("animationend", r);
    183    });
    184    elem.style.animation = "fade 100s 2";
    185    elem.classList.add("animate");
    186    // All the events fire...
    187    await Promise.all([
    188      animationStartPromise,
    189      animationIterationPromise,
    190      animationEndPromise,
    191    ]);
    192    elem.remove();
    193  }
    194 }, "CSS Animation animationstart, animationiteration, animationend fire on disabled form elements");
    195 
    196 promise_test(async () => {
    197  // For each form element type, set up transition event handlers.
    198  for (const localName of formElements) {
    199    const elem = document.createElement(localName);
    200    document.body.appendChild(elem);
    201    elem.disabled = true;
    202 
    203    const promiseToCancel = new Promise(r => {
    204      elem.addEventListener("animationcancel", r);
    205    });
    206 
    207    elem.addEventListener("animationstart", () => {
    208      // Cancel the animation by hiding it.
    209      elem.style.display = "none";
    210    });
    211 
    212    // Trigger the animation
    213    elem.style.animation = "fade 100s";
    214    elem.classList.add("animate");
    215    await promiseToCancel;
    216    // And we are done with this element.
    217    elem.remove();
    218  }
    219 }, "CSS Animation's animationcancel event fires on disabled form elements");
    220 
    221 promise_test(async () => {
    222  for (const localName of formElements) {
    223    const elem = document.createElement(localName);
    224    elem.disabled = true;
    225    document.body.appendChild(elem);
    226    // Element is disabled, so clicking must not fire events
    227    let pass = true;
    228    elem.onclick = e => {
    229      pass = false;
    230    };
    231    // Disabled elements are not clickable.
    232    await test_driver.click(elem);
    233    assert_true(
    234      pass,
    235      `${elem.constructor.name} is disabled, so onclick must not fire.`
    236    );
    237    // Element is (re)enabled... so this click() will fire an event.
    238    pass = false;
    239    elem.disabled = false;
    240    elem.onclick = () => {
    241      pass = true;
    242    };
    243    await test_driver.click(elem);
    244    assert_true(
    245      pass,
    246      `${elem.constructor.name} is enabled, so onclick must fire.`
    247    );
    248    elem.remove();
    249  }
    250 }, "Real clicks on disabled elements must not dispatch events.");
    251 </script>