widget/tests/window_mouse_scroll_win.html
author Brindusan Cristian <cbrindusan@mozilla.com>
Tue, 05 Mar 2019 22:42:15 +0200
changeset 520316 be4b780a7783cdf7d3ead37c98917dc0d2e94a1d
parent 517275 0176f4dc4345f1e7620f3984a7be294757616b2b
child 528459 0d9b9b96f5475adbed73922da696aeff7cbbaed3
permissions -rw-r--r--
Backed out 5 changesets (bug 1508976, bug 1522581) for android geckoview bustages at /usr/bin/python2.7. CLOSED TREE Backed out changeset 7a6be593b0be (bug 1522581) Backed out changeset c47b37ac1775 (bug 1522581) Backed out changeset 91c31d2a7706 (bug 1508976) Backed out changeset da57df805c56 (bug 1508976) Backed out changeset 4e5d97c93515 (bug 1508976)

<html lang="en-US"
      style="font-family: Arial; font-size: 10px; line-height: 16px;">
<head>
  <title>Test for mouse scroll handling on Windows</title>
  <script type="text/javascript"
          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
  <script type="text/javascript"
          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
  <link rel="stylesheet" type="text/css"
          href="chrome://mochikit/content/tests/SimpleTest/test.css" />
</head>
<body onunload="onUnload();">
<div id="display" style="width: 5000px; height: 5000px;">
<p id="p1" style="font-size: 16px; width: 100px; height: 100px;">1st &lt;p&gt;.</p>
<p id="p2" style="font-size: 32px; width: 100px; height: 100px;">2nd &lt;p&gt;.</p>
</div>
<script class="testbody" type="application/javascript">

window.opener.wrappedJSObject.SimpleTest.waitForFocus(prepareTests, window);

const nsIDOMWindowUtils = Ci.nsIDOMWindowUtils;

const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

const WHEEL_PAGESCROLL = 4294967295;

const WM_VSCROLL     = 0x0115;
const WM_HSCROLL     = 0x0114;
const WM_MOUSEWHEEL  = 0x020A;
const WM_MOUSEHWHEEL = 0x020E;

const SB_LINEUP    = 0;
const SB_LINELEFT  = 0;
const SB_LINEDOWN  = 1;
const SB_LINERIGHT = 1;
const SB_PAGEUP    = 2;
const SB_PAGELEFT  = 2;
const SB_PAGEDOWN  = 3;
const SB_PAGERIGHT = 3;

const SHIFT_L = 0x0100;
const SHIFT_R = 0x0200;
const CTRL_L  = 0x0400;
const CTRL_R  = 0x0800;
const ALT_L   = 0x1000;
const ALT_R   = 0x2000;

const DOM_PAGE_SCROLL_DELTA = 32768;

const kSystemScrollSpeedOverridePref = "mousewheel.system_scroll_override_on_root_content.enabled";

const kAltKeyActionPref         = "mousewheel.with_alt.action";
const kCtrlKeyActionPref        = "mousewheel.with_control.action";
const kShiftKeyActionPref       = "mousewheel.with_shift.action";
const kWinKeyActionPref         = "mousewheel.with_win.action";

const kAltKeyDeltaMultiplierXPref   = "mousewheel.with_alt.delta_multiplier_x";
const kAltKeyDeltaMultiplierYPref   = "mousewheel.with_alt.delta_multiplier_y";
const kCtrlKeyDeltaMultiplierXPref  = "mousewheel.with_control.delta_multiplier_x";
const kCtrlKeyDeltaMultiplierYPref  = "mousewheel.with_control.delta_multiplier_y";
const kShiftKeyDeltaMultiplierXPref = "mousewheel.with_shift.delta_multiplier_x";
const kShiftKeyDeltaMultiplierYPref = "mousewheel.with_shift.delta_multiplier_y";
const kWinKeyDeltaMultiplierXPref   = "mousewheel.with_win.delta_multiplier_x";
const kWinKeyDeltaMultiplierYPref   = "mousewheel.with_win.delta_multiplier_y";

const kEmulateWheelByWMSCROLLPref = "mousewheel.emulate_at_wm_scroll";
const kVAmountPref                = "mousewheel.windows.vertical_amount_override";
const kHAmountPref                = "mousewheel.windows.horizontal_amount_override";
const kTimeoutPref                = "mousewheel.windows.transaction.timeout";

const kMouseLineScrollEvent  = "DOMMouseScroll";
const kMousePixelScrollEvent = "MozMousePixelScroll";

const kVAxis = MouseScrollEvent.VERTICAL_AXIS;
const kHAxis = MouseScrollEvent.HORIZONTAL_AXIS;

var gLineHeight = 0;
var gCharWidth  = 0;
var gPageHeight = 0;
var gPageWidth  = 0;

var gP1 = document.getElementById("p1");
var gP2 = document.getElementById("p2");

var gOtherWindow;

function ok(aCondition, aMessage) {
  window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
}

function is(aLeft, aRight, aMessage) {
  window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
}

function isnot(aLeft, aRight, aMessage) {
  window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
}

function todo_is(aLeft, aRight, aMessage) {
  window.opener.wrappedJSObject.SimpleTest.todo_is(aLeft, aRight, aMessage);
}

