1 /*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
17 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25 #include "build/build_config.h"
26 #include "cc/layers/scrollbar_layer_base.h"
27 #include "cc/trees/property_tree.h"
28 #include "cc/trees/scroll_and_scale_set.h"
29 #include "cc/trees/scroll_node.h"
30 #include "cc/trees/sticky_position_constraint.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #include "third_party/blink/public/platform/web_cache.h"
33 #include "third_party/blink/public/platform/web_rect.h"
34 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
35 #include "third_party/blink/public/web/web_settings.h"
36 #include "third_party/blink/public/web/web_view_client.h"
37 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
38 #include "third_party/blink/renderer/core/css/style_sheet_list.h"
39 #include "third_party/blink/renderer/core/dom/events/add_event_listener_options_resolved.h"
40 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
41 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
42 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
43 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
44 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
45 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
46 #include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
47 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
48 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
49 #include "third_party/blink/renderer/core/html/html_object_element.h"
50 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
51 #include "third_party/blink/renderer/core/layout/layout_view.h"
52 #include "third_party/blink/renderer/core/page/page.h"
53 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
54 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h"
55 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
56 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
57 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
58 #include "third_party/blink/renderer/platform/geometry/int_point.h"
59 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
60 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
61 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
62 #include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
63 #include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
64 #include "third_party/blink/renderer/platform/graphics/touch_action.h"
65 #include "third_party/blink/renderer/platform/testing/find_cc_layer.h"
66 #include "third_party/blink/renderer/platform/testing/histogram_tester.h"
67 #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
68 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
69 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
70
71 namespace blink {
72
73 class ScrollingTest : public testing::Test, public PaintTestConfigurations {
74 public:
ScrollingTest()75 ScrollingTest() : base_url_("http://www.test.com/") {
76 helper_.Initialize(nullptr, nullptr, nullptr, &ConfigureSettings);
77 GetWebView()->MainFrameWidgetBase()->Resize(IntSize(320, 240));
78 GetWebView()->MainFrameWidgetBase()->UpdateAllLifecyclePhases(
79 DocumentUpdateReason::kTest);
80 }
81
~ScrollingTest()82 ~ScrollingTest() override {
83 url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
84 }
85
NavigateTo(const std::string & url)86 void NavigateTo(const std::string& url) {
87 frame_test_helpers::LoadFrame(GetWebView()->MainFrameImpl(), url);
88 }
89
LoadHTML(const std::string & html)90 void LoadHTML(const std::string& html) {
91 frame_test_helpers::LoadHTMLString(GetWebView()->MainFrameImpl(), html,
92 url_test_helpers::ToKURL("about:blank"));
93 }
94
ForceFullCompositingUpdate()95 void ForceFullCompositingUpdate() {
96 GetWebView()->MainFrameWidgetBase()->UpdateAllLifecyclePhases(
97 DocumentUpdateReason::kTest);
98 }
99
RegisterMockedHttpURLLoad(const std::string & file_name)100 void RegisterMockedHttpURLLoad(const std::string& file_name) {
101 // TODO(crbug.com/751425): We should use the mock functionality
102 // via |helper_|.
103 url_test_helpers::RegisterMockedURLLoadFromBase(
104 WebString::FromUTF8(base_url_), test::CoreTestDataPath(),
105 WebString::FromUTF8(file_name));
106 }
107
GetWebView() const108 WebViewImpl* GetWebView() const { return helper_.GetWebView(); }
GetFrame() const109 LocalFrame* GetFrame() const { return helper_.LocalMainFrame()->GetFrame(); }
GetWidgetClient() const110 frame_test_helpers::TestWebWidgetClient* GetWidgetClient() const {
111 return helper_.GetWebWidgetClient();
112 }
113
LoadAhem()114 void LoadAhem() { helper_.LoadAhem(); }
115
ScrollNodeForScrollableArea(const ScrollableArea * scrollable_area) const116 const cc::ScrollNode* ScrollNodeForScrollableArea(
117 const ScrollableArea* scrollable_area) const {
118 if (!scrollable_area)
119 return nullptr;
120 const auto* property_trees =
121 RootCcLayer()->layer_tree_host()->property_trees();
122 return property_trees->scroll_tree.Node(
123 property_trees->element_id_to_scroll_node_index.at(
124 scrollable_area->GetScrollElementId()));
125 }
126
ScrollNodeByDOMElementId(const char * dom_id) const127 const cc::ScrollNode* ScrollNodeByDOMElementId(const char* dom_id) const {
128 return ScrollNodeForScrollableArea(
129 GetFrame()->GetDocument()->getElementById(dom_id)->GetScrollableArea());
130 }
131
CurrentScrollOffset(cc::ElementId element_id) const132 gfx::ScrollOffset CurrentScrollOffset(cc::ElementId element_id) const {
133 return RootCcLayer()
134 ->layer_tree_host()
135 ->property_trees()
136 ->scroll_tree.current_scroll_offset(element_id);
137 }
138
CurrentScrollOffset(const cc::ScrollNode * scroll_node) const139 gfx::ScrollOffset CurrentScrollOffset(
140 const cc::ScrollNode* scroll_node) const {
141 return CurrentScrollOffset(scroll_node->element_id);
142 }
143
ScrollbarLayerForScrollNode(const cc::ScrollNode * scroll_node,cc::ScrollbarOrientation orientation) const144 const cc::ScrollbarLayerBase* ScrollbarLayerForScrollNode(
145 const cc::ScrollNode* scroll_node,
146 cc::ScrollbarOrientation orientation) const {
147 return blink::ScrollbarLayerForScrollNode(RootCcLayer(), scroll_node,
148 orientation);
149 }
150
RootCcLayer() const151 const cc::Layer* RootCcLayer() const {
152 return GetFrame()->View()->RootCcLayer();
153 }
154
LayerTreeHost() const155 cc::LayerTreeHost* LayerTreeHost() const {
156 return helper_.GetLayerTreeHost();
157 }
158
FrameScrollingContentsLayer(const LocalFrame & frame) const159 const cc::Layer* FrameScrollingContentsLayer(const LocalFrame& frame) const {
160 return ScrollingContentsCcLayerByScrollElementId(
161 RootCcLayer(), frame.View()->LayoutViewport()->GetScrollElementId());
162 }
163
MainFrameScrollingContentsLayer() const164 const cc::Layer* MainFrameScrollingContentsLayer() const {
165 return FrameScrollingContentsLayer(*GetFrame());
166 }
167
LayerByDOMElementId(const char * dom_id) const168 const cc::Layer* LayerByDOMElementId(const char* dom_id) const {
169 return CcLayersByDOMElementId(RootCcLayer(), dom_id)[0];
170 }
171
ScrollingContentsLayerByDOMElementId(const char * element_id) const172 const cc::Layer* ScrollingContentsLayerByDOMElementId(
173 const char* element_id) const {
174 const auto* scrollable_area = GetFrame()
175 ->GetDocument()
176 ->getElementById(element_id)
177 ->GetScrollableArea();
178 return ScrollingContentsCcLayerByScrollElementId(
179 RootCcLayer(), scrollable_area->GetScrollElementId());
180 }
181
182 protected:
183 std::string base_url_;
184
185 private:
ConfigureSettings(WebSettings * settings)186 static void ConfigureSettings(WebSettings* settings) {
187 settings->SetPreferCompositingToLCDTextEnabled(true);
188 }
189
190 frame_test_helpers::WebViewHelper helper_;
191 };
192
193 INSTANTIATE_PAINT_TEST_SUITE_P(ScrollingTest);
194
TEST_P(ScrollingTest,fastScrollingByDefault)195 TEST_P(ScrollingTest, fastScrollingByDefault) {
196 GetWebView()->MainFrameWidgetBase()->Resize(WebSize(800, 600));
197 LoadHTML("<div id='spacer' style='height: 1000px'></div>");
198 ForceFullCompositingUpdate();
199
200 // Make sure the scrolling coordinator is active.
201 LocalFrameView* frame_view = GetFrame()->View();
202 Page* page = GetFrame()->GetPage();
203 ASSERT_TRUE(page->GetScrollingCoordinator());
204 if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
205 ASSERT_TRUE(
206 page->GetScrollingCoordinator()->CoordinatesScrollingForFrameView(
207 frame_view));
208 }
209
210 // Fast scrolling should be enabled by default.
211 const auto* outer_scroll_node =
212 ScrollNodeForScrollableArea(frame_view->LayoutViewport());
213 ASSERT_TRUE(outer_scroll_node);
214 EXPECT_FALSE(outer_scroll_node->main_thread_scrolling_reasons);
215
216 ASSERT_EQ(cc::EventListenerProperties::kNone,
217 LayerTreeHost()->event_listener_properties(
218 cc::EventListenerClass::kTouchStartOrMove));
219 ASSERT_EQ(cc::EventListenerProperties::kNone,
220 LayerTreeHost()->event_listener_properties(
221 cc::EventListenerClass::kMouseWheel));
222
223 const auto* inner_scroll_node =
224 ScrollNodeForScrollableArea(&page->GetVisualViewport());
225 ASSERT_TRUE(inner_scroll_node);
226 EXPECT_FALSE(inner_scroll_node->main_thread_scrolling_reasons);
227 }
228
TEST_P(ScrollingTest,fastFractionalScrollingDiv)229 TEST_P(ScrollingTest, fastFractionalScrollingDiv) {
230 RegisterMockedHttpURLLoad("fractional-scroll-div.html");
231 NavigateTo(base_url_ + "fractional-scroll-div.html");
232 ForceFullCompositingUpdate();
233
234 Document* document = GetFrame()->GetDocument();
235 Element* scrollable_element = document->getElementById("scroller");
236 DCHECK(scrollable_element);
237
238 scrollable_element->setScrollTop(1.0);
239 scrollable_element->setScrollLeft(1.0);
240 ForceFullCompositingUpdate();
241
242 // Make sure the fractional scroll offset change 1.0 -> 1.2 gets propagated
243 // to compositor.
244 scrollable_element->setScrollTop(1.2);
245 scrollable_element->setScrollLeft(1.2);
246 ForceFullCompositingUpdate();
247
248 const auto* scroll_node = ScrollNodeByDOMElementId("scroller");
249 ASSERT_TRUE(scroll_node);
250 ASSERT_NEAR(1.2f, CurrentScrollOffset(scroll_node).x(), 0.01f);
251 ASSERT_NEAR(1.2f, CurrentScrollOffset(scroll_node).y(), 0.01f);
252 }
253
TEST_P(ScrollingTest,fastScrollingForFixedPosition)254 TEST_P(ScrollingTest, fastScrollingForFixedPosition) {
255 RegisterMockedHttpURLLoad("fixed-position.html");
256 NavigateTo(base_url_ + "fixed-position.html");
257 ForceFullCompositingUpdate();
258
259 const auto* scroll_node =
260 ScrollNodeForScrollableArea(GetFrame()->View()->LayoutViewport());
261 ASSERT_TRUE(scroll_node);
262 EXPECT_FALSE(scroll_node->main_thread_scrolling_reasons);
263 }
264
265 // Sticky constraints are stored on transform property tree nodes.
GetStickyConstraint(Element * element)266 static cc::StickyPositionConstraint GetStickyConstraint(Element* element) {
267 const auto* properties =
268 element->GetLayoutObject()->FirstFragment().PaintProperties();
269 DCHECK(properties);
270 return *properties->StickyTranslation()->GetStickyConstraint();
271 }
272
TEST_P(ScrollingTest,fastScrollingForStickyPosition)273 TEST_P(ScrollingTest, fastScrollingForStickyPosition) {
274 RegisterMockedHttpURLLoad("sticky-position.html");
275 NavigateTo(base_url_ + "sticky-position.html");
276 ForceFullCompositingUpdate();
277
278 // Sticky position should not fall back to main thread scrolling.
279 const auto* scroll_node =
280 ScrollNodeForScrollableArea(GetFrame()->View()->LayoutViewport());
281 ASSERT_TRUE(scroll_node);
282 EXPECT_FALSE(scroll_node->main_thread_scrolling_reasons);
283
284 Document* document = GetFrame()->GetDocument();
285 {
286 Element* element = document->getElementById("div-tl");
287 auto constraint = GetStickyConstraint(element);
288 EXPECT_TRUE(constraint.is_anchored_top && constraint.is_anchored_left &&
289 !constraint.is_anchored_right &&
290 !constraint.is_anchored_bottom);
291 EXPECT_EQ(1.f, constraint.top_offset);
292 EXPECT_EQ(1.f, constraint.left_offset);
293 EXPECT_EQ(gfx::RectF(100, 100, 10, 10),
294 constraint.scroll_container_relative_sticky_box_rect);
295 EXPECT_EQ(gfx::RectF(100, 100, 200, 200),
296 constraint.scroll_container_relative_containing_block_rect);
297 }
298 {
299 Element* element = document->getElementById("div-tr");
300 auto constraint = GetStickyConstraint(element);
301 EXPECT_TRUE(constraint.is_anchored_top && !constraint.is_anchored_left &&
302 constraint.is_anchored_right && !constraint.is_anchored_bottom);
303 }
304 {
305 Element* element = document->getElementById("div-bl");
306 auto constraint = GetStickyConstraint(element);
307 EXPECT_TRUE(!constraint.is_anchored_top && constraint.is_anchored_left &&
308 !constraint.is_anchored_right && constraint.is_anchored_bottom);
309 }
310 {
311 Element* element = document->getElementById("div-br");
312 auto constraint = GetStickyConstraint(element);
313 EXPECT_TRUE(!constraint.is_anchored_top && !constraint.is_anchored_left &&
314 constraint.is_anchored_right && constraint.is_anchored_bottom);
315 }
316 {
317 Element* element = document->getElementById("span-tl");
318 auto constraint = GetStickyConstraint(element);
319 EXPECT_TRUE(constraint.is_anchored_top && constraint.is_anchored_left &&
320 !constraint.is_anchored_right &&
321 !constraint.is_anchored_bottom);
322 }
323 {
324 Element* element = document->getElementById("span-tlbr");
325 auto constraint = GetStickyConstraint(element);
326 EXPECT_TRUE(constraint.is_anchored_top && constraint.is_anchored_left &&
327 constraint.is_anchored_right && constraint.is_anchored_bottom);
328 EXPECT_EQ(1.f, constraint.top_offset);
329 EXPECT_EQ(1.f, constraint.left_offset);
330 EXPECT_EQ(1.f, constraint.right_offset);
331 EXPECT_EQ(1.f, constraint.bottom_offset);
332 }
333 {
334 Element* element = document->getElementById("composited-top");
335 auto constraint = GetStickyConstraint(element);
336 EXPECT_TRUE(constraint.is_anchored_top);
337 EXPECT_EQ(gfx::RectF(100, 110, 10, 10),
338 constraint.scroll_container_relative_sticky_box_rect);
339 EXPECT_EQ(gfx::RectF(100, 100, 200, 200),
340 constraint.scroll_container_relative_containing_block_rect);
341 }
342 }
343
TEST_P(ScrollingTest,elementPointerEventHandler)344 TEST_P(ScrollingTest, elementPointerEventHandler) {
345 LoadHTML(R"HTML(
346 <div id="pointer" style="width: 100px; height: 100px;"></div>
347 <script>
348 pointer.addEventListener('pointerdown', function(event) {
349 }, {blocking: false} );
350 </script>
351 )HTML");
352 ForceFullCompositingUpdate();
353
354 const auto* cc_layer = MainFrameScrollingContentsLayer();
355
356 // Pointer event handlers should not generate blocking touch action regions.
357 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
358 TouchAction::kNone);
359 EXPECT_TRUE(region.IsEmpty());
360 }
361
TEST_P(ScrollingTest,touchEventHandler)362 TEST_P(ScrollingTest, touchEventHandler) {
363 RegisterMockedHttpURLLoad("touch-event-handler.html");
364 NavigateTo(base_url_ + "touch-event-handler.html");
365 ForceFullCompositingUpdate();
366
367 ASSERT_EQ(cc::EventListenerProperties::kBlocking,
368 LayerTreeHost()->event_listener_properties(
369 cc::EventListenerClass::kTouchStartOrMove));
370 }
371
TEST_P(ScrollingTest,elementBlockingTouchEventHandler)372 TEST_P(ScrollingTest, elementBlockingTouchEventHandler) {
373 LoadHTML(R"HTML(
374 <div id="blocking" style="width: 100px; height: 100px;"></div>
375 <script>
376 blocking.addEventListener('touchstart', function(event) {
377 }, {passive: false} );
378 </script>
379 )HTML");
380 ForceFullCompositingUpdate();
381
382 const auto* cc_layer = MainFrameScrollingContentsLayer();
383 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
384 TouchAction::kNone);
385 EXPECT_EQ(region.bounds(), gfx::Rect(8, 8, 100, 100));
386 }
387
TEST_P(ScrollingTest,touchEventHandlerPassive)388 TEST_P(ScrollingTest, touchEventHandlerPassive) {
389 RegisterMockedHttpURLLoad("touch-event-handler-passive.html");
390 NavigateTo(base_url_ + "touch-event-handler-passive.html");
391 ForceFullCompositingUpdate();
392
393 ASSERT_EQ(cc::EventListenerProperties::kPassive,
394 LayerTreeHost()->event_listener_properties(
395 cc::EventListenerClass::kTouchStartOrMove));
396 }
397
TEST_P(ScrollingTest,elementTouchEventHandlerPassive)398 TEST_P(ScrollingTest, elementTouchEventHandlerPassive) {
399 LoadHTML(R"HTML(
400 <div id="passive" style="width: 100px; height: 100px;"></div>
401 <script>
402 passive.addEventListener('touchstart', function(event) {
403 }, {passive: true} );
404 </script>
405 )HTML");
406 ForceFullCompositingUpdate();
407
408 const auto* cc_layer = MainFrameScrollingContentsLayer();
409
410 // Passive event handlers should not generate blocking touch action regions.
411 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
412 TouchAction::kNone);
413 EXPECT_TRUE(region.IsEmpty());
414 }
415
TEST_P(ScrollingTest,TouchActionRectsOnImage)416 TEST_P(ScrollingTest, TouchActionRectsOnImage) {
417 LoadHTML(R"HTML(
418 <img id="image" style="width: 100px; height: 100px; touch-action: none;">
419 )HTML");
420 ForceFullCompositingUpdate();
421
422 const auto* cc_layer = MainFrameScrollingContentsLayer();
423 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
424 TouchAction::kNone);
425 EXPECT_EQ(region.bounds(), gfx::Rect(8, 8, 100, 100));
426 }
427
TEST_P(ScrollingTest,touchEventHandlerBoth)428 TEST_P(ScrollingTest, touchEventHandlerBoth) {
429 RegisterMockedHttpURLLoad("touch-event-handler-both.html");
430 NavigateTo(base_url_ + "touch-event-handler-both.html");
431 ForceFullCompositingUpdate();
432
433 ASSERT_EQ(cc::EventListenerProperties::kBlockingAndPassive,
434 LayerTreeHost()->event_listener_properties(
435 cc::EventListenerClass::kTouchStartOrMove));
436 }
437
TEST_P(ScrollingTest,wheelEventHandler)438 TEST_P(ScrollingTest, wheelEventHandler) {
439 RegisterMockedHttpURLLoad("wheel-event-handler.html");
440 NavigateTo(base_url_ + "wheel-event-handler.html");
441 ForceFullCompositingUpdate();
442
443 ASSERT_EQ(cc::EventListenerProperties::kBlocking,
444 LayerTreeHost()->event_listener_properties(
445 cc::EventListenerClass::kMouseWheel));
446 }
447
TEST_P(ScrollingTest,wheelEventHandlerPassive)448 TEST_P(ScrollingTest, wheelEventHandlerPassive) {
449 RegisterMockedHttpURLLoad("wheel-event-handler-passive.html");
450 NavigateTo(base_url_ + "wheel-event-handler-passive.html");
451 ForceFullCompositingUpdate();
452
453 ASSERT_EQ(cc::EventListenerProperties::kPassive,
454 LayerTreeHost()->event_listener_properties(
455 cc::EventListenerClass::kMouseWheel));
456 }
457
TEST_P(ScrollingTest,wheelEventHandlerBoth)458 TEST_P(ScrollingTest, wheelEventHandlerBoth) {
459 RegisterMockedHttpURLLoad("wheel-event-handler-both.html");
460 NavigateTo(base_url_ + "wheel-event-handler-both.html");
461 ForceFullCompositingUpdate();
462
463 ASSERT_EQ(cc::EventListenerProperties::kBlockingAndPassive,
464 LayerTreeHost()->event_listener_properties(
465 cc::EventListenerClass::kMouseWheel));
466 }
467
TEST_P(ScrollingTest,scrollEventHandler)468 TEST_P(ScrollingTest, scrollEventHandler) {
469 RegisterMockedHttpURLLoad("scroll-event-handler.html");
470 NavigateTo(base_url_ + "scroll-event-handler.html");
471 ForceFullCompositingUpdate();
472
473 ASSERT_TRUE(GetWidgetClient()->HaveScrollEventHandlers());
474 }
475
TEST_P(ScrollingTest,updateEventHandlersDuringTeardown)476 TEST_P(ScrollingTest, updateEventHandlersDuringTeardown) {
477 RegisterMockedHttpURLLoad("scroll-event-handler-window.html");
478 NavigateTo(base_url_ + "scroll-event-handler-window.html");
479 ForceFullCompositingUpdate();
480
481 // Simulate detaching the document from its DOM window. This should not
482 // cause a crash when the WebViewImpl is closed by the test runner.
483 GetFrame()->GetDocument()->Shutdown();
484 }
485
TEST_P(ScrollingTest,clippedBodyTest)486 TEST_P(ScrollingTest, clippedBodyTest) {
487 RegisterMockedHttpURLLoad("clipped-body.html");
488 NavigateTo(base_url_ + "clipped-body.html");
489 ForceFullCompositingUpdate();
490
491 const auto* root_scroll_layer = MainFrameScrollingContentsLayer();
492 EXPECT_TRUE(root_scroll_layer->non_fast_scrollable_region().IsEmpty());
493 }
494
TEST_P(ScrollingTest,touchAction)495 TEST_P(ScrollingTest, touchAction) {
496 RegisterMockedHttpURLLoad("touch-action.html");
497 NavigateTo(base_url_ + "touch-action.html");
498 ForceFullCompositingUpdate();
499
500 const auto* cc_layer = ScrollingContentsLayerByDOMElementId("scrollable");
501 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
502 TouchAction::kPanX | TouchAction::kPanDown);
503 EXPECT_EQ(region.GetRegionComplexity(), 1);
504 EXPECT_EQ(region.bounds(), gfx::Rect(0, 0, 1000, 1000));
505 }
506
TEST_P(ScrollingTest,touchActionRegions)507 TEST_P(ScrollingTest, touchActionRegions) {
508 RegisterMockedHttpURLLoad("touch-action-regions.html");
509 NavigateTo(base_url_ + "touch-action-regions.html");
510 ForceFullCompositingUpdate();
511
512 const auto* cc_layer = ScrollingContentsLayerByDOMElementId("scrollable");
513
514 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
515 TouchAction::kPanDown | TouchAction::kPanX);
516 EXPECT_EQ(region.GetRegionComplexity(), 1);
517 EXPECT_EQ(region.bounds(), gfx::Rect(0, 0, 100, 100));
518
519 region = cc_layer->touch_action_region().GetRegionForTouchAction(
520 TouchAction::kPanDown | TouchAction::kPanRight);
521 EXPECT_EQ(region.GetRegionComplexity(), 1);
522 EXPECT_EQ(region.bounds(), gfx::Rect(0, 0, 50, 50));
523
524 region = cc_layer->touch_action_region().GetRegionForTouchAction(
525 TouchAction::kPanDown);
526 EXPECT_EQ(region.GetRegionComplexity(), 1);
527 EXPECT_EQ(region.bounds(), gfx::Rect(0, 100, 100, 100));
528 }
529
TEST_P(ScrollingTest,touchActionNesting)530 TEST_P(ScrollingTest, touchActionNesting) {
531 LoadHTML(R"HTML(
532 <style>
533 #scrollable {
534 width: 200px;
535 height: 200px;
536 background: blue;
537 overflow: scroll;
538 }
539 #touchaction {
540 touch-action: pan-x;
541 width: 100px;
542 height: 100px;
543 margin: 5px;
544 }
545 #child {
546 width: 150px;
547 height: 50px;
548 }
549 </style>
550 <div id="scrollable">
551 <div id="touchaction">
552 <div id="child"></div>
553 </div>
554 <div id="forcescroll" style="width: 1000px; height: 1000px;"></div>
555 </div>
556 )HTML");
557 ForceFullCompositingUpdate();
558
559 const auto* cc_layer = ScrollingContentsLayerByDOMElementId("scrollable");
560
561 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
562 TouchAction::kPanX);
563 EXPECT_EQ(region.GetRegionComplexity(), 2);
564 EXPECT_EQ(region.bounds(), gfx::Rect(5, 5, 150, 100));
565 }
566
TEST_P(ScrollingTest,nestedTouchActionInvalidation)567 TEST_P(ScrollingTest, nestedTouchActionInvalidation) {
568 LoadHTML(R"HTML(
569 <style>
570 #scrollable {
571 width: 200px;
572 height: 200px;
573 background: blue;
574 overflow: scroll;
575 }
576 #touchaction {
577 touch-action: pan-x;
578 width: 100px;
579 height: 100px;
580 margin: 5px;
581 }
582 #child {
583 width: 150px;
584 height: 50px;
585 }
586 </style>
587 <div id="scrollable">
588 <div id="touchaction">
589 <div id="child"></div>
590 </div>
591 <div id="forcescroll" style="width: 1000px; height: 1000px;"></div>
592 </div>
593 )HTML");
594 ForceFullCompositingUpdate();
595
596 const auto* cc_layer = ScrollingContentsLayerByDOMElementId("scrollable");
597
598 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
599 TouchAction::kPanX);
600 EXPECT_EQ(region.GetRegionComplexity(), 2);
601 EXPECT_EQ(region.bounds(), gfx::Rect(5, 5, 150, 100));
602
603 auto* scrollable = GetFrame()->GetDocument()->getElementById("scrollable");
604 scrollable->setAttribute("style", "touch-action: none", ASSERT_NO_EXCEPTION);
605 ForceFullCompositingUpdate();
606 region = cc_layer->touch_action_region().GetRegionForTouchAction(
607 TouchAction::kPanX);
608 EXPECT_TRUE(region.IsEmpty());
609 }
610
611 // Similar to nestedTouchActionInvalidation but tests that an ancestor with
612 // touch-action: pan-x and a descendant with touch-action: pan-y results in a
613 // touch-action rect of none for the descendant.
TEST_P(ScrollingTest,nestedTouchActionChangesUnion)614 TEST_P(ScrollingTest, nestedTouchActionChangesUnion) {
615 LoadHTML(R"HTML(
616 <style>
617 #ancestor {
618 width: 100px;
619 height: 100px;
620 }
621 #child {
622 touch-action: pan-x;
623 width: 150px;
624 height: 50px;
625 }
626 </style>
627 <div id="ancestor">
628 <div id="child"></div>
629 </div>
630 )HTML");
631 ForceFullCompositingUpdate();
632
633 const auto* cc_layer = MainFrameScrollingContentsLayer();
634
635 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
636 TouchAction::kPanX);
637 EXPECT_EQ(region.bounds(), gfx::Rect(8, 8, 150, 50));
638 region = cc_layer->touch_action_region().GetRegionForTouchAction(
639 TouchAction::kNone);
640 EXPECT_TRUE(region.IsEmpty());
641
642 Element* ancestor = GetFrame()->GetDocument()->getElementById("ancestor");
643 ancestor->setAttribute(html_names::kStyleAttr, "touch-action: pan-y");
644 ForceFullCompositingUpdate();
645
646 region = cc_layer->touch_action_region().GetRegionForTouchAction(
647 TouchAction::kPanY);
648 EXPECT_EQ(region.bounds(), gfx::Rect(8, 8, 100, 100));
649 region = cc_layer->touch_action_region().GetRegionForTouchAction(
650 TouchAction::kPanX);
651 EXPECT_TRUE(region.IsEmpty());
652 region = cc_layer->touch_action_region().GetRegionForTouchAction(
653 TouchAction::kNone);
654 EXPECT_EQ(region.bounds(), gfx::Rect(8, 8, 150, 50));
655 }
656
657 // Box shadow is not hit testable and should not be included in touch action.
TEST_P(ScrollingTest,touchActionExcludesBoxShadow)658 TEST_P(ScrollingTest, touchActionExcludesBoxShadow) {
659 LoadHTML(R"HTML(
660 <style>
661 #shadow {
662 width: 100px;
663 height: 100px;
664 touch-action: none;
665 box-shadow: 10px 5px 5px red;
666 }
667 </style>
668 <div id="shadow"></div>
669 )HTML");
670 ForceFullCompositingUpdate();
671
672 const auto* cc_layer = MainFrameScrollingContentsLayer();
673
674 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
675 TouchAction::kNone);
676 EXPECT_EQ(region.bounds(), gfx::Rect(8, 8, 100, 100));
677 }
678
TEST_P(ScrollingTest,touchActionOnInline)679 TEST_P(ScrollingTest, touchActionOnInline) {
680 RegisterMockedHttpURLLoad("touch-action-on-inline.html");
681 NavigateTo(base_url_ + "touch-action-on-inline.html");
682 LoadAhem();
683 ForceFullCompositingUpdate();
684
685 const auto* cc_layer = MainFrameScrollingContentsLayer();
686
687 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
688 TouchAction::kNone);
689 EXPECT_EQ(region.bounds(), gfx::Rect(8, 8, 120, 50));
690 }
691
TEST_P(ScrollingTest,touchActionOnText)692 TEST_P(ScrollingTest, touchActionOnText) {
693 RegisterMockedHttpURLLoad("touch-action-on-text.html");
694 NavigateTo(base_url_ + "touch-action-on-text.html");
695 LoadAhem();
696 ForceFullCompositingUpdate();
697
698 const auto* cc_layer = MainFrameScrollingContentsLayer();
699
700 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
701 TouchAction::kNone);
702 EXPECT_EQ(region.bounds(), gfx::Rect(8, 8, 160, 30));
703 }
704
TEST_P(ScrollingTest,touchActionWithVerticalRLWritingMode)705 TEST_P(ScrollingTest, touchActionWithVerticalRLWritingMode) {
706 RegisterMockedHttpURLLoad("touch-action-with-vertical-rl-writing-mode.html");
707 NavigateTo(base_url_ + "touch-action-with-vertical-rl-writing-mode.html");
708 LoadAhem();
709 ForceFullCompositingUpdate();
710
711 const auto* cc_layer = MainFrameScrollingContentsLayer();
712
713 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
714 TouchAction::kNone);
715 EXPECT_EQ(region.bounds(), gfx::Rect(292, 8, 20, 80));
716 }
717
TEST_P(ScrollingTest,touchActionBlockingHandler)718 TEST_P(ScrollingTest, touchActionBlockingHandler) {
719 RegisterMockedHttpURLLoad("touch-action-blocking-handler.html");
720 NavigateTo(base_url_ + "touch-action-blocking-handler.html");
721 ForceFullCompositingUpdate();
722
723 const auto* cc_layer = ScrollingContentsLayerByDOMElementId("scrollable");
724
725 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
726 TouchAction::kNone);
727 EXPECT_EQ(region.GetRegionComplexity(), 1);
728 EXPECT_EQ(region.bounds(), gfx::Rect(0, 0, 100, 100));
729
730 region = cc_layer->touch_action_region().GetRegionForTouchAction(
731 TouchAction::kPanY);
732 EXPECT_EQ(region.GetRegionComplexity(), 2);
733 EXPECT_EQ(region.bounds(), gfx::Rect(0, 0, 1000, 1000));
734 }
735
TEST_P(ScrollingTest,touchActionOnScrollingElement)736 TEST_P(ScrollingTest, touchActionOnScrollingElement) {
737 LoadHTML(R"HTML(
738 <style>
739 #scrollable {
740 width: 100px;
741 height: 100px;
742 overflow: scroll;
743 touch-action: pan-y;
744 }
745 #child {
746 width: 50px;
747 height: 150px;
748 }
749 </style>
750 <div id="scrollable">
751 <div id="child"></div>
752 </div>
753 )HTML");
754 ForceFullCompositingUpdate();
755
756 // The outer layer (not scrollable) will be fully marked as pan-y (100x100)
757 // and the scrollable layer will only have the contents marked as pan-y
758 // (50x150).
759 const auto* scrolling_contents_layer =
760 ScrollingContentsLayerByDOMElementId("scrollable");
761 cc::Region region =
762 scrolling_contents_layer->touch_action_region().GetRegionForTouchAction(
763 TouchAction::kPanY);
764 EXPECT_EQ(region.bounds(), gfx::Rect(0, 0, 50, 150));
765
766 const auto* container_layer =
767 RuntimeEnabledFeatures::CompositeAfterPaintEnabled()
768 ? MainFrameScrollingContentsLayer()
769 : LayerByDOMElementId("scrollable");
770 region = container_layer->touch_action_region().GetRegionForTouchAction(
771 TouchAction::kPanY);
772 EXPECT_EQ(region.bounds(),
773 RuntimeEnabledFeatures::CompositeAfterPaintEnabled()
774 ? gfx::Rect(8, 8, 100, 100)
775 : gfx::Rect(0, 0, 100, 100));
776 }
777
TEST_P(ScrollingTest,IframeWindowTouchHandler)778 TEST_P(ScrollingTest, IframeWindowTouchHandler) {
779 LoadHTML(R"HTML(
780 <iframe style="width: 275px; height: 250px; will-change: transform">
781 </iframe>
782 )HTML");
783 auto* child_frame =
784 To<WebLocalFrameImpl>(GetWebView()->MainFrameImpl()->FirstChild());
785 frame_test_helpers::LoadHTMLString(child_frame, R"HTML(
786 <p style="margin: 1000px"> Hello </p>
787 <script>
788 window.addEventListener('touchstart', (e) => {
789 e.preventDefault();
790 }, {passive: false});
791 </script>
792 )HTML",
793 url_test_helpers::ToKURL("about:blank"));
794 ForceFullCompositingUpdate();
795
796 const auto* child_cc_layer =
797 FrameScrollingContentsLayer(*child_frame->GetFrame());
798 cc::Region region_child_frame =
799 child_cc_layer->touch_action_region().GetRegionForTouchAction(
800 TouchAction::kNone);
801 cc::Region region_main_frame =
802 MainFrameScrollingContentsLayer()
803 ->touch_action_region()
804 .GetRegionForTouchAction(TouchAction::kNone);
805 EXPECT_TRUE(region_main_frame.bounds().IsEmpty());
806 EXPECT_FALSE(region_child_frame.bounds().IsEmpty());
807 // We only check for the content size for verification as the offset is 0x0
808 // due to child frame having its own composited layer.
809
810 // Because touch action rects are painted on the scrolling contents layer,
811 // the size of the rect should be equal to the entire scrolling contents area.
812 EXPECT_EQ(gfx::Rect(child_cc_layer->bounds()), region_child_frame.bounds());
813 }
814
TEST_P(ScrollingTest,WindowTouchEventHandler)815 TEST_P(ScrollingTest, WindowTouchEventHandler) {
816 LoadHTML(R"HTML(
817 <style>
818 html { width: 200px; height: 200px; }
819 body { width: 100px; height: 100px; }
820 </style>
821 <script>
822 window.addEventListener('touchstart', function(event) {
823 event.preventDefault();
824 }, {passive: false} );
825 </script>
826 )HTML");
827 ForceFullCompositingUpdate();
828
829 auto* cc_layer = MainFrameScrollingContentsLayer();
830
831 // The touch action region should include the entire frame, even though the
832 // document is smaller than the frame.
833 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
834 TouchAction::kNone);
835 EXPECT_EQ(region.bounds(), gfx::Rect(0, 0, 320, 240));
836 }
837
838 namespace {
839 class ScrollingTestMockEventListener final : public NativeEventListener {
840 public:
Invoke(ExecutionContext *,Event *)841 void Invoke(ExecutionContext*, Event*) override {}
842 };
843 } // namespace
844
TEST_P(ScrollingTest,WindowTouchEventHandlerInvalidation)845 TEST_P(ScrollingTest, WindowTouchEventHandlerInvalidation) {
846 LoadHTML("");
847 ForceFullCompositingUpdate();
848
849 auto* cc_layer = MainFrameScrollingContentsLayer();
850
851 // Initially there are no touch action regions.
852 auto region = cc_layer->touch_action_region().GetRegionForTouchAction(
853 TouchAction::kNone);
854 EXPECT_TRUE(region.IsEmpty());
855
856 // Adding a blocking window event handler should create a touch action region.
857 auto* listener = MakeGarbageCollected<ScrollingTestMockEventListener>();
858 auto* resolved_options =
859 MakeGarbageCollected<AddEventListenerOptionsResolved>();
860 resolved_options->setPassive(false);
861 GetFrame()->DomWindow()->addEventListener(event_type_names::kTouchstart,
862 listener, resolved_options);
863 ForceFullCompositingUpdate();
864 region = cc_layer->touch_action_region().GetRegionForTouchAction(
865 TouchAction::kNone);
866 EXPECT_FALSE(region.IsEmpty());
867
868 // Removing the window event handler also removes the blocking touch action
869 // region.
870 GetFrame()->DomWindow()->RemoveAllEventListeners();
871 ForceFullCompositingUpdate();
872 region = cc_layer->touch_action_region().GetRegionForTouchAction(
873 TouchAction::kNone);
874 EXPECT_TRUE(region.IsEmpty());
875 }
876
877 // Ensure we don't crash when a plugin becomes a LayoutInline
TEST_P(ScrollingTest,PluginBecomesLayoutInline)878 TEST_P(ScrollingTest, PluginBecomesLayoutInline) {
879 LoadHTML(R"HTML(
880 <style>
881 body {
882 margin: 0;
883 height: 3000px;
884 }
885 </style>
886 <object id="plugin" type="application/x-webkit-test-plugin"></object>
887 <script>
888 document.getElementById("plugin")
889 .appendChild(document.createElement("label"))
890 </script>
891 )HTML");
892
893 // This test passes if it doesn't crash. We're trying to make sure
894 // ScrollingCoordinator can deal with LayoutInline plugins when generating
895 // NonFastScrollableRegions.
896 auto* plugin = To<HTMLObjectElement>(
897 GetFrame()->GetDocument()->getElementById("plugin"));
898 ASSERT_TRUE(plugin->GetLayoutObject()->IsLayoutInline());
899 ForceFullCompositingUpdate();
900 }
901
902 // Ensure NonFastScrollableRegions are correctly generated for both fixed and
903 // in-flow plugins that need them.
TEST_P(ScrollingTest,NonFastScrollableRegionsForPlugins)904 TEST_P(ScrollingTest, NonFastScrollableRegionsForPlugins) {
905 LoadHTML(R"HTML(
906 <style>
907 body {
908 margin: 0;
909 height: 3000px;
910 }
911 #plugin {
912 width: 300px;
913 height: 300px;
914 }
915 #pluginfixed {
916 width: 200px;
917 height: 200px;
918 }
919 #fixed {
920 position: fixed;
921 top: 500px;
922 }
923 </style>
924 <div id="fixed">
925 <object id="pluginfixed" type="application/x-webkit-test-plugin"></object>
926 </div>
927 <object id="plugin" type="application/x-webkit-test-plugin"></object>
928 )HTML");
929
930 auto* plugin = To<HTMLObjectElement>(
931 GetFrame()->GetDocument()->getElementById("plugin"));
932 auto* plugin_fixed = To<HTMLObjectElement>(
933 GetFrame()->GetDocument()->getElementById("pluginfixed"));
934 // NonFastScrollableRegions are generated for plugins that require wheel
935 // events.
936 plugin->OwnedPlugin()->SetWantsWheelEvents(true);
937 plugin_fixed->OwnedPlugin()->SetWantsWheelEvents(true);
938
939 ForceFullCompositingUpdate();
940
941 // The non-fixed plugin should create a non-fast scrollable region in the
942 // scrolling contents layer of the LayoutView.
943 auto* viewport_non_fast_layer = MainFrameScrollingContentsLayer();
944 EXPECT_EQ(viewport_non_fast_layer->non_fast_scrollable_region().bounds(),
945 gfx::Rect(0, 0, 300, 300));
946
947 // The fixed plugin should create a non-fast scrollable region in a fixed
948 // cc::Layer.
949 auto* fixed_layer = LayerByDOMElementId("fixed");
950 EXPECT_EQ(fixed_layer->non_fast_scrollable_region().bounds(),
951 gfx::Rect(0, 0, 200, 200));
952 }
953
TEST_P(ScrollingTest,NonFastScrollableRegionWithBorder)954 TEST_P(ScrollingTest, NonFastScrollableRegionWithBorder) {
955 GetWebView()->GetPage()->GetSettings().SetPreferCompositingToLCDTextEnabled(
956 false);
957 LoadHTML(R"HTML(
958 <!DOCTYPE html>
959 <style>
960 body { margin: 0; }
961 #scroller {
962 height: 100px;
963 width: 100px;
964 overflow-y: scroll;
965 border: 10px solid black;
966 }
967 </style>
968 <div id="scroller">
969 <div id="forcescroll" style="height: 1000px;"></div>
970 </div>
971 )HTML");
972 ForceFullCompositingUpdate();
973
974 auto* non_fast_layer = MainFrameScrollingContentsLayer();
975 EXPECT_EQ(non_fast_layer->non_fast_scrollable_region().bounds(),
976 gfx::Rect(0, 0, 120, 120));
977 }
978
TEST_P(ScrollingTest,overflowScrolling)979 TEST_P(ScrollingTest, overflowScrolling) {
980 RegisterMockedHttpURLLoad("overflow-scrolling.html");
981 NavigateTo(base_url_ + "overflow-scrolling.html");
982 ForceFullCompositingUpdate();
983
984 // Verify the scroll node of the accelerated scrolling element.
985 const auto* scroll_node = ScrollNodeByDOMElementId("scrollable");
986 ASSERT_TRUE(scroll_node);
987 EXPECT_TRUE(scroll_node->user_scrollable_horizontal);
988 EXPECT_TRUE(scroll_node->user_scrollable_vertical);
989
990 EXPECT_TRUE(ScrollbarLayerForScrollNode(scroll_node, cc::HORIZONTAL));
991 EXPECT_TRUE(ScrollbarLayerForScrollNode(scroll_node, cc::VERTICAL));
992 }
993
TEST_P(ScrollingTest,overflowHidden)994 TEST_P(ScrollingTest, overflowHidden) {
995 RegisterMockedHttpURLLoad("overflow-hidden.html");
996 NavigateTo(base_url_ + "overflow-hidden.html");
997 ForceFullCompositingUpdate();
998
999 // Verify the scroll node of the accelerated scrolling element.
1000 const auto* scroll_node = ScrollNodeByDOMElementId("unscrollable-y");
1001 ASSERT_TRUE(scroll_node);
1002 EXPECT_TRUE(scroll_node->user_scrollable_horizontal);
1003 EXPECT_FALSE(scroll_node->user_scrollable_vertical);
1004
1005 scroll_node = ScrollNodeByDOMElementId("unscrollable-x");
1006 ASSERT_TRUE(scroll_node);
1007 EXPECT_FALSE(scroll_node->user_scrollable_horizontal);
1008 EXPECT_TRUE(scroll_node->user_scrollable_vertical);
1009 }
1010
TEST_P(ScrollingTest,iframeScrolling)1011 TEST_P(ScrollingTest, iframeScrolling) {
1012 RegisterMockedHttpURLLoad("iframe-scrolling.html");
1013 RegisterMockedHttpURLLoad("iframe-scrolling-inner.html");
1014 NavigateTo(base_url_ + "iframe-scrolling.html");
1015 ForceFullCompositingUpdate();
1016
1017 Element* scrollable_frame =
1018 GetFrame()->GetDocument()->getElementById("scrollable");
1019 ASSERT_TRUE(scrollable_frame);
1020
1021 LayoutObject* layout_object = scrollable_frame->GetLayoutObject();
1022 ASSERT_TRUE(layout_object);
1023 ASSERT_TRUE(layout_object->IsLayoutEmbeddedContent());
1024
1025 LayoutEmbeddedContent* layout_embedded_content =
1026 ToLayoutEmbeddedContent(layout_object);
1027 ASSERT_TRUE(layout_embedded_content);
1028
1029 LocalFrameView* inner_frame_view =
1030 To<LocalFrameView>(layout_embedded_content->ChildFrameView());
1031 ASSERT_TRUE(inner_frame_view);
1032
1033 // Verify the scroll node of the accelerated scrolling iframe.
1034 const auto* scroll_node =
1035 ScrollNodeForScrollableArea(inner_frame_view->LayoutViewport());
1036 ASSERT_TRUE(scroll_node);
1037 EXPECT_TRUE(ScrollbarLayerForScrollNode(scroll_node, cc::HORIZONTAL));
1038 EXPECT_TRUE(ScrollbarLayerForScrollNode(scroll_node, cc::VERTICAL));
1039 }
1040
TEST_P(ScrollingTest,rtlIframe)1041 TEST_P(ScrollingTest, rtlIframe) {
1042 RegisterMockedHttpURLLoad("rtl-iframe.html");
1043 RegisterMockedHttpURLLoad("rtl-iframe-inner.html");
1044 NavigateTo(base_url_ + "rtl-iframe.html");
1045 ForceFullCompositingUpdate();
1046
1047 Element* scrollable_frame =
1048 GetFrame()->GetDocument()->getElementById("scrollable");
1049 ASSERT_TRUE(scrollable_frame);
1050
1051 LayoutObject* layout_object = scrollable_frame->GetLayoutObject();
1052 ASSERT_TRUE(layout_object);
1053 ASSERT_TRUE(layout_object->IsLayoutEmbeddedContent());
1054
1055 LayoutEmbeddedContent* layout_embedded_content =
1056 ToLayoutEmbeddedContent(layout_object);
1057 ASSERT_TRUE(layout_embedded_content);
1058
1059 LocalFrameView* inner_frame_view =
1060 To<LocalFrameView>(layout_embedded_content->ChildFrameView());
1061 ASSERT_TRUE(inner_frame_view);
1062
1063 // Verify the scroll node of the accelerated scrolling iframe.
1064 const auto* scroll_node =
1065 ScrollNodeForScrollableArea(inner_frame_view->LayoutViewport());
1066 ASSERT_TRUE(scroll_node);
1067
1068 int expected_scroll_position = 958 + (inner_frame_view->LayoutViewport()
1069 ->VerticalScrollbar()
1070 ->IsOverlayScrollbar()
1071 ? 0
1072 : 15);
1073 ASSERT_EQ(expected_scroll_position, CurrentScrollOffset(scroll_node).x());
1074 }
1075
TEST_P(ScrollingTest,setupScrollbarLayerShouldNotCrash)1076 TEST_P(ScrollingTest, setupScrollbarLayerShouldNotCrash) {
1077 RegisterMockedHttpURLLoad("setup_scrollbar_layer_crash.html");
1078 NavigateTo(base_url_ + "setup_scrollbar_layer_crash.html");
1079 ForceFullCompositingUpdate();
1080 // This test document setup an iframe with scrollbars, then switch to
1081 // an empty document by javascript.
1082 }
1083
1084 #if defined(OS_MACOSX) || defined(OS_ANDROID)
TEST_P(ScrollingTest,DISABLED_setupScrollbarLayerShouldSetScrollLayerOpaque)1085 TEST_P(ScrollingTest, DISABLED_setupScrollbarLayerShouldSetScrollLayerOpaque)
1086 #else
1087 TEST_P(ScrollingTest, setupScrollbarLayerShouldSetScrollLayerOpaque)
1088 #endif
1089 {
1090 ScopedMockOverlayScrollbars mock_overlay_scrollbar(false);
1091
1092 RegisterMockedHttpURLLoad("wide_document.html");
1093 NavigateTo(base_url_ + "wide_document.html");
1094 ForceFullCompositingUpdate();
1095
1096 LocalFrameView* frame_view = GetFrame()->View();
1097 ASSERT_TRUE(frame_view);
1098
1099 const auto* scroll_node =
1100 ScrollNodeForScrollableArea(frame_view->LayoutViewport());
1101 ASSERT_TRUE(scroll_node);
1102
1103 const auto* horizontal_scrollbar_layer =
1104 ScrollbarLayerForScrollNode(scroll_node, cc::HORIZONTAL);
1105 ASSERT_TRUE(horizontal_scrollbar_layer);
1106 // TODO(crbug.com/1029620): CAP needs more accurate contents_opaque.
1107 if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
1108 EXPECT_EQ(!frame_view->LayoutViewport()
1109 ->HorizontalScrollbar()
1110 ->IsOverlayScrollbar(),
1111 horizontal_scrollbar_layer->contents_opaque());
1112 }
1113
1114 EXPECT_FALSE(ScrollbarLayerForScrollNode(scroll_node, cc::VERTICAL));
1115 }
1116
TEST_P(ScrollingTest,NestedIFramesMainThreadScrollingRegion)1117 TEST_P(ScrollingTest, NestedIFramesMainThreadScrollingRegion) {
1118 // This page has an absolute IFRAME. It contains a scrollable child DIV
1119 // that's nested within an intermediate IFRAME.
1120 GetWebView()->GetPage()->GetSettings().SetPreferCompositingToLCDTextEnabled(
1121 false);
1122 LoadHTML(R"HTML(
1123 <!DOCTYPE html>
1124 <style>
1125 #spacer {
1126 height: 10000px;
1127 }
1128 iframe {
1129 position: absolute;
1130 top: 1200px;
1131 left: 0px;
1132 width: 200px;
1133 height: 200px;
1134 border: 0;
1135 }
1136
1137 </style>
1138 <div id="spacer"></div>
1139 <iframe srcdoc="
1140 <!DOCTYPE html>
1141 <style>
1142 body { margin: 0; }
1143 iframe { width: 100px; height: 100px; border: 0; }
1144 </style>
1145 <iframe srcdoc='<!DOCTYPE html>
1146 <style>
1147 body { margin: 0; }
1148 div {
1149 width: 65px;
1150 height: 65px;
1151 overflow: auto;
1152 }
1153 p {
1154 width: 300px;
1155 height: 300px;
1156 }
1157 </style>
1158 <div>
1159 <p></p>
1160 </div>'>
1161 </iframe>">
1162 </iframe>
1163 )HTML");
1164
1165 ForceFullCompositingUpdate();
1166
1167 // Scroll the frame to ensure the rect is in the correct coordinate space.
1168 GetFrame()->GetDocument()->View()->GetScrollableArea()->SetScrollOffset(
1169 ScrollOffset(0, 1000), mojom::blink::ScrollType::kProgrammatic);
1170
1171 ForceFullCompositingUpdate();
1172
1173 auto* non_fast_layer = MainFrameScrollingContentsLayer();
1174 EXPECT_EQ(non_fast_layer->non_fast_scrollable_region().bounds(),
1175 gfx::Rect(0, 1200, 65, 65));
1176 }
1177
1178 // Same as above but test that the rect is correctly calculated into the fixed
1179 // region when the containing iframe is position: fixed.
TEST_P(ScrollingTest,NestedFixedIFramesMainThreadScrollingRegion)1180 TEST_P(ScrollingTest, NestedFixedIFramesMainThreadScrollingRegion) {
1181 // This page has a fixed IFRAME. It contains a scrollable child DIV that's
1182 // nested within an intermediate IFRAME.
1183 GetWebView()->GetPage()->GetSettings().SetPreferCompositingToLCDTextEnabled(
1184 false);
1185 LoadHTML(R"HTML(
1186 <!DOCTYPE html>
1187 <style>
1188 #spacer {
1189 height: 10000px;
1190 }
1191 #iframe {
1192 position: fixed;
1193 top: 20px;
1194 left: 0px;
1195 width: 200px;
1196 height: 200px;
1197 border: 20px solid blue;
1198 }
1199
1200 </style>
1201 <div id="spacer"></div>
1202 <iframe id="iframe" srcdoc="
1203 <!DOCTYPE html>
1204 <style>
1205 body { margin: 0; }
1206 iframe { width: 100px; height: 100px; border: 0; }
1207 </style>
1208 <iframe srcdoc='<!DOCTYPE html>
1209 <style>
1210 body { margin: 0; }
1211 div {
1212 width: 75px;
1213 height: 75px;
1214 overflow: auto;
1215 }
1216 p {
1217 width: 300px;
1218 height: 300px;
1219 }
1220 </style>
1221 <div>
1222 <p></p>
1223 </div>'>
1224 </iframe>">
1225 </iframe>
1226 )HTML");
1227
1228 ForceFullCompositingUpdate();
1229
1230 // Scroll the frame to ensure the rect is in the correct coordinate space.
1231 GetFrame()->GetDocument()->View()->GetScrollableArea()->SetScrollOffset(
1232 ScrollOffset(0, 1000), mojom::blink::ScrollType::kProgrammatic);
1233
1234 ForceFullCompositingUpdate();
1235 auto* non_fast_layer = LayerByDOMElementId("iframe");
1236 EXPECT_EQ(non_fast_layer->non_fast_scrollable_region().bounds(),
1237 gfx::Rect(20, 20, 75, 75));
1238 }
1239
TEST_P(ScrollingTest,IframeCompositedScrollingHideAndShow)1240 TEST_P(ScrollingTest, IframeCompositedScrollingHideAndShow) {
1241 GetWebView()->GetPage()->GetSettings().SetPreferCompositingToLCDTextEnabled(
1242 false);
1243 LoadHTML(R"HTML(
1244 <!DOCTYPE html>
1245 <style>
1246 body {
1247 margin: 0;
1248 }
1249 iframe {
1250 height: 100px;
1251 width: 100px;
1252 }
1253 </style>
1254 <iframe id="iframe" srcdoc="
1255 <!DOCTYPE html>
1256 <style>
1257 body {height: 1000px;}
1258 </style>"></iframe>
1259 )HTML");
1260
1261 ForceFullCompositingUpdate();
1262
1263 const auto* non_fast_layer = MainFrameScrollingContentsLayer();
1264
1265 // Should have a NFSR initially.
1266 EXPECT_EQ(non_fast_layer->non_fast_scrollable_region().bounds(),
1267 gfx::Rect(2, 2, 100, 100));
1268
1269 // Hiding the iframe should clear the NFSR.
1270 Element* iframe = GetFrame()->GetDocument()->getElementById("iframe");
1271 iframe->setAttribute(html_names::kStyleAttr, "display: none");
1272 ForceFullCompositingUpdate();
1273 EXPECT_TRUE(non_fast_layer->non_fast_scrollable_region().bounds().IsEmpty());
1274
1275 // Showing it again should compute the NFSR.
1276 iframe->setAttribute(html_names::kStyleAttr, "");
1277 ForceFullCompositingUpdate();
1278 EXPECT_EQ(non_fast_layer->non_fast_scrollable_region().bounds(),
1279 gfx::Rect(2, 2, 100, 100));
1280 }
1281
1282 // Same as above but the main frame is scrollable. This should cause the non
1283 // fast scrollable regions to go on the outer viewport's scroll layer.
TEST_P(ScrollingTest,IframeCompositedScrollingHideAndShowScrollable)1284 TEST_P(ScrollingTest, IframeCompositedScrollingHideAndShowScrollable) {
1285 GetWebView()->GetPage()->GetSettings().SetPreferCompositingToLCDTextEnabled(
1286 false);
1287 LoadHTML(R"HTML(
1288 <!DOCTYPE html>
1289 <style>
1290 body {
1291 height: 1000px;
1292 margin: 0;
1293 }
1294 iframe {
1295 height: 100px;
1296 width: 100px;
1297 }
1298 </style>
1299 <iframe id="iframe" srcdoc="
1300 <!DOCTYPE html>
1301 <style>
1302 body {height: 1000px;}
1303 </style>"></iframe>
1304 )HTML");
1305
1306 ForceFullCompositingUpdate();
1307
1308 Page* page = GetFrame()->GetPage();
1309 const auto* inner_viewport_scroll_layer =
1310 page->GetVisualViewport().LayerForScrolling();
1311 Element* iframe = GetFrame()->GetDocument()->getElementById("iframe");
1312
1313 const auto* outer_viewport_scroll_layer = MainFrameScrollingContentsLayer();
1314
1315 // Should have a NFSR initially.
1316 ForceFullCompositingUpdate();
1317 EXPECT_FALSE(outer_viewport_scroll_layer->non_fast_scrollable_region()
1318 .bounds()
1319 .IsEmpty());
1320
1321 // Ensure the visual viewport's scrolling layer didn't get an NFSR.
1322 EXPECT_TRUE(inner_viewport_scroll_layer->non_fast_scrollable_region()
1323 .bounds()
1324 .IsEmpty());
1325
1326 // Hiding the iframe should clear the NFSR.
1327 iframe->setAttribute(html_names::kStyleAttr, "display: none");
1328 ForceFullCompositingUpdate();
1329 EXPECT_TRUE(outer_viewport_scroll_layer->non_fast_scrollable_region()
1330 .bounds()
1331 .IsEmpty());
1332
1333 // Showing it again should compute the NFSR.
1334 iframe->setAttribute(html_names::kStyleAttr, "");
1335 ForceFullCompositingUpdate();
1336 EXPECT_FALSE(outer_viewport_scroll_layer->non_fast_scrollable_region()
1337 .bounds()
1338 .IsEmpty());
1339 }
1340
TEST_P(ScrollingTest,ScrollOffsetClobberedBeforeCompositingUpdate)1341 TEST_P(ScrollingTest, ScrollOffsetClobberedBeforeCompositingUpdate) {
1342 LoadHTML(R"HTML(
1343 <!DOCTYPE html>
1344 <style>
1345 #container {
1346 width: 300px;
1347 height: 300px;
1348 overflow: auto;
1349 will-change: transform;
1350 }
1351 #spacer {
1352 height: 1000px;
1353 }
1354 </style>
1355 <div id="container">
1356 <div id="spacer"></div>
1357 </div>
1358 )HTML");
1359 ForceFullCompositingUpdate();
1360
1361 auto* scrollable_area = GetFrame()
1362 ->GetDocument()
1363 ->getElementById("container")
1364 ->GetScrollableArea();
1365 ASSERT_EQ(0, scrollable_area->GetScrollOffset().Height());
1366 const auto* scroll_node = ScrollNodeForScrollableArea(scrollable_area);
1367
1368 // Simulate 100px of scroll coming from the compositor thread during a commit.
1369 gfx::ScrollOffset compositor_delta(0, 100.f);
1370 cc::ScrollAndScaleSet scroll_and_scale_set;
1371 scroll_and_scale_set.scrolls.push_back(
1372 {scrollable_area->GetScrollElementId(), compositor_delta, base::nullopt});
1373 RootCcLayer()->layer_tree_host()->ApplyScrollAndScale(&scroll_and_scale_set);
1374 // The compositor offset is reflected in blink and cc scroll tree.
1375 EXPECT_EQ(compositor_delta,
1376 gfx::ScrollOffset(scrollable_area->ScrollPosition()));
1377 EXPECT_EQ(compositor_delta, CurrentScrollOffset(scroll_node));
1378
1379 // Before updating the lifecycle, set the scroll offset back to what it was
1380 // before the commit from the main thread.
1381 scrollable_area->SetScrollOffset(ScrollOffset(0, 0),
1382 mojom::blink::ScrollType::kProgrammatic);
1383
1384 // Ensure the offset is up-to-date on the cc::Layer even though, as far as
1385 // the main thread is concerned, it was unchanged since the last time we
1386 // pushed the scroll offset.
1387 ForceFullCompositingUpdate();
1388 EXPECT_EQ(gfx::ScrollOffset(), CurrentScrollOffset(scroll_node));
1389 }
1390
TEST_P(ScrollingTest,UpdateVisualViewportScrollLayer)1391 TEST_P(ScrollingTest, UpdateVisualViewportScrollLayer) {
1392 LoadHTML(R"HTML(
1393 <!DOCTYPE html>
1394 <style>
1395 #box {
1396 width: 300px;
1397 height: 1000px;
1398 background-color: red;
1399 }
1400 </style>
1401 <div id="box">
1402 </div>
1403 )HTML");
1404 ForceFullCompositingUpdate();
1405
1406 Page* page = GetFrame()->GetPage();
1407 const auto* inner_viewport_scroll_node =
1408 ScrollNodeForScrollableArea(&page->GetVisualViewport());
1409
1410 page->GetVisualViewport().SetScale(2);
1411 ForceFullCompositingUpdate();
1412 EXPECT_EQ(gfx::ScrollOffset(0, 0),
1413 CurrentScrollOffset(inner_viewport_scroll_node));
1414
1415 page->GetVisualViewport().SetLocation(FloatPoint(10, 20));
1416 ForceFullCompositingUpdate();
1417 EXPECT_EQ(gfx::ScrollOffset(10, 20),
1418 CurrentScrollOffset(inner_viewport_scroll_node));
1419 }
1420
TEST_P(ScrollingTest,UpdateUMAMetricUpdated)1421 TEST_P(ScrollingTest, UpdateUMAMetricUpdated) {
1422 // The metrics are recorced in ScrollingCoordinator::UpdateAfterPaint() which
1423 // is not called in CompositeAfterPaint.
1424 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
1425 return;
1426
1427 HistogramTester histogram_tester;
1428 LoadHTML(R"HTML(
1429 <div id='bg' style='background: blue;'></div>
1430 <div id='scroller' style='overflow: scroll; width: 10px; height: 10px; background: blue'>
1431 <div id='forcescroll' style='height: 1000px;'></div>
1432 </div>
1433 )HTML");
1434
1435 // The initial counts should be zero.
1436 histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 0);
1437 histogram_tester.ExpectTotalCount(
1438 "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 0);
1439 histogram_tester.ExpectTotalCount(
1440 "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
1441 histogram_tester.ExpectTotalCount(
1442 "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 0);
1443
1444 // After an initial compositing update, we should have one scrolling update
1445 // recorded as PreFCP.
1446 GetWebView()->MainFrameWidgetBase()->RecordStartOfFrameMetrics();
1447 ForceFullCompositingUpdate();
1448 GetWebView()->MainFrameWidgetBase()->RecordEndOfFrameMetrics(
1449 base::TimeTicks(), 0);
1450 histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 1);
1451 histogram_tester.ExpectTotalCount(
1452 "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
1453 histogram_tester.ExpectTotalCount(
1454 "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
1455 histogram_tester.ExpectTotalCount(
1456 "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 0);
1457
1458 // An update with no scrolling changes should not cause a scrolling update.
1459 GetWebView()->MainFrameWidgetBase()->RecordStartOfFrameMetrics();
1460 ForceFullCompositingUpdate();
1461 GetWebView()->MainFrameWidgetBase()->RecordEndOfFrameMetrics(
1462 base::TimeTicks(), 0);
1463 histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 1);
1464 histogram_tester.ExpectTotalCount(
1465 "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
1466 histogram_tester.ExpectTotalCount(
1467 "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
1468 histogram_tester.ExpectTotalCount(
1469 "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 0);
1470
1471 // A change to background color does not need to cause a scrolling update but,
1472 // because we record hit test data, we also cause a scrolling coordinator
1473 // update when the background paints. Also render some text to get past FCP.
1474 // Note that this frame is still considered pre-FCP.
1475 auto* background = GetFrame()->GetDocument()->getElementById("bg");
1476 background->removeAttribute(html_names::kStyleAttr);
1477 background->setInnerHTML("Some Text");
1478 GetWebView()->MainFrameWidgetBase()->RecordStartOfFrameMetrics();
1479 ForceFullCompositingUpdate();
1480 GetWebView()->MainFrameWidgetBase()->RecordEndOfFrameMetrics(
1481 base::TimeTicks(), 0);
1482 histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 2);
1483 histogram_tester.ExpectTotalCount(
1484 "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 2);
1485 histogram_tester.ExpectTotalCount(
1486 "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
1487 histogram_tester.ExpectTotalCount(
1488 "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 1);
1489
1490 // Removing a scrollable area should cause a scrolling update.
1491 auto* scroller = GetFrame()->GetDocument()->getElementById("scroller");
1492 scroller->removeAttribute(html_names::kStyleAttr);
1493 GetWebView()->MainFrameWidgetBase()->RecordStartOfFrameMetrics();
1494 ForceFullCompositingUpdate();
1495 GetWebView()->MainFrameWidgetBase()->RecordEndOfFrameMetrics(
1496 base::TimeTicks(), 0);
1497 histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 3);
1498 histogram_tester.ExpectTotalCount(
1499 "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 2);
1500 histogram_tester.ExpectTotalCount(
1501 "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 1);
1502 histogram_tester.ExpectTotalCount(
1503 "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 1);
1504 }
1505
TEST_P(ScrollingTest,NonCompositedNonFastScrollableRegion)1506 TEST_P(ScrollingTest, NonCompositedNonFastScrollableRegion) {
1507 GetWebView()->GetPage()->GetSettings().SetPreferCompositingToLCDTextEnabled(
1508 false);
1509 LoadHTML(R"HTML(
1510 <!DOCTYPE html>
1511 <style>
1512 body { margin: 0; }
1513 #composited_container {
1514 will-change: transform;
1515 border: 20px solid blue;
1516 }
1517 #scroller {
1518 height: 200px;
1519 width: 200px;
1520 overflow-y: scroll;
1521 }
1522 </style>
1523 <div id="composited_container">
1524 <div id="scroller">
1525 <div id="forcescroll" style="height: 1000px;"></div>
1526 </div>
1527 </div>
1528 )HTML");
1529 ForceFullCompositingUpdate();
1530
1531 // The non-scrolling graphics layer should have a non-scrolling region for the
1532 // non-composited scroller.
1533 const auto* cc_layer = LayerByDOMElementId("composited_container");
1534 auto region = cc_layer->non_fast_scrollable_region();
1535 EXPECT_EQ(region.bounds(), gfx::Rect(20, 20, 200, 200));
1536 }
1537
TEST_P(ScrollingTest,NonCompositedResizerNonFastScrollableRegion)1538 TEST_P(ScrollingTest, NonCompositedResizerNonFastScrollableRegion) {
1539 GetWebView()->GetPage()->GetSettings().SetPreferCompositingToLCDTextEnabled(
1540 false);
1541 LoadHTML(R"HTML(
1542 <style>
1543 #container {
1544 will-change: transform;
1545 border: 20px solid blue;
1546 }
1547 #scroller {
1548 width: 80px;
1549 height: 80px;
1550 resize: both;
1551 overflow-y: scroll;
1552 }
1553 </style>
1554 <div id="container">
1555 <div id="offset" style="height: 35px;"></div>
1556 <div id="scroller"></div>
1557 </div>
1558 )HTML");
1559 ForceFullCompositingUpdate();
1560
1561 auto* container_cc_layer = LayerByDOMElementId("container");
1562 // The non-fast scrollable region should be on the container's graphics layer
1563 // and not one of the viewport scroll layers because the region should move
1564 // when the container moves and not when the viewport scrolls.
1565 auto region = container_cc_layer->non_fast_scrollable_region();
1566 EXPECT_EQ(region.bounds(), gfx::Rect(86, 121, 14, 14));
1567 }
1568
TEST_P(ScrollingTest,CompositedResizerNonFastScrollableRegion)1569 TEST_P(ScrollingTest, CompositedResizerNonFastScrollableRegion) {
1570 LoadHTML(R"HTML(
1571 <style>
1572 #container { will-change: transform; }
1573 #scroller {
1574 will-change: transform;
1575 width: 80px;
1576 height: 80px;
1577 resize: both;
1578 overflow-y: scroll;
1579 }
1580 </style>
1581 <div id="container">
1582 <div id="offset" style="height: 35px;"></div>
1583 <div id="scroller"></div>
1584 </div>
1585 )HTML");
1586 ForceFullCompositingUpdate();
1587
1588 auto region = LayerByDOMElementId("scroller")->non_fast_scrollable_region();
1589 EXPECT_EQ(region.bounds(), gfx::Rect(66, 66, 14, 14));
1590 }
1591
TEST_P(ScrollingTest,TouchActionUpdatesOutsideInterestRect)1592 TEST_P(ScrollingTest, TouchActionUpdatesOutsideInterestRect) {
1593 LoadHTML(R"HTML(
1594 <!DOCTYPE html>
1595 <style>
1596 #scroller {
1597 will-change: transform;
1598 width: 200px;
1599 height: 200px;
1600 background: blue;
1601 overflow-y: scroll;
1602 }
1603 .spacer {
1604 height: 1000px;
1605 }
1606 #touchaction {
1607 height: 100px;
1608 background: yellow;
1609 }
1610 </style>
1611 <div id="scroller">
1612 <div class="spacer"></div>
1613 <div class="spacer"></div>
1614 <div class="spacer"></div>
1615 <div class="spacer"></div>
1616 <div class="spacer"></div>
1617 <div id="touchaction">This should not scroll via touch.</div>
1618 </div>
1619 )HTML");
1620
1621 ForceFullCompositingUpdate();
1622
1623 auto* touch_action = GetFrame()->GetDocument()->getElementById("touchaction");
1624 touch_action->setAttribute(html_names::kStyleAttr, "touch-action: none;");
1625
1626 ForceFullCompositingUpdate();
1627
1628 auto* scroller = GetFrame()->GetDocument()->getElementById("scroller");
1629 scroller->GetScrollableArea()->SetScrollOffset(
1630 ScrollOffset(0, 5100), mojom::blink::ScrollType::kProgrammatic);
1631
1632 ForceFullCompositingUpdate();
1633
1634 auto* cc_layer = ScrollingContentsLayerByDOMElementId("scroller");
1635 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
1636 TouchAction::kNone);
1637 EXPECT_EQ(region.bounds(), gfx::Rect(0, 5000, 200, 100));
1638 }
1639
TEST_P(ScrollingTest,MainThreadScrollAndDeltaFromImplSide)1640 TEST_P(ScrollingTest, MainThreadScrollAndDeltaFromImplSide) {
1641 LoadHTML(R"HTML(
1642 <div id='scroller' style='overflow: scroll; width: 100px; height: 100px'>
1643 <div style='height: 1000px'></div>
1644 </div>
1645 )HTML");
1646 ForceFullCompositingUpdate();
1647
1648 auto* scroller = GetFrame()->GetDocument()->getElementById("scroller");
1649 auto* scrollable_area = scroller->GetLayoutBox()->GetScrollableArea();
1650 auto element_id = scrollable_area->GetScrollElementId();
1651
1652 EXPECT_EQ(gfx::ScrollOffset(), CurrentScrollOffset(element_id));
1653
1654 // Simulate a direct scroll update out of document lifecycle update.
1655 scroller->scrollTo(0, 200);
1656 EXPECT_EQ(FloatPoint(0, 200), scrollable_area->ScrollPosition());
1657 EXPECT_EQ(gfx::ScrollOffset(0, 200), CurrentScrollOffset(element_id));
1658
1659 // Simulate the scroll update with scroll delta from impl-side at the
1660 // beginning of BeginMainFrame.
1661 cc::ScrollAndScaleSet scroll_and_scale;
1662 scroll_and_scale.scrolls.push_back(cc::ScrollAndScaleSet::ScrollUpdateInfo(
1663 element_id, gfx::ScrollOffset(0, 10), base::nullopt));
1664 RootCcLayer()->layer_tree_host()->ApplyScrollAndScale(&scroll_and_scale);
1665 EXPECT_EQ(FloatPoint(0, 210), scrollable_area->ScrollPosition());
1666 EXPECT_EQ(gfx::ScrollOffset(0, 210), CurrentScrollOffset(element_id));
1667 }
1668
1669 class ScrollingTestWithAcceleratedContext : public ScrollingTest {
1670 protected:
SetUp()1671 void SetUp() override {
1672 auto factory = [](FakeGLES2Interface* gl, bool* gpu_compositing_disabled)
1673 -> std::unique_ptr<WebGraphicsContext3DProvider> {
1674 *gpu_compositing_disabled = false;
1675 gl->SetIsContextLost(false);
1676 return std::make_unique<FakeWebGraphicsContext3DProvider>(gl);
1677 };
1678 SharedGpuContext::SetContextProviderFactoryForTesting(
1679 WTF::BindRepeating(factory, WTF::Unretained(&gl_)));
1680 ScrollingTest::SetUp();
1681 }
1682
TearDown()1683 void TearDown() override {
1684 SharedGpuContext::ResetForTesting();
1685 ScrollingTest::TearDown();
1686 }
1687
1688 private:
1689 FakeGLES2Interface gl_;
1690 };
1691
1692 INSTANTIATE_PAINT_TEST_SUITE_P(ScrollingTestWithAcceleratedContext);
1693
TEST_P(ScrollingTestWithAcceleratedContext,CanvasTouchActionRects)1694 TEST_P(ScrollingTestWithAcceleratedContext, CanvasTouchActionRects) {
1695 LoadHTML(R"HTML(
1696 <canvas id="canvas" style="touch-action: none; will-change: transform;">
1697 <script>
1698 var canvas = document.getElementById("canvas");
1699 var ctx = canvas.getContext("2d");
1700 canvas.width = 400;
1701 canvas.height = 400;
1702 ctx.fillStyle = 'lightgrey';
1703 ctx.fillRect(0, 0, 400, 400);
1704 </script>
1705 )HTML");
1706 ForceFullCompositingUpdate();
1707
1708 const auto* cc_layer = LayerByDOMElementId("canvas");
1709 cc::Region region = cc_layer->touch_action_region().GetRegionForTouchAction(
1710 TouchAction::kNone);
1711 EXPECT_EQ(region.bounds(), gfx::Rect(0, 0, 400, 400));
1712 }
1713
1714 } // namespace blink
1715