1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
// A ScrollBehaviorTest runs a set of ScrollBehaviorTestCases. The only
// ScrollBehaviorTest method that should be called by external code is run().
// Creates a ScrollBehaviorTest with arguments:
// scrollElement - Element being scrolled.
// scrollEventTarget - Target for scroll events for |scrollElement|.
// testsCases - Array of ScrollBehaviorTestCases.
// getEndPosition - Callback that takes a test case and start position, and
// returns the corresponding end position (where positions
// are dictionaries with x and y fields).
// jsScroll - Callback that takes a test case and executes the corresponding
// js-driven scroll (e.g. by setting scrollLeft/scrollTop or by
// calling scroll, scrollTo, or scrollBy). This should assume that
// scrollElement's scroll-behavior CSS property has already been
// set appropriately.
function ScrollBehaviorTest(scrollElement,
scrollEventTarget,
testCases,
getEndPosition,
jsScroll) {
this.scrollElement = scrollElement;
this.scrollEventTarget = scrollEventTarget;
this.testCases = testCases;
this.currentTestCase = 0;
this.getEndPosition = getEndPosition;
this.jsScroll = jsScroll;
}
ScrollBehaviorTest.prototype.scrollListener = function(testCase) {
var endReached = (this.scrollElement.scrollLeft == testCase.endX && this.scrollElement.scrollTop == testCase.endY);
if (endReached) {
this.testCaseComplete();
return;
}
if (testCase.waitForEnd)
return;
// Wait for the animation to start, then instant-scroll to the end state.
if (this.scrollElement.scrollLeft != testCase.startX || this.scrollElement.scrollTop != testCase.startY) {
// Instant scroll, and then wait for the next scroll event. This allows
// the instant scroll to propagate to the compositor (when using
// composited scrolling) so that the next smooth scroll starts at this
// position (the compositor always starts smooth scrolls at the current
// scroll position on the compositor thread).
this.scrollElement.scrollTo({left: testCase.endX, top: testCase.endY, behavior: "instant"});
testCase.waitForEnd = true;
}
};
ScrollBehaviorTest.prototype.startNextTestCase = function() {
if (this.currentTestCase >= this.testCases.length) {
this.allTestCasesComplete();
return;
}
var testCase = this.testCases[this.currentTestCase];
if (testCase.pageScaleFactor && window.internals) {
internals.setPageScaleFactor(testCase.pageScaleFactor);
}
var isSmoothTest = (testCase.js == "smooth" || (testCase.css == "smooth" && testCase.js != "instant"));
this.asyncTest = async_test("Scroll x:" + testCase.x + ", y:" + testCase.y + ", smooth:" + isSmoothTest);
var currentPosition = {};
currentPosition.x = this.scrollElement.scrollLeft;
currentPosition.y = this.scrollElement.scrollTop;
var endPosition = this.getEndPosition(testCase, currentPosition);
testCase.setStartPosition(currentPosition);
testCase.setEndPosition(endPosition);
this.scrollElement.style.scrollBehavior = testCase.css;
this.jsScroll(testCase);
var scrollElement = this.scrollElement;
if (isSmoothTest) {
this.asyncTest.step(function() {
assert_equals(scrollElement.scrollLeft + ", " + scrollElement.scrollTop, testCase.startX + ", " + testCase.startY);
});
if (scrollElement.scrollLeft == testCase.endX && scrollElement.scrollTop == testCase.endY) {
// We've instant-scrolled. This means we've already failed the assert above, and will never
// reach an intermediate frame. End the test case now to avoid hanging while waiting for an
// intermediate frame.
this.testCaseComplete();
} else {
testCase.scrollListener = this.scrollListener.bind(this, testCase);
this.scrollEventTarget.addEventListener("scroll", testCase.scrollListener);
}
} else {
this.asyncTest.step(function() {
assert_equals(scrollElement.scrollLeft + ", " + scrollElement.scrollTop, testCase.endX + ", " + testCase.endY);
});
this.testCaseComplete();
}
}
ScrollBehaviorTest.prototype.testCaseComplete = function() {
var currentScrollListener = this.testCases[this.currentTestCase].scrollListener;
if (currentScrollListener) {
this.scrollEventTarget.removeEventListener("scroll", currentScrollListener);
}
this.asyncTest.done();
this.currentTestCase++;
this.startNextTestCase();
}
ScrollBehaviorTest.prototype.run = function() {
setup({explicit_done: true, explicit_timeout: true});
this.startNextTestCase();
}
ScrollBehaviorTest.prototype.allTestCasesComplete = function() {
done();
}
// A ScrollBehaviorTestCase represents a single scroll.
//
// Creates a ScrollBehaviorTestCase. |testData| is a dictionary with fields:
// css - Value of scroll-behavior CSS property.
// js - (optional) Value of scroll behavior used in javascript.
// x, y - Coordinates to be used when carrying out the scroll.
// waitForEnd - (must be provided for smooth scrolls) Whether the test runner should
// wait until the scroll is complete, rather than only waiting until
// the scroll is underway.
// pageScaleFactor - (optional) if set, applies pinch-zoom by the given factor.
function ScrollBehaviorTestCase(testData) {
this.js = testData.js;
this.css = testData.css;
this.waitForEnd = testData.waitForEnd;
this.x = testData.x;
this.y = testData.y;
this.pageScaleFactor = testData.pageScaleFactor;
}
ScrollBehaviorTestCase.prototype.setStartPosition = function(startPosition) {
this.startX = startPosition.x;
this.startY = startPosition.y;
}
ScrollBehaviorTestCase.prototype.setEndPosition = function(endPosition) {
this.endX = endPosition.x;
this.endY = endPosition.y;
}
|