function onUnload() {
  SpecialPowers.clearUserPref(kAltKeyActionPref);
  SpecialPowers.clearUserPref(kCtrlKeyActionPref);
  SpecialPowers.clearUserPref(kShiftKeyActionPref);
  SpecialPowers.clearUserPref(kWinKeyActionPref);

  SpecialPowers.clearUserPref(kAltKeyDeltaMultiplierXPref);
  SpecialPowers.clearUserPref(kAltKeyDeltaMultiplierYPref);
  SpecialPowers.clearUserPref(kCtrlKeyDeltaMultiplierXPref);
  SpecialPowers.clearUserPref(kCtrlKeyDeltaMultiplierYPref);
  SpecialPowers.clearUserPref(kShiftKeyDeltaMultiplierXPref);
  SpecialPowers.clearUserPref(kShiftKeyDeltaMultiplierYPref);
  SpecialPowers.clearUserPref(kWinKeyDeltaMultiplierXPref);
  SpecialPowers.clearUserPref(kWinKeyDeltaMultiplierYPref);

  SpecialPowers.clearUserPref(kSystemScrollSpeedOverridePref);
  SpecialPowers.clearUserPref(kEmulateWheelByWMSCROLLPref);
  SpecialPowers.clearUserPref(kVAmountPref);
  SpecialPowers.clearUserPref(kHAmountPref);
  SpecialPowers.clearUserPref(kTimeoutPref);
  window.opener.wrappedJSObject.SimpleTest.finish();
}

function getWindowUtils(aWindow) {
  if (!aWindow) {
    aWindow = window;
  }
  return aWindow.windowUtils;
}

function getPointInScreen(aElement, aWindow) {
  if (!aWindow) {
    aWindow = window;
  }
  var bounds = aElement.getBoundingClientRect();
  return { x: bounds.left + aWindow.mozInnerScreenX,
           y: bounds.top + aWindow.mozInnerScreenY };
}

function cut(aNum) {
  return (aNum >= 0) ? Math.floor(aNum) : Math.ceil(aNum);
}

/**
 * Make each steps for the tests in following arrays in global scope. Each item
 * of the arrays will be executed after previous test is finished.
 *
 * description:
 *   Set the description of the test.  This will be used for the message of is()
 *   or the others.
 *
 * message:
 *   aNativeMessage of nsIDOMWindowUtils.sendNativeMouseScrollEvent().
 *   Must be WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_VSCROLL or WM_HSCROLL.
 *
 * delta:
 *   The native delta value for WM_MOUSEWHEEL or WM_MOUSEHWHEEL.
 *   Or one of the SB_* const value for WM_VSCROLL or WM_HSCROLL.
 *
 * target:
 *   The target element, under the mouse cursor.
 *
 * window:
 *   The window which is used for getting nsIDOMWindowUtils.
 *
 * modifiers:
 *   Pressed modifier keys, 0 means no modifier key is pressed.
 *   Otherwise, one or more values of SHIFT_L, SHIFT_R, CTRL_L, CTRL_R,
 *   ALT_L or ALT_R.
 *
 * additionalFlags:
 *   aAdditionalFlags of nsIDOMWindowUtils.sendNativeMouseScrollEvent().
 *   See the document of nsIDOMWindowUtils for the detail of the values.
 *
 * onLineScrollEvent:
 *   Must be a function or null.
 *   If the value is a function, it will be called when DOMMouseScroll event
 *   is received by the synthesized event.
 *   If return true, the common checks are canceled.
 *
 * onPixelScrollEvent:
 *   Must be a function or null.
 *   If the value is a function, it will be called when MozMousePixelScroll
 *   event is received by the synthesized event.
 *   If return true, the common checks are canceled.
 *
 * expected:
 *   Must not be null and this must have:
 *     axis:
 *       kVAxis if the synthesized event causes vertical scroll. Otherwise,
 *       it causes horizontal scroll, kHAxis.
 *     lines:
 *       Integer value which is expected detail attribute value of
 *       DOMMouseScroll.  If the event shouldn't be fired, must be 0.
 *     pixels:
 *       Integer value or a function which returns double value.  The value is
 *       expected detail attribute value of MozMousePixelScroll.
 *       If the event shouldn't be fired, must be 0.
 *
 *   Note that if both lines and pixels are 0, the test framework waits
 *   a few seconds. After that, go to next test.
 *
 * init:
 *   Must be a function or null.  If this value is a function, it's called
 *   before synthesizing the native event.
 *
 * finish:
 *   Must be a function or null.  If this value is a function, it's called
 *   after received all expected events or timeout if no events are expected.
 */

