diff options
Diffstat (limited to '')
-rw-r--r-- | widget/tests/window_wheeltransaction.xhtml | 1569 |
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> |