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