// First, get the computed line height, char width, page height and page width.
var gPreparingSteps = [
  { description: "Preparing gLineHeight",
    message: WM_MOUSEWHEEL, delta: -120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    onLineScrollEvent(aEvent) {
      return true;
    },
    onPixelScrollEvent(aEvent) {
      gLineHeight = aEvent.detail;
      return true;
    },
    expected: {
      axis: kVAxis, lines: 1, pixels: 1,
    },
    init() {
      SpecialPowers.setIntPref(kVAmountPref, 1);
      SpecialPowers.setIntPref(kHAmountPref, 1);
    },
  },
  { description: "Preparing gCharWidth",
    message: WM_MOUSEHWHEEL, delta: 120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    onLineScrollEvent(aEvent) {
      return true;
    },
    onPixelScrollEvent(aEvent) {
      gCharWidth = aEvent.detail;
      return true;
    },
    expected: {
      axis: kVAxis, lines: 1, pixels: 1,
    },
  },
  { description: "Preparing gPageHeight",
    message: WM_MOUSEWHEEL, delta: -120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    onLineScrollEvent(aEvent) {
      return true;
    },
    onPixelScrollEvent(aEvent) {
      gPageHeight = aEvent.detail;
      return true;
    },
    expected: {
      axis: kHAxis, lines: 1, pixels: 1,
    },
    init() {
      SpecialPowers.setIntPref(kVAmountPref, 0xFFFF);
      SpecialPowers.setIntPref(kHAmountPref, 0xFFFF);
    },
  },
  { description: "Preparing gPageWidth",
    message: WM_MOUSEHWHEEL, delta: 120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    onLineScrollEvent(aEvent) {
      return true;
    },
    onPixelScrollEvent(aEvent) {
      gPageWidth = aEvent.detail;
      return true;
    },
    expected: {
      axis: kHAxis, lines: 1, pixels: 1,
    },
    finish() {
      ok(gLineHeight > 0, "gLineHeight isn't positive got " + gLineHeight);
      ok(gCharWidth > 0, "gCharWidth isn't positive got " + gCharWidth);
      ok(gPageHeight > 0, "gPageHeight isn't positive got " + gPageHeight);
      ok(gPageWidth > 0, "gPageWidth isn't positive got " + gPageWidth);

      ok(gPageHeight > gLineHeight,
         "gPageHeight must be larger than gLineHeight");
      ok(gPageWidth > gCharWidth,
         "gPageWidth must be larger than gCharWidth");
      runNextTest(gBasicTests, 0);
    },
  },
];

