tor-browser

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

test_mouse_events_after_touchend.html (8466B)


      1 <!doctype html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8">
      5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
      6 <title>Tests for mouse events after touchend</title>
      7 <script src="/tests/SimpleTest/SimpleTest.js"></script>
      8 <script src="/tests/SimpleTest/EventUtils.js"></script>
      9 <script src="/tests/SimpleTest/paint_listener.js"></script>
     10 <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
     11 <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
     12 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
     13 <style>
     14 #parent, #child {
     15  width: 300px;
     16  height: 64px;
     17  padding: 16px;
     18 }
     19 #parent {
     20  background-color: black;
     21 }
     22 #child {
     23  background-color: gray;
     24 }
     25 </style>
     26 <script>
     27 "use strict";
     28 
     29 SimpleTest.waitForExplicitFinish();
     30 SimpleTest.requestFlakyTimeout("Required for waiting to prevent double tap at second tap");
     31 SimpleTest.waitForFocus(async () => {
     32  function stringifyEvent(event) {
     33    return `{ type: ${event.type}, target: ${
     34      event.target.id || event.target.nodeName
     35    }${
     36      event.detail !== undefined ? `, detail: ${event.detail}` : ""
     37    }${
     38      event.button !== undefined ? `, button: ${event.button}` : ""
     39    }${
     40      event.buttons !== undefined ? `, buttons: ${event.buttons}` : ""
     41    } }`;
     42  }
     43  function stringifyEvents(arrayOfEvents) {
     44    if (!arrayOfEvents.length) {
     45      return "[]";
     46    }
     47    let ret = "";
     48    for (const event of arrayOfEvents) {
     49      if (ret === "") {
     50        ret = "[ ";
     51      } else {
     52        ret += ", ";
     53      }
     54      ret += stringifyEvent(event);
     55    }
     56    return ret + " ]";
     57  }
     58 
     59  let events = [];
     60  for (const type of ["mousemove",
     61                      "mousedown",
     62                      "mouseup",
     63                      "click",
     64                      "dblclick",
     65                      "contextmenu",
     66                      "touchend"]) {
     67    if (type == "touchend") {
     68      addEventListener(type, event => {
     69        info(`Received: ${stringifyEvent(event)}`);
     70        events.push({type, target: event.target});
     71      }, {capture: true});
     72    } else {
     73      addEventListener(type, event => {
     74        info(`Received: ${stringifyEvent(event)}`);
     75        events.push({
     76          type: event.type,
     77          target: event.target,
     78          detail: event.detail,
     79          button: event.button,
     80          buttons: event.buttons,
     81        });
     82      }, {capture: true});
     83    }
     84  }
     85 
     86  function shiftEventsBefore(arrayOfEvents, aType) {
     87    const index = arrayOfEvents.findIndex(event => event.type == aType);
     88    if (index <= 0) {
     89      return [];
     90    }
     91    let ret = [];
     92    for (let i = 0; i < index; i++) {
     93      ret.push(arrayOfEvents.shift());
     94    }
     95    return ret;
     96  }
     97 
     98  const parent = document.getElementById("parent");
     99  const child = document.getElementById("child");
    100 
    101  function promiseEvent(aType) {
    102    return new Promise(resolve =>
    103      addEventListener(aType, resolve, {once: true})
    104    );
    105  }
    106 
    107  async function promiseFlushingAPZGestureState() {
    108    await promiseApzFlushedRepaints();
    109    // Wait for a while to avoid that the next tap will be treated as 2nd tap of
    110    // a double tap.
    111    return new Promise(
    112      resolve => setTimeout(
    113        resolve,
    114        // NOTE: x1.0 is not enough to avoid intermittent failures.
    115        SpecialPowers.getIntPref("apz.max_tap_time") * 1.2
    116      )
    117    );
    118  }
    119 
    120  await waitUntilApzStable();
    121  for (const prefValue of [true, false]) {
    122    await SpecialPowers.pushPrefEnv({
    123      set: [
    124        ["test.events.async.enabled", prefValue],
    125        ["ui.click_hold_context_menus.delay", 15000], // disable long tap
    126      ]
    127    });
    128    const desc = `(test.events.async.enabled=${prefValue})`;
    129 
    130    await (async function test_single_tap() {
    131      await promiseFlushingAPZGestureState();
    132      info("test_single_tap: testing...");
    133      events = [];
    134      const waitForClick = promiseEvent("click");
    135      synthesizeTouch(child, 5, 5);
    136      await waitForClick;
    137      is(
    138        stringifyEvents(events),
    139        stringifyEvents([
    140          { type: "touchend", target: child },
    141          { type: "mousemove", target: child, detail: 0, button: 0, buttons: 0 },
    142          { type: "mousedown", target: child, detail: 1, button: 0, buttons: 1 },
    143          { type: "mouseup", target: child, detail: 1, button: 0, buttons: 0 },
    144          { type: "click", target: child, detail: 1, button: 0, buttons: 0 },
    145        ]),
    146        `Single tap should cause a click ${desc}`
    147      );
    148    })();
    149 
    150    await (async function test_single_tap_with_consuming_pointerdown() {
    151      await promiseFlushingAPZGestureState();
    152      info("test_single_tap_with_consuming_pointerdown: testing...");
    153      events = [];
    154      const waitForTouchEnd = promiseEvent("click");
    155      child.addEventListener("pointerdown", event => {
    156        event.preventDefault();
    157      }, {once: true});
    158      synthesizeTouch(child, 5, 5);
    159      await waitForTouchEnd;
    160      const result = stringifyEvents(events);
    161      const expected = stringifyEvents([
    162        { type: "touchend", target: child },
    163        { type: "click", target: child, detail: 1, button: 0, buttons: 0 },
    164      ]);
    165      // If testing on Windows, the result is really unstable. Let's allow to
    166      // fail for now.
    167      (navigator.platform.includes("Win") && result != expected ? todo_is : is)(
    168        result,
    169        expected,
    170        `Single tap should not cause mouse events if pointerdown is consumed, but click event should be fired ${desc}`
    171      );
    172    })();
    173 
    174    await (async function test_single_tap_with_consuming_touchstart() {
    175      await promiseFlushingAPZGestureState();
    176      info("test_single_tap_with_consuming_touchstart: testing...");
    177      events = [];
    178      const waitForTouchEnd = promiseEvent("touchend");
    179      child.addEventListener("touchstart", event => {
    180        event.preventDefault();
    181      }, {once: true});
    182      synthesizeTouch(child, 5, 5);
    183      await waitForTouchEnd;
    184      const result = stringifyEvents(events);
    185      const expected = stringifyEvents([{ type: "touchend", target: child }]);
    186      // If testing this with APZ, the result is really unstable. Let's allow to
    187      // fail for now.
    188      (prefValue && result != expected ? todo_is : is)(
    189        result,
    190        expected,
    191        `Single tap should not cause mouse events if touchstart is consumed ${desc}`
    192      );
    193    })();
    194 
    195 
    196    await (async function test_single_tap_with_consuming_touchend() {
    197      await promiseFlushingAPZGestureState();
    198      info("test_single_tap_with_consuming_touchend: testing...");
    199      events = [];
    200      const waitForTouchEnd = promiseEvent("touchend");
    201      child.addEventListener("touchend", event => {
    202        event.preventDefault();
    203      }, {once: true});
    204      synthesizeTouch(child, 5, 5);
    205      await waitForTouchEnd;
    206      is(
    207        stringifyEvents(shiftEventsBefore(events)),
    208        stringifyEvents([]),
    209        `test_single_tap_with_consuming_touchstart() shouldn't cause mouse events after touchend`
    210      )
    211      is(
    212        stringifyEvents(events),
    213        stringifyEvents([
    214          { type: "touchend", target: child },
    215        ]),
    216        `Single tap should not cause mouse events if touchend is consumed ${desc}`
    217      );
    218    })();
    219 
    220    await (async function test_multi_touch() {
    221      await promiseFlushingAPZGestureState();
    222      events = [];
    223      info("test_multi_touch: testing...");
    224      const waitForTouchEnd = new Promise(resolve => {
    225        let count = 0;
    226        function onTouchEnd(event) {
    227          if (++count == 2) {
    228            removeEventListener("touchend", onTouchEnd, {capture: true});
    229            requestAnimationFrame(() => requestAnimationFrame(resolve));
    230          }
    231        }
    232        addEventListener("touchend", onTouchEnd, {capture: true});
    233      });
    234      synthesizeTouch(child, [5, 25], 5);
    235      await waitForTouchEnd;
    236      is(
    237        stringifyEvents(shiftEventsBefore(events)),
    238        stringifyEvents([]),
    239        `test_single_tap_with_consuming_touchend() shouldn't cause mouse events after touchend`
    240      )
    241      is(
    242        stringifyEvents(events),
    243        stringifyEvents([
    244          { type: "touchend", target: child },
    245          { type: "touchend", target: child },
    246        ]),
    247        `Multiple touch should not cause mouse events ${desc}`
    248      );
    249    })();
    250 
    251    // FIXME: Add long tap tests which won't frequently fail.
    252  }
    253  SimpleTest.finish();
    254 });
    255 </script>
    256 </head>
    257 <body><div id="parent"><div id="child"></div></div></body>
    258 </html>