tor-browser

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

browser_gestureSupport.js (29748B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 Services.scriptloader.loadSubScript(
      6  "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_utils.js",
      7  this
      8 );
      9 
     10 Services.scriptloader.loadSubScript(
     11  "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
     12  this
     13 );
     14 
     15 // Simple gestures tests
     16 //
     17 // Some of these tests require the ability to disable the fact that the
     18 // Firefox chrome intentionally prevents "simple gesture" events from
     19 // reaching web content.
     20 
     21 var test_utils;
     22 var test_commandset;
     23 var test_prefBranch = "browser.gesture.";
     24 var test_normalTab;
     25 
     26 async function test() {
     27  waitForExplicitFinish();
     28 
     29  await SpecialPowers.pushPrefEnv({
     30    set: [["test.wait300msAfterTabSwitch", true]],
     31  });
     32 
     33  // Disable the default gestures support during this part of the test
     34  gGestureSupport.init(false);
     35 
     36  test_utils = window.windowUtils;
     37 
     38  // Run the tests of "simple gesture" events generally
     39  test_EnsureConstantsAreDisjoint();
     40  test_TestEventListeners();
     41  test_TestEventCreation();
     42 
     43  // Reenable the default gestures support. The remaining tests target
     44  // the Firefox gesture functionality.
     45  gGestureSupport.init(true);
     46 
     47  const aPage = "about:about";
     48  test_normalTab = await BrowserTestUtils.openNewForegroundTab(
     49    gBrowser,
     50    aPage,
     51    true /* waitForLoad */
     52  );
     53 
     54  // Test Firefox's gestures support.
     55  test_commandset = document.getElementById("mainCommandSet");
     56  await test_swipeGestures();
     57  await test_latchedGesture("pinch", "out", "in", "MozMagnifyGesture");
     58  await test_thresholdGesture("pinch", "out", "in", "MozMagnifyGesture");
     59  test_rotateGestures();
     60 }
     61 
     62 var test_eventCount = 0;
     63 var test_expectedType;
     64 var test_expectedDirection;
     65 var test_expectedDelta;
     66 var test_expectedModifiers;
     67 var test_expectedClickCount;
     68 var test_imageTab;
     69 
     70 function test_gestureListener(evt) {
     71  is(
     72    evt.type,
     73    test_expectedType,
     74    "evt.type (" + evt.type + ") does not match expected value"
     75  );
     76  is(
     77    evt.target,
     78    test_utils.elementFromPoint(60, 60, false, false),
     79    "evt.target (" + evt.target + ") does not match expected value"
     80  );
     81  is(
     82    evt.clientX,
     83    60,
     84    "evt.clientX (" + evt.clientX + ") does not match expected value"
     85  );
     86  is(
     87    evt.clientY,
     88    60,
     89    "evt.clientY (" + evt.clientY + ") does not match expected value"
     90  );
     91  isnot(
     92    evt.screenX,
     93    0,
     94    "evt.screenX (" + evt.screenX + ") does not match expected value"
     95  );
     96  isnot(
     97    evt.screenY,
     98    0,
     99    "evt.screenY (" + evt.screenY + ") does not match expected value"
    100  );
    101 
    102  is(
    103    evt.direction,
    104    test_expectedDirection,
    105    "evt.direction (" + evt.direction + ") does not match expected value"
    106  );
    107  is(
    108    evt.delta,
    109    test_expectedDelta,
    110    "evt.delta (" + evt.delta + ") does not match expected value"
    111  );
    112 
    113  is(
    114    evt.shiftKey,
    115    (test_expectedModifiers & Event.SHIFT_MASK) != 0,
    116    "evt.shiftKey did not match expected value"
    117  );
    118  is(
    119    evt.ctrlKey,
    120    (test_expectedModifiers & Event.CONTROL_MASK) != 0,
    121    "evt.ctrlKey did not match expected value"
    122  );
    123  is(
    124    evt.altKey,
    125    (test_expectedModifiers & Event.ALT_MASK) != 0,
    126    "evt.altKey did not match expected value"
    127  );
    128  is(
    129    evt.metaKey,
    130    (test_expectedModifiers & Event.META_MASK) != 0,
    131    "evt.metaKey did not match expected value"
    132  );
    133 
    134  if (evt.type == "MozTapGesture") {
    135    is(
    136      evt.clickCount,
    137      test_expectedClickCount,
    138      "evt.clickCount does not match"
    139    );
    140  }
    141 
    142  test_eventCount++;
    143 }
    144 
    145 function test_helper1(type, direction, delta, modifiers) {
    146  // Setup the expected values
    147  test_expectedType = type;
    148  test_expectedDirection = direction;
    149  test_expectedDelta = delta;
    150  test_expectedModifiers = modifiers;
    151 
    152  let expectedEventCount = test_eventCount + 1;
    153 
    154  document.addEventListener(type, test_gestureListener, true);
    155  test_utils.sendSimpleGestureEvent(type, 60, 60, direction, delta, modifiers);
    156  document.removeEventListener(type, test_gestureListener, true);
    157 
    158  is(
    159    expectedEventCount,
    160    test_eventCount,
    161    "Event (" + type + ") was never received by event listener"
    162  );
    163 }
    164 
    165 function test_clicks(type, clicks) {
    166  // Setup the expected values
    167  test_expectedType = type;
    168  test_expectedDirection = 0;
    169  test_expectedDelta = 0;
    170  test_expectedModifiers = 0;
    171  test_expectedClickCount = clicks;
    172 
    173  let expectedEventCount = test_eventCount + 1;
    174 
    175  document.addEventListener(type, test_gestureListener, true);
    176  test_utils.sendSimpleGestureEvent(type, 60, 60, 0, 0, 0, clicks);
    177  document.removeEventListener(type, test_gestureListener, true);
    178 
    179  is(
    180    expectedEventCount,
    181    test_eventCount,
    182    "Event (" + type + ") was never received by event listener"
    183  );
    184 }
    185 
    186 function test_TestEventListeners() {
    187  let e = test_helper1; // easier to type this name
    188 
    189  // Swipe gesture animation events
    190  e("MozSwipeGestureStart", 0, -0.7, 0);
    191  e("MozSwipeGestureUpdate", 0, -0.4, 0);
    192  e("MozSwipeGestureEnd", 0, 0, 0);
    193  e("MozSwipeGestureStart", 0, 0.6, 0);
    194  e("MozSwipeGestureUpdate", 0, 0.3, 0);
    195  e("MozSwipeGestureEnd", 0, 1, 0);
    196 
    197  // Swipe gesture event
    198  e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_LEFT, 0.0, 0);
    199  e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0.0, 0);
    200  e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_UP, 0.0, 0);
    201  e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_DOWN, 0.0, 0);
    202  e(
    203    "MozSwipeGesture",
    204    SimpleGestureEvent.DIRECTION_UP | SimpleGestureEvent.DIRECTION_LEFT,
    205    0.0,
    206    0
    207  );
    208  e(
    209    "MozSwipeGesture",
    210    SimpleGestureEvent.DIRECTION_DOWN | SimpleGestureEvent.DIRECTION_RIGHT,
    211    0.0,
    212    0
    213  );
    214  e(
    215    "MozSwipeGesture",
    216    SimpleGestureEvent.DIRECTION_UP | SimpleGestureEvent.DIRECTION_RIGHT,
    217    0.0,
    218    0
    219  );
    220  e(
    221    "MozSwipeGesture",
    222    SimpleGestureEvent.DIRECTION_DOWN | SimpleGestureEvent.DIRECTION_LEFT,
    223    0.0,
    224    0
    225  );
    226 
    227  // magnify gesture events
    228  e("MozMagnifyGestureStart", 0, 50.0, 0);
    229  e("MozMagnifyGestureUpdate", 0, -25.0, 0);
    230  e("MozMagnifyGestureUpdate", 0, 5.0, 0);
    231  e("MozMagnifyGesture", 0, 30.0, 0);
    232 
    233  // rotate gesture events
    234  e("MozRotateGestureStart", SimpleGestureEvent.ROTATION_CLOCKWISE, 33.0, 0);
    235  e(
    236    "MozRotateGestureUpdate",
    237    SimpleGestureEvent.ROTATION_COUNTERCLOCKWISE,
    238    -13.0,
    239    0
    240  );
    241  e("MozRotateGestureUpdate", SimpleGestureEvent.ROTATION_CLOCKWISE, 13.0, 0);
    242  e("MozRotateGesture", SimpleGestureEvent.ROTATION_CLOCKWISE, 33.0, 0);
    243 
    244  // Tap and presstap gesture events
    245  test_clicks("MozTapGesture", 1);
    246  test_clicks("MozTapGesture", 2);
    247  test_clicks("MozTapGesture", 3);
    248  test_clicks("MozPressTapGesture", 1);
    249 
    250  // simple delivery test for edgeui gestures
    251  e("MozEdgeUIStarted", 0, 0, 0);
    252  e("MozEdgeUICanceled", 0, 0, 0);
    253  e("MozEdgeUICompleted", 0, 0, 0);
    254 
    255  // event.shiftKey
    256  let modifier = Event.SHIFT_MASK;
    257  e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0, modifier);
    258 
    259  // event.metaKey
    260  modifier = Event.META_MASK;
    261  e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0, modifier);
    262 
    263  // event.altKey
    264  modifier = Event.ALT_MASK;
    265  e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0, modifier);
    266 
    267  // event.ctrlKey
    268  modifier = Event.CONTROL_MASK;
    269  e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0, modifier);
    270 }
    271 
    272 function test_eventDispatchListener(evt) {
    273  test_eventCount++;
    274  evt.stopPropagation();
    275 }
    276 
    277 function test_helper2(
    278  type,
    279  direction,
    280  delta,
    281  altKey,
    282  ctrlKey,
    283  shiftKey,
    284  metaKey
    285 ) {
    286  let event = null;
    287  let successful;
    288 
    289  try {
    290    event = document.createEvent("SimpleGestureEvent");
    291    successful = true;
    292  } catch (ex) {
    293    successful = false;
    294  }
    295  ok(successful, "Unable to create SimpleGestureEvent");
    296 
    297  try {
    298    event.initSimpleGestureEvent(
    299      type,
    300      true,
    301      true,
    302      window,
    303      1,
    304      10,
    305      10,
    306      10,
    307      10,
    308      ctrlKey,
    309      altKey,
    310      shiftKey,
    311      metaKey,
    312      1,
    313      window,
    314      0,
    315      direction,
    316      delta,
    317      0
    318    );
    319    successful = true;
    320  } catch (ex) {
    321    successful = false;
    322  }
    323  ok(successful, "event.initSimpleGestureEvent should not fail");
    324 
    325  // Make sure the event fields match the expected values
    326  is(event.type, type, "Mismatch on evt.type");
    327  is(event.direction, direction, "Mismatch on evt.direction");
    328  is(event.delta, delta, "Mismatch on evt.delta");
    329  is(event.altKey, altKey, "Mismatch on evt.altKey");
    330  is(event.ctrlKey, ctrlKey, "Mismatch on evt.ctrlKey");
    331  is(event.shiftKey, shiftKey, "Mismatch on evt.shiftKey");
    332  is(event.metaKey, metaKey, "Mismatch on evt.metaKey");
    333  is(event.view, window, "Mismatch on evt.view");
    334  is(event.detail, 1, "Mismatch on evt.detail");
    335  is(event.clientX, 10, "Mismatch on evt.clientX");
    336  is(event.clientY, 10, "Mismatch on evt.clientY");
    337  is(event.screenX, 10, "Mismatch on evt.screenX");
    338  is(event.screenY, 10, "Mismatch on evt.screenY");
    339  is(event.button, 1, "Mismatch on evt.button");
    340  is(event.relatedTarget, window, "Mismatch on evt.relatedTarget");
    341 
    342  // Test event dispatch
    343  let expectedEventCount = test_eventCount + 1;
    344  document.addEventListener(type, test_eventDispatchListener, true);
    345  document.dispatchEvent(event);
    346  document.removeEventListener(type, test_eventDispatchListener, true);
    347  is(
    348    expectedEventCount,
    349    test_eventCount,
    350    "Dispatched event was never received by listener"
    351  );
    352 }
    353 
    354 function test_TestEventCreation() {
    355  // Event creation
    356  test_helper2(
    357    "MozMagnifyGesture",
    358    SimpleGestureEvent.DIRECTION_RIGHT,
    359    20.0,
    360    true,
    361    false,
    362    true,
    363    false
    364  );
    365  test_helper2(
    366    "MozMagnifyGesture",
    367    SimpleGestureEvent.DIRECTION_LEFT,
    368    -20.0,
    369    false,
    370    true,
    371    false,
    372    true
    373  );
    374 }
    375 
    376 function test_EnsureConstantsAreDisjoint() {
    377  let up = SimpleGestureEvent.DIRECTION_UP;
    378  let down = SimpleGestureEvent.DIRECTION_DOWN;
    379  let left = SimpleGestureEvent.DIRECTION_LEFT;
    380  let right = SimpleGestureEvent.DIRECTION_RIGHT;
    381 
    382  let clockwise = SimpleGestureEvent.ROTATION_CLOCKWISE;
    383  let cclockwise = SimpleGestureEvent.ROTATION_COUNTERCLOCKWISE;
    384 
    385  ok(up ^ down, "DIRECTION_UP and DIRECTION_DOWN are not bitwise disjoint");
    386  ok(up ^ left, "DIRECTION_UP and DIRECTION_LEFT are not bitwise disjoint");
    387  ok(up ^ right, "DIRECTION_UP and DIRECTION_RIGHT are not bitwise disjoint");
    388  ok(down ^ left, "DIRECTION_DOWN and DIRECTION_LEFT are not bitwise disjoint");
    389  ok(
    390    down ^ right,
    391    "DIRECTION_DOWN and DIRECTION_RIGHT are not bitwise disjoint"
    392  );
    393  ok(
    394    left ^ right,
    395    "DIRECTION_LEFT and DIRECTION_RIGHT are not bitwise disjoint"
    396  );
    397  ok(
    398    clockwise ^ cclockwise,
    399    "ROTATION_CLOCKWISE and ROTATION_COUNTERCLOCKWISE are not bitwise disjoint"
    400  );
    401 }
    402 
    403 // Helper for test of latched event processing. Emits the actual
    404 // gesture events to test whether the commands associated with the
    405 // gesture will only trigger once for each direction of movement.
    406 async function test_emitLatchedEvents(eventPrefix, initialDelta, cmd) {
    407  let cumulativeDelta = 0;
    408  let isIncreasing = initialDelta > 0;
    409 
    410  let expect = {};
    411  // Reset the call counters and initialize expected values
    412  for (let dir in cmd) {
    413    cmd[dir].callCount = expect[dir] = 0;
    414  }
    415 
    416  let check = (aDir, aMsg) =>
    417    Assert.equal(cmd[aDir].callCount, expect[aDir], aMsg);
    418  let checkBoth = function (aNum, aInc, aDec) {
    419    let prefix = "Step " + aNum + ": ";
    420    check("inc", prefix + aInc);
    421    check("dec", prefix + aDec);
    422  };
    423 
    424  // Send the "Start" event.
    425  await synthesizeSimpleGestureEvent(
    426    test_normalTab.linkedBrowser,
    427    eventPrefix + "Start",
    428    10,
    429    10,
    430    0,
    431    initialDelta,
    432    0,
    433    0
    434  );
    435  cumulativeDelta += initialDelta;
    436  if (isIncreasing) {
    437    expect.inc++;
    438    checkBoth(
    439      1,
    440      "Increasing command was not triggered",
    441      "Decreasing command was triggered"
    442    );
    443  } else {
    444    expect.dec++;
    445    checkBoth(
    446      1,
    447      "Increasing command was triggered",
    448      "Decreasing command was not triggered"
    449    );
    450  }
    451 
    452  // Send random values in the same direction and ensure neither
    453  // command triggers.
    454  for (let i = 0; i < 5; i++) {
    455    let delta = Math.random() * (isIncreasing ? 100 : -100);
    456 
    457    await synthesizeSimpleGestureEvent(
    458      test_normalTab.linkedBrowser,
    459      eventPrefix + "Update",
    460      10,
    461      10,
    462      0,
    463      delta,
    464      0,
    465      0
    466    );
    467    cumulativeDelta += delta;
    468    checkBoth(
    469      2,
    470      "Increasing command was triggered",
    471      "Decreasing command was triggered"
    472    );
    473  }
    474 
    475  // Now go back in the opposite direction.
    476  await synthesizeSimpleGestureEvent(
    477    test_normalTab.linkedBrowser,
    478    eventPrefix + "Update",
    479    10,
    480    10,
    481    0,
    482    -initialDelta,
    483    0,
    484    0
    485  );
    486  cumulativeDelta += -initialDelta;
    487  if (isIncreasing) {
    488    expect.dec++;
    489    checkBoth(
    490      3,
    491      "Increasing command was triggered",
    492      "Decreasing command was not triggered"
    493    );
    494  } else {
    495    expect.inc++;
    496    checkBoth(
    497      3,
    498      "Increasing command was not triggered",
    499      "Decreasing command was triggered"
    500    );
    501  }
    502 
    503  // Send random values in the opposite direction and ensure neither
    504  // command triggers.
    505  for (let i = 0; i < 5; i++) {
    506    let delta = Math.random() * (isIncreasing ? -100 : 100);
    507    await synthesizeSimpleGestureEvent(
    508      test_normalTab.linkedBrowser,
    509      eventPrefix + "Update",
    510      10,
    511      10,
    512      0,
    513      delta,
    514      0,
    515      0
    516    );
    517    cumulativeDelta += delta;
    518    checkBoth(
    519      4,
    520      "Increasing command was triggered",
    521      "Decreasing command was triggered"
    522    );
    523  }
    524 
    525  // Go back to the original direction. The original command should trigger.
    526  await synthesizeSimpleGestureEvent(
    527    test_normalTab.linkedBrowser,
    528    eventPrefix + "Update",
    529    10,
    530    10,
    531    0,
    532    initialDelta,
    533    0,
    534    0
    535  );
    536  cumulativeDelta += initialDelta;
    537  if (isIncreasing) {
    538    expect.inc++;
    539    checkBoth(
    540      5,
    541      "Increasing command was not triggered",
    542      "Decreasing command was triggered"
    543    );
    544  } else {
    545    expect.dec++;
    546    checkBoth(
    547      5,
    548      "Increasing command was triggered",
    549      "Decreasing command was not triggered"
    550    );
    551  }
    552 
    553  // Send the wrap-up event. No commands should be triggered.
    554  await synthesizeSimpleGestureEvent(
    555    test_normalTab.linkedBrowser,
    556    eventPrefix,
    557    10,
    558    10,
    559    0,
    560    cumulativeDelta,
    561    0,
    562    0
    563  );
    564  checkBoth(
    565    6,
    566    "Increasing command was triggered",
    567    "Decreasing command was triggered"
    568  );
    569 }
    570 
    571 function test_addCommand(prefName, id) {
    572  let cmd = test_commandset.appendChild(document.createXULElement("command"));
    573  cmd.setAttribute("id", id);
    574  cmd.addEventListener("command", event => event.target.callCount++);
    575 
    576  cmd.origPrefName = prefName;
    577  cmd.origPrefValue = Services.prefs.getCharPref(prefName);
    578  Services.prefs.setCharPref(prefName, id);
    579 
    580  return cmd;
    581 }
    582 
    583 function test_removeCommand(cmd) {
    584  Services.prefs.setCharPref(cmd.origPrefName, cmd.origPrefValue);
    585  test_commandset.removeChild(cmd);
    586 }
    587 
    588 // Test whether latched events are only called once per direction of motion.
    589 async function test_latchedGesture(gesture, inc, dec, eventPrefix) {
    590  let branch = test_prefBranch + gesture + ".";
    591 
    592  // Put the gesture into latched mode.
    593  let oldLatchedValue = Services.prefs.getBoolPref(branch + "latched");
    594  Services.prefs.setBoolPref(branch + "latched", true);
    595 
    596  // Install the test commands for increasing and decreasing motion.
    597  let cmd = {
    598    inc: test_addCommand(branch + inc, "test:incMotion"),
    599    dec: test_addCommand(branch + dec, "test:decMotion"),
    600  };
    601 
    602  // Test the gestures in each direction.
    603  await test_emitLatchedEvents(eventPrefix, 500, cmd);
    604  await test_emitLatchedEvents(eventPrefix, -500, cmd);
    605 
    606  // Restore the gesture to its original configuration.
    607  Services.prefs.setBoolPref(branch + "latched", oldLatchedValue);
    608  for (let dir in cmd) {
    609    test_removeCommand(cmd[dir]);
    610  }
    611 }
    612 
    613 // Test whether non-latched events are triggered upon sufficient motion.
    614 async function test_thresholdGesture(gesture, inc, dec, eventPrefix) {
    615  let branch = test_prefBranch + gesture + ".";
    616 
    617  // Disable latched mode for this gesture.
    618  let oldLatchedValue = Services.prefs.getBoolPref(branch + "latched");
    619  Services.prefs.setBoolPref(branch + "latched", false);
    620 
    621  // Set the triggering threshold value to 50.
    622  let oldThresholdValue = Services.prefs.getIntPref(branch + "threshold");
    623  Services.prefs.setIntPref(branch + "threshold", 50);
    624 
    625  // Install the test commands for increasing and decreasing motion.
    626  let cmdInc = test_addCommand(branch + inc, "test:incMotion");
    627  let cmdDec = test_addCommand(branch + dec, "test:decMotion");
    628 
    629  // Send the start event but stop short of triggering threshold.
    630  cmdInc.callCount = cmdDec.callCount = 0;
    631  await synthesizeSimpleGestureEvent(
    632    test_normalTab.linkedBrowser,
    633    eventPrefix + "Start",
    634    10,
    635    10,
    636    0,
    637    49.5,
    638    0,
    639    0
    640  );
    641  Assert.equal(cmdInc.callCount, 0, "Increasing command was triggered");
    642  Assert.equal(cmdDec.callCount, 0, "Decreasing command was triggered");
    643 
    644  // Now trigger the threshold.
    645  cmdInc.callCount = cmdDec.callCount = 0;
    646  await synthesizeSimpleGestureEvent(
    647    test_normalTab.linkedBrowser,
    648    eventPrefix + "Update",
    649    10,
    650    10,
    651    0,
    652    1,
    653    0,
    654    0
    655  );
    656  Assert.equal(cmdInc.callCount, 1, "Increasing command was not triggered");
    657  Assert.equal(cmdDec.callCount, 0, "Decreasing command was triggered");
    658 
    659  // The tracking counter should go to zero. Go back the other way and
    660  // stop short of triggering the threshold.
    661  cmdInc.callCount = cmdDec.callCount = 0;
    662  await synthesizeSimpleGestureEvent(
    663    test_normalTab.linkedBrowser,
    664    eventPrefix + "Update",
    665    10,
    666    10,
    667    0,
    668    -49.5,
    669    0,
    670    0
    671  );
    672  Assert.equal(cmdInc.callCount, 0, "Increasing command was triggered");
    673  Assert.equal(cmdDec.callCount, 0, "Decreasing command was triggered");
    674 
    675  // Now cross the threshold and trigger the decreasing command.
    676  cmdInc.callCount = cmdDec.callCount = 0;
    677  await synthesizeSimpleGestureEvent(
    678    test_normalTab.linkedBrowser,
    679    eventPrefix + "Update",
    680    10,
    681    10,
    682    0,
    683    -1.5,
    684    0,
    685    0
    686  );
    687  Assert.equal(cmdInc.callCount, 0, "Increasing command was triggered");
    688  Assert.equal(cmdDec.callCount, 1, "Decreasing command was not triggered");
    689 
    690  // Send the wrap-up event. No commands should trigger.
    691  cmdInc.callCount = cmdDec.callCount = 0;
    692  await synthesizeSimpleGestureEvent(
    693    test_normalTab.linkedBrowser,
    694    eventPrefix,
    695    0,
    696    0,
    697    0,
    698    -0.5,
    699    0,
    700    0
    701  );
    702  Assert.equal(cmdInc.callCount, 0, "Increasing command was triggered");
    703  Assert.equal(cmdDec.callCount, 0, "Decreasing command was triggered");
    704 
    705  // Restore the gesture to its original configuration.
    706  Services.prefs.setBoolPref(branch + "latched", oldLatchedValue);
    707  Services.prefs.setIntPref(branch + "threshold", oldThresholdValue);
    708  test_removeCommand(cmdInc);
    709  test_removeCommand(cmdDec);
    710 }
    711 
    712 async function test_swipeGestures() {
    713  // easier to type names for the direction constants
    714  let up = SimpleGestureEvent.DIRECTION_UP;
    715  let down = SimpleGestureEvent.DIRECTION_DOWN;
    716  let left = SimpleGestureEvent.DIRECTION_LEFT;
    717  let right = SimpleGestureEvent.DIRECTION_RIGHT;
    718 
    719  let branch = test_prefBranch + "swipe.";
    720 
    721  // Install the test commands for the swipe gestures.
    722  let cmdUp = test_addCommand(branch + "up", "test:swipeUp");
    723  let cmdDown = test_addCommand(branch + "down", "test:swipeDown");
    724  let cmdLeft = test_addCommand(branch + "left", "test:swipeLeft");
    725  let cmdRight = test_addCommand(branch + "right", "test:swipeRight");
    726 
    727  function resetCounts() {
    728    cmdUp.callCount = 0;
    729    cmdDown.callCount = 0;
    730    cmdLeft.callCount = 0;
    731    cmdRight.callCount = 0;
    732  }
    733 
    734  // UP
    735  resetCounts();
    736  await synthesizeSimpleGestureEvent(
    737    test_normalTab.linkedBrowser,
    738    "MozSwipeGesture",
    739    10,
    740    10,
    741    up,
    742    0,
    743    0,
    744    0
    745  );
    746  Assert.equal(cmdUp.callCount, 1, "Step 1: Up command was not triggered");
    747  Assert.equal(cmdDown.callCount, 0, "Step 1: Down command was triggered");
    748  Assert.equal(cmdLeft.callCount, 0, "Step 1: Left command was triggered");
    749  Assert.equal(cmdRight.callCount, 0, "Step 1: Right command was triggered");
    750 
    751  // DOWN
    752  resetCounts();
    753  await synthesizeSimpleGestureEvent(
    754    test_normalTab.linkedBrowser,
    755    "MozSwipeGesture",
    756    10,
    757    10,
    758    down,
    759    0,
    760    0,
    761    0
    762  );
    763  Assert.equal(cmdUp.callCount, 0, "Step 2: Up command was triggered");
    764  Assert.equal(cmdDown.callCount, 1, "Step 2: Down command was not triggered");
    765  Assert.equal(cmdLeft.callCount, 0, "Step 2: Left command was triggered");
    766  Assert.equal(cmdRight.callCount, 0, "Step 2: Right command was triggered");
    767 
    768  // LEFT
    769  resetCounts();
    770  await synthesizeSimpleGestureEvent(
    771    test_normalTab.linkedBrowser,
    772    "MozSwipeGesture",
    773    10,
    774    10,
    775    left,
    776    0,
    777    0,
    778    0
    779  );
    780  Assert.equal(cmdUp.callCount, 0, "Step 3: Up command was triggered");
    781  Assert.equal(cmdDown.callCount, 0, "Step 3: Down command was triggered");
    782  Assert.equal(cmdLeft.callCount, 1, "Step 3: Left command was not triggered");
    783  Assert.equal(cmdRight.callCount, 0, "Step 3: Right command was triggered");
    784 
    785  // RIGHT
    786  resetCounts();
    787  await synthesizeSimpleGestureEvent(
    788    test_normalTab.linkedBrowser,
    789    "MozSwipeGesture",
    790    10,
    791    10,
    792    right,
    793    0,
    794    0,
    795    0
    796  );
    797  Assert.equal(cmdUp.callCount, 0, "Step 4: Up command was triggered");
    798  Assert.equal(cmdDown.callCount, 0, "Step 4: Down command was triggered");
    799  Assert.equal(cmdLeft.callCount, 0, "Step 4: Left command was triggered");
    800  Assert.equal(
    801    cmdRight.callCount,
    802    1,
    803    "Step 4: Right command was not triggered"
    804  );
    805 
    806  // Make sure combinations do not trigger events.
    807  let combos = [up | left, up | right, down | left, down | right];
    808  for (let i = 0; i < combos.length; i++) {
    809    resetCounts();
    810    await synthesizeSimpleGestureEvent(
    811      test_normalTab.linkedBrowser,
    812      "MozSwipeGesture",
    813      10,
    814      10,
    815      combos[i],
    816      0,
    817      0,
    818      0
    819    );
    820    Assert.equal(
    821      cmdUp.callCount,
    822      0,
    823      "Step 5-" + i + ": Up command was triggered"
    824    );
    825    Assert.equal(
    826      cmdDown.callCount,
    827      0,
    828      "Step 5-" + i + ": Down command was triggered"
    829    );
    830    Assert.equal(
    831      cmdLeft.callCount,
    832      0,
    833      "Step 5-" + i + ": Left command was triggered"
    834    );
    835    Assert.equal(
    836      cmdRight.callCount,
    837      0,
    838      "Step 5-" + i + ": Right command was triggered"
    839    );
    840  }
    841 
    842  // Remove the test commands.
    843  test_removeCommand(cmdUp);
    844  test_removeCommand(cmdDown);
    845  test_removeCommand(cmdLeft);
    846  test_removeCommand(cmdRight);
    847 }
    848 
    849 function test_rotateHelperGetImageRotation(aImageElement) {
    850  // Get the true image rotation from the transform matrix, bounded
    851  // to 0 <= result < 360
    852  let transformValue = content.window.getComputedStyle(aImageElement).transform;
    853  if (transformValue == "none") {
    854    return 0;
    855  }
    856 
    857  transformValue = transformValue.split("(")[1].split(")")[0].split(",");
    858  var rotation = Math.round(
    859    Math.atan2(transformValue[1], transformValue[0]) * (180 / Math.PI)
    860  );
    861  return rotation < 0 ? rotation + 360 : rotation;
    862 }
    863 
    864 async function test_rotateHelperOneGesture(
    865  aImageElement,
    866  aCurrentRotation,
    867  aDirection,
    868  aAmount,
    869  aStop
    870 ) {
    871  if (aAmount <= 0 || aAmount > 90) {
    872    // Bound to 0 < aAmount <= 90
    873    return;
    874  }
    875 
    876  // easier to type names for the direction constants
    877  let clockwise = SimpleGestureEvent.ROTATION_CLOCKWISE;
    878 
    879  let delta = aAmount * (aDirection == clockwise ? 1 : -1);
    880 
    881  // Kill transition time on image so test isn't wrong and doesn't take 10 seconds
    882  aImageElement.style.transitionDuration = "0s";
    883 
    884  // Start the gesture, perform an update, and force flush
    885  await synthesizeSimpleGestureEvent(
    886    test_imageTab.linkedBrowser,
    887    "MozRotateGestureStart",
    888    10,
    889    10,
    890    aDirection,
    891    0.001,
    892    0,
    893    0
    894  );
    895  await synthesizeSimpleGestureEvent(
    896    test_imageTab.linkedBrowser,
    897    "MozRotateGestureUpdate",
    898    10,
    899    10,
    900    aDirection,
    901    delta,
    902    0,
    903    0
    904  );
    905  aImageElement.clientTop;
    906 
    907  // If stop, check intermediate
    908  if (aStop) {
    909    // Send near-zero-delta to stop, and force flush
    910    await synthesizeSimpleGestureEvent(
    911      test_imageTab.linkedBrowser,
    912      "MozRotateGestureUpdate",
    913      10,
    914      10,
    915      aDirection,
    916      0.001,
    917      0,
    918      0
    919    );
    920    aImageElement.clientTop;
    921 
    922    let stopExpectedRotation = (aCurrentRotation + delta) % 360;
    923    if (stopExpectedRotation < 0) {
    924      stopExpectedRotation += 360;
    925    }
    926 
    927    is(
    928      stopExpectedRotation,
    929      test_rotateHelperGetImageRotation(aImageElement),
    930      "Image rotation at gesture stop/hold: expected=" +
    931        stopExpectedRotation +
    932        ", observed=" +
    933        test_rotateHelperGetImageRotation(aImageElement) +
    934        ", init=" +
    935        aCurrentRotation +
    936        ", amt=" +
    937        aAmount +
    938        ", dir=" +
    939        (aDirection == clockwise ? "cl" : "ccl")
    940    );
    941  }
    942  // End it and force flush
    943  await synthesizeSimpleGestureEvent(
    944    test_imageTab.linkedBrowser,
    945    "MozRotateGesture",
    946    10,
    947    10,
    948    aDirection,
    949    0,
    950    0,
    951    0
    952  );
    953  aImageElement.clientTop;
    954 
    955  let finalExpectedRotation;
    956 
    957  if (aAmount < 45 && aStop) {
    958    // Rotate a bit, then stop.  Expect no change at end of gesture.
    959    finalExpectedRotation = aCurrentRotation;
    960  } else {
    961    // Either not stopping (expect 90 degree change in aDirection), OR
    962    // stopping but after 45, (expect 90 degree change in aDirection)
    963    finalExpectedRotation =
    964      (aCurrentRotation + (aDirection == clockwise ? 1 : -1) * 90) % 360;
    965    if (finalExpectedRotation < 0) {
    966      finalExpectedRotation += 360;
    967    }
    968  }
    969 
    970  is(
    971    finalExpectedRotation,
    972    test_rotateHelperGetImageRotation(aImageElement),
    973    "Image rotation gesture end: expected=" +
    974      finalExpectedRotation +
    975      ", observed=" +
    976      test_rotateHelperGetImageRotation(aImageElement) +
    977      ", init=" +
    978      aCurrentRotation +
    979      ", amt=" +
    980      aAmount +
    981      ", dir=" +
    982      (aDirection == clockwise ? "cl" : "ccl")
    983  );
    984 }
    985 
    986 async function test_rotateGesturesOnTab() {
    987  gBrowser.selectedBrowser.removeEventListener(
    988    "load",
    989    test_rotateGesturesOnTab,
    990    true
    991  );
    992 
    993  if (!ImageDocument.isInstance(content.document)) {
    994    ok(false, "Image document failed to open for rotation testing");
    995    gBrowser.removeTab(test_imageTab);
    996    BrowserTestUtils.removeTab(test_normalTab);
    997    test_imageTab = null;
    998    test_normalTab = null;
    999    finish();
   1000    return;
   1001  }
   1002 
   1003  // easier to type names for the direction constants
   1004  let cl = SimpleGestureEvent.ROTATION_CLOCKWISE;
   1005  let ccl = SimpleGestureEvent.ROTATION_COUNTERCLOCKWISE;
   1006 
   1007  let imgElem =
   1008    content.document.body && content.document.body.firstElementChild;
   1009 
   1010  if (!imgElem) {
   1011    ok(false, "Could not get image element on ImageDocument for rotation!");
   1012    gBrowser.removeTab(test_imageTab);
   1013    BrowserTestUtils.removeTab(test_normalTab);
   1014    test_imageTab = null;
   1015    test_normalTab = null;
   1016    finish();
   1017    return;
   1018  }
   1019 
   1020  // Quick function to normalize rotation to 0 <= r < 360
   1021  var normRot = function (rotation) {
   1022    rotation = rotation % 360;
   1023    if (rotation < 0) {
   1024      rotation += 360;
   1025    }
   1026    return rotation;
   1027  };
   1028 
   1029  for (var initRot = 0; initRot < 360; initRot += 90) {
   1030    // Test each case: at each 90 degree snap; cl/ccl;
   1031    // amount more or less than 45; stop and hold or don't (32 total tests)
   1032    // The amount added to the initRot is where it is expected to be
   1033    await test_rotateHelperOneGesture(
   1034      imgElem,
   1035      normRot(initRot + 0),
   1036      cl,
   1037      35,
   1038      true
   1039    );
   1040    await test_rotateHelperOneGesture(
   1041      imgElem,
   1042      normRot(initRot + 0),
   1043      cl,
   1044      35,
   1045      false
   1046    );
   1047    await test_rotateHelperOneGesture(
   1048      imgElem,
   1049      normRot(initRot + 90),
   1050      cl,
   1051      55,
   1052      true
   1053    );
   1054    await test_rotateHelperOneGesture(
   1055      imgElem,
   1056      normRot(initRot + 180),
   1057      cl,
   1058      55,
   1059      false
   1060    );
   1061    await test_rotateHelperOneGesture(
   1062      imgElem,
   1063      normRot(initRot + 270),
   1064      ccl,
   1065      35,
   1066      true
   1067    );
   1068    await test_rotateHelperOneGesture(
   1069      imgElem,
   1070      normRot(initRot + 270),
   1071      ccl,
   1072      35,
   1073      false
   1074    );
   1075    await test_rotateHelperOneGesture(
   1076      imgElem,
   1077      normRot(initRot + 180),
   1078      ccl,
   1079      55,
   1080      true
   1081    );
   1082    await test_rotateHelperOneGesture(
   1083      imgElem,
   1084      normRot(initRot + 90),
   1085      ccl,
   1086      55,
   1087      false
   1088    );
   1089 
   1090    // Manually rotate it 90 degrees clockwise to prepare for next iteration,
   1091    // and force flush
   1092    await synthesizeSimpleGestureEvent(
   1093      test_imageTab.linkedBrowser,
   1094      "MozRotateGestureStart",
   1095      10,
   1096      10,
   1097      cl,
   1098      0.001,
   1099      0,
   1100      0
   1101    );
   1102    await synthesizeSimpleGestureEvent(
   1103      test_imageTab.linkedBrowser,
   1104      "MozRotateGestureUpdate",
   1105      10,
   1106      10,
   1107      cl,
   1108      90,
   1109      0,
   1110      0
   1111    );
   1112    await synthesizeSimpleGestureEvent(
   1113      test_imageTab.linkedBrowser,
   1114      "MozRotateGestureUpdate",
   1115      10,
   1116      10,
   1117      cl,
   1118      0.001,
   1119      0,
   1120      0
   1121    );
   1122    await synthesizeSimpleGestureEvent(
   1123      test_imageTab.linkedBrowser,
   1124      "MozRotateGesture",
   1125      10,
   1126      10,
   1127      cl,
   1128      0,
   1129      0,
   1130      0
   1131    );
   1132    imgElem.clientTop;
   1133  }
   1134 
   1135  gBrowser.removeTab(test_imageTab);
   1136  BrowserTestUtils.removeTab(test_normalTab);
   1137  test_imageTab = null;
   1138  test_normalTab = null;
   1139  finish();
   1140 }
   1141 
   1142 function test_rotateGestures() {
   1143  test_imageTab = BrowserTestUtils.addTab(
   1144    gBrowser,
   1145    "chrome://branding/content/about-logo.png"
   1146  );
   1147  gBrowser.selectedTab = test_imageTab;
   1148 
   1149  gBrowser.selectedBrowser.addEventListener(
   1150    "load",
   1151    test_rotateGesturesOnTab,
   1152    true
   1153  );
   1154 }