var gBasicTests = [
  // Widget shouldn't dispatch a pixel event if the delta can be devided by
  // lines to be scrolled.  However, pixel events should be fired by ESM.
  { description: "WM_MOUSEWHEEL, -120, 3 lines",
    message: WM_MOUSEWHEEL, delta: -120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 3, pixels() { return gLineHeight * 3; },
    },
    init() {
      SpecialPowers.setIntPref(kVAmountPref, 3);
      SpecialPowers.setIntPref(kHAmountPref, 3);
    },
  },

  { description: "WM_MOUSEWHEEL, 120, -3 lines",
    message: WM_MOUSEWHEEL, delta: 120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: -3, pixels() { return gLineHeight * -3; },
    },
  },

  { description: "WM_MOUSEHWHEEL, 120, 3 chars",
    message: WM_MOUSEHWHEEL, delta: 120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 3, pixels() { return gCharWidth * 3; },
    },
  },

  { description: "WM_MOUSEHWHEEL, -120, -3 chars",
    message: WM_MOUSEHWHEEL, delta: -120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: -3, pixels() { return gCharWidth * -3; },
    },
  },

  // Pixel scroll event should be fired always but line scroll event should be
  // fired only when accumulated delta value is over a line.
  { description: "WM_MOUSEWHEEL, -20, 0.5 lines",
    message: WM_MOUSEWHEEL, delta: -20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels() { return gLineHeight / 2; },
    },
  },
  { description: "WM_MOUSEWHEEL, -20, 0.5 lines (pending: 0.5 lines)",
    message: WM_MOUSEWHEEL, delta: -20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight / 2; },
    },
  },
  { description: "WM_MOUSEWHEEL, -20, 0.5 lines",
    message: WM_MOUSEWHEEL, delta: -20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels() { return gLineHeight / 2; },
    },
  },

  { description: "WM_MOUSEWHEEL, 20, -0.5 lines (pending: 0.5 lines)",
    message: WM_MOUSEWHEEL, delta: 20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels() { return gLineHeight / -2; },
    },
  },
  { description: "WM_MOUSEWHEEL, 20, -0.5 lines (pending: -0.5 lines)",
    message: WM_MOUSEWHEEL, delta: 20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: -1, pixels() { return -gLineHeight / 2; },
    },
  },
  { description: "WM_MOUSEWHEEL, 20, -0.5 lines",
    message: WM_MOUSEWHEEL, delta: 20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels() { return gLineHeight / -2; },
    },
  },

  { description: "WM_MOUSEHWHEEL, 20, 0.5 chars",
    message: WM_MOUSEHWHEEL, delta: 20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels() { return gCharWidth / 2; },
    },
  },
  { description: "WM_MOUSEHWHEEL, 20, 0.5 chars (pending: 0.5 chars)",
    message: WM_MOUSEHWHEEL, delta: 20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth / 2; },
    },
  },
  { description: "WM_MOUSEHWHEEL, 20, 0.5 chars",
    message: WM_MOUSEHWHEEL, delta: 20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels() { return gCharWidth / 2; },
    },
  },

  { description: "WM_MOUSEHWHEEL, -20, -0.5 chars (pending: 0.5 chars)",
    message: WM_MOUSEHWHEEL, delta: -20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels() { return gCharWidth / -2; },
    },
  },
  { description: "WM_MOUSEHWHEEL, -20, -0.5 chars (pending: -0.5 chars)",
    message: WM_MOUSEHWHEEL, delta: -20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: -1, pixels() { return -gCharWidth / 2; },
    },
  },
  { description: "WM_MOUSEHWHEEL, -20, -0.5 chars",
    message: WM_MOUSEHWHEEL, delta: -20,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels() { return gCharWidth / -2; },
    },
  },

  // Even if the mouse cursor is an element whose font-size is different than
  // the scrollable element, the pixel scroll amount shouldn't be changed.
  // Widget shouldn't dispatch a pixel event if the delta can be devided by
  // lines to be scrolled.  However, pixel events should be fired by ESM.
  { description: "WM_MOUSEWHEEL, -120, 3 lines, on the other div whose font-size is larger",
    message: WM_MOUSEWHEEL, delta: -120,
    target: gP2, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 3, pixels() { return gLineHeight * 3; },
    },
  },

  { description: "WM_MOUSEWHEEL, 120, -3 lines, on the other div whose font-size is larger",
    message: WM_MOUSEWHEEL, delta: 120,
    target: gP2, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: -3, pixels() { return gLineHeight * -3; },
    },
  },

  { description: "WM_MOUSEHWHEEL, 120, 3 chars, on the other div whose font-size is larger",
    message: WM_MOUSEHWHEEL, delta: 120,
    target: gP2, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 3, pixels() { return gCharWidth * 3; },
    },
  },

  { description: "WM_MOUSEHWHEEL, -120, -3 chars, on the other div whose font-size is larger",
    message: WM_MOUSEHWHEEL, delta: -120,
    target: gP2, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: -3, pixels() { return gCharWidth * -3; },
    },
  },

  // Modifier key tests
  { description: "WM_MOUSEWHEEL, -40, 1 line with left Shift",
    message: WM_MOUSEWHEEL, delta: -40,
    target: gP1, x: 10, y: 10, window,
    modifiers: SHIFT_L,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },
  { description: "WM_MOUSEWHEEL, -40, 1 line with right Shift",
    message: WM_MOUSEWHEEL, delta: -40,
    target: gP1, x: 10, y: 10, window,
    modifiers: SHIFT_R,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },
  { description: "WM_MOUSEWHEEL, -40, 1 line with left Ctrl",
    message: WM_MOUSEWHEEL, delta: -40,
    target: gP1, x: 10, y: 10, window,
    modifiers: CTRL_L,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },
  { description: "WM_MOUSEWHEEL, -40, 1 line with right Ctrl",
    message: WM_MOUSEWHEEL, delta: -40,
    target: gP1, x: 10, y: 10, window,
    modifiers: CTRL_R,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },
  { description: "WM_MOUSEWHEEL, -40, 1 line with left Alt",
    message: WM_MOUSEWHEEL, delta: -40,
    target: gP1, x: 10, y: 10, window,
    modifiers: ALT_L,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },
  { description: "WM_MOUSEWHEEL, -40, 1 line with right Alt",
    message: WM_MOUSEWHEEL, delta: -40,
    target: gP1, x: 10, y: 10, window,
    modifiers: ALT_R,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },

  { description: "WM_MOUSEHWHEEL, 40, 1 character with left Shift",
    message: WM_MOUSEHWHEEL, delta: 40,
    target: gP1, x: 10, y: 10, window,
    modifiers: SHIFT_L,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_MOUSEHWHEEL, 40, 1 character with right Shift",
    message: WM_MOUSEHWHEEL, delta: 40,
    target: gP1, x: 10, y: 10, window,
    modifiers: SHIFT_R,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_MOUSEHWHEEL, 40, 1 character with left Ctrl",
    message: WM_MOUSEHWHEEL, delta: 40,
    target: gP1, x: 10, y: 10, window,
    modifiers: CTRL_L,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_MOUSEHWHEEL, 40, 1 character with right Ctrl",
    message: WM_MOUSEHWHEEL, delta: 40,
    target: gP1, x: 10, y: 10, window,
    modifiers: CTRL_R,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_MOUSEHWHEEL, 40, 1 character with left Alt",
    message: WM_MOUSEHWHEEL, delta: 40,
    target: gP1, x: 10, y: 10, window,
    modifiers: ALT_L,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_MOUSEHWHEEL, 40, 1 character with right Alt",
    message: WM_MOUSEHWHEEL, delta: 40,
    target: gP1, x: 10, y: 10, window,
    modifiers: ALT_R,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },

    finish() {
      runNextTest(gScrollMessageTests, 0);
    },
  },
];

