tor-browser

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

browser_target_configuration_command_touch_events.js (7809B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Test touch event simulation.
      7 const TEST_DOCUMENT = "target_configuration_test_doc.sjs";
      8 const TEST_URI = URL_ROOT_COM_SSL + TEST_DOCUMENT;
      9 
     10 add_task(async function () {
     11  // Disable click hold and double tap zooming as it might interfere with the test
     12  await pushPref("ui.click_hold_context_menus", false);
     13  await pushPref("apz.allow_double_tap_zooming", false);
     14 
     15  const tab = await addTab(TEST_URI);
     16 
     17  info("Create commands for the tab");
     18  const commands = await CommandsFactory.forTab(tab);
     19 
     20  const targetConfigurationCommand = commands.targetConfigurationCommand;
     21  const targetCommand = commands.targetCommand;
     22  await targetCommand.startListening();
     23 
     24  info("Touch simulation is disabled at the beginning");
     25  await checkTopLevelDocumentTouchSimulation({ enabled: false });
     26  await checkIframeTouchSimulation({
     27    enabled: false,
     28  });
     29 
     30  info("Enable touch simulation");
     31  await targetConfigurationCommand.updateConfiguration({
     32    touchEventsOverride: "enabled",
     33  });
     34  await checkTopLevelDocumentTouchSimulation({ enabled: true });
     35  await checkIframeTouchSimulation({
     36    enabled: true,
     37  });
     38 
     39  info("Reload the page");
     40  await BrowserTestUtils.reloadTab(tab, {
     41    includeSubFrames: true,
     42  });
     43 
     44  is(
     45    await topLevelDocumentMatchesCoarsePointerAtStartup(),
     46    true,
     47    "The touch simulation was enabled in the content page when it loaded after reloading"
     48  );
     49  await checkTopLevelDocumentTouchSimulation({ enabled: true });
     50 
     51  is(
     52    await iframeMatchesCoarsePointerAtStartup(),
     53    true,
     54    "The touch simulation was enabled in the iframe when it loaded after reloading"
     55  );
     56  await checkIframeTouchSimulation({
     57    enabled: true,
     58  });
     59 
     60  info(
     61    "Create another commands instance and check that destroying it won't reset the touch simulation"
     62  );
     63  const otherCommands = await CommandsFactory.forTab(tab);
     64  const otherTargetConfigurationCommand =
     65    otherCommands.targetConfigurationCommand;
     66  const otherTargetCommand = otherCommands.targetCommand;
     67 
     68  await otherTargetCommand.startListening();
     69  // Watch targets so we wait for server communication to settle (e.g. attach calls), as
     70  // this could cause intermittent failures.
     71  await otherTargetCommand.watchTargets({
     72    types: [otherTargetCommand.TYPES.FRAME],
     73    onAvailable: () => {},
     74  });
     75 
     76  // Let's update the configuration with this commands instance to make sure we hit the TargetConfigurationActor
     77  await otherTargetConfigurationCommand.updateConfiguration({
     78    colorSchemeSimulation: "dark",
     79  });
     80 
     81  otherTargetCommand.destroy();
     82  await otherCommands.destroy();
     83 
     84  await checkTopLevelDocumentTouchSimulation({ enabled: true });
     85  await checkIframeTouchSimulation({
     86    enabled: true,
     87  });
     88 
     89  const previousBrowsingContextId = gBrowser.selectedBrowser.browsingContext.id;
     90  info(
     91    "Check that navigating to a page that forces the creation of a new browsing context keep the simulation enabled"
     92  );
     93 
     94  const onBrowserLoaded = BrowserTestUtils.browserLoaded(
     95    gBrowser.selectedBrowser,
     96    true
     97  );
     98  BrowserTestUtils.startLoadingURIString(
     99    gBrowser.selectedBrowser,
    100    URL_ROOT_ORG_SSL + TEST_DOCUMENT + "?crossOriginIsolated=true"
    101  );
    102  await onBrowserLoaded;
    103 
    104  isnot(
    105    gBrowser.selectedBrowser.browsingContext.id,
    106    previousBrowsingContextId,
    107    "A new browsing context was created"
    108  );
    109 
    110  is(
    111    await topLevelDocumentMatchesCoarsePointerAtStartup(),
    112    true,
    113    "The touch simulation was enabled in the content page when it loaded after navigating to a new browsing context"
    114  );
    115  await checkTopLevelDocumentTouchSimulation({
    116    enabled: true,
    117  });
    118 
    119  is(
    120    await iframeMatchesCoarsePointerAtStartup(),
    121    true,
    122    "The touch simulation was enabled in the iframe when it loaded after navigating to a new browsing context"
    123  );
    124  await checkIframeTouchSimulation({
    125    enabled: true,
    126  });
    127 
    128  info(
    129    "Check that destroying the commands we enabled the simulation in will disable the simulation"
    130  );
    131  targetCommand.destroy();
    132  await commands.destroy();
    133 
    134  await checkTopLevelDocumentTouchSimulation({ enabled: false });
    135  await checkIframeTouchSimulation({
    136    enabled: false,
    137  });
    138 });
    139 
    140 function matchesCoarsePointer(browserOrBrowsingContext) {
    141  return SpecialPowers.spawn(
    142    browserOrBrowsingContext,
    143    [],
    144    () => content.matchMedia("(pointer: coarse)").matches
    145  );
    146 }
    147 
    148 function matchesCoarsePointerAtStartup(browserOrBrowsingContext) {
    149  return SpecialPowers.spawn(
    150    browserOrBrowsingContext,
    151    [],
    152    () => content.wrappedJSObject.initialMatchesCoarsePointer
    153  );
    154 }
    155 
    156 async function isTouchEventEmitted(browserOrBrowsingContext) {
    157  const onTimeout = wait(1000).then(() => "TIMEOUT");
    158  const onTouchEvent = SpecialPowers.spawn(
    159    browserOrBrowsingContext,
    160    [],
    161    async () => {
    162      content.touchStartController = new content.AbortController();
    163      const el = content.document.querySelector("button");
    164 
    165      let gotTouchEndEvent = false;
    166 
    167      const promise = new Promise(resolve => {
    168        el.addEventListener(
    169          "touchend",
    170          () => {
    171            gotTouchEndEvent = true;
    172            resolve();
    173          },
    174          {
    175            signal: content.touchStartController.signal,
    176            once: true,
    177          }
    178        );
    179      });
    180 
    181      // For some reason, it might happen that the event is properly registered and transformed
    182      // in the touch simulator, but not received by the event listener we set up just before.
    183      // So here let's try to "tap" 3 times to give us more chance to catch the event.
    184      for (let i = 0; i < 3; i++) {
    185        if (gotTouchEndEvent) {
    186          break;
    187        }
    188 
    189        // Simulate a "tap" with mousedown and then mouseup.
    190        EventUtils.synthesizeMouseAtCenter(
    191          el,
    192          { type: "mousedown", isSynthesized: false },
    193          content
    194        );
    195 
    196        await new Promise(res => content.setTimeout(res, 10));
    197        EventUtils.synthesizeMouseAtCenter(
    198          el,
    199          { type: "mouseup", isSynthesized: false },
    200          content
    201        );
    202        await new Promise(res => content.setTimeout(res, 50));
    203      }
    204 
    205      return promise;
    206    }
    207  );
    208 
    209  const result = await Promise.race([onTimeout, onTouchEvent]);
    210 
    211  // Remove the event listener
    212  await SpecialPowers.spawn(browserOrBrowsingContext, [], () => {
    213    content.touchStartController.abort();
    214    delete content.touchStartController;
    215  });
    216 
    217  return result !== "TIMEOUT";
    218 }
    219 
    220 async function checkTopLevelDocumentTouchSimulation({ enabled }) {
    221  is(
    222    await matchesCoarsePointer(gBrowser.selectedBrowser),
    223    enabled,
    224    `The touch simulation is ${
    225      enabled ? "enabled" : "disabled"
    226    } on the top level document`
    227  );
    228 
    229  is(
    230    await isTouchEventEmitted(gBrowser.selectedBrowser),
    231    enabled,
    232    `touch events are ${enabled ? "" : "not "}emitted on the top level document`
    233  );
    234 }
    235 
    236 function topLevelDocumentMatchesCoarsePointerAtStartup() {
    237  return matchesCoarsePointerAtStartup(gBrowser.selectedBrowser);
    238 }
    239 
    240 function getIframeBrowsingContext() {
    241  return SpecialPowers.spawn(
    242    gBrowser.selectedBrowser,
    243    [],
    244    () => content.document.querySelector("iframe").browsingContext
    245  );
    246 }
    247 
    248 async function checkIframeTouchSimulation({ enabled }) {
    249  const iframeBC = await getIframeBrowsingContext();
    250  is(
    251    await matchesCoarsePointer(iframeBC),
    252    enabled,
    253    `The touch simulation is ${enabled ? "enabled" : "disabled"} on the iframe`
    254  );
    255 
    256  is(
    257    await isTouchEventEmitted(iframeBC),
    258    enabled,
    259    `touch events are ${enabled ? "" : "not "}emitted on the iframe`
    260  );
    261 }
    262 
    263 async function iframeMatchesCoarsePointerAtStartup() {
    264  const iframeBC = await getIframeBrowsingContext();
    265  return matchesCoarsePointerAtStartup(iframeBC);
    266 }