<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 src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
  <script 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.arguments[0].SimpleTest.waitForFocus(prepareTests, window);

const nsIDOMWindowUtils = Ci.nsIDOMWindowUtils;

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.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.arguments[0].SimpleTest.ok(aCondition, aMessage);
}

function is(aLeft, aRight, aMessage) {
  window.arguments[0].SimpleTest.is(aLeft, aRight, aMessage);
}

function isnot(aLeft, aRight, aMessage) {
  window.arguments[0].SimpleTest.isnot(aLeft, aRight, aMessage);
}

function todo_is(aLeft, aRight, aMessage) {
  window.arguments[0].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.arguments[0].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("window_mouse_scroll_win_2.html", "_blank",
                             "chrome,width=100,height=100,top=700,left=700");

  window.arguments[0].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>