var gPageScrllTests = [
  // Pixel scroll event should be fired always but line scroll event should be
  // fired only when accumulated delta value is over a line.
  { description: "WM_MOUSEWHEEL, -60, 0.5 pages",
    message: WM_MOUSEWHEEL, delta: -60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels() { return gPageHeight / 2; },
    },
  },
  { description: "WM_MOUSEWHEEL, -60, 0.5 pages (pending: 0.5 pages)",
    message: WM_MOUSEWHEEL, delta: -60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: DOM_PAGE_SCROLL_DELTA,
      pixels() { return ((gPageHeight / 2) + (gPageHeight % 2)); },
    },
  },
  { description: "WM_MOUSEWHEEL, -60, 0.5 pages",
    message: WM_MOUSEWHEEL, delta: -60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels() { return gPageHeight / 2; },
    },
  },

  { description: "WM_MOUSEWHEEL, 60, -0.5 pages (pending: 0.5 pages)",
    message: WM_MOUSEWHEEL, delta: 60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels() { return gPageHeight / -2; },
    },
  },
  { description: "WM_MOUSEWHEEL, 60, -0.5 pages (pending: -0.5 pages)",
    message: WM_MOUSEWHEEL, delta: 60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: -DOM_PAGE_SCROLL_DELTA,
      pixels() { return -((gPageHeight / 2) + (gPageHeight % 2)); },
    },
  },
  { description: "WM_MOUSEWHEEL, 60, -0.5 pages",
    message: WM_MOUSEWHEEL, delta: 60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels() { return gPageHeight / -2; },
    },
  },

  { description: "WM_MOUSEHWHEEL, 60, 0.5 pages",
    message: WM_MOUSEHWHEEL, delta: 60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels() { return gPageWidth / 2; },
    },
  },
  { description: "WM_MOUSEHWHEEL, 60, 0.5 pages (pending: 0.5 pages)",
    message: WM_MOUSEHWHEEL, delta: 60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: DOM_PAGE_SCROLL_DELTA,
      pixels() { return ((gPageWidth / 2) + (gPageWidth % 2)); },
    },
  },
  { description: "WM_MOUSEHWHEEL, 60, 0.5 pages",
    message: WM_MOUSEHWHEEL, delta: 60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels() { return gPageWidth / 2; },
    },
  },

  { description: "WM_MOUSEHWHEEL, -60, -0.5 pages (pending: 0.5 pages)",
    message: WM_MOUSEHWHEEL, delta: -60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels() { return gCharWidth / -2; },
    },
  },
  { description: "WM_MOUSEHWHEEL, -60, -0.5 pages (pending: -0.5 pages)",
    message: WM_MOUSEHWHEEL, delta: -60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: -DOM_PAGE_SCROLL_DELTA,
      pixels() { return -((gCharWidth / 2) + (gCharWidth % 2)); },
    },
  },
  { description: "WM_MOUSEHWHEEL, -60, -0.5 pages",
    message: WM_MOUSEHWHEEL, delta: -60,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels() { return gCharWidth / -2; },
    },
  },
];

var gScrollMessageTests = [
  // Widget should dispatch neither line scroll event nor pixel scroll event if
  // the WM_*SCROLL's lParam is NULL and mouse wheel emulation is disabled.
  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is NULL, emulation disabled",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels: 0,
    },
    init() {
      SpecialPowers.setIntPref(kVAmountPref, 3);
      SpecialPowers.setIntPref(kHAmountPref, 3);
      SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
    },
  },

  { description: "WM_VSCROLL, SB_LINEUP, lParam is NULL, emulation disabled",
    message: WM_VSCROLL, delta: SB_LINEUP,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 0, pixels: 0,
    },
  },

  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is NULL, emulation disabled",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels: 0,
    },
  },

  { description: "WM_HSCROLL, SB_LINELEFT, lParam is NULL, emulation disabled",
    message: WM_HSCROLL, delta: SB_LINELEFT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 0, pixels: 0,
    },
  },

  // Widget should emulate mouse wheel behavior for WM_*SCROLL even if the
  // kEmulateWheelByWMSCROLLPref is disabled but the message's lParam is not
  // NULL. Then, widget doesn't dispatch a pixel event for WM_*SCROLL messages,
  // but ESM dispatches it instead.
  { description: "WM_VSCROLL, SB_LINEUP, lParam is not NULL, emulation disabled",
    message: WM_VSCROLL, delta: SB_LINEUP,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: -1, pixels() { return -gLineHeight; },
    },
    init() {
      SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
    },
  },

  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },

  { description: "WM_HSCROLL, SB_LINELEFT, lParam is not NULL, emulation disabled",
    message: WM_HSCROLL, delta: SB_LINELEFT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: -1, pixels() { return -gCharWidth; },
    },
  },

  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },

  { description: "WM_VSCROLL, SB_PAGEUP, lParam is not NULL, emulation disabled",
    message: WM_VSCROLL, delta: SB_PAGEUP,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: -DOM_PAGE_SCROLL_DELTA,
      pixels() { return -gPageHeight; },
    },
  },

  { description: "WM_VSCROLL, SB_PAGEDOWN, lParam is not NULL, emulation disabled",
    message: WM_VSCROLL, delta: SB_PAGEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: DOM_PAGE_SCROLL_DELTA,
      pixels() { return gPageHeight; },
    },
  },

  { description: "WM_HSCROLL, SB_PAGELEFT, lParam is not NULL, emulation disabled",
    message: WM_HSCROLL, delta: SB_PAGELEFT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: -DOM_PAGE_SCROLL_DELTA,
      pixels() { return -gPageWidth; },
    },
  },

  { description: "WM_HSCROLL, SB_PAGERIGHT, lParam is not NULL, emulation disabled",
    message: WM_HSCROLL, delta: SB_PAGERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: DOM_PAGE_SCROLL_DELTA,
      pixels() { return gPageWidth; },
    },
  },

  // Widget should emulate mouse wheel behavior for WM_*SCROLL when the
  // kEmulateWheelByWMSCROLLPref is enabled even if the message's lParam is
  // NULL. Then, widget doesn't dispatch a pixel event for WM_*SCROLL messages,
  // but ESM dispatches it instead.
  { description: "WM_VSCROLL, SB_LINEUP, lParam is NULL, emulation enabled",
    message: WM_VSCROLL, delta: SB_LINEUP,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: -1, pixels() { return -gLineHeight; },
    },
    init() {
      SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, true);
    },
  },

  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is NULL, emulation enabled",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },

  { description: "WM_HSCROLL, SB_LINELEFT, lParam is NULL, emulation enabled",
    message: WM_HSCROLL, delta: SB_LINELEFT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: -1, pixels() { return -gCharWidth; },
    },
  },

  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is NULL, emulation enabled",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },

  { description: "WM_VSCROLL, SB_PAGEUP, lParam is NULL, emulation enabled",
    message: WM_VSCROLL, delta: SB_PAGEUP,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: -DOM_PAGE_SCROLL_DELTA,
      pixels() { return -gPageHeight; },
    },
  },

  { description: "WM_VSCROLL, SB_PAGEDOWN, lParam is NULL, emulation enabled",
    message: WM_VSCROLL, delta: SB_PAGEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: DOM_PAGE_SCROLL_DELTA,
      pixels() { return gPageHeight; },
    },
  },

  { description: "WM_HSCROLL, SB_PAGELEFT, lParam is NULL, emulation enabled",
    message: WM_HSCROLL, delta: SB_PAGELEFT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: -DOM_PAGE_SCROLL_DELTA,
      pixels() { return -gPageWidth; },
    },
  },

  { description: "WM_HSCROLL, SB_PAGERIGHT, lParam is NULL, emulation enabled",
    message: WM_HSCROLL, delta: SB_PAGERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: DOM_PAGE_SCROLL_DELTA,
      pixels() { return gPageWidth; },
    },
  },

  // Modifier key tests for WM_*SCROLL
  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with left Shift",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: SHIFT_L,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
    init() {
      SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
    },
  },
  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with right Shift",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: SHIFT_R,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },
  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with left Ctrl",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: CTRL_L,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },
  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with right Ctrl",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: CTRL_L,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },
  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with left Alt",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: ALT_L,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },
  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, with right Alt",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: ALT_R,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },

  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with left Shift",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: SHIFT_L,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with right Shift",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: SHIFT_R,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with left Ctrl",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: CTRL_L,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with right Ctrl",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: CTRL_L,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with left Alt",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: ALT_L,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },
  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, with right Alt",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: ALT_R,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },

    finish() {
      runDeactiveWindowTests();
    },
  },
];

