tor-browser

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

browser_touch_simulation.js (9608B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Test global touch simulation button
      7 
      8 const TEST_URL = `${URL_ROOT_SSL}touch.html`;
      9 const PREF_DOM_META_VIEWPORT_ENABLED = "dom.meta-viewport.enabled";
     10 
     11 // A 300ms delay between a `touchend` and `click` event is added whenever double-tap zoom
     12 // is allowed.
     13 const DELAY_MIN = 250;
     14 
     15 addRDMTask(TEST_URL, async function ({ ui }) {
     16  reloadOnTouchChange(true);
     17 
     18  await waitBootstrap(ui);
     19  await testWithNoTouch(ui);
     20  await toggleTouchSimulation(ui);
     21  await promiseContentReflow(ui);
     22  await testWithTouch(ui);
     23  await testWithMetaViewportEnabled(ui);
     24  await testWithMetaViewportDisabled(ui);
     25  testTouchButton(ui);
     26 
     27  reloadOnTouchChange(false);
     28 });
     29 
     30 async function testWithNoTouch(ui) {
     31  await SpecialPowers.spawn(ui.getViewportBrowser(), [], async function () {
     32    const div = content.document.querySelector("div");
     33    let x = 0,
     34      y = 0;
     35 
     36    info("testWithNoTouch: Initial test parameter and mouse mouse outside div");
     37    x = -1;
     38    y = -1;
     39    await EventUtils.synthesizeMouse(
     40      div,
     41      x,
     42      y,
     43      { type: "mousemove", isSynthesized: false },
     44      content
     45    );
     46    div.style.transform = "none";
     47    div.style.backgroundColor = "";
     48 
     49    info("testWithNoTouch: Move mouse into the div element");
     50    await EventUtils.synthesizeMouseAtCenter(
     51      div,
     52      { type: "mousemove", isSynthesized: false },
     53      content
     54    );
     55    is(div.style.backgroundColor, "red", "mouseenter or mouseover should work");
     56 
     57    info("testWithNoTouch: Drag the div element");
     58    await EventUtils.synthesizeMouseAtCenter(
     59      div,
     60      { type: "mousedown", isSynthesized: false },
     61      content
     62    );
     63    x = 100;
     64    y = 100;
     65    await EventUtils.synthesizeMouse(
     66      div,
     67      x,
     68      y,
     69      { type: "mousemove", isSynthesized: false },
     70      content
     71    );
     72    is(div.style.transform, "none", "touchmove shouldn't work");
     73    await EventUtils.synthesizeMouse(
     74      div,
     75      x,
     76      y,
     77      { type: "mouseup", isSynthesized: false },
     78      content
     79    );
     80 
     81    info("testWithNoTouch: Move mouse out of the div element");
     82    x = -1;
     83    y = -1;
     84    await EventUtils.synthesizeMouse(
     85      div,
     86      x,
     87      y,
     88      { type: "mousemove", isSynthesized: false },
     89      content
     90    );
     91    is(div.style.backgroundColor, "blue", "mouseout or mouseleave should work");
     92 
     93    info("testWithNoTouch: Click the div element");
     94    await EventUtils.synthesizeClick(div);
     95    is(
     96      div.dataset.isDelay,
     97      "false",
     98      "300ms delay between touch events and mouse events should not work"
     99    );
    100 
    101    // Assuming that this test runs on devices having no touch screen device.
    102    ok(
    103      !content.matchMedia("(pointer: coarse)").matches,
    104      "pointer: coarse shouldn't be matched"
    105    );
    106    ok(
    107      !content.matchMedia("(hover: none)").matches,
    108      "hover: none shouldn't be matched"
    109    );
    110    ok(
    111      !content.matchMedia("(any-pointer: coarse)").matches,
    112      "any-pointer: coarse shouldn't be matched"
    113    );
    114    ok(
    115      !content.matchMedia("(any-hover: none)").matches,
    116      "any-hover: none shouldn't be matched"
    117    );
    118  });
    119 }
    120 
    121 async function testWithTouch(ui) {
    122  await SpecialPowers.spawn(ui.getViewportBrowser(), [], async function () {
    123    const div = content.document.querySelector("div");
    124    let x = 0,
    125      y = 0;
    126 
    127    info("testWithTouch: Initial test parameter and mouse mouse outside div");
    128    x = -1;
    129    y = -1;
    130    await EventUtils.synthesizeMouse(
    131      div,
    132      x,
    133      y,
    134      { type: "mousemove", isSynthesized: false },
    135      content
    136    );
    137    div.style.transform = "none";
    138    div.style.backgroundColor = "";
    139 
    140    info("testWithTouch: Move mouse into the div element");
    141    await EventUtils.synthesizeMouseAtCenter(
    142      div,
    143      { type: "mousemove", isSynthesized: false },
    144      content
    145    );
    146    isnot(
    147      div.style.backgroundColor,
    148      "red",
    149      "mouseenter or mouseover should not work"
    150    );
    151 
    152    info("testWithTouch: Drag the div element");
    153    await EventUtils.synthesizeMouseAtCenter(
    154      div,
    155      { type: "mousedown", isSynthesized: false },
    156      content
    157    );
    158    x = 100;
    159    y = 100;
    160    const touchMovePromise = ContentTaskUtils.waitForEvent(div, "touchmove");
    161    await EventUtils.synthesizeMouse(
    162      div,
    163      x,
    164      y,
    165      { type: "mousemove", isSynthesized: false },
    166      content
    167    );
    168    await touchMovePromise;
    169    isnot(div.style.transform, "none", "touchmove should work");
    170    await EventUtils.synthesizeMouse(
    171      div,
    172      x,
    173      y,
    174      { type: "mouseup", isSynthesized: false },
    175      content
    176    );
    177 
    178    info("testWithTouch: Move mouse out of the div element");
    179    x = -1;
    180    y = -1;
    181    await EventUtils.synthesizeMouse(
    182      div,
    183      x,
    184      y,
    185      { type: "mousemove", isSynthesized: false },
    186      content
    187    );
    188    isnot(
    189      div.style.backgroundColor,
    190      "blue",
    191      "mouseout or mouseleave should not work"
    192    );
    193 
    194    ok(
    195      content.matchMedia("(pointer: coarse)").matches,
    196      "pointer: coarse should be matched"
    197    );
    198    ok(
    199      content.matchMedia("(hover: none)").matches,
    200      "hover: none should be matched"
    201    );
    202    ok(
    203      content.matchMedia("(any-pointer: coarse)").matches,
    204      "any-pointer: coarse should be matched"
    205    );
    206    ok(
    207      content.matchMedia("(any-hover: none)").matches,
    208      "any-hover: none should be matched"
    209    );
    210  });
    211 
    212  // Capturing touch events with the content window as a registered listener causes the
    213  // "changedTouches" field to be undefined when using deprecated TouchEvent APIs.
    214  // See Bug 1549220 and Bug 1588438 for more information on this issue.
    215  info("Test that changed touches captured on the content window are defined.");
    216  await SpecialPowers.spawn(ui.getViewportBrowser(), [], async function () {
    217    const div = content.document.querySelector("div");
    218 
    219    content.addEventListener(
    220      "touchstart",
    221      event => {
    222        const changedTouch = event.changedTouches[0];
    223        ok(changedTouch, "Changed touch is defined.");
    224      },
    225      { once: true }
    226    );
    227    await EventUtils.synthesizeClick(div);
    228  });
    229 }
    230 
    231 async function testWithMetaViewportEnabled(ui) {
    232  await SpecialPowers.pushPrefEnv({
    233    set: [[PREF_DOM_META_VIEWPORT_ENABLED, true]],
    234  });
    235 
    236  await SpecialPowers.spawn(
    237    ui.getViewportBrowser(),
    238    [{ delay_min: DELAY_MIN }],
    239    async function ({ delay_min }) {
    240      // A helper for testing the delay between touchend and click events.
    241      async function testDelay(mvc, el) {
    242        const touchendPromise = ContentTaskUtils.waitForEvent(el, "touchend");
    243        const clickPromise = ContentTaskUtils.waitForEvent(el, "click");
    244        await EventUtils.synthesizeClick(el);
    245        const { timeStamp: touchendTimestamp } = await touchendPromise;
    246        const { timeStamp: clickTimeStamp } = await clickPromise;
    247        const delay = clickTimeStamp - touchendTimestamp;
    248 
    249        const expected = delay >= delay_min;
    250 
    251        ok(
    252          expected,
    253          `${mvc}: There should be greater than a ${delay_min}ms delay between touch events and mouse events. Got delay of ${delay}ms`
    254        );
    255      }
    256 
    257      // A helper function for waiting for reflow to complete.
    258      const promiseReflow = () => {
    259        return new Promise(resolve => {
    260          content.window.requestAnimationFrame(() => {
    261            content.window.requestAnimationFrame(resolve);
    262          });
    263        });
    264      };
    265 
    266      const meta = content.document.querySelector("meta[name=viewport]");
    267      const div = content.document.querySelector("div");
    268 
    269      info(
    270        "testWithMetaViewportEnabled: " +
    271          "click the div element with <meta name='viewport'>"
    272      );
    273      meta.content = "";
    274      await promiseReflow();
    275      await testDelay("(empty)", div);
    276    }
    277  );
    278 
    279  await SpecialPowers.popPrefEnv();
    280 }
    281 
    282 async function testWithMetaViewportDisabled(ui) {
    283  await SpecialPowers.pushPrefEnv({
    284    set: [[PREF_DOM_META_VIEWPORT_ENABLED, false]],
    285  });
    286 
    287  await SpecialPowers.spawn(
    288    ui.getViewportBrowser(),
    289    [{ delay_min: DELAY_MIN }],
    290    async function ({ delay_min }) {
    291      const meta = content.document.querySelector("meta[name=viewport]");
    292      const div = content.document.querySelector("div");
    293 
    294      info(
    295        "testWithMetaViewportDisabled: click the div with <meta name='viewport'>"
    296      );
    297      meta.content = "";
    298      const touchendPromise = ContentTaskUtils.waitForEvent(div, "touchend");
    299      const clickPromise = ContentTaskUtils.waitForEvent(div, "click");
    300      await EventUtils.synthesizeClick(div);
    301      const { timeStamp: touchendTimestamp } = await touchendPromise;
    302      const { timeStamp: clickTimeStamp } = await clickPromise;
    303      const delay = clickTimeStamp - touchendTimestamp;
    304 
    305      const expected = delay >= delay_min;
    306 
    307      ok(
    308        expected,
    309        `There should be greater than a ${delay_min}ms delay between touch events and mouse events. Got delay of ${delay}ms`
    310      );
    311    }
    312  );
    313 }
    314 
    315 function testTouchButton(ui) {
    316  const { document } = ui.toolWindow;
    317  const touchButton = document.getElementById("touch-simulation-button");
    318 
    319  ok(
    320    touchButton.classList.contains("checked"),
    321    "Touch simulation is active at end of test."
    322  );
    323 
    324  touchButton.click();
    325 
    326  ok(
    327    !touchButton.classList.contains("checked"),
    328    "Touch simulation is stopped on click."
    329  );
    330 
    331  touchButton.click();
    332 
    333  ok(
    334    touchButton.classList.contains("checked"),
    335    "Touch simulation is started on click."
    336  );
    337 }
    338 
    339 async function waitBootstrap(ui) {
    340  await waitForFrameLoad(ui, TEST_URL);
    341 }