tor-browser

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

event-propagate-disabled-keyboard.tentative.html (4478B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <title>KeyboardEvent propagation on disabled form elements</title>
      4 <link rel="author" href="mailto:avandolder@mozilla.com">
      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 <script src="/resources/testdriver-actions.js"></script>
     10 <script>
     11  class CustomControl extends HTMLElement {
     12    static get formAssociated() {return true;}
     13 
     14    constructor() {
     15      super();
     16      this.internals = this.attachInternals();
     17 
     18      this.attachShadow({mode: "open", delegatesFocus: true});
     19      this.shadowRoot.append(
     20        document.querySelector("template").content.cloneNode(true)
     21      );
     22    }
     23 
     24    get target() {
     25      return this.shadowRoot.getElementById("target");
     26    }
     27  }
     28 
     29  customElements.define("custom-control", CustomControl)
     30 </script>
     31 
     32 <template>
     33  <div tabindex="0" id="target">
     34    <slot></slot>
     35  </div>
     36 </template>
     37 
     38 <div tabindex="0" id="reset"></div>
     39 
     40 <form id="form">
     41  <input> <!-- Sanity check with non-disabled control -->
     42  <select disabled></select>
     43  <textarea disabled></textarea>
     44  <input disabled type="button">
     45  <input disabled type="checkbox">
     46  <input disabled type="color" value="#000000">
     47  <input disabled type="date">
     48  <input disabled type="datetime-local">
     49  <input disabled type="email">
     50  <input disabled type="file">
     51  <input disabled type="image">
     52  <input disabled type="month">
     53  <input disabled type="number">
     54  <input disabled type="password">
     55  <input disabled type="radio">
     56  <input disabled type="range" value="0">
     57  <input disabled type="reset">
     58  <input disabled type="search">
     59  <input disabled type="submit">
     60  <input disabled type="tel">
     61  <input disabled type="text">
     62  <input disabled type="time">
     63  <input disabled type="url">
     64  <input disabled type="week">
     65 
     66  <fieldset disabled><span tabindex="0">Span</span></fieldset>
     67  <button disabled><span tabindex="0">Span</span></button>
     68  <custom-control disabled>Text</custom-control>
     69 </form>
     70 
     71 <script>
     72  const keyEvents = ["keydown", "keyup"];
     73 
     74  function setupTest(t, element, observingElement) {
     75    const observedEvents = [];
     76    const controller = new AbortController();
     77    const {signal} = controller;
     78    const listenerFn = t.step_func(event => {
     79      observedEvents.push(event.type);
     80    });
     81    for (const event of keyEvents) {
     82      observingElement.addEventListener(event, listenerFn, {signal});
     83    }
     84    t.add_cleanup(() => controller.abort());
     85 
     86    const target = element;
     87    return {target, observedEvents};
     88  }
     89 
     90  function fire_trusted_key_events(target) {
     91    const actions = new test_driver.Actions();
     92    return actions.keyDown("a").keyUp("a").send();
     93  }
     94 
     95  function fire_untrusted_key_events(target) {
     96    target.dispatchEvent(new KeyboardEvent("keydown", {bubbles: true}));
     97    target.dispatchEvent(new KeyboardEvent("keyup", {bubbles: true}));
     98  }
     99 
    100  const observingElement = document.getElementById("form");
    101  const reset = document.getElementById("reset");
    102 
    103  for (const element of observingElement.children) {
    104    promise_test(async t => {
    105      const {observedEvents} = setupTest(t, element, observingElement);
    106 
    107      const target = element.firstElementChild ?? element.target ?? element;
    108      await t.step_func(fire_untrusted_key_events)(target);
    109      await new Promise(resolve => t.step_timeout(resolve, 0));
    110 
    111      assert_array_equals(observedEvents, keyEvents, "Observed events");
    112    }, `Untrusted key events on ${element.outerHTML}, observed from <${observingElement.localName}>`);
    113 
    114    // Only test elements with children for trusted key events.
    115    if (!element.firstElementChild && !element.target) {
    116      continue;
    117    }
    118 
    119    promise_test(async t => {
    120      const {observedEvents} = setupTest(t, element, observingElement);
    121 
    122      const target = element.firstElementChild ?? element.target;
    123 
    124      reset.focus();
    125      assert_not_equals(document.activeElement, target, "Reset current focus");
    126      target.focus();
    127      assert_equals(document.activeElement, element.firstElementChild ?? element, "Focus the target element");
    128 
    129      await t.step_func(fire_trusted_key_events)(target);
    130      await new Promise(resolve => t.step_timeout(resolve, 0));
    131 
    132      assert_array_equals(observedEvents, keyEvents, "Observed events");
    133    }, `Trusted key events on ${element.outerHTML}, observed from <${observingElement.localName}>`);
    134  }
    135 </script>