var gDeactiveWindowTests = [
  // Typically, mouse drivers send wheel messages to focused window.
  // However, we prefer to scroll a scrollable element under the mouse cursor.
  { description: "WM_MOUSEWHEEL, -120, 3 lines, window is deactive",
    message: WM_MOUSEWHEEL, delta: -120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 3, pixels() { return gLineHeight * 3; },
    },
    init() {
      SpecialPowers.setIntPref(kVAmountPref, 3);
      SpecialPowers.setIntPref(kHAmountPref, 3);
    },
    onLineScrollEvent(aEvent) {
      var fm = Services.focus;
      is(fm.activeWindow, gOtherWindow, "The other window isn't activated");
    },
  },

  { description: "WM_MOUSEWHEEL, 120, -3 lines, window is deactive",
    message: WM_MOUSEWHEEL, delta: 120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: -3, pixels() { return gLineHeight * -3; },
    },
  },

  { description: "WM_MOUSEHWHEEL, 120, 3 chars, window is deactive",
    message: WM_MOUSEHWHEEL, delta: 120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 3, pixels() { return gCharWidth * 3; },
    },
  },

  { description: "WM_MOUSEHWHEEL, -120, -3 chars, window is deactive",
    message: WM_MOUSEHWHEEL, delta: -120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: -3, pixels() { return gCharWidth * -3; },
    },
  },

  // Of course, even if some drivers prefer the cursor position, we don't need
  // to change anything.
  { description: "WM_MOUSEWHEEL, -120, 3 lines, window is deactive (receive the message directly)",
    message: WM_MOUSEWHEEL, delta: -120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kVAxis, lines: 3, pixels() { return gLineHeight * 3; },
    },
  },

  { description: "WM_MOUSEWHEEL, 120, -3 lines, window is deactive (receive the message directly)",
    message: WM_MOUSEWHEEL, delta: 120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kVAxis, lines: -3, pixels() { return gLineHeight * -3; },
    },
  },

  { description: "WM_MOUSEHWHEEL, 120, 3 chars, window is deactive (receive the message directly)",
    message: WM_MOUSEHWHEEL, delta: 120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kHAxis, lines: 3, pixels() { return gCharWidth * 3; },
    },
  },

  { description: "WM_MOUSEHWHEEL, -120, -3 chars, window is deactive (receive the message directly)",
    message: WM_MOUSEHWHEEL, delta: -120,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kHAxis, lines: -3, pixels() { return gCharWidth * -3; },
    },
  },

  // Same for WM_*SCROLL if lParam is not NULL
  { description: "WM_VSCROLL, SB_LINEUP, lParam is not NULL, emulation disabled, window is deactive",
    message: WM_VSCROLL, delta: SB_LINEUP,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: -1, pixels() { return -gLineHeight; },
    },
    init() {
      SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
    },
  },

  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, window is deactive",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },

  { description: "WM_HSCROLL, SB_LINELEFT, lParam is not NULL, emulation disabled, window is deactive",
    message: WM_HSCROLL, delta: SB_LINELEFT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: -1, pixels() { return -gCharWidth; },
    },
  },

  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, window is deactive",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },

  // Same for WM_*SCROLL if lParam is NULL but emulation is enabled
  { description: "WM_VSCROLL, SB_LINEUP, lParam is NULL, emulation enabled, window is deactive",
    message: WM_VSCROLL, delta: SB_LINEUP,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: -1, pixels() { return -gLineHeight; },
    },
    init() {
      SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, true);
    },
  },

  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is NULL, emulation enabled, window is deactive",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },

  { description: "WM_HSCROLL, SB_LINELEFT, lParam is NULL, emulation enabled, window is deactive",
    message: WM_HSCROLL, delta: SB_LINELEFT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: -1, pixels() { return -gCharWidth; },
    },
  },

  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is NULL, emulation enabled, window is deactive",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: 0,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },

  // Same for WM_*SCROLL if lParam is not NULL and message sent to the deactive window directly
  { description: "WM_VSCROLL, SB_LINEUP, lParam is not NULL, emulation disabled, window is deactive (receive the message directly)",
    message: WM_VSCROLL, delta: SB_LINEUP,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL |
                     nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kVAxis, lines: -1, pixels() { return -gLineHeight; },
    },
    init() {
      SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, false);
    },
  },

  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is not NULL, emulation disabled, window is deactive (receive the message directly)",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL |
                     nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },

  { description: "WM_HSCROLL, SB_LINELEFT, lParam is not NULL, emulation disabled, window is deactive (receive the message directly)",
    message: WM_HSCROLL, delta: SB_LINELEFT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL |
                     nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kHAxis, lines: -1, pixels() { return -gCharWidth; },
    },
  },

  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is not NULL, emulation disabled, window is deactive (receive the message directly)",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL |
                     nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },
  },

  // Same for WM_*SCROLL if lParam is NULL but emulation is enabled, and message sent to the deactive window directly
  { description: "WM_VSCROLL, SB_LINEUP, lParam is NULL, emulation enabled, window is deactive (receive the message directly)",
    message: WM_VSCROLL, delta: SB_LINEUP,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kVAxis, lines: -1, pixels() { return -gLineHeight; },
    },
    init() {
      SpecialPowers.setBoolPref(kEmulateWheelByWMSCROLLPref, true);
    },
  },

  { description: "WM_VSCROLL, SB_LINEDOWN, lParam is NULL, emulation enabled, window is deactive (receive the message directly)",
    message: WM_VSCROLL, delta: SB_LINEDOWN,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kVAxis, lines: 1, pixels() { return gLineHeight; },
    },
  },

  { description: "WM_HSCROLL, SB_LINELEFT, lParam is NULL, emulation enabled, window is deactive (receive the message directly)",
    message: WM_HSCROLL, delta: SB_LINELEFT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kHAxis, lines: -1, pixels() { return -gCharWidth; },
    },
  },

  { description: "WM_HSCROLL, SB_LINERIGHT, lParam is NULL, emulation enabled, window is deactive (receive the message directly)",
    message: WM_HSCROLL, delta: SB_LINERIGHT,
    target: gP1, x: 10, y: 10, window,
    modifiers: 0,
    additionalFlags: nsIDOMWindowUtils.MOUSESCROLL_PREFER_WIDGET_AT_POINT,
    expected: {
      axis: kHAxis, lines: 1, pixels() { return gCharWidth; },
    },

    finish() {
      gOtherWindow.close();
      gOtherWindow = null;
      window.close();
    },
  },
];

