gecko/widget/tests/window_wheeltransaction.xul
Markus Stange 615c2afa27 Bug 1209970 - Fire scroll events early in the refresh tick. r=mats
With APZ we want to be firing scroll events to content more consistently, so
we tie them to the refresh driver tick rather than firing them on paint or
haphazardly on the next spin of the event loop.

Patch by Markus Stange, test fixes by Kartikaya Gupta
2015-12-17 17:19:30 -05:00

1561 lines
64 KiB
XML

<?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 type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
<script type="application/javascript"
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.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
}
function is(aLeft, aRight, aMessage)
{
window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
}
function isnot(aLeft, aRight, aMessage)
{
window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
}
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 = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);
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, false);
gRootView.addEventListener("MozMouseScrollTransactionTimeout",
onTransactionTimeout, false);
function finish()
{
window.close();
}
function onload()
{
runNextTestList();
}
function onunload()
{
resetTimeoutPrefs();
gPrefSvc.clearUserPref(kPrefSmoothScroll);
gPrefSvc.clearUserPref(kPrefTestEventsAsyncEnabled);
disableNonTestMouseEvents(false);
SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
window.opener.wrappedJSObject.SimpleTest.finish();
}
const kSubView1Offset = { x: 20, y: 20 };
const kSubView2Offset = { x: 20, y: 20 + 100 + 20 };
const kSubView3Offset = { x: 20, y: 20 + (100 + 20) * 2 };
function _getSubViewTestPtForV(aPt)
{
return { x: aPt.x + 10, y: aPt.y + 10 };
}
const kPtInRootViewForV = { x: kSubView1Offset.x + 10,
y: kSubView1Offset.y - 10 };
const kPtInSubView1ForV = _getSubViewTestPtForV(kSubView1Offset);
const kPtInSubView2ForV = _getSubViewTestPtForV(kSubView2Offset);
const kPtInSubView3ForV = _getSubViewTestPtForV(kSubView3Offset);
function _convertTestPtForH(aPt)
{
return { x: aPt.y, y: aPt.x };
}
const kPtInRootViewForH = _convertTestPtForH(kPtInRootViewForV);
const kPtInSubView1ForH = _convertTestPtForH(kPtInSubView1ForV);
const kPtInSubView2ForH = _convertTestPtForH(kPtInSubView2ForV);
const kPtInSubView3ForH = _convertTestPtForH(kPtInSubView3ForV);
/**
* 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: kPtInRootViewForV,
isForward: true, isVertical: true, expectedView: gRootView,
description: "Continuous scrolling test for root view (vertical/forward)" },
{ func: testContinuousScroll, delay: 0, offset: kPtInRootViewForV,
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: kPtInRootViewForH,
isForward: true, isVertical: false, expectedView: gRootView,
description: "Continuous scrolling test for root view (horizontal/forward)" },
{ func: testContinuousScroll, delay: 0, offset: kPtInRootViewForH,
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: kPtInSubView1ForV,
isForward: true, isVertical: true, expectedView: gSubView1,
description: "Continuous scrolling test for sub view 1 (vertical/forward)" },
{ func: testContinuousScroll, delay: 0, offset: kPtInSubView1ForV,
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: kPtInSubView1ForH,
isForward: true, isVertical: false, expectedView: gSubView1,
description: "Continuous scrolling test for sub view 1 (horizontal/forward)" },
{ func: testContinuousScroll, delay: 0, offset: kPtInSubView1ForH,
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: kPtInSubView2ForV,
isForward: true, isVertical: true, expectedView: gSubView2,
description: "Continuous scrolling test for sub view 2 (vertical/forward)" },
{ func: testContinuousScroll, delay: 0, offset: kPtInSubView2ForV,
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: kPtInSubView2ForH,
isForward: true, isVertical: false, expectedView: gRootView,
description: "Continuous scrolling test for sub view 2 (horizontal/forward)" },
{ func: testContinuousScroll, delay: 0, offset: kPtInSubView2ForH,
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: kPtInSubView3ForV,
isForward: true, isVertical: true, expectedView: gRootView,
description: "Continuous scrolling test for sub view 3 (vertical/forward)" },
{ func: testContinuousScroll, delay: 0, offset: kPtInSubView3ForV,
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: kPtInSubView3ForH,
isForward: true, isVertical: false, expectedView: gSubView3,
description: "Continuous scrolling test for sub view 3 (horizontal/forward)" },
{ func: testContinuousScroll, delay: 0, offset: kPtInSubView3ForH,
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: kPtInRootViewForV,
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: kPtInRootViewForV,
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: kPtInSubView1ForV,
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: kPtInRootViewForH,
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: kPtInRootViewForH,
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: kPtInSubView1ForH,
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: kPtInSubView2ForV,
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: kPtInSubView2ForV,
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: kPtInSubView3ForV,
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: kPtInSubView3ForV,
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: kPtInRootViewForV,
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: kPtInRootViewForV,
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: kPtInSubView1ForV,
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: kPtInRootViewForH,
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: kPtInRootViewForH,
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: kPtInSubView1ForH,
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: kPtInRootViewForV,
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: kPtInRootViewForV,
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: kPtInSubView1ForV,
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: kPtInRootViewForH,
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: kPtInRootViewForH,
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: kPtInSubView1ForH,
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: kPtInRootViewForV,
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: kPtInRootViewForV,
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: kPtInSubView1ForV,
description: "sendMouseMoveEvent" },
{ func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
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: kPtInSubView1ForV,
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: kPtInSubView1ForV,
description: "sendMouseMoveEvent" },
{ func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
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: kPtInSubView1ForV,
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: kPtInSubView1ForV,
description: "sendMouseMoveEvent" },
{ func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
offset: kPtInSubView1ForV,
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: kPtInSubView1ForV,
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: kPtInSubView1ForV,
description: "sendMouseMoveEvent" },
{ func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
offset: kPtInSubView1ForV,
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: kPtInRootViewForH,
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: kPtInRootViewForH,
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: kPtInSubView1ForH,
description: "sendMouseMoveEvent" },
{ func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
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: kPtInSubView1ForH,
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: kPtInSubView1ForH,
description: "sendMouseMoveEvent" },
{ func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForH,
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: kPtInSubView1ForH,
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: kPtInSubView1ForH,
description: "sendMouseMoveEvent" },
{ func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
offset: kPtInSubView1ForH,
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: kPtInSubView1ForH,
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: kPtInSubView1ForH,
description: "sendMouseMoveEvent" },
{ func: testOneTimeScroll, delay: gEnoughForIgnoreMoveDelay,
offset: kPtInSubView1ForH,
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: kPtInSubView1ForV,
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: kPtInRootViewForV,
description: "sendMouseMoveEvent" },
// Send Wheel event over |gRootView| which should be scrolled.
{ func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForV,
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: kPtInSubView1ForH,
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: kPtInRootViewForH,
description: "sendMouseMoveEvent" },
// Send Wheel event over |gRootView| which should be scrolled.
{ func: testOneTimeScroll, delay: 0, offset: kPtInRootViewForH,
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: kPtInRootViewForV,
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: kPtInRootViewForV,
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: kPtInSubView1ForV,
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: kPtInSubView1ForV,
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: kPtInSubView1ForV,
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: kPtInRootViewForH,
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: kPtInRootViewForH,
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: kPtInSubView1ForH,
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: kPtInSubView1ForH,
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: kPtInSubView1ForH,
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: kPtInSubView1ForV,
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: kPtInSubView1ForV,
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: kPtInSubView1ForV,
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: kPtInSubView1ForH,
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: kPtInSubView1ForH,
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: kPtInSubView1ForH,
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: kPtInSubView1ForV,
isForward: true, isVertical: true, expectedView: gSubView1,
delta: 5000,
description: "Very large delta scrolling (v-1)" },
{ func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
isForward: true, isVertical: true, expectedView: gSubView1,
delta: 5000,
description: "Very large delta scrolling (v-2)" },
{ func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
isForward: true, isVertical: false, expectedView: gSubView1,
delta: 5000,
description: "Very large delta scrolling (h-1)" },
{ func: testOneTimeScroll, delay: 0, offset: kPtInSubView1ForV,
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;
}
function initInRootView(aElement, aPt)
{
aElement.offset =
gCurrentTest.forVertical ? aPt : { x: aPt.y, y: aPt.x };
}
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()
{
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
* 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
* 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
* 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>