summaryrefslogtreecommitdiffstats
path: root/widget/tests/window_wheeltransaction.xhtml
diff options
context:
space:
mode:
Diffstat (limited to 'widget/tests/window_wheeltransaction.xhtml')
-rw-r--r--widget/tests/window_wheeltransaction.xhtml1569
1 files changed, 1569 insertions, 0 deletions
diff --git a/widget/tests/window_wheeltransaction.xhtml b/widget/tests/window_wheeltransaction.xhtml
new file mode 100644
index 0000000000..f3c081b105
--- /dev/null
+++ b/widget/tests/window_wheeltransaction.xhtml
@@ -0,0 +1,1569 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Wheel scroll tests"
+ width="600" height="600"
+ onload="onload();"
+ onunload="onunload();"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js" />
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<style type="text/css">
+ #rootview {
+ overflow: auto;
+ width: 400px;
+ height: 400px;
+ border: 1px solid;
+ }
+ #container {
+ overflow: auto;
+ width: 600px;
+ height: 600px;
+ }
+ #rootview pre {
+ margin: 20px 0 20px 20px;
+ padding: 0;
+ overflow: auto;
+ display: block;
+ width: 100px;
+ height: 100.5px;
+ font-size: 16px;
+ }
+</style>
+<div id="rootview" onscroll="onScrollView(event);">
+ <div id="container">
+ <pre id="subview1" onscroll="onScrollView(event);">
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+ </pre>
+ <pre id="subview2" onscroll="onScrollView(event);">
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+Text.
+ </pre>
+ <pre id="subview3" onscroll="onScrollView(event);">
+Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
+ </pre>
+ </div>
+</div>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+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);
+}
+
+var gCurrentTestListStatus = { nextListIndex: 0 };
+var gCurrentTest;
+
+const kListenEvent_None = 0;
+const kListenEvent_OnScroll = 1;
+const kListenEvent_OnScrollFailed = 2;
+const kListenEvent_OnTransactionTimeout = 4;
+const kListenEvent_All = kListenEvent_OnScroll |
+ kListenEvent_OnScrollFailed |
+ kListenEvent_OnTransactionTimeout;
+var gLitesnEvents = kListenEvent_None;
+
+/**
+ * At unexpected transaction timeout, we need to stop *all* timers. But it is
+ * difficult and it can be create more complex testing code. So, we should use
+ * only one timer at one time. For that, we must store the timer id to this
+ * variable. And the functions which may be called via a timer must clear the
+ * current timer by |_clearTimer| function.
+ */
+var gTimer;
+
+var gPrefSvc = SpecialPowers.Services.prefs;
+const kPrefSmoothScroll = "general.smoothScroll";
+const kPrefNameTimeout = "mousewheel.transaction.timeout";
+const kPrefNameIgnoreMoveDelay = "mousewheel.transaction.ignoremovedelay";
+const kPrefTestEventsAsyncEnabled = "test.events.async.enabled";
+
+const kDefaultTimeout = gPrefSvc.getIntPref(kPrefNameTimeout);
+const kDefaultIgnoreMoveDelay = gPrefSvc.getIntPref(kPrefNameIgnoreMoveDelay);
+
+gPrefSvc.setBoolPref(kPrefSmoothScroll, false);
+gPrefSvc.setBoolPref(kPrefTestEventsAsyncEnabled, true);
+
+var gTimeout, gIgnoreMoveDelay;
+var gEnoughForTimeout, gEnoughForIgnoreMoveDelay;
+
+function setTimeoutPrefs(aTimeout, aIgnoreMoveDelay)
+{
+ gPrefSvc.setIntPref(kPrefNameTimeout, aTimeout);
+ gPrefSvc.setIntPref(kPrefNameIgnoreMoveDelay, aIgnoreMoveDelay);
+ gTimeout = aTimeout;
+ gIgnoreMoveDelay = aIgnoreMoveDelay;
+ gEnoughForTimeout = gTimeout * 2;
+ gEnoughForIgnoreMoveDelay = gIgnoreMoveDelay * 1.2;
+}
+
+function resetTimeoutPrefs()
+{
+ if (gTimeout == kDefaultTimeout)
+ return;
+ setTimeoutPrefs(kDefaultTimeout, kDefaultIgnoreMoveDelay);
+ initTestList();
+}
+
+function growUpTimeoutPrefs()
+{
+ if (gTimeout != kDefaultTimeout)
+ return;
+ setTimeoutPrefs(5000, 1000);
+ initTestList();
+}
+
+// setting enough time for testing.
+gPrefSvc.setIntPref(kPrefNameTimeout, gTimeout);
+gPrefSvc.setIntPref(kPrefNameIgnoreMoveDelay, gIgnoreMoveDelay);
+
+var gRootView = document.getElementById("rootview");
+var gSubView1 = document.getElementById("subview1");
+var gSubView2 = document.getElementById("subview2");
+var gSubView3 = document.getElementById("subview3");
+
+gRootView.addEventListener("MozMouseScrollFailed", onMouseScrollFailed);
+gRootView.addEventListener("MozMouseScrollTransactionTimeout",
+ onTransactionTimeout);
+
+function finish()
+{
+ window.close();
+}
+
+async function onload()
+{
+ // Before actually running tests, we disable auto-dir scrolling, becasue the
+ // tests in this file are meant to test scrolling transactions, not meant to
+ // test default actions for wheel events, so we simply disabled auto-dir
+ // scrolling, which are well tested in
+ // dom/events/test/window_wheel_default_action.html.
+ await SpecialPowers.pushPrefEnv({"set": [["mousewheel.autodir.enabled",
+ false]]});
+
+ runNextTestList();
+}
+
+function onunload()
+{
+ resetTimeoutPrefs();
+ gPrefSvc.clearUserPref(kPrefSmoothScroll);
+ gPrefSvc.clearUserPref(kPrefTestEventsAsyncEnabled);
+ disableNonTestMouseEvents(false);
+ SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+ window.arguments[0].SimpleTest.finish();
+}
+
+function offsetForRootView()
+{
+ let rootViewRect = gRootView.getBoundingClientRect();
+ let subView1Rect = gSubView1.getBoundingClientRect();
+ return {
+ x: (subView1Rect.left - rootViewRect.left) / 2,
+ y: (subView1Rect.top - rootViewRect.top) / 2,
+ }
+}
+
+function _offsetFor(aSubView)
+{
+ let rootViewRect = gRootView.getBoundingClientRect();
+ let subViewRect = aSubView.getBoundingClientRect();
+ return {
+ x: subViewRect.left - rootViewRect.left + subViewRect.width / 2,
+ y: subViewRect.top - rootViewRect.top + subViewRect.height / 2,
+ }
+}
+
+function offsetForSubView1()
+{
+ return _offsetFor(gSubView1);
+}
+
+function offsetForSubView2()
+{
+ return _offsetFor(gSubView2);
+}
+
+function offsetForSubView3()
+{
+ return _offsetFor(gSubView3);
+}
+
+/**
+ * Define the tests here:
+ * Scrolls are processed async always. Therefore, we need to call all tests
+ * by timer. gTestLists is array of testing lists. In other words, an item
+ * of gTestList is a group of one or more testing. Each items has following
+ * properties:
+ *
+ * - retryWhenTransactionTimeout
+ * The testing of wheel transaction might be fialed randomly by
+ * timeout. Then, automatically the failed test list will be retested
+ * automatically only this number of times.
+ *
+ * - steps
+ * This property is array of testing. Each steps must have following
+ * properties at least.
+ *
+ * - func
+ * This property means function which will be called via
+ * |setTimeout|. The function cannot have params. If you need
+ * some additional parameters, you can specify some original
+ * properties for the test function. If you do so, you should
+ * document it in the testing function.
+ * - delay
+ * This property means delay time until the function to be called.
+ * I.e., the value used for the second param of |setTimeout|.
+ *
+ * And also you need one more property when you call a testing function.
+ *
+ * - description
+ * This property is description of the test. This is used for
+ * logging.
+ *
+ * At testing, you can access to current step via |gCurrentTest|.
+ */
+
+var gTestLists;
+function initTestList()
+{
+ gTestLists = [
+ /**************************************************************************
+ * Continuous scrolling test for |gRootView|
+ * |gRootView| has both scrollbars and it has three children which are
+ * |gSubView1|, |gSubView2| and |gSubView3|. They have scrollbars. If
+ * the current transaction targets |gRootView|, other children should not
+ * be scrolled even if the wheel events are fired on them.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Vertical wheel events should scroll |gRootView| even if the position
+ // of wheel events in a child view which has scrollbar.
+ { func: testContinuousScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Continuous scrolling test for root view (vertical/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Continuous scrolling test for root view (vertical/backward)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Horizontal wheel events should scroll |gRootView| even if the
+ // position of wheel events in a child view which has scrollbar.
+ { func: testContinuousScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Continuous scrolling test for root view (horizontal/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Continuous scrolling test for root view (horizontal/backward)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Continuous scrolling test for |gSubView1|
+ * |gSubView1| has both scrollbars.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Vertical wheel events should scroll |gSubView1|.
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Continuous scrolling test for sub view 1 (vertical/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: true, expectedView: gSubView1,
+ description: "Continuous scrolling test for sub view 1 (vertical/backward)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Horitontal wheel events should scroll |gSubView1|.
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ description: "Continuous scrolling test for sub view 1 (horizontal/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: false, expectedView: gSubView1,
+ description: "Continuous scrolling test for sub view 1 (horizontal/backward)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Continuous scrolling test for |gSubView2|
+ * |gSubView2| has only vertical scrollbar.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Vertical wheel events should scroll |gSubView2|.
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView2,
+ isForward: true, isVertical: true, expectedView: gSubView2,
+ description: "Continuous scrolling test for sub view 2 (vertical/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView2,
+ isForward: false, isVertical: true, expectedView: gSubView2,
+ description: "Continuous scrolling test for sub view 2 (vertical/backward)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Horizontal wheel events should scroll its nearest scrollable ancestor
+ // view, i.e., it is |gRootView|.
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView2,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Continuous scrolling test for sub view 2 (horizontal/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView2,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Continuous scrolling test for sub view 2 (horizontal/backward)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Continuous scrolling test for |gSubView3|
+ * |gSubView3| has only horizontal scrollbar.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Vertical wheel events should scroll its nearest scrollable ancestor
+ // view, i.e., it is |gRootView|.
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView3,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Continuous scrolling test for sub view 3 (vertical/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView3,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Continuous scrolling test for sub view 3 (vertical/backward)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Horitontal wheel events should scroll |gSubView3|.
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView3,
+ isForward: true, isVertical: false, expectedView: gSubView3,
+ description: "Continuous scrolling test for sub view 3 (horizontal/forward)" },
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView3,
+ isForward: false, isVertical: false, expectedView: gSubView3,
+ description: "Continuous scrolling test for sub view 3 (horizontal/backward)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Don't reset transaction by a different direction wheel event
+ * Even if a wheel event doesn't same direction as last wheel event, the
+ * current transaction should not be reset.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical -> Horizontal
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView| by a vertical wheel
+ // event.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Don't reset transaction by a different direction wheel event (1-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Don't reset transaction by a different direction wheel event (1-2)" },
+ // Send a horizontal wheel event over |gSubView1| but |gRootView| should
+ // be scrolled.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Don't reset transaction by a different direction wheel event (1-3)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal -> Vertical
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView| by a horizontal wheel
+ // event.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Don't reset transaction by a different direction wheel event (2-1)" },
+ // Scroll back to left-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Don't reset transaction by a different direction wheel event (2-2)" },
+ // Send a vertical wheel event over |gSubView1| but |gRootView| should
+ // be scrolled.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Don't reset transaction by a different direction wheel event (2-3)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Don't reset transaction even if a wheel event cannot scroll
+ * Even if a wheel event cannot scroll to specified direction in the
+ * current target view, the transaction should not be reset. E.g., there
+ * are some devices which can scroll obliquely. If so, probably, users
+ * cannot input only intended direction.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // A view only has vertical scrollbar case.
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gSubView2|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView2,
+ isForward: true, isVertical: true, expectedView: gSubView2,
+ description: "Don't reset transaction even if a wheel event cannot scroll (1-1)" },
+ // |gSubView2| doesn't have horizontal scrollbar but should not scroll
+ // any views.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView2,
+ isForward: true, isVertical: false, expectedView: null,
+ description: "Don't reset transaction even if a wheel event cannot scroll (1-2)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // A view only has horizontal scrollbar case.
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gSubView3|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView3,
+ isForward: true, isVertical: false, expectedView: gSubView3,
+ description: "Don't reset transaction even if a wheel event cannot scroll (2-1)" },
+ // |gSubView3| doesn't have vertical scrollbar but should not scroll any
+ // views.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView3,
+ isForward: true, isVertical: true, expectedView: null,
+ description: "Don't reset transaction even if a wheel event cannot scroll (2-2)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Reset transaction by mouse down/mouse up events
+ * Mouse down and mouse up events should cause resetting the current
+ * transaction.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by mouse down/mouse up events (v-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by mouse down/mouse up events (v-2)" },
+ // Send mouse button events which should reset the current transaction.
+ // So, the next wheel event should scroll |gSubView1|.
+ { func: sendMouseButtonEvents, delay: 0,
+ description: "sendMouseButtonEvents" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Reset transaction by mouse down/mouse up events (v-3)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Reset transaction by mouse down/mouse up events (h-1)" },
+ // Scroll back to left-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Reset transaction by mouse down/mouse up events (h-2)" },
+ // Send mouse button events which should reset the current transaction.
+ // So, the next wheel event should scroll |gSubView1|.
+ { func: sendMouseButtonEvents, delay: 0,
+ description: "sendMouseButtonEvents" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ description: "Reset transaction by mouse down/mouse up events (h-3)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Reset transaction by a key event
+ * A key event should cause resetting the current transaction.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a key event (v-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a key event (v-2)" },
+ // Send a key event which should reset the current transaction. So, the
+ // next wheel event should scroll |gSubView1|.
+ { func: sendKeyEvents, delay: 0, key: "a",
+ description: "sendKeyEvents" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Reset transaction by a key event (v-3)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Reset transaction by a key event (h-1)" },
+ // Scroll back to left-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Reset transaction by a key event (h-2)" },
+ // Send a key event which should reset the current transaction. So, the
+ // next wheel event should scroll |gSubView1|.
+ { func: sendKeyEvents, delay: 0, key: "a",
+ description: "sendKeyEvents" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ description: "Reset transaction by a key event (h-3)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Reset transaction by a mouse move event
+ * A mouse move event can cause reseting the current transaction even if
+ * mouse cursor is inside the target view of current transaction. Only
+ * when a wheel event is fired after |gIgnoreMoveDelay| milliseconds since
+ * the first mouse move event from last wheel event, the transaction
+ * should be reset.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a mouse move event (v-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a mouse move event (v-2)" },
+ // Send a mouse move event immediately after last wheel event, then,
+ // current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: 0, offset: offsetForSubView1,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-3)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-4)" },
+ // Send a mouse move event after |gIgnoreMoveDelay| milliseconds since
+ // last wheel event, then, current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: gEnoughForIgnoreMoveDelay,
+ offset: offsetForSubView1,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-5)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-6)" },
+ // Send a wheel event after |gIgnoreMoveDelay| milliseconds since last
+ // mouse move event but it is fired immediately after the last wheel
+ // event, then, current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: 0, offset: offsetForSubView1,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
+ offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-7)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (v-8)" },
+ // Send a wheel event after |gIgnoreMoveDelay| milliseconds have passed
+ // since last mouse move event which is fired after |gIgnoreMoveDelay|
+ // milliseconds since last wheel event, then, current transaction should
+ // be reset.
+ { func: sendMouseMoveEvent, delay: gEnoughForIgnoreMoveDelay,
+ offset: offsetForSubView1,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
+ offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ canFailRandomly: { possibleView: gRootView },
+ description: "Reset transaction by a mouse move event (v-9)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-2)" },
+ // Send a mouse move event immediately after last wheel event, then,
+ // current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: 0, offset: offsetForSubView1,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-3)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-4)" },
+ // Send a mouse move event after |gIgnoreMoveDelay| milliseconds since
+ // last wheel event, then, current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: gEnoughForIgnoreMoveDelay,
+ offset: offsetForSubView1,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-5)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-6)" },
+ // Send a wheel event after |gIgnoreMoveDelay| milliseconds since last
+ // mouse move event but it is fired immediately after the last wheel
+ // event, then, current transaction should be kept.
+ { func: sendMouseMoveEvent, delay: 0, offset: offsetForSubView1,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
+ offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-7)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Reset transaction by a mouse move event (h-8)" },
+ // Send a wheel event after |gIgnoreMoveDelay| milliseconds have passed
+ // since last mouse move event which is fired after |gIgnoreMoveDelay|
+ // milliseconds since last wheel event, then, current transaction should
+ // be reset.
+ { func: sendMouseMoveEvent, delay: gEnoughForIgnoreMoveDelay,
+ offset: offsetForSubView1,
+ description: "sendMouseMoveEvent" },
+ { func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
+ offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ canFailRandomly: { possibleView: gRootView },
+ description: "Reset transaction by a mouse move event (h-9)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Reset transaction by a mouse move event on outside of view
+ * When mouse cursor is moved to outside of the current target view, the
+ * transaction should be reset immediately.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gSubView1|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Reset transaction by a mouse move event on outside of view (v-1)" },
+ // Send mouse move event over |gRootView|.
+ { func: sendMouseMoveEvent, delay: 0, offset: offsetForRootView,
+ description: "sendMouseMoveEvent" },
+ // Send Wheel event over |gRootView| which should be scrolled.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a mouse move event on outside of view (v-2)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Create a transaction which targets |gSubView1|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Reset transaction by a mouse move event on outside of view (h-1)" },
+ // Send mouse move event over |gRootView|.
+ { func: sendMouseMoveEvent, delay: 0, offset: offsetForRootView,
+ description: "sendMouseMoveEvent" },
+ // Send Wheel event over |gRootView| which should be scrolled.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Reset transaction by a mouse move event on outside of view (h-2)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Timeout test
+ * A view should not be scrolled during another to be transaction for
+ * another view scrolling. However, a wheel event which is sent after
+ * timeout, a view which is under the mouse cursor should be scrolled.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // First, create a transaction which should target the |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Timeout test (v-1)" },
+ // Scroll back to top-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ description: "Timeout test (v-2)" },
+ // A wheel event over |gSubView1| should not scroll it during current
+ // transaction.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Timeout test (v-3)" },
+ // Scroll back to top-most again.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: true, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Timeout test (v-4)" },
+ // A wheel event over |gSubView1| after timeout should scroll
+ // |gSubView1|.
+ { func: testOneTimeScroll, delay: gEnoughForTimeout,
+ offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ isTimeoutTesting: true,
+ description: "Timeout test (v-5)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // First, create a transaction which should target the |gRootView|.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Timeout test (h-1)" },
+ // Scroll back to left-most for easy cursor position specifying.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForRootView,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ description: "Timeout test (h-2)" },
+ // A wheel event over |gSubView1| should not scroll it during current
+ // transaction.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Timeout test (h-3)" },
+ // Scroll back to left-most again.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: false, isVertical: false, expectedView: gRootView,
+ canFailRandomly: { possibleView: gSubView1 },
+ description: "Timeout test (h-4)" },
+ // A wheel event over |gSubView1| after timeout should scroll
+ // |gSubView1|.
+ { func: testOneTimeScroll, delay: gEnoughForTimeout,
+ offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ isTimeoutTesting: true,
+ description: "Timeout test (h-5)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Timeout test even with many wheel events
+ * This tests whether timeout is occurred event if wheel events are sent.
+ * The transaction should not be updated by non-scrollable wheel events.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Vertical case
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Scroll |gSubView1| to bottom-most.
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ description: "Timeout test even with many wheel events (v-1)" },
+ // Don't scroll any views before timeout.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: null,
+ canFailRandomly: { possibleView: gRootView },
+ description: "Timeout test even with many wheel events (v-2)" },
+ // Recreate a transaction which is scrolling |gRootView| after time out.
+ { func: testRestartScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gRootView,
+ description: "Timeout test even with many wheel events (v-3)" }
+ ]
+ },
+
+
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ // Horizontal case
+ { func: initElements, delay: 0, forVertical: false,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ // Scroll |gSubView1| to right-most.
+ { func: testContinuousScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ description: "Timeout test even with many wheel events (h-1)" },
+ // Don't scroll any views before timeout.
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: null,
+ canFailRandomly: { possibleView: gRootView },
+ description: "Timeout test even with many wheel events (h-2)" },
+ // Recreate a transaction which is scrolling |gRootView| after time out.
+ { func: testRestartScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gRootView,
+ description: "Timeout test even with many wheel events (h-3)" }
+ ]
+ },
+
+
+ /**************************************************************************
+ * Very large scrolling wheel event
+ * If the delta value is larger than the scrolling page size, it should be
+ * scrolled only one page instead of the delta value.
+ **************************************************************************/
+ { retryWhenTransactionTimeout: 5,
+ steps: [
+ { func: initElements, delay: 0, forVertical: true,
+ description: "initElements" },
+ { func: clearWheelTransaction, delay: 0,
+ description: "clearWheelTransaction" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ delta: 5000,
+ description: "Very large delta scrolling (v-1)" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: true, expectedView: gSubView1,
+ delta: 5000,
+ description: "Very large delta scrolling (v-2)" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ delta: 5000,
+ description: "Very large delta scrolling (h-1)" },
+ { func: testOneTimeScroll, delay: 0, offset: offsetForSubView1,
+ isForward: true, isVertical: false, expectedView: gSubView1,
+ delta: 5000,
+ description: "Very large delta scrolling (h-2)" }
+ ]
+ }
+ ];
+}
+
+/******************************************************************************
+ * Actions for preparing tests
+ ******************************************************************************/
+
+function initElements()
+{
+ _clearTimer();
+
+ function resetScrollPosition(aElement)
+ {
+ aElement.scrollTop = 0;
+ aElement.scrollLeft = 0;
+ }
+
+ const kDisplay = gCurrentTest.forVertical ? "block" : "inline-block";
+ gSubView1.style.display = kDisplay;
+ gSubView2.style.display = kDisplay;
+ gSubView3.style.display = kDisplay;
+
+ resetScrollPosition(gRootView);
+ resetScrollPosition(gSubView1);
+ resetScrollPosition(gSubView2);
+ resetScrollPosition(gSubView3);
+ _getDOMWindowUtils(window).advanceTimeAndRefresh(0);
+
+ runNextTestStep();
+}
+
+function clearWheelTransaction()
+{
+ _clearTimer();
+ _clearTransaction();
+ runNextTestStep();
+}
+
+function sendKeyEvents()
+{
+ _clearTimer();
+ synthesizeKey(gCurrentTest.key, {}, window);
+ runNextTestStep();
+}
+
+function sendMouseButtonEvents()
+{
+ _clearTimer();
+ synthesizeMouse(gRootView, -1, -1, { type:"mousedown" }, window);
+ synthesizeMouse(gRootView, -1, -1, { type:"mouseup" }, window);
+ runNextTestStep();
+}
+
+function sendMouseMoveEvent()
+{
+ _clearTimer();
+ _fireMouseMoveEvent(gCurrentTest.offset());
+ runNextTestStep();
+}
+
+/******************************************************************************
+ * Utilities for testing functions
+ ******************************************************************************/
+
+function _clearTransaction()
+{
+ synthesizeMouse(gRootView, -1, -1, { type:"mousedown" }, window);
+ synthesizeMouse(gRootView, -1, -1, { type:"mouseup" }, window);
+}
+
+function _saveScrollPositions()
+{
+ function save(aElement)
+ {
+ aElement.prevTop = aElement.scrollTop;
+ aElement.prevLeft = aElement.scrollLeft;
+ }
+ save(gRootView);
+ save(gSubView1);
+ save(gSubView2);
+ save(gSubView3);
+}
+
+function _fireMouseMoveEvent(aOffset)
+{
+ synthesizeMouse(gRootView, aOffset.x, aOffset.y, { type:"mousemove" }, window);
+}
+
+function _fireWheelScrollEvent(aOffset, aIsVertical, aForward, aDelta)
+{
+ var event = { deltaMode: WheelEvent.DOM_DELTA_LINE };
+ if (aIsVertical) {
+ event.deltaY = aForward ? aDelta : -aDelta;
+ } else {
+ event.deltaX = aForward ? aDelta : -aDelta;
+ }
+ sendWheelAndPaint(gRootView, aOffset.x, aOffset.y, event, null, window);
+}
+
+function _canScroll(aElement, aIsVertical, aForward)
+{
+ if (aIsVertical) {
+ if (!aForward)
+ return aElement.scrollTop > 0;
+ return aElement.scrollHeight > aElement.scrollTop + aElement.clientHeight;
+ }
+ if (!aForward)
+ return aElement.scrollLeft > 0;
+ return aElement.scrollWidth > aElement.scrollLeft + aElement.clientWidth;
+}
+
+const kNotScrolled = 0;
+const kScrolledToTop = 1;
+const kScrolledToBottom = 2;
+const kScrolledToLeft = 4;
+const kScrolledToRight = 8;
+
+const kScrolledVertical = kScrolledToTop | kScrolledToBottom;
+const kScrolledHorizontal = kScrolledToLeft | kScrolledToRight;
+
+function _getScrolledState(aElement)
+{
+ var ret = kNotScrolled;
+ if (aElement.scrollTop != aElement.prevTop) {
+ ret |= aElement.scrollTop < aElement.prevTop ? kScrolledToTop :
+ kScrolledToBottom;
+ }
+ if (aElement.scrollLeft != aElement.prevLeft) {
+ ret |= aElement.scrollLeft < aElement.prevLeft ? kScrolledToLeft :
+ kScrolledToRight;
+ }
+ return ret;
+}
+
+function _getExpectedScrolledState()
+{
+ // eslint-disable-next-line no-nested-ternary
+ return gCurrentTest.isVertical ?
+ gCurrentTest.isForward ? kScrolledToBottom : kScrolledToTop :
+ gCurrentTest.isForward ? kScrolledToRight : kScrolledToLeft;
+}
+
+function _getScrolledStateText(aScrolledState)
+{
+ if (aScrolledState == kNotScrolled)
+ return "Not scrolled";
+
+ var s = "scrolled to ";
+ if (aScrolledState & kScrolledVertical) {
+ s += aScrolledState & kScrolledToTop ? "backward" : "forward";
+ s += " (vertical)"
+ if (aScrolledState & kScrolledHorizontal)
+ s += " and to ";
+ }
+ if (aScrolledState & kScrolledHorizontal) {
+ s += aScrolledState & kScrolledToLeft ? "backward" : "forward";
+ s += " (horizontal)"
+ }
+ return s;
+}
+
+function _getCurrentTestList()
+{
+ return gTestLists[gCurrentTestListStatus.nextListIndex - 1];
+}
+
+function _clearTimer()
+{
+ clearTimeout(gTimer);
+ gTimer = 0;
+}
+
+/******************************************************************************
+ * Testing functions
+ ******************************************************************************/
+
+/**
+ * Note that testing functions must set following variables:
+ *
+ * gCurrentTest.repeatTest: See comment in |continueTest|.
+ * gCurrentTest.autoRepeatDelay: See comment in |continueTest|.
+ * gListenScrollEvent: When this is not true, the event handlers ignores the
+ * events.
+ */
+
+function testContinuousScroll()
+{
+ /**
+ * Testing continuous scrolling. This function synthesizes a wheel event. If
+ * the test was success, this function will be recalled automatically.
+ * And when a generating wheel event cannot scroll the expected view, this
+ * function fires the wheel event only one time.
+ *
+ * @param gCurrentTest.offset
+ * A function to compute the cursor position of firing wheel event.
+ * The values are offset from |gRootView|.
+ * @param gCurrentTest.isVertical
+ * Whether the wheel event is for virtical scrolling or horizontal.
+ * @param gCurrentTest.isForward
+ * Whether the wheel event is to forward or to backward.
+ * @param gCurrentTest.expectedView
+ * The expected view which will be scrolled by wheel event. This
+ * value must not be null.
+ */
+
+ _clearTimer();
+ _saveScrollPositions();
+ if (!gCurrentTest.expectedView) {
+ runNextTestStep();
+ return;
+ }
+
+ gLitesnEvents = kListenEvent_All;
+ gCurrentTest.repeatTest = true;
+ gCurrentTest.autoRepeatDelay = 0;
+
+ if (!_canScroll(gCurrentTest.expectedView,
+ gCurrentTest.isVertical, gCurrentTest.isForward)) {
+ gCurrentTest.expectedView = null;
+ }
+ var delta = gCurrentTest.delta ? gCurrentTest.delta : 4;
+ _fireWheelScrollEvent(gCurrentTest.offset(),
+ gCurrentTest.isVertical, gCurrentTest.isForward, delta);
+}
+
+function testOneTimeScroll()
+{
+ /**
+ * Testing one wheel event. |runNextTestStep| will be called immediately
+ * after this function by |onScrollView| or |onTimeout|.
+ *
+ * @param gCurrentTest.offset
+ * A function to compute the cursor position of firing wheel event.
+ * The values are offset from |gRootView|.
+ * @param gCurrentTest.isVertical
+ * Whether the wheel event is for virtical scrolling or horizontal.
+ * @param gCurrentTest.isForward
+ * Whether the wheel event is to forward or to backward.
+ * @param gCurrentTest.expectedView
+ * The expected view which will be scrolled by wheel event. This
+ * value can be null. It means any views should not be scrolled.
+ */
+
+ _clearTimer();
+ _saveScrollPositions();
+
+ gLitesnEvents = kListenEvent_All;
+ gCurrentTest.repeatTest = false;
+ gCurrentTest.autoRepeatDelay = 0;
+
+ var delta = gCurrentTest.delta ? gCurrentTest.delta : 4;
+ _fireWheelScrollEvent(gCurrentTest.offset(),
+ gCurrentTest.isVertical, gCurrentTest.isForward, delta);
+}
+
+function testRestartScroll()
+{
+ /**
+ * Testing restart to scroll in expected view after timeout from the current
+ * transaction. This function recall this itself until to success this test
+ * or timeout from this test.
+ *
+ * @param gCurrentTest.offset
+ * A function to compute the cursor position of firing wheel event.
+ * The values are offset from |gRootView|.
+ * @param gCurrentTest.isVertical
+ * Whether the wheel event is for virtical scrolling or horizontal.
+ * @param gCurrentTest.isForward
+ * Whether the wheel event is to forward or to backward.
+ * @param gCurrentTest.expectedView
+ * The expected view which will be scrolled by wheel event. This
+ * value must not be null.
+ */
+
+ _clearTimer();
+ _saveScrollPositions();
+
+ if (!gCurrentTest.wasTransactionTimeout) {
+ gCurrentTest.repeatTest = true;
+ gCurrentTest.autoRepeatDelay = gTimeout / 3;
+ gLitesnEvents = kListenEvent_All;
+ gCurrentTest.isTimeoutTesting = true;
+ if (gCurrentTest.expectedView) {
+ gCurrentTest.expectedViewAfterTimeout = gCurrentTest.expectedView;
+ gCurrentTest.expectedView = null;
+ }
+ } else {
+ gCurrentTest.repeatTest = false;
+ gCurrentTest.autoRepeatDelay = 0;
+ gLitesnEvents = kListenEvent_All;
+ gCurrentTest.isTimeoutTesting = false;
+ gCurrentTest.expectedView = gCurrentTest.expectedViewAfterTimeout;
+ }
+
+ var delta = gCurrentTest.delta ? gCurrentTest.delta : 4;
+ _fireWheelScrollEvent(gCurrentTest.offset(),
+ gCurrentTest.isVertical, gCurrentTest.isForward, delta);
+}
+
+/******************************************************************************
+ * Event handlers
+ ******************************************************************************/
+
+function onScrollView(aEvent)
+{
+ /**
+ * Scroll event handler of |gRootView|, |gSubView1|, |gSubView2| and
+ * |gSubView3|. If testing is failed, this function cancels all left tests.
+ * For checking the event is expected, the event firer must call
+ * |_saveScrollPositions|.
+ *
+ * @param gCurrentTest.expectedView
+ * The expected view which should be scrolled by the wheel event.
+ * This value can be null. It means any views should not be
+ * scrolled.
+ * @param gCurrentTest.isVertical
+ * The expected view should be scrolled vertical or horizontal.
+ * @param gCurrentTest.isForward
+ * The expected view should be scrolled to forward or backward.
+ * @param gCurrentTest.canFailRandomly
+ * If this is not undefined, this test can fail by unexpected view
+ * scrolling which is caused by unexpected timeout. If this is
+ * defined, |gCurrentTest.possibleView| must be set. If the view is
+ * same as the event target, the failure can be random. At this
+ * time, we should retry the current test list.
+ */
+
+ if (!(gLitesnEvents & kListenEvent_OnScroll))
+ return;
+
+ // Now testing a timeout, but a view is scrolled before timeout.
+ if (gCurrentTest.isTimeoutTesting && !gCurrentTest.wasTransactionTimeout) {
+ is(aEvent.target.id, "",
+ "The view scrolled before timeout (the expected view after timeout is " +
+ gCurrentTest.expectedView ? gCurrentTest.expectedView.id : "null" +
+ "): " + gCurrentTest.description);
+ runNextTestList();
+ return;
+ }
+
+ // Check whether the scrolled event should be fired or not.
+ if (!gCurrentTest.expectedView) {
+ is(aEvent.target.id, "",
+ "no views should be scrolled (" +
+ _getScrolledStateText(_getScrolledState(aEvent.target)) + "): " +
+ gCurrentTest.description);
+ runNextTestList();
+ return;
+ }
+
+ // Check whether the scrolled view is expected or not.
+ if (aEvent.target != gCurrentTest.expectedView) {
+ // If current test can fail randomly and the possible view is same as the
+ // event target, this failure may be caused by unexpected timeout.
+ // At this time, we should retry the current tests with slower settings.
+ if (gCurrentTest.canFailRandomly &&
+ gCurrentTest.canFailRandomly.possibleView == aEvent.target &&
+ gCurrentTestListStatus.retryWhenTransactionTimeout > 0) {
+ gCurrentTestListStatus.retryWhenTransactionTimeout--;
+ retryCurrentTestList();
+ return;
+ }
+ is(aEvent.target.id, gCurrentTest.expectedView.id,
+ "wrong view was scrolled: " + gCurrentTest.description);
+ runNextTestList();
+ return;
+ }
+
+ // Check whether the scrolling direction is expected or not.
+ var expectedState = _getExpectedScrolledState();
+ var currentState = _getScrolledState(aEvent.target);
+ if (expectedState != currentState) {
+ is(_getScrolledStateText(currentState),
+ _getScrolledStateText(expectedState),
+ "scrolled to wrong direction: " + gCurrentTest.description);
+ runNextTestList();
+ return;
+ }
+
+ ok(true, "passed: " + gCurrentTest.description);
+ continueTest();
+}
+
+function onMouseScrollFailed()
+{
+ /**
+ * Scroll failed event handler. If testing is failed, this function cancels
+ * all remains of current test-list, and go to next test-list.
+ *
+ * NOTE: This event is fired immediately after |_fireWheelScrollEvent|.
+ *
+ * @param gCurrentTest.expectedView
+ * The expected view which should be scrolled by the wheel event.
+ * This value can be null. It means any views should not be
+ * scrolled. When this is not null, this event means the test may
+ * be failed.
+ */
+
+ if (!(gLitesnEvents & kListenEvent_OnScrollFailed))
+ return;
+
+ ok(!gCurrentTest.expectedView,
+ "failed to scroll on current target: " + gCurrentTest.description);
+ if (gCurrentTest.expectedView) {
+ runNextTestList();
+ return;
+ }
+
+ continueTest();
+}
+
+function onTransactionTimeout()
+{
+ /**
+ * Scroll transaction timeout event handler. If the timeout is unexpected,
+ * i.e., |gCurrentTest.isTimeoutTesting| is not true, this function retry
+ * the current test-list. However, if the current test-list failed by timeout
+ * |gCurrentTestListStatus.retryWhenTransactionTimeout| times already, marking
+ * to failed the current test-list, and go to next test-list.
+ *
+ * @param gCurrentTest.expectedView
+ * The expected view which should be scrolled by the wheel event.
+ * This value can be null. It means any views should not be
+ * scrolled. When this is not null, this event means the testing may
+ * be failed.
+ * @param gCurrentTest.isTimeoutTesting
+ * If this value is true, the current testing have waited this
+ * event. Otherwise, the testing may be failed.
+ * @param gCurrentTestListStatus.retryWhenTransactionTimeout
+ * If |gCurrentTest.isTimeoutTesting| is not true but this event is
+ * fired, the failure may be randomly. Then, this event handler
+ * retry to test the current test-list until this cound will be zero.
+ */
+
+ if (!gCurrentTest.isTimeoutTesting &&
+ gCurrentTestListStatus.retryWhenTransactionTimeout > 0) {
+ gCurrentTestListStatus.retryWhenTransactionTimeout--;
+ // retry current test list
+ retryCurrentTestList();
+ return;
+ }
+
+ gCurrentTest.wasTransactionTimeout = true;
+
+ if (!(gLitesnEvents & kListenEvent_OnTransactionTimeout))
+ return;
+
+ ok(gCurrentTest.isTimeoutTesting,
+ "transaction timeout: " + gCurrentTest.description);
+ if (!gCurrentTest.isTimeoutTesting) {
+ runNextTestList();
+ return;
+ }
+
+ continueTest();
+}
+
+/******************************************************************************
+ * Main function for this tests
+ ******************************************************************************/
+
+function runNextTestStep()
+{
+ // When this is first time or the current test list is finised, load next
+ // test-list.
+ _clearTimer();
+ if (!gCurrentTest)
+ runNextTestList();
+ else
+ runTestStepAt(gCurrentTestListStatus.nextStepIndex);
+}
+
+function runNextTestList()
+{
+ _clearTimer();
+
+ gLitesnEvents = kListenEvent_None;
+ _clearTransaction();
+ resetTimeoutPrefs();
+ if (gCurrentTestListStatus.nextListIndex >= gTestLists.length) {
+ finish();
+ return;
+ }
+
+ gCurrentTestListStatus.nextListIndex++;
+ gCurrentTestListStatus.retryWhenTransactionTimeout =
+ _getCurrentTestList().retryWhenTransactionTimeout;
+ runTestStepAt(0);
+}
+
+function runTestStepAt(aStepIndex)
+{
+ _clearTimer();
+
+ disableNonTestMouseEvents(true);
+
+ // load a step of testing.
+ gCurrentTestListStatus.nextStepIndex = aStepIndex;
+ gCurrentTest =
+ _getCurrentTestList().steps[gCurrentTestListStatus.nextStepIndex++];
+ if (gCurrentTest) {
+ gCurrentTest.wasTransactionTimeout = false;
+ gTimer = setTimeout(gCurrentTest.func, gCurrentTest.delay);
+ } else {
+ // If current test-list doesn't have more testing, go to next test-list
+ // after cleaning up the current transaction.
+ _clearTransaction();
+ runNextTestList();
+ }
+}
+
+function retryCurrentTestList()
+{
+ _clearTimer();
+
+ gLitesnEvents = kListenEvent_None;
+ _clearTransaction();
+ ok(true, "WARNING: retry current test-list...");
+ growUpTimeoutPrefs(); // retry the test with longer timeout settings.
+ runTestStepAt(0);
+}
+
+function continueTest()
+{
+ /**
+ * This function is called from an event handler when a test succeeded.
+ *
+ * @param gCurrentTest.repeatTest
+ * When this is true, onScrollView calls |gCurrentTest.func|. So,
+ * same test can repeat. Otherwise, this calls |runNextTestStep|.
+ * @param gCurrentTest.autoRepeatDelay
+ * The delay value in milliseconds, this is used to call
+ * |gCurrentTest.func| via |setTimeout|.
+ */
+
+ _clearTimer();
+ gLitesnEvents = kListenEvent_OnTransactionTimeout;
+
+ // We should call each functions via setTimeout. Because sometimes this test
+ // is broken by stack overflow.
+ if (gCurrentTest.repeatTest) {
+ gTimer = setTimeout(gCurrentTest.func, gCurrentTest.autoRepeatDelay);
+ } else {
+ gTimer = setTimeout(runNextTestStep, 0);
+ }
+}
+
+]]>
+</script>
+
+</window>