function runDeactiveWindowTests() {
  gOtherWindow = window.open("data:text/html,", "_blank",
                             "chrome,width=100,height=100,top=700,left=700");

  window.opener.wrappedJSObject.SimpleTest.waitForFocus(function() {
    runNextTest(gDeactiveWindowTests, 0);
  }, gOtherWindow);
}

function runNextTest(aTests, aIndex) {
  if (aIndex > 0 && aTests[aIndex - 1] && aTests[aIndex - 1].finish) {
    aTests[aIndex - 1].finish();
  }

  if (aTests.length == aIndex) {
    return;
  }

  var test = aTests[aIndex++];
  if (test.init) {
    test.init();
  }
  test.handled = { lines: false, pixels: false };

  switch (test.message) {
    case WM_MOUSEWHEEL:
    case WM_MOUSEHWHEEL:
    case WM_VSCROLL:
    case WM_HSCROLL:
      var expectedLines = test.expected.lines;
      var expectedPixels =
        cut((typeof test.expected.pixels == "function") ?
               test.expected.pixels() : test.expected.pixels);
      var handler = function(aEvent) {
        var doCommonTests = true;

        if (!aEvent) {
          ok(!test.handled.lines,
             test.description + ", line scroll event has been handled");
          ok(!test.handled.pixels,
             test.description + ", pixel scroll event has been handled");
          doCommonTests = false;
        } else if (aEvent.type == kMouseLineScrollEvent) {
          ok(!test.handled.lines,
             test.description + ":(" + aEvent.type + "), same event has already been handled");
          test.handled.lines = true;
          isnot(expectedLines, 0,
                test.description + ":(" + aEvent.type + "), event shouldn't be fired");
          if (test.onLineScrollEvent && test.onLineScrollEvent(aEvent)) {
            doCommonTests = false;
          }
        } else if (aEvent.type == kMousePixelScrollEvent) {
          ok(!test.handled.pixels,
             test.description + ":(" + aEvent.type + "), same event has already been handled");
          test.handled.pixels = true;
          isnot(expectedPixels, 0,
                test.description + ":(" + aEvent.type + "), event shouldn't be fired");
          if (test.onPixelScrollEvent && test.onPixelScrollEvent(aEvent)) {
            doCommonTests = false;
          }
        }

        if (doCommonTests) {
          var expectedDelta =
            (aEvent.type == kMouseLineScrollEvent) ?
              expectedLines : expectedPixels;
          is(aEvent.target.id, test.target.id,
             test.description + ":(" + aEvent.type + "), ID mismatch");
          is(aEvent.axis, test.expected.axis,
             test.description + ":(" + aEvent.type + "), axis mismatch");
          ok(aEvent.detail != 0,
             test.description + ":(" + aEvent.type + "), delta must not be 0");
          is(aEvent.detail, expectedDelta,
             test.description + ":(" + aEvent.type + "), delta mismatch");
          is(aEvent.shiftKey, (test.modifiers & (SHIFT_L | SHIFT_R)) != 0,
             test.description + ":(" + aEvent.type + "), shiftKey mismatch");
          is(aEvent.ctrlKey, (test.modifiers & (CTRL_L | CTRL_R)) != 0,
             test.description + ":(" + aEvent.type + "), ctrlKey mismatch");
          is(aEvent.altKey, (test.modifiers & (ALT_L | ALT_R)) != 0,
             test.description + ":(" + aEvent.type + "), altKey mismatch");
        }

        if (!aEvent || (test.handled.lines || expectedLines == 0) &&
                       (test.handled.pixels || expectedPixels == 0)) {
          // Don't scroll actually.
          if (aEvent) {
            aEvent.preventDefault();
          }
          test.target.removeEventListener(kMouseLineScrollEvent, handler, true);
          test.target.removeEventListener(kMousePixelScrollEvent, handler, true);
          setTimeout(runNextTest, 0, aTests, aIndex);
        }
      };

      test.target.addEventListener(kMouseLineScrollEvent, handler, true);
      test.target.addEventListener(kMousePixelScrollEvent, handler, true);

      if (expectedLines == 0 && expectedPixels == 0) {
        // The timeout might not be enough if system is slow by other process,
        // so, the test might be passed unexpectedly.  However, it must be able
        // to be detected by random orange.
        setTimeout(handler, 500);
      }

      var utils = getWindowUtils(test.window);
      var ptInScreen = getPointInScreen(test.target, test.window);
      var isVertical =
        ((test.message == WM_MOUSEWHEEL) || (test.message == WM_VSCROLL));
      var deltaX = !isVertical ? test.delta : 0;
      var deltaY = isVertical ? test.delta : 0;
      utils.sendNativeMouseScrollEvent(ptInScreen.x + test.x,
                                       ptInScreen.y + test.y,
                                       test.message, deltaX, deltaY, 0,
                                       test.modifiers,
                                       test.additionalFlags,
                                       test.target);
      break;
    default:
      ok(false, test.description + ": invalid message");
      // Let's timeout.
  }
}

