Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(83)

Side by Side Diff: third_party/WebKit/Source/core/animation/ScrollTimelineTest.cpp

Issue 2873493002: Basic ScrollTimeline implementation for Animation Worklet (Closed)
Patch Set: Rebase Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/animation/ScrollTimeline.h"
6
7 #include "bindings/core/v8/ExceptionState.h"
8 #include "core/animation/ScrollTimelineOptions.h"
9 #include "core/dom/Document.h"
10 #include "core/frame/FrameView.h"
11 #include "core/layout/LayoutBoxModelObject.h"
12 #include "core/paint/PaintLayerScrollableArea.h"
13 #include "core/testing/DummyPageHolder.h"
14 #include "platform/wtf/text/WTFString.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace blink {
18
19 class ScrollTimelineTest : public ::testing::Test {
20 protected:
21 void SetUp() override { page_holder_ = DummyPageHolder::Create(); }
22
23 Document& GetDocument() { return page_holder_->GetDocument(); }
24
25 void SetInnerHTML(const String& html) {
26 GetDocument().documentElement()->setInnerHTML(html);
27 GetDocument().View()->UpdateAllLifecyclePhases();
28 }
29
30 ScrollTimelineOptions CreateOptions(Element* scrollSource = nullptr,
31 double time_range = 0,
32 String orientation = "block") {
33 ScrollTimelineOptions options;
34 options.setScrollSource(scrollSource);
35 options.setTimeRange(
36 DoubleOrScrollTimelineAutoKeyword::fromDouble(time_range));
37 options.setOrientation(orientation);
38 return options;
39 }
40
41 private:
42 std::unique_ptr<DummyPageHolder> page_holder_;
43 };
44
45 TEST_F(ScrollTimelineTest, CreateScrollTimeline) {
46 SetInnerHTML(
47 "<style>#scroller { height: 100px; width: 100px; overflow: auto; }"
48 "#content { height: 250px; width: 250px; }</style>"
49 "<div id='scroller'><div id='content'></div></div>");
50
51 Element* scroller = GetDocument().getElementById("scroller");
52 ASSERT_TRUE(scroller);
53
54 ScrollTimelineOptions options = CreateOptions(scroller, 100, "inline");
55 DummyExceptionStateForTesting exception_state;
56 ScrollTimeline* scroll_timeline =
57 ScrollTimeline::Create(GetDocument(), options, exception_state);
58 ASSERT_TRUE(scroll_timeline);
59 EXPECT_FALSE(exception_state.HadException());
60
61 EXPECT_EQ(scroller, scroll_timeline->scrollSource());
62 EXPECT_EQ("inline", scroll_timeline->orientation());
63 DoubleOrScrollTimelineAutoKeyword time_range;
64 scroll_timeline->timeRange(time_range);
65 EXPECT_TRUE(time_range.isDouble());
66 EXPECT_EQ(100, time_range.getAsDouble());
67 }
68
69 TEST_F(ScrollTimelineTest, CreateScrollTimelineWithoutScrollingSource) {
70 ScrollTimelineOptions options = CreateOptions(nullptr);
71 DummyExceptionStateForTesting exception_state;
72 ScrollTimeline* scroll_timeline =
73 ScrollTimeline::Create(GetDocument(), options, exception_state);
74 ASSERT_TRUE(scroll_timeline);
75 EXPECT_FALSE(exception_state.HadException());
76
77 EXPECT_EQ(GetDocument().scrollingElement(), scroll_timeline->scrollSource());
alancutter (OOO until 2018) 2017/06/08 05:00:32 These tests should be testharness.js tests where p
smcgruer 2017/06/08 14:43:47 I believe all are covered by WPT-compatible layout
alancutter (OOO until 2018) 2017/06/09 04:06:19 I find unit tests to be more maintenance heavy tha
smcgruer 2017/06/22 14:34:25 Done.
78 }
79
80 TEST_F(ScrollTimelineTest, CreateScrollTimelineWithInvalidOrientation) {
81 ScrollTimelineOptions options = CreateOptions(nullptr, 100, "invalid");
82 DummyExceptionStateForTesting exception_state;
83 EXPECT_FALSE(ScrollTimeline::Create(GetDocument(), options, exception_state));
84 EXPECT_TRUE(exception_state.HadException());
85 }
86
87 // TODO(smcgruer): Remove once 'auto' timeRange is supported.
88 TEST_F(ScrollTimelineTest, CreateScrollTimelineWithInvalidTimeRange) {
89 ScrollTimelineOptions options;
90 options.setTimeRange(
91 DoubleOrScrollTimelineAutoKeyword::fromScrollTimelineAutoKeyword("auto"));
92 DummyExceptionStateForTesting exception_state;
93 EXPECT_FALSE(ScrollTimeline::Create(GetDocument(), options, exception_state));
94 EXPECT_TRUE(exception_state.HadException());
95 }
96
97 TEST_F(ScrollTimelineTest, CurrentTimeCorrect) {
98 SetInnerHTML(
99 "<style>#scroller { height: 100px; width: 100px; overflow: auto; }"
100 "#content { height: 250px; width: 250px; }</style>"
101 "<div id='scroller'><div id='content'></div></div>");
102
103 Element* scroller = GetDocument().getElementById("scroller");
104 PaintLayerScrollableArea* scrollable_area =
105 scroller->GetLayoutBoxModelObject()->GetScrollableArea();
106 ASSERT_TRUE(scroller);
107 ASSERT_TRUE(scrollable_area);
108
109 DummyExceptionStateForTesting exception_state;
110
111 // For simplicity, we set the timeRange such that currentTime maps directly to
112 // the value scrolled. We have a square scroller/contents, so can just compute
113 // one edge and use it for inline and block.
114 double scroller_size = scroller->scrollHeight() - scroller->clientHeight();
115
116 ScrollTimelineOptions block_options =
117 CreateOptions(scroller, scroller_size, "block");
118 ScrollTimeline* block_timeline =
119 ScrollTimeline::Create(GetDocument(), block_options, exception_state);
120 EXPECT_TRUE(block_timeline);
121 EXPECT_FALSE(exception_state.HadException());
122
123 ScrollTimelineOptions inline_options =
124 CreateOptions(scroller, scroller_size, "inline");
125 ScrollTimeline* inline_timeline =
126 ScrollTimeline::Create(GetDocument(), inline_options, exception_state);
127 EXPECT_TRUE(inline_timeline);
128 EXPECT_FALSE(exception_state.HadException());
129
130 // Unscrolled, both timelines should read a current time of 0.
131 EXPECT_DOUBLE_EQ(0, block_timeline->currentTime());
132 EXPECT_DOUBLE_EQ(0, inline_timeline->currentTime());
133
134 // Now do some scrolling and make sure that the ScrollTimelines update.
135 //
136 // As noted above, we have mapped timeRange such that currentTime should just
137 // be the scroll offset.
138 scrollable_area->ScrollToAbsolutePosition(FloatPoint(75, 50));
139 ASSERT_EQ(75.0, scrollable_area->ScrollPosition().X());
140 ASSERT_EQ(50.0, scrollable_area->ScrollPosition().Y());
141
142 EXPECT_DOUBLE_EQ(50, block_timeline->currentTime());
143 EXPECT_DOUBLE_EQ(75, inline_timeline->currentTime());
144 }
145
146 TEST_F(ScrollTimelineTest, CurrentTimeForInlineElementIsNaN) {
147 SetInnerHTML(
148 "<style>#scroller { height: 100px; width: 100px; overflow: auto; "
149 "display: inline; }"
150 "#content { height: 250px; width: 250px; }</style>"
151 "<div id='scroller'><div id='content'></div></div>");
152
153 Element* scroller = GetDocument().getElementById("scroller");
154 ASSERT_TRUE(scroller);
155
156 // We should still be able to create a timeline with an inline element as the
157 // scrollSource.
158 ScrollTimelineOptions options = CreateOptions(scroller);
159 DummyExceptionStateForTesting exception_state;
160 ScrollTimeline* scroll_timeline =
161 ScrollTimeline::Create(GetDocument(), options, exception_state);
162 ASSERT_TRUE(scroll_timeline);
163 EXPECT_FALSE(exception_state.HadException());
164
165 // However it should return NaN for the currentTime.
166 EXPECT_TRUE(std::isnan(scroll_timeline->currentTime()));
167 }
168
169 TEST_F(ScrollTimelineTest, CurrentTimeForDisplayNoneElementIsNaN) {
170 SetInnerHTML(
171 "<style>#scroller { height: 100px; width: 100px; overflow: auto; "
172 "display: none; }"
173 "#content { height: 250px; width: 250px; }</style>"
174 "<div id='scroller'><div id='content'></div></div>");
175
176 Element* scroller = GetDocument().getElementById("scroller");
177 ASSERT_TRUE(scroller);
178
179 // We should still be able to create a timeline with a display:none element as
180 // the scrollSource.
181 ScrollTimelineOptions options = CreateOptions(scroller);
182 DummyExceptionStateForTesting exception_state;
183 ScrollTimeline* scroll_timeline =
184 ScrollTimeline::Create(GetDocument(), options, exception_state);
185 ASSERT_TRUE(scroll_timeline);
186 EXPECT_FALSE(exception_state.HadException());
187
188 // However it should return NaN for the currentTime.
189 EXPECT_TRUE(std::isnan(scroll_timeline->currentTime()));
190 }
191
192 TEST_F(ScrollTimelineTest, CurrentTimeForNonAttachedElementIsNaN) {
193 // We manually create an entire overflow element tree, but do not attach it to
194 // the document.
195 Element* scroller = GetDocument().createElement("div");
196 Element* content = GetDocument().createElement("div");
197 ASSERT_TRUE(scroller);
198 ASSERT_TRUE(content);
199
200 scroller->SetInlineStyleProperty(CSSPropertyOverflow, CSSValueAuto);
201 scroller->SetInlineStyleProperty(CSSPropertyHeight, 100,
202 CSSPrimitiveValue::UnitType::kPixels);
203 scroller->SetInlineStyleProperty(CSSPropertyWidth, 100,
204 CSSPrimitiveValue::UnitType::kPixels);
205 content->SetInlineStyleProperty(CSSPropertyHeight, 250,
206 CSSPrimitiveValue::UnitType::kPixels);
207 content->SetInlineStyleProperty(CSSPropertyWidth, 250,
208 CSSPrimitiveValue::UnitType::kPixels);
209 scroller->AppendChild(content);
210
211 GetDocument().View()->UpdateAllLifecyclePhases();
212
213 // We should still be able to create a timeline with an unattached element as
214 // the scrollSource.
215 ScrollTimelineOptions options = CreateOptions(scroller);
216 DummyExceptionStateForTesting exception_state;
217 ScrollTimeline* scroll_timeline =
218 ScrollTimeline::Create(GetDocument(), options, exception_state);
219 ASSERT_TRUE(scroll_timeline);
220 EXPECT_FALSE(exception_state.HadException());
221
222 // However it should return NaN for the currentTime.
223 EXPECT_TRUE(std::isnan(scroll_timeline->currentTime()));
224 }
225
226 TEST_F(ScrollTimelineTest, CurrentTimeForNonScrollerIsNaN) {
227 // The so-called scroller here is deliberately overflow: visible.
228 SetInnerHTML(
229 "<style>#scroller { height: 100px; width: 100px; overflow: visible; }"
230 "#content { height: 250px; width: 250px; }</style>"
231 "<div id='scroller'><div id='content'></div></div>");
232
233 Element* scroller = GetDocument().getElementById("scroller");
234 ASSERT_TRUE(scroller);
235
236 // We should still be able to create a timeline with a non-scroller as the
237 // scrollSource.
238 ScrollTimelineOptions options = CreateOptions(scroller);
239 DummyExceptionStateForTesting exception_state;
240 ScrollTimeline* scroll_timeline =
241 ScrollTimeline::Create(GetDocument(), options, exception_state);
242 ASSERT_TRUE(scroll_timeline);
243 EXPECT_FALSE(exception_state.HadException());
244
245 // However it should return NaN for the currentTime.
246 EXPECT_TRUE(std::isnan(scroll_timeline->currentTime()));
247 }
248
249 TEST_F(ScrollTimelineTest, CurrentTimeAdjustsForTimeRangeCorrectly) {
250 // It is unfortunately difficult to calculate what scroll offset results in an
251 // exact currentTime. Scrolling is caluclated in integers which allows for the
252 // possibility of rounding, and scrollbar widths differ between platforms
253 // which means it is not possible to ensure a divisible scroller size.
254 //
255 // Instead we make the scroller content big enough that a 1-pixel rounding
256 // difference results in a negligible difference in the output value.
257 SetInnerHTML(
258 "<style>#scroller { height: 100px; width: 100px; overflow: auto; }"
259 "#content { height: 1000px; width: 1000px; }</style>"
260 "<div id='scroller'><div id='content'></div></div>");
261
262 Element* scroller = GetDocument().getElementById("scroller");
263 PaintLayerScrollableArea* scrollable_area =
264 scroller->GetLayoutBoxModelObject()->GetScrollableArea();
265 ASSERT_TRUE(scroller);
266 ASSERT_TRUE(scrollable_area);
267
268 ScrollTimelineOptions options = CreateOptions(scroller, 100);
269 DummyExceptionStateForTesting exception_state;
270 ScrollTimeline* scroll_timeline =
271 ScrollTimeline::Create(GetDocument(), options, exception_state);
272 ASSERT_TRUE(scroll_timeline);
273 EXPECT_FALSE(exception_state.HadException());
274
275 // Mapping timeRange to 100 gives a form of 'percentage scrolled', so
276 // calculate where the 50% scroll mark would be.
277 double halfway_y = (scroller->scrollHeight() - scroller->clientHeight()) / 2.;
278 scrollable_area->ScrollToAbsolutePosition(
279 FloatPoint(0, std::round(halfway_y)));
280
281 EXPECT_NEAR(50, scroll_timeline->currentTime(), 0.5);
282 }
283
284 TEST_F(ScrollTimelineTest, CurrentTimeHandlesRtlDirection) {
285 SetInnerHTML(
286 "<style>#scroller { height: 100px; width: 100px; overflow: auto; "
287 "direction: rtl; }"
288 "#content { height: 250px; width: 250px; }</style>"
289 "<div id='scroller'><div id='content'></div></div>");
290
291 Element* scroller = GetDocument().getElementById("scroller");
292 PaintLayerScrollableArea* scrollable_area =
293 scroller->GetLayoutBoxModelObject()->GetScrollableArea();
294 ASSERT_TRUE(scroller);
295 ASSERT_TRUE(scrollable_area);
296
297 DummyExceptionStateForTesting exception_state;
298
299 // For simplicity, we set the timeRange such that currentTime maps directly to
300 // the value scrolled. We have a square scroller/contents, so can just compute
301 // one edge and use it for inline and block.
302 double scroller_size = scroller->scrollHeight() - scroller->clientHeight();
303
304 ScrollTimelineOptions block_options =
305 CreateOptions(scroller, scroller_size, "block");
306 ScrollTimeline* block_timeline =
307 ScrollTimeline::Create(GetDocument(), block_options, exception_state);
308 EXPECT_TRUE(block_timeline);
309 EXPECT_FALSE(exception_state.HadException());
310
311 ScrollTimelineOptions inline_options =
312 CreateOptions(scroller, scroller_size, "inline");
313 ScrollTimeline* inline_timeline =
314 ScrollTimeline::Create(GetDocument(), inline_options, exception_state);
315 EXPECT_TRUE(inline_timeline);
316 EXPECT_FALSE(exception_state.HadException());
317
318 // Unscrolled, both timelines should read a current time of 0 even though the
319 // X-axis will have started at the right hand side for rtl.
320 EXPECT_DOUBLE_EQ(0, block_timeline->currentTime());
321 EXPECT_DOUBLE_EQ(0, inline_timeline->currentTime());
322
323 // Absolute position scroll ignores the writing mode, so the actual offset in
324 // the inline direction should be inverted. The block direction should be
325 // unaffected.
326 scrollable_area->ScrollToAbsolutePosition(FloatPoint(75, 50));
327 ASSERT_EQ(75.0, scrollable_area->ScrollPosition().X());
328 ASSERT_EQ(50.0, scrollable_area->ScrollPosition().Y());
329
330 EXPECT_DOUBLE_EQ(50, block_timeline->currentTime());
331 EXPECT_DOUBLE_EQ(scroller_size - 75, inline_timeline->currentTime());
332 }
333
334 TEST_F(ScrollTimelineTest, CurrentTimeHandlesVerticalRlWritingMode) {
335 SetInnerHTML(
336 "<style>#scroller { height: 100px; width: 100px; overflow: auto; "
337 "writing-mode: vertical-rl; }"
338 "#content { height: 250px; width: 250px; }</style>"
339 "<div id='scroller'><div id='content'></div></div>");
340
341 Element* scroller = GetDocument().getElementById("scroller");
342 PaintLayerScrollableArea* scrollable_area =
343 scroller->GetLayoutBoxModelObject()->GetScrollableArea();
344 ASSERT_TRUE(scroller);
345 ASSERT_TRUE(scrollable_area);
346
347 DummyExceptionStateForTesting exception_state;
348
349 // For simplicity, we set the timeRange such that currentTime maps directly to
350 // the value scrolled. We have a square scroller/contents, so can just compute
351 // one edge and use it for inline and block.
352 double scroller_size = scroller->scrollHeight() - scroller->clientHeight();
353
354 ScrollTimelineOptions block_options =
355 CreateOptions(scroller, scroller_size, "block");
356 ScrollTimeline* block_timeline =
357 ScrollTimeline::Create(GetDocument(), block_options, exception_state);
358 EXPECT_TRUE(block_timeline);
359 EXPECT_FALSE(exception_state.HadException());
360
361 ScrollTimelineOptions inline_options =
362 CreateOptions(scroller, scroller_size, "inline");
363 ScrollTimeline* inline_timeline =
364 ScrollTimeline::Create(GetDocument(), inline_options, exception_state);
365 EXPECT_TRUE(inline_timeline);
366 EXPECT_FALSE(exception_state.HadException());
367
368 // Unscrolled, both timelines should read a current time of 0 even though the
369 // X-axis will have started at the right hand side for vertical-rl.
370 EXPECT_DOUBLE_EQ(0, block_timeline->currentTime());
371 EXPECT_DOUBLE_EQ(0, inline_timeline->currentTime());
372
373 // For vertical-rl, the X-axis starts on the right-hand-side and is the block
374 // axis. The Y-axis is normal but is the inline axis.
375 scrollable_area->ScrollToAbsolutePosition(FloatPoint(75, 50));
376 ASSERT_EQ(75.0, scrollable_area->ScrollPosition().X());
377 ASSERT_EQ(50.0, scrollable_area->ScrollPosition().Y());
378
379 EXPECT_DOUBLE_EQ(scroller_size - 75, block_timeline->currentTime());
380 EXPECT_DOUBLE_EQ(50, inline_timeline->currentTime());
381 }
382
383 TEST_F(ScrollTimelineTest, CurrentTimeHandlesVerticalLrWritingMode) {
384 SetInnerHTML(
385 "<style>#scroller { height: 100px; width: 100px; overflow: auto; "
386 "writing-mode: vertical-lr; }"
387 "#content { height: 250px; width: 250px; }</style>"
388 "<div id='scroller'><div id='content'></div></div>");
389
390 Element* scroller = GetDocument().getElementById("scroller");
391 PaintLayerScrollableArea* scrollable_area =
392 scroller->GetLayoutBoxModelObject()->GetScrollableArea();
393 ASSERT_TRUE(scroller);
394 ASSERT_TRUE(scrollable_area);
395
396 DummyExceptionStateForTesting exception_state;
397
398 // For simplicity, we set the timeRange such that currentTime maps directly to
399 // the value scrolled. We have a square scroller/contents, so can just compute
400 // one edge and use it for inline and block.
401 double scroller_size = scroller->scrollHeight() - scroller->clientHeight();
402
403 ScrollTimelineOptions block_options =
404 CreateOptions(scroller, scroller_size, "block");
405 ScrollTimeline* block_timeline =
406 ScrollTimeline::Create(GetDocument(), block_options, exception_state);
407 EXPECT_TRUE(block_timeline);
408 EXPECT_FALSE(exception_state.HadException());
409
410 ScrollTimelineOptions inline_options =
411 CreateOptions(scroller, scroller_size, "inline");
412 ScrollTimeline* inline_timeline =
413 ScrollTimeline::Create(GetDocument(), inline_options, exception_state);
414 EXPECT_TRUE(inline_timeline);
415 EXPECT_FALSE(exception_state.HadException());
416
417 // Unscrolled, both timelines should read a current time of 0.
418 EXPECT_DOUBLE_EQ(0, block_timeline->currentTime());
419 EXPECT_DOUBLE_EQ(0, inline_timeline->currentTime());
420
421 // For vertical-rl, both axes start at their 'normal' positions but the X-axis
422 // is the block direction and the Y-axis is the inline direction.
423 scrollable_area->ScrollToAbsolutePosition(FloatPoint(75, 50));
424 ASSERT_EQ(75.0, scrollable_area->ScrollPosition().X());
425 ASSERT_EQ(50.0, scrollable_area->ScrollPosition().Y());
426
427 EXPECT_DOUBLE_EQ(75, block_timeline->currentTime());
428 EXPECT_DOUBLE_EQ(50, inline_timeline->currentTime());
429 }
430
431 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698