function prepareTests() {
  // Disable special action with modifier key
  SpecialPowers.setIntPref(kAltKeyActionPref, 1);
  SpecialPowers.setIntPref(kCtrlKeyActionPref, 1);
  SpecialPowers.setIntPref(kShiftKeyActionPref, 1);
  SpecialPowers.setIntPref(kWinKeyActionPref, 1);

  SpecialPowers.setIntPref(kAltKeyDeltaMultiplierXPref, 100);
  SpecialPowers.setIntPref(kAltKeyDeltaMultiplierYPref, 100);
  SpecialPowers.setIntPref(kCtrlKeyDeltaMultiplierXPref, 100);
  SpecialPowers.setIntPref(kCtrlKeyDeltaMultiplierYPref, 100);
  SpecialPowers.setIntPref(kShiftKeyDeltaMultiplierXPref, 100);
  SpecialPowers.setIntPref(kShiftKeyDeltaMultiplierYPref, 100);
  SpecialPowers.setIntPref(kWinKeyDeltaMultiplierXPref, 100);
  SpecialPowers.setIntPref(kWinKeyDeltaMultiplierYPref, 100);

  SpecialPowers.setBoolPref(kSystemScrollSpeedOverridePref, false);
  SpecialPowers.setIntPref(kTimeoutPref, -1);

  runNextTest(gPreparingSteps, 0);
}

</script>
</body>

</html>