summaryrefslogtreecommitdiff
path: root/chromium/cc/trees
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/cc/trees')
-rw-r--r--chromium/cc/trees/damage_tracker_unittest.cc19
-rw-r--r--chromium/cc/trees/draw_properties_unittest.cc238
-rw-r--r--chromium/cc/trees/draw_property_utils.cc163
-rw-r--r--chromium/cc/trees/draw_property_utils.h12
-rw-r--r--chromium/cc/trees/effect_node.cc7
-rw-r--r--chromium/cc/trees/effect_node.h7
-rw-r--r--chromium/cc/trees/frame_rate_counter.cc133
-rw-r--r--chromium/cc/trees/frame_rate_counter.h60
-rw-r--r--chromium/cc/trees/image_animation_controller.cc21
-rw-r--r--chromium/cc/trees/layer_tree_host.cc72
-rw-r--r--chromium/cc/trees/layer_tree_host.h50
-rw-r--r--chromium/cc/trees/layer_tree_host_client.h10
-rw-r--r--chromium/cc/trees/layer_tree_host_impl.cc419
-rw-r--r--chromium/cc/trees/layer_tree_host_impl.h96
-rw-r--r--chromium/cc/trees/layer_tree_host_impl_unittest.cc1771
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_blending.cc38
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_filters.cc49
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_masks.cc38
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc1
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest.cc137
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc10
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_scroll.cc494
-rw-r--r--chromium/cc/trees/layer_tree_impl.cc111
-rw-r--r--chromium/cc/trees/layer_tree_impl.h21
-rw-r--r--chromium/cc/trees/layer_tree_settings.h8
-rw-r--r--chromium/cc/trees/mutator_host.h1
-rw-r--r--chromium/cc/trees/occlusion_tracker.h2
-rw-r--r--chromium/cc/trees/property_tree.cc46
-rw-r--r--chromium/cc/trees/property_tree.h1
-rw-r--r--chromium/cc/trees/property_tree_builder.cc1
-rw-r--r--chromium/cc/trees/property_tree_builder_unittest.cc2
-rw-r--r--chromium/cc/trees/proxy.h1
-rw-r--r--chromium/cc/trees/proxy_impl.cc52
-rw-r--r--chromium/cc/trees/proxy_impl.h14
-rw-r--r--chromium/cc/trees/proxy_main.cc16
-rw-r--r--chromium/cc/trees/proxy_main.h6
-rw-r--r--chromium/cc/trees/render_frame_metadata.h8
-rw-r--r--chromium/cc/trees/scroll_and_scale_set.h4
-rw-r--r--chromium/cc/trees/single_thread_proxy.cc24
-rw-r--r--chromium/cc/trees/single_thread_proxy.h19
-rw-r--r--chromium/cc/trees/task_runner_provider.h2
-rw-r--r--chromium/cc/trees/transform_node.cc3
-rw-r--r--chromium/cc/trees/transform_node.h4
-rw-r--r--chromium/cc/trees/ukm_manager.cc90
-rw-r--r--chromium/cc/trees/ukm_manager.h13
-rw-r--r--chromium/cc/trees/ukm_manager_unittest.cc353
46 files changed, 3570 insertions, 1077 deletions
diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc
index cc784ab1213..db9fcc1fcf6 100644
--- a/chromium/cc/trees/damage_tracker_unittest.cc
+++ b/chromium/cc/trees/damage_tracker_unittest.cc
@@ -236,6 +236,23 @@ class DamageTrackerTest : public LayerTreeImplTestBase, public testing::Test {
return root;
}
+ LayerImpl* CreateAndSetUpTestTreeWithTwoSurfacesDrawingFullyVisible() {
+ LayerImpl* root = CreateTestTreeWithTwoSurfaces();
+ // Make sure render surface takes content outside visible rect into
+ // consideration.
+ root->layer_tree_impl()
+ ->property_trees()
+ ->effect_tree.Node(child1_->effect_tree_index())
+ ->backdrop_filters.Append(
+ FilterOperation::CreateZoomFilter(2.f /* zoom */, 0 /* inset */));
+
+ // Setup includes going past the first frame which always damages
+ // everything, so that we can actually perform specific tests.
+ EmulateDrawingOneFrame(root);
+
+ return root;
+ }
+
LayerImpl* CreateAndSetUpTestTreeWithFourSurfaces() {
LayerImpl* root = CreateTestTreeWithFourSurfaces();
@@ -1833,7 +1850,7 @@ TEST_F(DamageTrackerTest, DamageRectTooBigWithFilter) {
}
TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) {
- LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
+ LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfacesDrawingFullyVisible();
// Really far left.
grand_child1_->SetOffsetToTransformParent(
diff --git a/chromium/cc/trees/draw_properties_unittest.cc b/chromium/cc/trees/draw_properties_unittest.cc
index 6abe4863df5..b19a402d142 100644
--- a/chromium/cc/trees/draw_properties_unittest.cc
+++ b/chromium/cc/trees/draw_properties_unittest.cc
@@ -12,6 +12,7 @@
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
+#include "base/test/scoped_feature_list.h"
#include "cc/animation/animation.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
@@ -1415,10 +1416,13 @@ TEST_F(DrawPropertiesTest, DrawableContentRectForLayers) {
UpdateActiveTreeDrawProperties();
- EXPECT_EQ(gfx::Rect(5, 5, 10, 10), grand_child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(15, 15, 5, 5), grand_child3->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(15, 15, 5, 5), grand_child3->drawable_content_rect());
- EXPECT_TRUE(grand_child4->drawable_content_rect().IsEmpty());
+ EXPECT_EQ(gfx::Rect(5, 5, 10, 10),
+ grand_child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(15, 15, 5, 5),
+ grand_child3->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(15, 15, 5, 5),
+ grand_child3->visible_drawable_content_rect());
+ EXPECT_TRUE(grand_child4->visible_drawable_content_rect().IsEmpty());
}
TEST_F(DrawPropertiesTest, ClipRectIsPropagatedCorrectlyToSurfaces) {
@@ -1775,7 +1779,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsForIdentityTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 2: Layer is outside the surface rect.
layer_content_rect = gfx::Rect(120, 120, 30, 30);
@@ -1785,7 +1789,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsForIdentityTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 3: Layer is partially overlapping the surface rect.
layer_content_rect = gfx::Rect(80, 80, 30, 30);
@@ -1795,7 +1799,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsForIdentityTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// Test visible layer rect and drawable content rect are calculated correctly
@@ -1815,7 +1819,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor2DRotations) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 2: Layer is outside the surface rect.
layer_to_surface_transform.MakeIdentity();
@@ -1827,7 +1831,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor2DRotations) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 3: The layer is rotated about its top-left corner. In surface space,
// the layer is oriented diagonally, with the left half outside of the render
@@ -1842,7 +1846,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor2DRotations) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 4: The layer is rotated about its top-left corner, and translated
// upwards. In surface space, the layer is oriented diagonally, with only the
@@ -1859,7 +1863,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor2DRotations) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// Test visible layer rect and drawable content rect are calculated correctly
@@ -1880,7 +1884,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dOrthographicTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 2: Orthographic projection of a layer rotated about y-axis by 45
// degrees, but shifted to the side so only the right-half the layer would be
@@ -1899,7 +1903,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dOrthographicTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// Test visible layer rect and drawable content rect are calculated correctly
@@ -1931,7 +1935,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dPerspectiveTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
// Case 2: same projection as before, except that the layer is also translated
// to the side, so that only the right half of the layer should be visible.
@@ -1951,7 +1955,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dPerspectiveTransform) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// There is currently no explicit concept of an orthographic projection plane
@@ -1979,7 +1983,7 @@ TEST_F(DrawPropertiesDrawRectsTest,
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
// Test visible layer rect and drawable content rect are calculated correctly
@@ -2018,7 +2022,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsFor3dPerspectiveWhenClippedByW) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
static bool ProjectionClips(const gfx::Transform& map_transform,
@@ -2070,7 +2074,7 @@ TEST_F(DrawPropertiesDrawRectsTest, DrawRectsForPerspectiveUnprojection) {
target_surface_rect, layer_to_surface_transform, layer_content_rect);
EXPECT_EQ(expected_visible_layer_rect, drawing_layer->visible_layer_rect());
EXPECT_EQ(expected_drawable_content_rect,
- drawing_layer->drawable_content_rect());
+ drawing_layer->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsForSimpleLayers) {
@@ -2106,10 +2110,14 @@ TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsForSimpleLayers) {
EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2_layer->visible_layer_rect());
EXPECT_TRUE(child3_layer->visible_layer_rect().IsEmpty());
- // layer drawable_content_rects are not clipped.
- EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1_layer->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 50, 50), child2_layer->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(125, 125, 50, 50), child3_layer->drawable_content_rect());
+ // layer visible_drawable_content_rects are in target space, but still only
+ // the visible part.
+ EXPECT_EQ(gfx::Rect(0, 0, 50, 50),
+ child1_layer->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25),
+ child2_layer->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(125, 125, 0, 0),
+ child3_layer->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest,
@@ -2153,9 +2161,11 @@ TEST_F(DrawPropertiesTest,
EXPECT_TRUE(grand_child3->visible_layer_rect().IsEmpty());
// All grandchild DrawableContentRects should also be clipped by child.
- EXPECT_EQ(gfx::Rect(5, 5, 50, 50), grand_child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 25, 25), grand_child2->drawable_content_rect());
- EXPECT_TRUE(grand_child3->drawable_content_rect().IsEmpty());
+ EXPECT_EQ(gfx::Rect(5, 5, 50, 50),
+ grand_child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25),
+ grand_child2->visible_drawable_content_rect());
+ EXPECT_TRUE(grand_child3->visible_drawable_content_rect().IsEmpty());
}
TEST_F(DrawPropertiesTest, VisibleContentRectWithClippingAndScaling) {
@@ -2286,7 +2296,7 @@ TEST_F(DrawPropertiesTest,
// An unclipped surface grows its DrawableContentRect to include all drawable
// regions of the subtree.
- EXPECT_EQ(gfx::RectF(5.f, 5.f, 170.f, 170.f),
+ EXPECT_EQ(gfx::RectF(5.f, 5.f, 95.f, 95.f),
GetRenderSurface(render_surface)->DrawableContentRect());
// All layers that draw content into the unclipped surface are also unclipped.
@@ -2295,9 +2305,9 @@ TEST_F(DrawPropertiesTest,
EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2->visible_layer_rect());
EXPECT_EQ(gfx::Rect(0, 0, 0, 0), child3->visible_layer_rect());
- EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 50, 50), child2->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(125, 125, 50, 50), child3->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25), child2->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(125, 125, 0, 0), child3->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest, VisibleContentRectsForClippedSurfaceWithEmptyClip) {
@@ -2361,7 +2371,7 @@ TEST_F(DrawPropertiesTest,
UpdateActiveTreeDrawProperties();
EXPECT_TRUE(child->visible_layer_rect().IsEmpty());
- EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
+ EXPECT_TRUE(child->visible_drawable_content_rect().IsEmpty());
// Case 2: a matrix with flattened z, uninvertible and not visible according
// to the CSS spec.
@@ -2373,7 +2383,7 @@ TEST_F(DrawPropertiesTest,
UpdateActiveTreeDrawProperties();
EXPECT_TRUE(child->visible_layer_rect().IsEmpty());
- EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
+ EXPECT_TRUE(child->visible_drawable_content_rect().IsEmpty());
// Case 3: a matrix with flattened z, also uninvertible and not visible.
uninvertible_matrix.MakeIdentity();
@@ -2385,7 +2395,7 @@ TEST_F(DrawPropertiesTest,
UpdateActiveTreeDrawProperties();
EXPECT_TRUE(child->visible_layer_rect().IsEmpty());
- EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
+ EXPECT_TRUE(child->visible_drawable_content_rect().IsEmpty());
}
TEST_F(DrawPropertiesTest,
@@ -2592,10 +2602,11 @@ TEST_F(DrawPropertiesTest,
EXPECT_EQ(gfx::Rect(0, 0, 25, 25), child2->visible_layer_rect());
EXPECT_TRUE(child3->visible_layer_rect().IsEmpty());
- // But the DrawableContentRects are unclipped.
- EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 50, 50), child2->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(125, 125, 50, 50), child3->drawable_content_rect());
+ // The visible_drawable_content_rect would be the visible rect in target
+ // space.
+ EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25), child2->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(125, 125, 0, 0), child3->visible_drawable_content_rect());
}
// Check that clipping does not propagate down surfaces.
@@ -2651,8 +2662,9 @@ TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsForSurfaceHierarchy) {
// render_surface1 lives in the "unclipped universe" of render_surface1, and
// is only implicitly clipped by render_surface1's content rect. So,
- // render_surface2 grows to enclose all drawable content of its subtree.
- EXPECT_EQ(gfx::RectF(5.f, 5.f, 170.f, 170.f),
+ // render_surface2 grows to enclose all visible drawable content of its
+ // subtree.
+ EXPECT_EQ(gfx::RectF(5.f, 5.f, 95.f, 95.f),
GetRenderSurface(render_surface2)->DrawableContentRect());
// All layers that draw content into render_surface2 think they are unclipped
@@ -2662,9 +2674,9 @@ TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsForSurfaceHierarchy) {
EXPECT_EQ(gfx::Rect(0, 0, 0, 0), child3->visible_layer_rect());
// DrawableContentRects are also unclipped.
- EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(75, 75, 50, 50), child2->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(125, 125, 50, 50), child3->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(5, 5, 50, 50), child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(75, 75, 25, 25), child2->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(125, 125, 0, 0), child3->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest,
@@ -2943,7 +2955,8 @@ TEST_F(DrawPropertiesTest,
// All layers that draw content into the unclipped surface are also unclipped.
EXPECT_EQ(gfx::Rect(0, 0, 50, 50), child1->visible_layer_rect());
- EXPECT_EQ(expected_surface_drawable_content, child1->drawable_content_rect());
+ EXPECT_EQ(expected_surface_drawable_content,
+ child1->visible_drawable_content_rect());
}
// Layers that have non-axis aligned bounds (due to transforms) have an
@@ -2967,6 +2980,14 @@ TEST_F(DrawPropertiesTest,
CreateEffectNode(render_surface).render_surface_reason =
RenderSurfaceReason::kTest;
CopyProperties(render_surface, child1);
+ // Make sure render surface takes content outside visible rect into
+ // consideration.
+ root->layer_tree_impl()
+ ->property_trees()
+ ->effect_tree.Node(child1->effect_tree_index())
+ ->backdrop_filters.Append(
+ FilterOperation::CreateZoomFilter(1.f /* zoom */, 0 /* inset */));
+
auto& child1_transform_node = CreateTransformNode(child1);
child1_transform_node.origin = gfx::Point3F(25.f, 25.f, 0.f);
child1_transform_node.post_translation = gfx::Vector2dF(25.f, 25.f);
@@ -2993,7 +3014,7 @@ TEST_F(DrawPropertiesTest,
EXPECT_EQ(gfx::Rect(0, 0, 25, 50), child1->visible_layer_rect());
// The child's DrawableContentRect is unclipped.
- EXPECT_EQ(unclipped_surface_content, child1->drawable_content_rect());
+ EXPECT_EQ(unclipped_surface_content, child1->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsInHighDPI) {
@@ -3048,13 +3069,16 @@ TEST_F(DrawPropertiesTest, DrawableAndVisibleContentRectsInHighDPI) {
GetRenderSurface(render_surface1)->DrawableContentRect());
// render_surface2 lives in the "unclipped universe" of render_surface1, and
- // is only implicitly clipped by render_surface1.
- EXPECT_EQ(gfx::RectF(10.f, 10.f, 350.f, 350.f),
+ // is only implicitly clipped by render_surface1, though it would only contain
+ // the visible content.
+ EXPECT_EQ(gfx::RectF(10.f, 10.f, 180.f, 180.f),
GetRenderSurface(render_surface2)->DrawableContentRect());
- EXPECT_EQ(gfx::Rect(10, 10, 100, 100), child1->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(150, 150, 100, 100), child2->drawable_content_rect());
- EXPECT_EQ(gfx::Rect(250, 250, 100, 100), child3->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(10, 10, 100, 100),
+ child1->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(150, 150, 30, 30),
+ child2->visible_drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(250, 250, 0, 0), child3->visible_drawable_content_rect());
// The root layer does not actually draw content of its own.
EXPECT_EQ(gfx::Rect(0, 0, 0, 0), root->visible_layer_rect());
@@ -3400,8 +3424,8 @@ TEST_F(DrawPropertiesTest, RenderSurfaceTransformsInHighDPI) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
child->ScreenSpaceTransform(),
duplicate_child_non_owner->ScreenSpaceTransform());
- EXPECT_EQ(child->drawable_content_rect(),
- duplicate_child_non_owner->drawable_content_rect());
+ EXPECT_EQ(child->visible_drawable_content_rect(),
+ duplicate_child_non_owner->visible_drawable_content_rect());
EXPECT_EQ(child->bounds(), duplicate_child_non_owner->bounds());
gfx::Transform expected_render_surface_draw_transform;
@@ -3552,6 +3576,74 @@ TEST_F(DrawPropertiesTest, OpacityAnimatingOnPendingTree) {
active_child->effect_tree_index()));
}
+class TransformInteropTest : public DrawPropertiesTestBase,
+ public testing::Test {
+ public:
+ TransformInteropTest() : DrawPropertiesTestBase(TransformInteropSettings()) {}
+
+ protected:
+ LayerTreeSettings TransformInteropSettings() {
+ LayerListSettings settings;
+
+ settings.enable_transform_interop = true;
+ return settings;
+ }
+};
+
+TEST_F(TransformInteropTest, BackfaceInvisibleTransform) {
+ LayerImpl* root = root_layer();
+ root->SetDrawsContent(true);
+ LayerImpl* back_facing = AddLayer<LayerImpl>();
+ LayerImpl* back_facing_double_sided = AddLayer<LayerImpl>();
+ LayerImpl* front_facing = AddLayer<LayerImpl>();
+ back_facing->SetDrawsContent(true);
+ back_facing_double_sided->SetDrawsContent(true);
+ front_facing->SetDrawsContent(true);
+
+ root->SetBounds(gfx::Size(50, 50));
+ back_facing->SetBounds(gfx::Size(50, 50));
+ back_facing_double_sided->SetBounds(gfx::Size(50, 50));
+ front_facing->SetBounds(gfx::Size(50, 50));
+ CopyProperties(root, back_facing);
+ CopyProperties(root, front_facing);
+
+ back_facing->SetShouldCheckBackfaceVisibility(true);
+ back_facing_double_sided->SetShouldCheckBackfaceVisibility(false);
+ front_facing->SetShouldCheckBackfaceVisibility(true);
+
+ auto& back_facing_transform_node = CreateTransformNode(back_facing);
+ back_facing_transform_node.flattens_inherited_transform = false;
+ back_facing_transform_node.sorting_context_id = 1;
+ gfx::Transform rotate_about_y;
+ rotate_about_y.RotateAboutYAxis(180.0);
+ back_facing_transform_node.local = rotate_about_y;
+
+ CopyProperties(back_facing, back_facing_double_sided);
+
+ UpdateActiveTreeDrawProperties();
+
+ EXPECT_TRUE(draw_property_utils::IsLayerBackFaceVisible(
+ back_facing, back_facing->transform_tree_index(),
+ host_impl()->active_tree()->property_trees()));
+ EXPECT_TRUE(draw_property_utils::IsLayerBackFaceVisible(
+ back_facing, back_facing_double_sided->transform_tree_index(),
+ host_impl()->active_tree()->property_trees()));
+ EXPECT_FALSE(draw_property_utils::IsLayerBackFaceVisible(
+ front_facing, front_facing->transform_tree_index(),
+ host_impl()->active_tree()->property_trees()));
+
+ EXPECT_TRUE(
+ draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation(
+ back_facing, host_impl()->active_tree()->property_trees()));
+ EXPECT_FALSE(
+ draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation(
+ back_facing_double_sided,
+ host_impl()->active_tree()->property_trees()));
+ EXPECT_FALSE(
+ draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation(
+ front_facing, host_impl()->active_tree()->property_trees()));
+}
+
using LCDTextTestParam = std::tuple<bool, bool>;
class LCDTextTest : public DrawPropertiesTestBase,
public testing::TestWithParam<LCDTextTestParam> {
@@ -3699,6 +3791,16 @@ TEST_P(LCDTextTest, CanUseLCDText) {
CheckCanUseLCDText(LCDTextDisallowedReason::kNone, grand_child_);
}
+TEST_P(LCDTextTest, CanUseLCDTextWithContentsOpaqueForText) {
+ child_->SetContentsOpaque(false);
+ child_->SetBackgroundColor(SK_ColorGREEN);
+ child_->SetContentsOpaqueForText(true);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kNone, child_);
+
+ child_->SetContentsOpaqueForText(false);
+ CheckCanUseLCDText(LCDTextDisallowedReason::kContentsNotOpaque, child_);
+}
+
TEST_P(LCDTextTest, CanUseLCDTextWithAnimation) {
// Sanity check: Make sure can_use_lcd_text_ is set on each node.
UpdateActiveTreeDrawProperties();
@@ -4232,19 +4334,19 @@ TEST_F(DrawPropertiesTest, ClipParentWithInterveningRenderSurface) {
EXPECT_EQ(gfx::Rect(0, 0, 5, 5), render_surface2->clip_rect());
EXPECT_TRUE(render_surface2->is_clipped());
- // The content rects of render_surface2 should have expanded to contain the
- // clip child.
- EXPECT_EQ(gfx::Rect(0, 0, 40, 40),
- GetRenderSurface(render_surface1)->content_rect());
- EXPECT_EQ(gfx::Rect(-10, -10, 60, 60),
- GetRenderSurface(render_surface2)->content_rect());
-
// The clip child should have inherited the clip parent's clip (projected to
// the right space, of course), but as render_surface1 already applies that
// clip, clip_child need not apply it again.
EXPECT_EQ(gfx::Rect(), clip_child->clip_rect());
EXPECT_EQ(gfx::Rect(9, 9, 40, 40), clip_child->visible_layer_rect());
EXPECT_FALSE(clip_child->is_clipped());
+
+ // The content rects of render_surface2 should have expanded to contain the
+ // clip child, but only the visible part of the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40),
+ GetRenderSurface(render_surface1)->content_rect());
+ EXPECT_EQ(clip_child->visible_drawable_content_rect(),
+ GetRenderSurface(render_surface2)->content_rect());
}
TEST_F(DrawPropertiesTest, ClipParentScrolledInterveningLayer) {
@@ -4329,19 +4431,19 @@ TEST_F(DrawPropertiesTest, ClipParentScrolledInterveningLayer) {
EXPECT_EQ(gfx::Rect(0, 0, 5, 5), render_surface2->clip_rect());
EXPECT_TRUE(render_surface2->is_clipped());
- // The content rects of render_surface2 should have expanded to contain the
- // clip child.
- EXPECT_EQ(gfx::Rect(0, 0, 40, 40),
- GetRenderSurface(render_surface1)->content_rect());
- EXPECT_EQ(gfx::Rect(-10, -10, 60, 60),
- GetRenderSurface(render_surface2)->content_rect());
-
// The clip child should have inherited the clip parent's clip (projected to
// the right space, of course), but as render_surface1 already applies that
// clip, clip_child need not apply it again.
EXPECT_EQ(gfx::Rect(), clip_child->clip_rect());
EXPECT_EQ(gfx::Rect(12, 12, 40, 40), clip_child->visible_layer_rect());
EXPECT_FALSE(clip_child->is_clipped());
+
+ // The content rects of render_surface2 should have expanded to contain the
+ // clip child, but only the visible part of the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40),
+ GetRenderSurface(render_surface1)->content_rect());
+ EXPECT_EQ(clip_child->visible_drawable_content_rect(),
+ GetRenderSurface(render_surface2)->content_rect());
}
TEST_F(DrawPropertiesTest, DescendantsOfClipChildren) {
@@ -6904,7 +7006,8 @@ TEST_F(DrawPropertiesTest, RenderSurfaceWithUnclippedDescendantsClipsSubtree) {
EXPECT_TRUE(test_layer->is_clipped());
EXPECT_FALSE(test_layer->render_target()->is_clipped());
EXPECT_EQ(gfx::Rect(-2, -2, 30, 30), test_layer->clip_rect());
- EXPECT_EQ(gfx::Rect(28, 28), test_layer->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(26, 26), test_layer->visible_layer_rect());
+ EXPECT_EQ(gfx::Rect(26, 26), test_layer->visible_drawable_content_rect());
}
TEST_F(DrawPropertiesTest,
@@ -7059,7 +7162,7 @@ TEST_F(DrawPropertiesTest, RenderSurfaceContentRectWithMultipleSurfaces) {
GetRenderSurface(unclipped_surface)->content_rect());
EXPECT_EQ(gfx::Rect(50, 50),
GetRenderSurface(unclipped_desc_surface)->content_rect());
- EXPECT_EQ(gfx::Rect(60, 60),
+ EXPECT_EQ(gfx::Rect(50, 50),
GetRenderSurface(unclipped_desc_surface2)->content_rect());
EXPECT_EQ(gfx::Rect(50, 50),
GetRenderSurface(clipped_surface)->content_rect());
@@ -7724,7 +7827,8 @@ TEST_F(DrawPropertiesTestWithLayerTree, CopyRequestScalingTest) {
EXPECT_EQ(gfx::Rect(10, 10), ImplOf(test_layer)->visible_layer_rect());
EXPECT_EQ(transform, ImplOf(test_layer)->DrawTransform());
EXPECT_EQ(gfx::Rect(50, 50), ImplOf(test_layer)->clip_rect());
- EXPECT_EQ(gfx::Rect(50, 50), ImplOf(test_layer)->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(50, 50),
+ ImplOf(test_layer)->visible_drawable_content_rect());
// Clear the copy request and call UpdateSurfaceContentsScale.
GetPropertyTrees(root.get())->effect_tree.ClearCopyRequests();
diff --git a/chromium/cc/trees/draw_property_utils.cc b/chromium/cc/trees/draw_property_utils.cc
index f9fdebf8218..7c65e513700 100644
--- a/chromium/cc/trees/draw_property_utils.cc
+++ b/chromium/cc/trees/draw_property_utils.cc
@@ -11,6 +11,7 @@
#include "base/containers/adapters.h"
#include "base/containers/stack.h"
+#include "base/logging.h"
#include "cc/base/math_util.h"
#include "cc/layers/draw_properties.h"
#include "cc/layers/layer.h"
@@ -381,6 +382,12 @@ bool IsTargetSpaceTransformBackFaceVisible(
LayerImpl* layer,
int transform_tree_index,
const PropertyTrees* property_trees) {
+ const TransformTree& transform_tree = property_trees->transform_tree;
+ const TransformNode& transform_node =
+ *transform_tree.Node(transform_tree_index);
+ if (transform_node.delegates_to_parent_for_backface)
+ transform_tree_index = transform_node.parent_id;
+
gfx::Transform to_target;
property_trees->GetToTarget(transform_tree_index,
layer->render_target_effect_tree_index(),
@@ -388,12 +395,42 @@ bool IsTargetSpaceTransformBackFaceVisible(
return to_target.IsBackFaceVisible();
}
-template <typename LayerType>
-bool IsLayerBackFaceVisible(LayerType* layer,
- int transform_tree_index,
- const PropertyTrees* property_trees) {
- return IsTargetSpaceTransformBackFaceVisible(layer, transform_tree_index,
- property_trees);
+bool IsTransformToRootOf3DRenderingContextBackFaceVisible(
+ Layer* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees) {
+ // We do not skip back face invisible layers on main thread as target space
+ // transform will not be available here.
+ return false;
+}
+
+bool IsTransformToRootOf3DRenderingContextBackFaceVisible(
+ LayerImpl* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees) {
+ const TransformTree& transform_tree = property_trees->transform_tree;
+
+ const TransformNode& transform_node =
+ *transform_tree.Node(transform_tree_index);
+ if (transform_node.delegates_to_parent_for_backface)
+ transform_tree_index = transform_node.parent_id;
+
+ int root_id = transform_tree_index;
+ int sorting_context_id = transform_node.sorting_context_id;
+
+ while (root_id > 0 && transform_tree.Node(root_id - 1)->sorting_context_id ==
+ sorting_context_id)
+ root_id--;
+
+ // TODO(chrishtr): cache this on the transform trees if needed, similar to
+ // |to_target| and |to_screen|.
+ gfx::Transform to_3d_root;
+ if (transform_tree_index != root_id)
+ property_trees->transform_tree.CombineTransformsBetween(
+ transform_tree_index, root_id, &to_3d_root);
+ to_3d_root.PreconcatTransform(
+ property_trees->transform_tree.Node(root_id)->to_parent);
+ return to_3d_root.IsBackFaceVisible();
}
inline bool TransformToScreenIsKnown(Layer* layer,
@@ -445,7 +482,8 @@ bool LayerNeedsUpdate(LayerType* layer,
// backface is not visible.
if (TransformToScreenIsKnown(layer, backface_transform_id, tree) &&
!HasSingularTransform(backface_transform_id, tree) &&
- IsLayerBackFaceVisible(layer, backface_transform_id, property_trees)) {
+ draw_property_utils::IsLayerBackFaceVisible(
+ layer, backface_transform_id, property_trees)) {
UMA_HISTOGRAM_BOOLEAN(
"Compositing.Renderer.LayerUpdateSkippedDueToBackface", true);
return false;
@@ -474,35 +512,6 @@ inline bool LayerShouldBeSkippedForDrawPropertiesComputation(
!effect_node->is_drawn;
}
-inline bool LayerShouldBeSkippedForDrawPropertiesComputation(
- LayerImpl* layer,
- const TransformTree& transform_tree,
- const EffectTree& effect_tree) {
- const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index());
-
- if (effect_node->HasRenderSurface() && effect_node->subtree_has_copy_request)
- return false;
-
- // Skip if the node's subtree is hidden and no need to cache.
- if (effect_node->subtree_hidden && !effect_node->cache_render_surface)
- return true;
-
- // If the layer transform is not invertible, it should be skipped. In case the
- // transform is animating and singular, we should not skip it.
- const TransformNode* transform_node =
- transform_tree.Node(layer->transform_tree_index());
-
- if (!transform_node->node_and_ancestors_are_animated_or_invertible ||
- !effect_node->is_drawn)
- return true;
-
- UMA_HISTOGRAM_BOOLEAN(
- "Compositing.Renderer.LayerSkippedForDrawPropertiesDueToBackface",
- effect_node->hidden_by_backface_visibility);
-
- return effect_node->hidden_by_backface_visibility;
-}
-
gfx::Rect LayerDrawableContentRect(
const LayerImpl* layer,
const gfx::Rect& layer_bounds_in_target_space,
@@ -922,8 +931,8 @@ void ComputeInitialRenderSurfaceList(LayerTreeImpl* layer_tree_impl,
bool is_root = layer_tree_impl->IsRootLayer(layer);
bool skip_draw_properties_computation =
- LayerShouldBeSkippedForDrawPropertiesComputation(
- layer, property_trees->transform_tree, property_trees->effect_tree);
+ draw_property_utils::LayerShouldBeSkippedForDrawPropertiesComputation(
+ layer, property_trees);
bool skip_for_invertibility = SkipForInvertibility(layer, property_trees);
@@ -1138,11 +1147,19 @@ void ComputeDrawPropertiesOfVisibleLayers(const LayerImplList* layer_list,
// Compute drawable content rects
for (LayerImpl* layer : *layer_list) {
- gfx::Rect bounds_in_target_space = MathUtil::MapEnclosingClippedRect(
- layer->draw_properties().target_space_transform,
- gfx::Rect(layer->bounds()));
- layer->draw_properties().drawable_content_rect = LayerDrawableContentRect(
- layer, bounds_in_target_space, layer->draw_properties().clip_rect);
+ bool only_draws_visible_content =
+ property_trees->effect_tree.Node(layer->effect_tree_index())
+ ->only_draws_visible_content;
+ gfx::Rect drawable_bounds = gfx::Rect(layer->visible_layer_rect());
+ if (!only_draws_visible_content) {
+ drawable_bounds = gfx::Rect(layer->bounds());
+ }
+ gfx::Rect visible_bounds_in_target_space =
+ MathUtil::MapEnclosingClippedRect(
+ layer->draw_properties().target_space_transform, drawable_bounds);
+ layer->draw_properties().visible_drawable_content_rect =
+ LayerDrawableContentRect(layer, visible_bounds_in_target_space,
+ layer->draw_properties().clip_rect);
}
}
@@ -1164,6 +1181,61 @@ bool NodeMayContainBackdropBlurFilter(const EffectNode& node) {
} // namespace
+bool CC_EXPORT LayerShouldBeSkippedForDrawPropertiesComputation(
+ LayerImpl* layer,
+ const PropertyTrees* property_trees) {
+ const TransformTree& transform_tree = property_trees->transform_tree;
+ const EffectTree& effect_tree = property_trees->effect_tree;
+ const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index());
+
+ if (effect_node->HasRenderSurface() && effect_node->subtree_has_copy_request)
+ return false;
+
+ // Skip if the node's subtree is hidden and no need to cache.
+ if (effect_node->subtree_hidden && !effect_node->cache_render_surface)
+ return true;
+
+ // If the layer transform is not invertible, it should be skipped. In case the
+ // transform is animating and singular, we should not skip it.
+ const TransformNode* transform_node =
+ transform_tree.Node(layer->transform_tree_index());
+
+ if (!transform_node->node_and_ancestors_are_animated_or_invertible ||
+ !effect_node->is_drawn)
+ return true;
+ if (layer->layer_tree_impl()->settings().enable_transform_interop) {
+ return layer->should_check_backface_visibility() &&
+ IsLayerBackFaceVisible(layer, layer->transform_tree_index(),
+ property_trees);
+ } else {
+ return effect_node->hidden_by_backface_visibility;
+ }
+}
+
+bool CC_EXPORT IsLayerBackFaceVisible(LayerImpl* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees) {
+ if (layer->layer_tree_impl()->settings().enable_transform_interop) {
+ return IsTransformToRootOf3DRenderingContextBackFaceVisible(
+ layer, transform_tree_index, property_trees);
+ } else {
+ return IsTargetSpaceTransformBackFaceVisible(layer, transform_tree_index,
+ property_trees);
+ }
+}
+
+bool CC_EXPORT IsLayerBackFaceVisible(Layer* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees) {
+ if (layer->layer_tree_host()->GetSettings().enable_transform_interop) {
+ return IsTransformToRootOf3DRenderingContextBackFaceVisible(
+ layer, transform_tree_index, property_trees);
+ } else {
+ return IsTargetSpaceTransformBackFaceVisible(layer, transform_tree_index,
+ property_trees);
+ }
+}
+
void ConcatInverseSurfaceContentsScale(const EffectNode* effect_node,
gfx::Transform* transform) {
DCHECK(effect_node->HasRenderSurface());
@@ -1195,7 +1267,6 @@ void FindLayersThatNeedUpdates(LayerTreeHost* layer_tree_host,
void FindLayersThatNeedUpdates(LayerTreeImpl* layer_tree_impl,
std::vector<LayerImpl*>* visible_layer_list) {
const PropertyTrees* property_trees = layer_tree_impl->property_trees();
- const TransformTree& transform_tree = property_trees->transform_tree;
const EffectTree& effect_tree = property_trees->effect_tree;
for (auto* layer_impl : *layer_tree_impl) {
@@ -1204,8 +1275,8 @@ void FindLayersThatNeedUpdates(LayerTreeImpl* layer_tree_impl,
layer_impl->EnsureValidPropertyTreeIndices();
if (!IsRootLayer(layer_impl) &&
- LayerShouldBeSkippedForDrawPropertiesComputation(
- layer_impl, transform_tree, effect_tree))
+ LayerShouldBeSkippedForDrawPropertiesComputation(layer_impl,
+ property_trees))
continue;
bool layer_is_drawn =
diff --git a/chromium/cc/trees/draw_property_utils.h b/chromium/cc/trees/draw_property_utils.h
index c42684eefce..b80b38e3ab9 100644
--- a/chromium/cc/trees/draw_property_utils.h
+++ b/chromium/cc/trees/draw_property_utils.h
@@ -68,6 +68,18 @@ void CC_EXPORT CalculateDrawProperties(
RenderSurfaceList* output_render_surface_list,
LayerImplList* output_update_layer_list_for_testing = nullptr);
+bool CC_EXPORT LayerShouldBeSkippedForDrawPropertiesComputation(
+ LayerImpl* layer,
+ const PropertyTrees* property_trees);
+
+bool CC_EXPORT IsLayerBackFaceVisible(LayerImpl* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees);
+
+bool CC_EXPORT IsLayerBackFaceVisible(Layer* layer,
+ int transform_tree_index,
+ const PropertyTrees* property_trees);
+
#if DCHECK_IS_ON()
// Checks and logs if double background blur exists in any layers. Returns
// true if no double background blur is detected, false otherwise.
diff --git a/chromium/cc/trees/effect_node.cc b/chromium/cc/trees/effect_node.cc
index a412c237f1c..9bae1e90b74 100644
--- a/chromium/cc/trees/effect_node.cc
+++ b/chromium/cc/trees/effect_node.cc
@@ -23,6 +23,7 @@ EffectNode::EffectNode()
double_sided(true),
trilinear_filtering(false),
is_drawn(true),
+ only_draws_visible_content(true),
subtree_hidden(false),
has_potential_filter_animation(false),
has_potential_backdrop_filter_animation(false),
@@ -56,7 +57,6 @@ bool EffectNode::operator==(const EffectNode& other) const {
backdrop_filters == other.backdrop_filters &&
backdrop_filter_bounds == other.backdrop_filter_bounds &&
backdrop_mask_element_id == other.backdrop_mask_element_id &&
- filters_origin == other.filters_origin &&
rounded_corner_bounds == other.rounded_corner_bounds &&
is_fast_rounded_corner == other.is_fast_rounded_corner &&
// The specific reason is just for tracing/testing/debugging, so just
@@ -67,7 +67,9 @@ bool EffectNode::operator==(const EffectNode& other) const {
hidden_by_backface_visibility == other.hidden_by_backface_visibility &&
double_sided == other.double_sided &&
trilinear_filtering == other.trilinear_filtering &&
- is_drawn == other.is_drawn && subtree_hidden == other.subtree_hidden &&
+ is_drawn == other.is_drawn &&
+ only_draws_visible_content == other.only_draws_visible_content &&
+ subtree_hidden == other.subtree_hidden &&
has_potential_filter_animation ==
other.has_potential_filter_animation &&
has_potential_backdrop_filter_animation ==
@@ -163,6 +165,7 @@ void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const {
hidden_by_backface_visibility);
value->SetBoolean("trilinear_filtering", trilinear_filtering);
value->SetBoolean("is_drawn", is_drawn);
+ value->SetBoolean("only_draws_visible_content", only_draws_visible_content);
value->SetBoolean("has_potential_filter_animation",
has_potential_filter_animation);
value->SetBoolean("has_potential_backdrop_filter_animation",
diff --git a/chromium/cc/trees/effect_node.h b/chromium/cc/trees/effect_node.h
index 01f662563e1..6663457250c 100644
--- a/chromium/cc/trees/effect_node.h
+++ b/chromium/cc/trees/effect_node.h
@@ -96,6 +96,13 @@ struct CC_EXPORT EffectNode {
bool double_sided : 1;
bool trilinear_filtering : 1;
bool is_drawn : 1;
+ // In most cases we only need to draw the visible part of any content
+ // contributing to the effect. For copy request case, we would need to copy
+ // the entire content, and could not only draw the visible part. In the rare
+ // case of a backdrop zoom filter we need to take into consideration the
+ // content offscreen to make sure the backdrop zoom filter is applied with the
+ // correct center.
+ bool only_draws_visible_content : 1;
// TODO(jaydasika) : Delete this after implementation of
// SetHideLayerAndSubtree is cleaned up. (crbug.com/595843)
bool subtree_hidden : 1;
diff --git a/chromium/cc/trees/frame_rate_counter.cc b/chromium/cc/trees/frame_rate_counter.cc
deleted file mode 100644
index 816877c4ab3..00000000000
--- a/chromium/cc/trees/frame_rate_counter.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/trees/frame_rate_counter.h"
-
-#include <stddef.h>
-
-#include <algorithm>
-#include <limits>
-
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-
-namespace cc {
-
-// The following constants are measured in seconds.
-
-// Two thresholds (measured in seconds) that describe what is considered to be a
-// "no-op frame" that should not be counted.
-// - if the frame is too fast, then given our compositor implementation, the
-// frame probably was a no-op and did not draw.
-// - if the frame is too slow, then there is probably not animating content, so
-// we should not pollute the average.
-static const double kFrameTooFast = 1.0 / 70.0;
-static const double kFrameTooSlow = 1.5;
-
-// If a frame takes longer than this threshold (measured in seconds) then we
-// (naively) assume that it missed a screen refresh; that is, we dropped a
-// frame.
-// TODO(brianderson): Determine this threshold based on monitor refresh rate,
-// crbug.com/138642.
-static const double kDroppedFrameTime = 1.0 / 50.0;
-
-// static
-std::unique_ptr<FrameRateCounter> FrameRateCounter::Create(
- bool has_impl_thread) {
- return base::WrapUnique(new FrameRateCounter(has_impl_thread));
-}
-
-base::TimeDelta FrameRateCounter::RecentFrameInterval(size_t n) const {
- DCHECK_GT(n, 0u);
- DCHECK_LT(n, ring_buffer_.BufferSize());
- return ring_buffer_.ReadBuffer(n) - ring_buffer_.ReadBuffer(n - 1);
-}
-
-FrameRateCounter::FrameRateCounter(bool has_impl_thread)
- : has_impl_thread_(has_impl_thread), dropped_frame_count_(0) {}
-
-void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp) {
- ring_buffer_.SaveToBuffer(timestamp);
-
- // Check if frame interval can be computed.
- if (ring_buffer_.CurrentIndex() < 2)
- return;
-
- base::TimeDelta frame_interval_seconds =
- RecentFrameInterval(ring_buffer_.BufferSize() - 1);
-
- if (!IsBadFrameInterval(frame_interval_seconds) &&
- frame_interval_seconds.InSecondsF() > kDroppedFrameTime)
- dropped_frame_count_ +=
- frame_interval_seconds.InSecondsF() / kDroppedFrameTime;
-}
-
-bool FrameRateCounter::IsBadFrameInterval(
- base::TimeDelta interval_between_consecutive_frames) const {
- double delta = interval_between_consecutive_frames.InSecondsF();
- bool scheduler_allows_double_frames = !has_impl_thread_;
- bool interval_too_fast =
- scheduler_allows_double_frames ? delta < kFrameTooFast : delta <= 0.0;
- bool interval_too_slow = delta > kFrameTooSlow;
- return interval_too_fast || interval_too_slow;
-}
-
-void FrameRateCounter::GetMinAndMaxFPS(double* min_fps, double* max_fps) const {
- *min_fps = std::numeric_limits<double>::max();
- *max_fps = 0.0;
-
- for (RingBufferType::Iterator it = --ring_buffer_.End(); it; --it) {
- base::TimeDelta delta = RecentFrameInterval(it.index() + 1);
-
- if (IsBadFrameInterval(delta))
- continue;
-
- DCHECK_GT(delta.InSecondsF(), 0.f);
- double fps = 1.0 / delta.InSecondsF();
-
- *min_fps = std::min(fps, *min_fps);
- *max_fps = std::max(fps, *max_fps);
- }
-
- if (*min_fps > *max_fps)
- *min_fps = *max_fps;
-}
-
-double FrameRateCounter::GetAverageFPS() const {
- int frame_count = 0;
- double frame_times_total = 0.0;
- double average_fps = 0.0;
-
- // Walk backwards through the samples looking for a run of good frame
- // timings from which to compute the mean.
- //
- // Slow frames occur just because the user is inactive, and should be
- // ignored. Fast frames are ignored if the scheduler is in single-thread
- // mode in order to represent the true frame rate in spite of the fact that
- // the first few swapbuffers happen instantly which skews the statistics
- // too much for short lived animations.
- //
- // IsBadFrameInterval encapsulates the frame too slow/frame too fast logic.
-
- for (RingBufferType::Iterator it = --ring_buffer_.End();
- it && frame_times_total < 1.0; --it) {
- base::TimeDelta delta = RecentFrameInterval(it.index() + 1);
-
- if (!IsBadFrameInterval(delta)) {
- frame_count++;
- frame_times_total += delta.InSecondsF();
- } else if (frame_count) {
- break;
- }
- }
-
- if (frame_count) {
- DCHECK_GT(frame_times_total, 0.0);
- average_fps = frame_count / frame_times_total;
- }
-
- return average_fps;
-}
-
-} // namespace cc
diff --git a/chromium/cc/trees/frame_rate_counter.h b/chromium/cc/trees/frame_rate_counter.h
deleted file mode 100644
index f1445fb16da..00000000000
--- a/chromium/cc/trees/frame_rate_counter.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_TREES_FRAME_RATE_COUNTER_H_
-#define CC_TREES_FRAME_RATE_COUNTER_H_
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/containers/ring_buffer.h"
-#include "base/time/time.h"
-
-namespace cc {
-
-// This class maintains a history of timestamps, and provides functionality to
-// intelligently compute average frames per second.
-class FrameRateCounter {
- public:
- static std::unique_ptr<FrameRateCounter> Create(bool has_impl_thread);
-
- FrameRateCounter(const FrameRateCounter&) = delete;
- FrameRateCounter& operator=(const FrameRateCounter&) = delete;
-
- size_t current_frame_number() const { return ring_buffer_.CurrentIndex(); }
- int dropped_frame_count() const { return dropped_frame_count_; }
- size_t time_stamp_history_size() const { return ring_buffer_.BufferSize(); }
-
- void SaveTimeStamp(base::TimeTicks timestamp);
-
- // n = 0 returns the oldest frame interval retained in the history, while n =
- // time_stamp_history_size() - 1 returns the most recent frame interval.
- base::TimeDelta RecentFrameInterval(size_t n) const;
-
- // This is a heuristic that can be used to ignore frames in a reasonable way.
- // Returns true if the given frame interval is too fast or too slow, based on
- // constant thresholds.
- bool IsBadFrameInterval(
- base::TimeDelta interval_between_consecutive_frames) const;
-
- void GetMinAndMaxFPS(double* min_fps, double* max_fps) const;
- double GetAverageFPS() const;
-
- typedef base::RingBuffer<base::TimeTicks, 136> RingBufferType;
- RingBufferType::Iterator begin() const { return ring_buffer_.Begin(); }
- RingBufferType::Iterator end() const { return ring_buffer_.End(); }
-
- private:
- explicit FrameRateCounter(bool has_impl_thread);
-
- RingBufferType ring_buffer_;
-
- bool has_impl_thread_;
- int dropped_frame_count_;
-};
-
-} // namespace cc
-
-#endif // CC_TREES_FRAME_RATE_COUNTER_H_
diff --git a/chromium/cc/trees/image_animation_controller.cc b/chromium/cc/trees/image_animation_controller.cc
index a7e1ebf6de6..2cb4f9d1bd1 100644
--- a/chromium/cc/trees/image_animation_controller.cc
+++ b/chromium/cc/trees/image_animation_controller.cc
@@ -4,6 +4,8 @@
#include "cc/trees/image_animation_controller.h"
+#include <sstream>
+
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
@@ -360,6 +362,25 @@ bool ImageAnimationController::AnimationState::AdvanceFrame(
// skipped trying to catch up.
DCHECK_GT(num_of_frames_advanced, 0u);
last_num_frames_skipped_ = num_of_frames_advanced - 1u;
+ switch (repetitions_completed_) {
+ case 0:
+ UMA_HISTOGRAM_COUNTS_100000(
+ "AnimatedImage.NumOfFramesSkipped.FirstAnimationLoop",
+ last_num_frames_skipped_);
+ break;
+ case 1:
+ UMA_HISTOGRAM_COUNTS_100000(
+ "AnimatedImage.NumOfFramesSkipped.SecondAnimationLoop",
+ last_num_frames_skipped_);
+ break;
+ case 2:
+ case 3:
+ case 4:
+ UMA_HISTOGRAM_COUNTS_100000(
+ "AnimatedImage.NumOfFramesSkipped.ThirdToFifthAnimationLoop",
+ last_num_frames_skipped_);
+ break;
+ }
UMA_HISTOGRAM_COUNTS_100000("AnimatedImage.NumOfFramesSkipped.Compositor",
last_num_frames_skipped_);
return needs_invalidation();
diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc
index b1a4798682b..d5e57eaa4a5 100644
--- a/chromium/cc/trees/layer_tree_host.cc
+++ b/chromium/cc/trees/layer_tree_host.cc
@@ -18,6 +18,7 @@
#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/location.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
@@ -94,6 +95,9 @@ LayerTreeHost::InitParams::InitParams(InitParams&&) = default;
LayerTreeHost::InitParams& LayerTreeHost::InitParams::operator=(InitParams&&) =
default;
+LayerTreeHost::ScrollAnimationState::ScrollAnimationState() = default;
+LayerTreeHost::ScrollAnimationState::~ScrollAnimationState() = default;
+
std::unique_ptr<LayerTreeHost> LayerTreeHost::CreateThreaded(
scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner,
InitParams params) {
@@ -263,14 +267,6 @@ const LayerTreeSettings& LayerTreeHost::GetSettings() const {
void LayerTreeHost::QueueSwapPromise(
std::unique_ptr<SwapPromise> swap_promise) {
swap_promise_manager_.QueueSwapPromise(std::move(swap_promise));
-
- // Request a main frame if one is not already in progress. This might either
- // A) request a commit ahead of time or B) request a commit which is not
- // needed because there are not pending updates. If B) then the frame will
- // be aborted early and the swap promises will be broken (see
- // EarlyOut_NoUpdates).
- if (!inside_main_frame_)
- SetNeedsAnimate();
}
void LayerTreeHost::WillBeginMainFrame() {
@@ -609,6 +605,11 @@ void LayerTreeHost::SetNeedsAnimate() {
events_metrics_manager_.SaveActiveEventMetrics();
}
+void LayerTreeHost::SetNeedsAnimateIfNotInsideMainFrame() {
+ if (!inside_main_frame_)
+ SetNeedsAnimate();
+}
+
DISABLE_CFI_PERF
void LayerTreeHost::SetNeedsUpdateLayers() {
proxy_->SetNeedsUpdateLayers();
@@ -785,6 +786,13 @@ bool LayerTreeHost::CaptureContent(std::vector<NodeId>* content) {
return true;
}
+void LayerTreeHost::DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) {
+ client_->DidObserveFirstScrollDelay(first_scroll_delay,
+ first_scroll_timestamp);
+}
+
bool LayerTreeHost::DoUpdateLayers() {
TRACE_EVENT1("cc,benchmark", "LayerTreeHost::DoUpdateLayers",
"source_frame_number", SourceFrameNumber());
@@ -849,6 +857,24 @@ void LayerTreeHost::ApplyViewportChanges(const ScrollAndScaleSet& info) {
if (info.inner_viewport_scroll.element_id)
inner_viewport_scroll_delta = info.inner_viewport_scroll.scroll_delta;
+ // When a new scroll-animation starts, it is necessary to check
+ // |info.manipulation_info| to make sure the scroll-animation was started by
+ // an input event.
+ // If there is already an ongoing scroll-animation, then it is necessary to
+ // only look at |info.ongoing_scroll_animation| (since it is possible for the
+ // scroll-animation to continue even if no event was handled).
+ bool new_ongoing_scroll =
+ scroll_animation_.in_progress
+ ? info.ongoing_scroll_animation
+ : (info.ongoing_scroll_animation && info.manipulation_info);
+ if (scroll_animation_.in_progress && !new_ongoing_scroll) {
+ scroll_animation_.in_progress = false;
+ if (!scroll_animation_.end_notification.is_null())
+ std::move(scroll_animation_.end_notification).Run();
+ } else {
+ scroll_animation_.in_progress = new_ongoing_scroll;
+ }
+
if (inner_viewport_scroll_delta.IsZero() && info.page_scale_delta == 1.f &&
info.elastic_overscroll_delta.IsZero() && !info.top_controls_delta &&
!info.bottom_controls_delta &&
@@ -985,6 +1011,14 @@ void LayerTreeHost::NotifyThroughputTrackerResults(
client_->NotifyThroughputTrackerResults(std::move(results));
}
+void LayerTreeHost::SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ client_->SubmitThroughputData(source_id, aggregated_percent, impl_percent,
+ main_percent);
+}
+
const base::WeakPtr<InputHandler>& LayerTreeHost::GetInputHandler() const {
return input_handler_weak_ptr_;
}
@@ -1068,6 +1102,15 @@ void LayerTreeHost::RequestPresentationTimeForNextFrame(
pending_presentation_time_callbacks_.push_back(std::move(callback));
}
+void LayerTreeHost::RequestScrollAnimationEndNotification(
+ base::OnceClosure callback) {
+ DCHECK(scroll_animation_.end_notification.is_null());
+ if (scroll_animation_.in_progress)
+ scroll_animation_.end_notification = std::move(callback);
+ else
+ std::move(callback).Run();
+}
+
void LayerTreeHost::SetRootLayer(scoped_refptr<Layer> root_layer) {
if (root_layer_.get() == root_layer.get())
return;
@@ -1118,9 +1161,13 @@ Layer* LayerTreeHost::InnerViewportScrollLayerForTesting() const {
}
Layer* LayerTreeHost::OuterViewportScrollLayerForTesting() const {
+ return LayerByElementId(OuterViewportScrollElementId());
+}
+
+ElementId LayerTreeHost::OuterViewportScrollElementId() const {
auto* scroll_node =
property_trees()->scroll_tree.Node(viewport_property_ids_.outer_scroll);
- return scroll_node ? LayerByElementId(scroll_node->element_id) : nullptr;
+ return scroll_node ? scroll_node->element_id : ElementId();
}
void LayerTreeHost::RegisterSelection(const LayerSelection& selection) {
@@ -1439,10 +1486,6 @@ void LayerTreeHost::AddLayerShouldPushProperties(Layer* layer) {
layers_that_should_push_properties_.insert(layer);
}
-void LayerTreeHost::RemoveLayerShouldPushProperties(Layer* layer) {
- layers_that_should_push_properties_.erase(layer);
-}
-
void LayerTreeHost::ClearLayersThatShouldPushProperties() {
layers_that_should_push_properties_.clear();
}
@@ -1566,6 +1609,9 @@ void LayerTreeHost::PushLayerTreePropertiesTo(LayerTreeImpl* tree_impl) {
}
tree_impl->set_display_transform_hint(display_transform_hint_);
+
+ if (delegated_ink_metadata_)
+ tree_impl->set_delegated_ink_metadata(std::move(delegated_ink_metadata_));
}
void LayerTreeHost::PushSurfaceRangesTo(LayerTreeImpl* tree_impl) {
diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h
index 8aae435b230..f365bd9a909 100644
--- a/chromium/cc/trees/layer_tree_host.h
+++ b/chromium/cc/trees/layer_tree_host.h
@@ -13,6 +13,7 @@
#include <set>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "base/callback_forward.h"
@@ -49,6 +50,7 @@
#include "cc/trees/swap_promise_manager.h"
#include "cc/trees/target_property.h"
#include "cc/trees/viewport_layers.h"
+#include "components/viz/common/delegated_ink_metadata.h"
#include "components/viz/common/resources/resource_format.h"
#include "components/viz/common/surfaces/local_surface_id_allocation.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
@@ -215,6 +217,9 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
// full commit synchronization or layer updates.
void SetNeedsAnimate();
+ // Calls SetNeedsAnimate() if there is no main frame already in progress.
+ void SetNeedsAnimateIfNotInsideMainFrame();
+
// Requests a main frame update and also ensure that the host pulls layer
// updates from the client, even if no content might have changed, without
// forcing a full commit synchronization.
@@ -316,6 +321,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
base::OnceCallback<void(const gfx::PresentationFeedback&)>;
void RequestPresentationTimeForNextFrame(PresentationTimeCallback callback);
+ // Registers a callback that is run when any ongoing scroll-animation ends. If
+ // there are no ongoing animations, then the callback is run immediately.
+ void RequestScrollAnimationEndNotification(base::OnceClosure callback);
+
// Layer tree accessors and modifiers ------------------------
// Sets or gets the root of the Layer tree. Children of the root Layer are
@@ -352,6 +361,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
Layer* InnerViewportScrollLayerForTesting() const;
Layer* OuterViewportScrollLayerForTesting() const;
+ ElementId OuterViewportScrollElementId() const;
+
// Sets or gets the position of touch handles for a text selection. These are
// submitted to the display compositor along with the Layer tree's contents
// allowing it to present the selection handles. This is done because the
@@ -388,8 +399,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
void SetViewportVisibleRect(const gfx::Rect& visible_rect);
- gfx::Rect viewport_visible_rect() const { return viewport_visible_rect_; }
-
gfx::Rect device_viewport_rect() const { return device_viewport_rect_; }
void SetBrowserControlsParams(const BrowserControlsParams& params);
@@ -457,6 +466,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
return raster_color_space_;
}
+ bool HasCompositorDrivenScrollAnimationForTesting() const {
+ return scroll_animation_.in_progress;
+ }
+
// This layer tree may be embedded in a hierarchy that has page scale
// factor controlled at the top level. We represent that scale here as
// 'external_page_scale_factor', a value that affects raster scale in the
@@ -503,7 +516,6 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
// These are internal methods, called from the Layer itself when changing a
// property or completing a PushPropertiesTo.
void AddLayerShouldPushProperties(Layer* layer);
- void RemoveLayerShouldPushProperties(Layer* layer);
void ClearLayersThatShouldPushProperties();
// The current set of all Layers attached to the LayerTreeHost's tree that
// have been marked as needing PushPropertiesTo in the next commit.
@@ -593,6 +605,10 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time,
ActiveFrameSequenceTrackers trackers);
void NotifyThroughputTrackerResults(CustomTrackerResults results);
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent);
LayerTreeHostClient* client() { return client_; }
LayerTreeHostSchedulingClient* scheduling_client() {
@@ -693,6 +709,17 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
impl_commit_start_time_ = commit_start_time;
}
+ void SetDelegatedInkMetadata(
+ std::unique_ptr<viz::DelegatedInkMetadata> metadata) {
+ delegated_ink_metadata_ = std::move(metadata);
+ }
+ viz::DelegatedInkMetadata* DelegatedInkMetadataForTesting() {
+ return delegated_ink_metadata_.get();
+ }
+
+ void DidObserveFirstScrollDelay(base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp);
+
protected:
LayerTreeHost(InitParams params, CompositorMode mode);
@@ -886,6 +913,17 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
// added here.
std::vector<PresentationTimeCallback> pending_presentation_time_callbacks_;
+ struct ScrollAnimationState {
+ ScrollAnimationState();
+ ~ScrollAnimationState();
+
+ // Tracks whether there is an ongoing compositor-driven scroll animation.
+ bool in_progress = false;
+
+ // Callback to run when the scroll-animation ends.
+ base::OnceClosure end_notification;
+ } scroll_animation_;
+
// Latency information for work done in ProxyMain::BeginMainFrame. The
// unique_ptr is allocated in RequestMainFrameUpdate, and passed to Blink's
// LocalFrameView that fills in the fields. This object adds the timing for
@@ -899,6 +937,12 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
EventsMetricsManager events_metrics_manager_;
+ // Metadata required for drawing a delegated ink trail onto the end of a
+ // stroke. std::unique_ptr was specifically chosen so that it would be cleared
+ // as it is forwarded along the pipeline to avoid old information incorrectly
+ // sticking around and potentially being reused.
+ std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata_;
+
// Used to vend weak pointers to LayerTreeHost to ScopedDeferMainFrameUpdate
// objects.
base::WeakPtrFactory<LayerTreeHost> defer_main_frame_update_weak_ptr_factory_{
diff --git a/chromium/cc/trees/layer_tree_host_client.h b/chromium/cc/trees/layer_tree_host_client.h
index ae48bd94a18..b4b8c607969 100644
--- a/chromium/cc/trees/layer_tree_host_client.h
+++ b/chromium/cc/trees/layer_tree_host_client.h
@@ -11,6 +11,7 @@
#include "base/time/time.h"
#include "cc/input/browser_controls_state.h"
#include "cc/metrics/frame_sequence_tracker_collection.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
#include "ui/gfx/geometry/scroll_offset.h"
#include "ui/gfx/geometry/vector2d_f.h"
@@ -101,6 +102,9 @@ class LayerTreeHostClient {
virtual void WillUpdateLayers() = 0;
virtual void DidUpdateLayers() = 0;
+ virtual void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) = 0;
// Notification that the proxy started or stopped deferring main frame updates
virtual void OnDeferMainFrameUpdatesChanged(bool) = 0;
@@ -169,6 +173,12 @@ class LayerTreeHostClient {
// RecordEndOfFrameMetrics.
virtual std::unique_ptr<BeginMainFrameMetrics> GetBeginMainFrameMetrics() = 0;
virtual void NotifyThroughputTrackerResults(CustomTrackerResults results) = 0;
+ // LayerTreeHost calls this when there is new throughput data. The client send
+ // the data to the browser process who will aggregate them and report to UKM.
+ virtual void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) = 0;
protected:
virtual ~LayerTreeHostClient() = default;
diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc
index 4fcc3210311..0fbcdf35620 100644
--- a/chromium/cc/trees/layer_tree_host_impl.cc
+++ b/chromium/cc/trees/layer_tree_host_impl.cc
@@ -20,6 +20,7 @@
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/location.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/metrics/histogram.h"
@@ -30,6 +31,7 @@
#include "base/trace_event/traced_value.h"
#include "build/build_config.h"
#include "cc/base/devtools_instrumentation.h"
+#include "cc/base/features.h"
#include "cc/base/histograms.h"
#include "cc/base/math_util.h"
#include "cc/base/switches.h"
@@ -40,6 +42,7 @@
#include "cc/input/page_scale_animation.h"
#include "cc/input/scroll_elasticity_helper.h"
#include "cc/input/scroll_state.h"
+#include "cc/input/scroll_utils.h"
#include "cc/input/scrollbar.h"
#include "cc/input/scrollbar_animation_controller.h"
#include "cc/input/scroller_size_metrics.h"
@@ -79,7 +82,6 @@
#include "cc/trees/debug_rect_history.h"
#include "cc/trees/draw_property_utils.h"
#include "cc/trees/effect_node.h"
-#include "cc/trees/frame_rate_counter.h"
#include "cc/trees/image_animation_controller.h"
#include "cc/trees/latency_info_swap_promise_monitor.h"
#include "cc/trees/layer_tree_frame_sink.h"
@@ -125,6 +127,7 @@
#include "ui/gfx/geometry/scroll_offset.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
+#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/gfx/skia_util.h"
namespace cc {
@@ -248,6 +251,41 @@ void PopulateMetadataContentColorUsage(
}
}
+// Registers callbacks, as needed, to track First Scroll Latency.
+void ApplyFirstScrollTracking(const ui::LatencyInfo* latency,
+ uint32_t frame_token,
+ LayerTreeHostImpl* impl) {
+ base::TimeTicks creation_timestamp;
+ // If |latency| isn't tracking a scroll, we don't need to do extra
+ // first-scroll tracking.
+ auto scroll_update_started =
+ ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT;
+ auto scroll_update_continued =
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT;
+ if (!latency->FindLatency(scroll_update_started, &creation_timestamp) &&
+ !latency->FindLatency(scroll_update_continued, &creation_timestamp)) {
+ return;
+ }
+
+ // Construct a callback that, given presentation feedback, will report the
+ // time span between the scroll input-event creation and the
+ // presentation timestamp.
+ LayerTreeHost::PresentationTimeCallback presentation_callback =
+ base::BindOnce(
+ [](base::TimeTicks event_creation,
+ LayerTreeHostImpl* layer_tree_host_impl,
+ const gfx::PresentationFeedback& feedback) {
+ layer_tree_host_impl->DidObserveScrollDelay(
+ feedback.timestamp - event_creation, event_creation);
+ },
+ creation_timestamp, impl);
+
+ // Tell the |LayerTreeHostImpl| to run our callback with the presentation
+ // feedback corresponding to the given |frame_token|.
+ impl->RegisterCompositorPresentationTimeCallback(
+ frame_token, std::move(presentation_callback));
+}
+
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class SourceIdConsistency : int {
@@ -333,8 +371,6 @@ LayerTreeHostImpl::LayerTreeHostImpl(
? std::numeric_limits<size_t>::max()
: settings.scheduled_raster_task_limit,
settings.ToTileManagerSettings()),
- fps_counter_(
- FrameRateCounter::Create(task_runner_provider_->HasImplThread())),
memory_history_(MemoryHistory::Create()),
debug_rect_history_(DebugRectHistory::Create()),
mutator_host_(std::move(mutator_host)),
@@ -379,12 +415,14 @@ LayerTreeHostImpl::LayerTreeHostImpl(
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableLayerTreeHostMemoryPressure)) {
- memory_pressure_listener_.reset(
- new base::MemoryPressureListener(base::BindRepeating(
- &LayerTreeHostImpl::OnMemoryPressure, base::Unretained(this))));
+ memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
+ FROM_HERE, base::BindRepeating(&LayerTreeHostImpl::OnMemoryPressure,
+ base::Unretained(this)));
}
SetDebugState(settings.initial_debug_state);
+ compositor_frame_reporting_controller_->SetDroppedFrameCounter(
+ &dropped_frame_counter_);
}
LayerTreeHostImpl::~LayerTreeHostImpl() {
@@ -428,7 +466,6 @@ LayerTreeHostImpl::~LayerTreeHostImpl() {
// Clear the UKM Manager so that we do not try to report when the
// UKM System has shut down.
compositor_frame_reporting_controller_->SetUkmManager(nullptr);
- frame_trackers_.SetUkmManager(nullptr);
}
void LayerTreeHostImpl::WillSendBeginMainFrame() {
@@ -1034,6 +1071,7 @@ bool LayerTreeHostImpl::ScrollLayerTo(ElementId element_id,
}
bool LayerTreeHostImpl::ScrollingShouldSwitchtoMainThread() {
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode();
@@ -1550,8 +1588,9 @@ DrawResult LayerTreeHostImpl::PrepareToDraw(FrameData* frame) {
base::saturated_cast<int>(active_tree_->picture_layers().size()), 1,
400, 20);
- // TODO(yigu): Maybe we should use the same check above. Need to figure out
- // why exactly we skip 0.
+ // TODO(pdr): Instead of skipping empty picture layers, maybe we should
+ // accumulate layer->GetRasterSource()->GetMemoryUsage() above and skip
+ // recording when the accumulated memory usage is 0.
if (!active_tree()->picture_layers().empty()) {
UMA_HISTOGRAM_CUSTOM_COUNTS(
base::StringPrintf("Compositing.%s.GPUMemoryForTilingsInKb",
@@ -1719,7 +1758,7 @@ void LayerTreeHostImpl::ResetTreesForTesting() {
}
size_t LayerTreeHostImpl::SourceAnimationFrameNumberForTesting() const {
- return fps_counter_->current_frame_number();
+ return *next_frame_token_;
}
void LayerTreeHostImpl::UpdateTileManagerMemoryPolicy(
@@ -2192,6 +2231,15 @@ viz::CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() {
metadata.display_transform_hint = active_tree_->display_transform_hint();
+ if (std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata =
+ active_tree_->take_delegated_ink_metadata()) {
+ TRACE_EVENT_INSTANT1(
+ "cc", "Delegated Ink Metadata set on compositor frame metadata",
+ TRACE_EVENT_SCOPE_THREAD, "point",
+ delegated_ink_metadata->point().ToString());
+ metadata.delegated_ink_metadata = std::move(delegated_ink_metadata);
+ }
+
return metadata;
}
@@ -2324,6 +2372,19 @@ bool LayerTreeHostImpl::DrawLayers(FrameData* frame) {
std::move(compositor_frame),
/*hit_test_data_changed=*/false, debug_state_.show_hit_test_borders);
+ // This is expected to be true roughly every 5 seconds.
+ if (frame_trackers_.HasThroughputData()) {
+ ukm::SourceId source_id = ukm_manager_->source_id();
+ // source_id can be invalid in tests.
+ if (source_id != ukm::kInvalidSourceId) {
+ int aggregated_percent = frame_trackers_.TakeLastAggregatedPercent();
+ int impl_percent = frame_trackers_.TakeLastImplPercent();
+ base::Optional<int> main_percent = frame_trackers_.TakeLastMainPercent();
+ client_->SubmitThroughputData(source_id, aggregated_percent, impl_percent,
+ main_percent);
+ }
+ }
+
#if DCHECK_IS_ON()
if (!doing_sync_draw_) {
// The throughput computation (in |FrameSequenceTracker|) depends on the
@@ -2387,8 +2448,6 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame(
TRACE_ID_GLOBAL(CurrentBeginFrameArgs().trace_id),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"step", "GenerateCompositorFrame");
- base::TimeTicks frame_time = CurrentBeginFrameArgs().frame_time;
- fps_counter_->SaveTimeStamp(frame_time);
rendering_stats_instrumentation_->IncrementFrameCount(1);
memory_history_->SaveEntry(tile_manager_.memory_stats_from_last_assign());
@@ -2457,6 +2516,8 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame(
if (render_frame_metadata_observer_) {
last_draw_render_frame_metadata_ = MakeRenderFrameMetadata(frame);
+ last_draw_render_frame_metadata_->has_delegated_ink_metadata =
+ metadata.delegated_ink_metadata.get();
// We cache the value of any new vertical scroll direction so that we can
// accurately determine when the next change in vertical scroll direction
@@ -2477,6 +2538,8 @@ viz::CompositorFrame LayerTreeHostImpl::GenerateCompositorFrame(
base::TimeTicks draw_time = base::TimeTicks::Now();
ukm::SourceId exemplar = metadata.latency_info.front().ukm_source_id();
+ ApplyFirstScrollTracking(&metadata.latency_info.front(),
+ metadata.frame_token, this);
bool all_valid = true;
bool all_unique = true;
for (auto& latency : metadata.latency_info) {
@@ -3653,6 +3716,16 @@ void LayerTreeHostImpl::DidChangeBrowserControlsPosition() {
SetFullViewportDamage();
}
+void LayerTreeHostImpl::DidObserveScrollDelay(
+ base::TimeDelta scroll_delay,
+ base::TimeTicks scroll_timestamp) {
+ // Record First Scroll Delay.
+ if (!has_observed_first_scroll_delay_) {
+ client_->DidObserveFirstScrollDelay(scroll_delay, scroll_timestamp);
+ has_observed_first_scroll_delay_ = true;
+ }
+}
+
float LayerTreeHostImpl::TopControlsHeight() const {
return active_tree_->top_controls_height();
}
@@ -3690,20 +3763,43 @@ void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) {
input_handler_client_ = client;
}
-gfx::Vector2dF LayerTreeHostImpl::ResolveScrollPercentageToPixels(
+gfx::Vector2dF LayerTreeHostImpl::ResolveScrollGranularityToPixels(
const ScrollNode& scroll_node,
- const gfx::Vector2dF& scroll_delta) {
- gfx::Vector2dF scroll_delta_in_pixels;
- scroll_delta_in_pixels.set_x(scroll_delta.x() *
- scroll_node.container_bounds.width());
- scroll_delta_in_pixels.set_y(scroll_delta.y() *
- scroll_node.container_bounds.height());
- return scroll_delta_in_pixels;
+ const gfx::Vector2dF& scroll_delta,
+ ui::ScrollGranularity granularity) {
+ gfx::Vector2dF pixel_delta = scroll_delta;
+
+ if (granularity == ui::ScrollGranularity::kScrollByPage) {
+ // Page should use a percentage of the scroller so change the parameters
+ // and let the percentage case below resolve it.
+ granularity = ui::ScrollGranularity::kScrollByPercentage;
+ pixel_delta.Scale(kMinFractionToStepWhenPaging);
+ }
+
+ if (granularity == ui::ScrollGranularity::kScrollByPercentage) {
+ gfx::SizeF scroller_size = gfx::SizeF(scroll_node.container_bounds);
+
+ gfx::SizeF viewport_size =
+ InnerViewportScrollNode()
+ ? gfx::SizeF(InnerViewportScrollNode()->container_bounds)
+ : gfx::SizeF(active_tree()->GetDeviceViewport().size());
+
+ // Convert from rootframe coordinates to screen coordinates (physical
+ // pixels).
+ scroller_size.Scale(active_tree()->page_scale_factor_for_scroll());
+
+ pixel_delta = ScrollUtils::ResolveScrollPercentageToPixels(
+ scroll_delta, scroller_size, viewport_size);
+ }
+
+ return pixel_delta;
}
InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll(
const ScrollTree& scroll_tree,
ScrollNode* scroll_node) const {
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
+
InputHandler::ScrollStatus scroll_status;
scroll_status.main_thread_scrolling_reasons =
MainThreadScrollingReason::kNotScrollingOnMain;
@@ -3736,9 +3832,9 @@ InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll(
}
// If an associated scrolling layer is not found, the scroll node must not
- // support impl-scrolling. The root, secondary root, and inner viewports are
- // all exceptions to this and may not have a layer because it is not required
- // for hit testing.
+ // support impl-scrolling. The root, secondary root, and inner viewports
+ // are all exceptions to this and may not have a layer because it is not
+ // required for hit testing.
if (scroll_node->id != ScrollTree::kRootNodeId &&
scroll_node->id != ScrollTree::kSecondaryRootNodeId &&
!scroll_node->scrolls_inner_viewport &&
@@ -3804,6 +3900,7 @@ ScrollNode* LayerTreeHostImpl::FindScrollNodeForCompositedScrolling(
LayerImpl* layer_impl,
bool* scroll_on_main_thread,
uint32_t* main_thread_scrolling_reasons) const {
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
DCHECK(scroll_on_main_thread);
DCHECK(main_thread_scrolling_reasons);
*main_thread_scrolling_reasons =
@@ -3894,7 +3991,13 @@ InputHandler::ScrollStatus LayerTreeHostImpl::RootScrollBegin(
scroll_state->data()->set_current_native_scrolling_element(
OuterViewportScrollNode()->element_id);
- return ScrollBegin(scroll_state, type);
+ InputHandler::ScrollStatus scroll_status = ScrollBegin(scroll_state, type);
+
+ // Since we provided an ElementId, there should never be a need to perform a
+ // hit test.
+ DCHECK(!scroll_status.needs_main_thread_hit_test);
+
+ return scroll_status;
}
InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
@@ -3937,59 +4040,145 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
// this should only happen in ScrollEnd. We should DCHECK here that the state
// is cleared instead. https://crbug.com/1016229
ClearCurrentlyScrollingNode();
+ auto& scroll_tree = active_tree_->property_trees()->scroll_tree;
- if (auto specified_element_id =
- scroll_state->data()->current_native_scrolling_element()) {
+ ElementId target_element_id = scroll_state->target_element_id();
+
+ if (target_element_id && !scroll_state->is_main_thread_hit_tested()) {
+ TRACE_EVENT_INSTANT0("cc", "Latched scroll node provided",
+ TRACE_EVENT_SCOPE_THREAD);
// If the caller passed in an element_id we can skip all the hit-testing
// bits and provide a node straight-away.
- auto& scroll_tree = active_tree_->property_trees()->scroll_tree;
- scrolling_node = scroll_tree.FindNodeFromElementId(specified_element_id);
-
- // We still need to confirm the targeted node exists and can scroll on the
- // compositor.
- if (scrolling_node) {
- scroll_status = TryScroll(active_tree_->property_trees()->scroll_tree,
- scrolling_node);
- if (IsMainThreadScrolling(scroll_status, scrolling_node))
- scroll_on_main_thread = true;
+ scrolling_node = scroll_tree.FindNodeFromElementId(target_element_id);
+
+ // In unified scrolling, if we found a node we get to scroll it.
+ if (!base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ // We still need to confirm the targeted node exists and can scroll on
+ // the compositor.
+ if (scrolling_node) {
+ scroll_status = TryScroll(active_tree_->property_trees()->scroll_tree,
+ scrolling_node);
+ if (IsMainThreadScrolling(scroll_status, scrolling_node))
+ scroll_on_main_thread = true;
+ }
}
} else {
- gfx::Point viewport_point(scroll_state->position_x(),
- scroll_state->position_y());
+ ScrollNode* starting_node = nullptr;
+ if (target_element_id) {
+ TRACE_EVENT_INSTANT0("cc", "Unlatched scroll node provided",
+ TRACE_EVENT_SCOPE_THREAD);
+ // We had an element id but we should still perform the walk up the
+ // scroll tree from the targeted node to latch to a scroller that can
+ // scroll in the given direction. This mode is only used when scroll
+ // unification is enabled and the targeted scroller comes back from a
+ // main thread hit test.
+ DCHECK(scroll_state->data()->is_main_thread_hit_tested);
+ DCHECK(base::FeatureList::IsEnabled(features::kScrollUnification));
+ starting_node = scroll_tree.FindNodeFromElementId(target_element_id);
+
+ if (!starting_node) {
+ // The main thread sent us an element_id that the compositor doesn't
+ // have a scroll node for. This can happen in some racy conditions, a
+ // freshly created scroller hasn't yet been committed or a
+ // scroller-destroying commit beats the hit test back to the compositor
+ // thread. However, these cases shouldn't be user perceptible.
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNoScrollingLayer;
+ scroll_status.thread = SCROLL_IGNORED;
+ return scroll_status;
+ }
+ } else {
+ TRACE_EVENT_INSTANT0("cc", "Hit Testing for ScrollNode",
+ TRACE_EVENT_SCOPE_THREAD);
+ gfx::Point viewport_point(scroll_state->position_x(),
+ scroll_state->position_y());
+ gfx::PointF device_viewport_point = gfx::ScalePoint(
+ gfx::PointF(viewport_point), active_tree_->device_scale_factor());
+
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ if (scroll_state->data()->is_main_thread_hit_tested) {
+ // The client should have discarded the scroll when the hit test came
+ // back with an invalid element id. If we somehow get here, we should
+ // drop the scroll as continuing could cause us to infinitely bounce
+ // back and forth between here and hit testing on the main thread.
+ NOTREACHED();
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNoScrollingLayer;
+ scroll_status.thread = SCROLL_IGNORED;
+ return scroll_status;
+ }
- gfx::PointF device_viewport_point = gfx::ScalePoint(
- gfx::PointF(viewport_point), active_tree_->device_scale_factor());
- LayerImpl* layer_impl =
- active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
+ // Touch dragging the scrollbar requires falling back to main-thread
+ // scrolling.
+ // TODO(bokan): This could be trivially handled in the compositor by
+ // the new ScrollbarController and should be removed.
+ {
+ LayerImpl* first_scrolling_layer_or_scrollbar =
+ active_tree_->FindFirstScrollingLayerOrScrollbarThatIsHitByPoint(
+ device_viewport_point);
+ if (IsTouchDraggingScrollbar(first_scrolling_layer_or_scrollbar,
+ type)) {
+ TRACE_EVENT_INSTANT0("cc", "Scrollbar Scrolling",
+ TRACE_EVENT_SCOPE_THREAD);
+ scroll_status.thread = SCROLL_ON_MAIN_THREAD;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kScrollbarScrolling;
+ return scroll_status;
+ }
+ }
- if (layer_impl) {
- LayerImpl* first_scrolling_layer_or_scrollbar =
- active_tree_->FindFirstScrollingLayerOrScrollbarThatIsHitByPoint(
- device_viewport_point);
+ ScrollHitTestResult scroll_hit_test =
+ HitTestScrollNode(device_viewport_point);
+
+ if (!scroll_hit_test.hit_test_successful) {
+ // This result tells the client that the compositor doesn't have
+ // enough information to target this scroll. The client should
+ // perform a hit test in Blink and call this method again, with the
+ // ElementId of the hit-tested scroll node.
+ TRACE_EVENT_INSTANT0("cc", "Request Main Thread Hit Test",
+ TRACE_EVENT_SCOPE_THREAD);
+ scroll_status.thread = SCROLL_ON_IMPL_THREAD;
+ scroll_status.needs_main_thread_hit_test = true;
+ return scroll_status;
+ }
- // Touch dragging the scrollbar requires falling back to main-thread
- // scrolling.
- if (IsTouchDraggingScrollbar(first_scrolling_layer_or_scrollbar, type)) {
- TRACE_EVENT_INSTANT0("cc", "Scrollbar Scrolling",
- TRACE_EVENT_SCOPE_THREAD);
- scroll_status.thread = SCROLL_ON_MAIN_THREAD;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kScrollbarScrolling;
- return scroll_status;
- } else if (!IsInitialScrollHitTestReliable(
- layer_impl, first_scrolling_layer_or_scrollbar)) {
- TRACE_EVENT_INSTANT0("cc", "Failed Hit Test", TRACE_EVENT_SCOPE_THREAD);
- scroll_status.thread = SCROLL_UNKNOWN;
- scroll_status.main_thread_scrolling_reasons =
- MainThreadScrollingReason::kFailedHitTest;
- return scroll_status;
+ starting_node = scroll_hit_test.scroll_node;
+ } else {
+ LayerImpl* layer_impl =
+ active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
+
+ if (layer_impl) {
+ LayerImpl* first_scrolling_layer_or_scrollbar =
+ active_tree_->FindFirstScrollingLayerOrScrollbarThatIsHitByPoint(
+ device_viewport_point);
+
+ // Touch dragging the scrollbar requires falling back to main-thread
+ // scrolling.
+ if (IsTouchDraggingScrollbar(first_scrolling_layer_or_scrollbar,
+ type)) {
+ TRACE_EVENT_INSTANT0("cc", "Scrollbar Scrolling",
+ TRACE_EVENT_SCOPE_THREAD);
+ scroll_status.thread = SCROLL_ON_MAIN_THREAD;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kScrollbarScrolling;
+ return scroll_status;
+ } else if (!IsInitialScrollHitTestReliable(
+ layer_impl, first_scrolling_layer_or_scrollbar)) {
+ TRACE_EVENT_INSTANT0("cc", "Failed Hit Test",
+ TRACE_EVENT_SCOPE_THREAD);
+ scroll_status.thread = SCROLL_UNKNOWN;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kFailedHitTest;
+ return scroll_status;
+ }
+ }
+
+ starting_node = FindScrollNodeForCompositedScrolling(
+ device_viewport_point, layer_impl, &scroll_on_main_thread,
+ &scroll_status.main_thread_scrolling_reasons);
}
}
- ScrollNode* starting_node = FindScrollNodeForCompositedScrolling(
- device_viewport_point, layer_impl, &scroll_on_main_thread,
- &scroll_status.main_thread_scrolling_reasons);
-
// The above finds the ScrollNode that's hit by the given point but we
// still need to walk up the scroll tree looking for the first node that
// can consume delta from the scroll state.
@@ -3997,6 +4186,10 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
}
if (scroll_on_main_thread) {
+ // Under scroll unification we can request a main thread hit test, but we
+ // should never send scrolls to the main thread.
+ DCHECK(!base::FeatureList::IsEnabled(features::kScrollUnification));
+
RecordCompositorSlowScrollMetric(type, MAIN_THREAD);
scroll_status.thread = SCROLL_ON_MAIN_THREAD;
return scroll_status;
@@ -4004,11 +4197,20 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
scroll_status.main_thread_scrolling_reasons =
MainThreadScrollingReason::kNoScrollingLayer;
if (settings_.is_layer_tree_for_subframe) {
+ // OOPIFs never have a viewport scroll node so if we can't scroll
+ // we need to be bubble up to the parent frame. This happens by
+ // returning SCROLL_UNKNOWN.
TRACE_EVENT_INSTANT0("cc", "Ignored - No ScrollNode (OOPIF)",
TRACE_EVENT_SCOPE_THREAD);
scroll_status.thread = SCROLL_UNKNOWN;
} else {
- TRACE_EVENT_INSTANT0("cc", "Ignroed - No ScrollNode",
+ // If we didn't hit a layer above we'd usually fallback to the
+ // viewport scroll node. However, there may not be one if a scroll
+ // is received before the root layer has been attached. Chrome now
+ // drops input until the first commit is received so this probably
+ // can't happen in a typical browser session but there may still be
+ // configurations where input is allowed prior to a commit.
+ TRACE_EVENT_INSTANT0("cc", "Ignored - No ScrollNode",
TRACE_EVENT_SCOPE_THREAD);
scroll_status.thread = SCROLL_IGNORED;
}
@@ -4034,13 +4236,22 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
return scroll_status;
}
-ScrollNode* LayerTreeHostImpl::HitTestScrollNode(
+LayerTreeHostImpl::ScrollHitTestResult LayerTreeHostImpl::HitTestScrollNode(
const gfx::PointF& device_viewport_point) const {
+ ScrollHitTestResult result;
+ result.scroll_node = nullptr;
+ result.hit_test_successful = false;
+
LayerImpl* layer_impl =
active_tree_->FindLayerThatIsHitByPoint(device_viewport_point);
- if (!layer_impl)
- return nullptr;
+ if (!layer_impl) {
+ result.hit_test_successful = true;
+ if (InnerViewportScrollNode())
+ result.scroll_node = GetNodeToScroll(InnerViewportScrollNode());
+
+ return result;
+ }
// There are some cases where the hit layer may not be correct (e.g. layer
// squashing). If we detect this case, we can't target a scroll node here.
@@ -4052,10 +4263,19 @@ ScrollNode* LayerTreeHostImpl::HitTestScrollNode(
if (!IsInitialScrollHitTestReliable(layer_impl,
first_scrolling_layer_or_scrollbar)) {
TRACE_EVENT_INSTANT0("cc", "Failed Hit Test", TRACE_EVENT_SCOPE_THREAD);
- return nullptr;
+ return result;
}
}
+ // If we hit a non-fast scrollable region, that means there's some reason we
+ // can't scroll in this region. Primarily, because there's another scroller
+ // there that isn't composited and we don't know about so we'll return
+ // nullptr in that case.
+ if (active_tree_->PointHitsNonFastScrollableRegion(device_viewport_point,
+ *layer_impl)) {
+ return result;
+ }
+
// If we hit a scrollbar layer, get the ScrollNode from its associated
// scrolling layer, rather than directly from the scrollbar layer. The latter
// would return the parent scroller's ScrollNode.
@@ -4068,7 +4288,9 @@ ScrollNode* LayerTreeHostImpl::HitTestScrollNode(
ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
ScrollNode* scroll_node = scroll_tree.Node(layer_impl->scroll_tree_index());
- return GetNodeToScroll(scroll_node);
+ result.scroll_node = GetNodeToScroll(scroll_node);
+ result.hit_test_successful = true;
+ return result;
}
// Requires falling back to main thread scrolling when it hit tests in scrollbar
@@ -4602,10 +4824,9 @@ bool LayerTreeHostImpl::CanConsumeDelta(const ScrollState& scroll_state,
return false;
}
delta_to_scroll = local_scroll_delta;
- } else if (scroll_state.delta_granularity() ==
- ui::ScrollGranularity::kScrollByPercentage) {
- delta_to_scroll =
- ResolveScrollPercentageToPixels(scroll_node, delta_to_scroll);
+ } else {
+ delta_to_scroll = ResolveScrollGranularityToPixels(
+ scroll_node, delta_to_scroll, scroll_state.delta_granularity());
}
if (ComputeScrollDelta(scroll_node, delta_to_scroll) != gfx::Vector2dF())
@@ -4656,21 +4877,27 @@ InputHandlerScrollResult LayerTreeHostImpl::ScrollUpdate(
// The current_native_scrolling_element should only be set for ScrollBegin.
DCHECK(!scroll_state->data()->current_native_scrolling_element());
+ TRACE_EVENT2("cc", "LayerTreeHostImpl::ScrollUpdate", "dx",
+ scroll_state->delta_x(), "dy", scroll_state->delta_y());
if (!CurrentlyScrollingNode())
return InputHandlerScrollResult();
last_scroll_update_state_ = *scroll_state;
- bool is_delta_percent_units = scroll_state->delta_granularity() ==
- ui::ScrollGranularity::kScrollByPercentage;
- if (is_delta_percent_units) {
- gfx::Vector2dF resolvedScrollDelta = ResolveScrollPercentageToPixels(
- *CurrentlyScrollingNode(),
- gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y()));
-
- scroll_state->data()->delta_x = resolvedScrollDelta.x();
- scroll_state->data()->delta_y = resolvedScrollDelta.y();
+ gfx::Vector2dF resolvedScrollDelta = ResolveScrollGranularityToPixels(
+ *CurrentlyScrollingNode(),
+ gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y()),
+ scroll_state->delta_granularity());
+
+ scroll_state->data()->delta_x = resolvedScrollDelta.x();
+ scroll_state->data()->delta_y = resolvedScrollDelta.y();
+ // The decision of whether or not we'll animate a scroll comes down to
+ // whether the granularity is specified in precise pixels or not. Thus we
+ // need to preserve a precise granularity if that's what was specified; all
+ // others are animated and so can be resolved to regular pixels.
+ if (scroll_state->delta_granularity() !=
+ ui::ScrollGranularity::kScrollByPrecisePixel) {
scroll_state->data()->delta_granularity =
ui::ScrollGranularity::kScrollByPixel;
}
@@ -4994,9 +5221,6 @@ void LayerTreeHostImpl::RecordScrollBegin(
ScrollBeginThreadState scroll_start_state) {
auto tracker_type = GetTrackerTypeForScroll(input_type);
DCHECK_NE(tracker_type, FrameSequenceTrackerType::kMaxType);
- auto* metrics = frame_trackers_.StartSequence(tracker_type);
- if (!metrics)
- return;
// The main-thread is the 'scrolling thread' if:
// (1) the scroll is driven by the main thread, or
@@ -5015,7 +5239,7 @@ void LayerTreeHostImpl::RecordScrollBegin(
scrolling_thread = FrameSequenceMetrics::ThreadType::kMain;
break;
}
- metrics->SetScrollingThread(scrolling_thread);
+ frame_trackers_.StartScrollSequence(tracker_type, scrolling_thread);
}
void LayerTreeHostImpl::RecordScrollEnd(ui::ScrollInputType input_type) {
@@ -5078,11 +5302,14 @@ InputHandlerPointerResult LayerTreeHostImpl::MouseMoveAt(
gfx::PointF device_viewport_point = gfx::ScalePoint(
gfx::PointF(viewport_point), active_tree_->device_scale_factor());
- ScrollNode* scroll_node = HitTestScrollNode(device_viewport_point);
+
+ ScrollHitTestResult hit_test = HitTestScrollNode(device_viewport_point);
+
+ ScrollNode* scroll_node = hit_test.scroll_node;
// The hit test can fail in some cases, e.g. we don't know if a region of a
// squashed layer has content or is empty.
- if (!scroll_node)
+ if (!hit_test.hit_test_successful || !scroll_node)
return result;
// Scrollbars for the viewport are registered with the outer viewport layer.
@@ -5242,6 +5469,9 @@ std::unique_ptr<ScrollAndScaleSet> LayerTreeHostImpl::ProcessScrollDeltas() {
scroll_info->overscroll_delta = overscroll_delta_for_main_thread_;
overscroll_delta_for_main_thread_ = gfx::Vector2dF();
+ scroll_info->ongoing_scroll_animation =
+ !!mutator_host_->ImplOnlyScrollAnimatingElement();
+
// Use the |last_latched_scroller_| rather than the |CurrentlyScrollingNode|
// since the latter may be cleared by a GSE before we've committed these
// values to the main thread.
@@ -5398,9 +5628,10 @@ void LayerTreeHostImpl::RegisterScrollbarAnimationController(
}
void LayerTreeHostImpl::DidUnregisterScrollbarLayer(
- ElementId scroll_element_id) {
+ ElementId scroll_element_id,
+ ScrollbarOrientation orientation) {
scrollbar_animation_controllers_.erase(scroll_element_id);
- scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id);
+ scrollbar_controller_->DidUnregisterScrollbar(scroll_element_id, orientation);
}
ScrollbarAnimationController*
@@ -6230,7 +6461,7 @@ void LayerTreeHostImpl::InitializeUkm(
void LayerTreeHostImpl::SetActiveURL(const GURL& url, ukm::SourceId source_id) {
tile_manager_.set_active_url(url);
-
+ has_observed_first_scroll_delay_ = false;
// The active tree might still be from content for the previous page when the
// recorder is updated here, since new content will be pushed with the next
// main frame. But we should only get a few impl frames wrong here in that
diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h
index f7ce4e673e6..47dcba6decb 100644
--- a/chromium/cc/trees/layer_tree_host_impl.h
+++ b/chromium/cc/trees/layer_tree_host_impl.h
@@ -29,6 +29,7 @@
#include "cc/input/scrollbar_animation_controller.h"
#include "cc/input/scrollbar_controller.h"
#include "cc/layers/layer_collections.h"
+#include "cc/metrics/dropped_frame_counter.h"
#include "cc/metrics/event_metrics.h"
#include "cc/metrics/events_metrics_manager.h"
#include "cc/metrics/frame_sequence_tracker_collection.h"
@@ -83,7 +84,6 @@ class BrowserControlsOffsetManager;
class CompositorFrameReportingController;
class DebugRectHistory;
class EvictionTilePriorityQueue;
-class FrameRateCounter;
class ImageAnimationController;
class LCDTextMetricsReporter;
class LayerImpl;
@@ -176,6 +176,17 @@ class LayerTreeHostImplClient {
virtual void NotifyThroughputTrackerResults(CustomTrackerResults results) = 0;
+ // Send the throughput data to the main thread's LayerTreeHostClient, which
+ // then send the data to the browser process and eventually report to UKM.
+ virtual void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) = 0;
+
+ virtual void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) = 0;
+
protected:
virtual ~LayerTreeHostImplClient() = default;
};
@@ -333,6 +344,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
float CurrentTopControlsShownRatio() const override;
float CurrentBottomControlsShownRatio() const override;
void DidChangeBrowserControlsPosition() override;
+ void DidObserveScrollDelay(base::TimeDelta scroll_delay,
+ base::TimeTicks scroll_timestamp);
bool HaveRootScrollNode() const override;
void SetNeedsCommit() override;
@@ -469,7 +482,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void RegisterScrollbarAnimationController(ElementId scroll_element_id,
float initial_opacity);
- void DidUnregisterScrollbarLayer(ElementId scroll_element_id);
+ void DidUnregisterScrollbarLayer(ElementId scroll_element_id,
+ ScrollbarOrientation orientation);
ScrollbarAnimationController* ScrollbarAnimationControllerForElementId(
ElementId scroll_element_id) const;
void FlashAllScrollbars(bool did_scroll);
@@ -610,7 +624,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
const ScrollNode* CurrentlyScrollingNode() const;
bool scroll_affects_scroll_handler() const {
- return scroll_affects_scroll_handler_;
+ return settings_.enable_synchronized_scrolling &&
+ scroll_affects_scroll_handler_;
}
void QueueSwapPromiseForMainThreadScrollUpdate(
std::unique_ptr<SwapPromise> swap_promise);
@@ -641,9 +656,8 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
const gfx::Transform& DrawTransform() const;
std::unique_ptr<ScrollAndScaleSet> ProcessScrollDeltas();
- FrameRateCounter* fps_counter() { return fps_counter_.get(); }
- base::Optional<int> current_universal_throughput() {
- return frame_trackers_.current_universal_throughput();
+ DroppedFrameCounter* dropped_frame_counter() {
+ return &dropped_frame_counter_;
}
MemoryHistory* memory_history() { return memory_history_.get(); }
DebugRectHistory* debug_rect_history() { return debug_rect_history_.get(); }
@@ -735,6 +749,13 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
gfx::Vector2dF ComputeScrollDelta(const ScrollNode& scroll_node,
const gfx::Vector2dF& delta);
+ // Resolves a delta in the given granularity for the |scroll_node| into
+ // physical pixels to scroll.
+ gfx::Vector2dF ResolveScrollGranularityToPixels(
+ const ScrollNode& scroll_node,
+ const gfx::Vector2dF& scroll_delta,
+ ui::ScrollGranularity granularity);
+
void ScheduleMicroBenchmark(std::unique_ptr<MicroBenchmarkImpl> benchmark);
viz::CompositorFrameMetadata MakeCompositorFrameMetadata();
@@ -785,15 +806,6 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
: task_runner_provider_->MainThreadTaskRunner();
}
- // Determines whether the given scroll node can scroll on the compositor
- // thread or if there are any reasons it must be scrolled on the main thread
- // or not at all. Note: in general, this is not sufficient to determine if a
- // scroll can occur on the compositor thread. If hit testing to a scroll
- // node, the caller must also check whether the hit point intersects a
- // non-fast-scrolling-region of any ancestor scrolling layers.
- InputHandler::ScrollStatus TryScroll(const ScrollTree& scroll_tree,
- ScrollNode* scroll_node) const;
-
// Return all ScrollNode indices that have an associated layer with a non-fast
// region that intersects the point.
base::flat_set<int> NonFastScrollableNodes(
@@ -894,14 +906,20 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void CollectScrollDeltas(ScrollAndScaleSet* scroll_info);
void CollectScrollbarUpdates(ScrollAndScaleSet* scroll_info) const;
- gfx::Vector2dF ResolveScrollPercentageToPixels(
- const ScrollNode& scroll_node,
- const gfx::Vector2dF& resolved_pixels);
-
// Returns the ScrollNode we should use to scroll, accounting for viewport
// scroll chaining rules.
ScrollNode* GetNodeToScroll(ScrollNode* node) const;
+ // Determines whether the given scroll node can scroll on the compositor
+ // thread or if there are any reasons it must be scrolled on the main thread
+ // or not at all. Note: in general, this is not sufficient to determine if a
+ // scroll can occur on the compositor thread. If hit testing to a scroll
+ // node, the caller must also check whether the hit point intersects a
+ // non-fast-scrolling-region of any ancestor scrolling layers. Can be removed
+ // after scroll unification https://crbug.com/476553.
+ InputHandler::ScrollStatus TryScroll(const ScrollTree& scroll_tree,
+ ScrollNode* scroll_node) const;
+
// Transforms viewport start point and scroll delta to local start point and
// local delta, respectively. If the transformation of either the start or end
// point of a scroll is clipped, the function returns false.
@@ -957,13 +975,14 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
LayerImpl* first_scrolling_layer_or_drawn_scrollbar,
ui::ScrollInputType type);
- // Initial scroll hit testing can be unreliable in the presence of squashed
- // layers. In this case, we fall back to main thread scrolling. This function
- // compares |layer_impl| returned from a regular hit test to the layer
- // returned from a hit test performed only on scrollers and scrollbars. If the
- // closest scrolling ancestor of |layer_impl| is not the other layer, then the
- // layer_impl must be a squasing layer overtop of some other scroller and we
- // must rely on the main thread.
+ // |layer| is returned from a regular hit test, and
+ // |first_scrolling_layer_or_drawn_scrollbar| is returned from a hit test
+ // performed only on scrollers and scrollbars. Initial scroll hit testing can
+ // be unreliable if the latter is not the direct scroll ancestor of the
+ // former. In this case, we will fall back to main thread scrolling because
+ // the compositor thread doesn't know which layer to scroll. This happens when
+ // a layer covers a scroller that doesn't scroll the former, or a scroller is
+ // masked by a mask layer for mask image, clip-path, rounded border, etc.
//
// Note, position: fixed layers use the inner viewport as their ScrollNode
// (since they don't scroll with the outer viewport), however, scrolls from
@@ -1018,21 +1037,35 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
void ClearCurrentlyScrollingNode();
// Performs a hit test to determine the ScrollNode to use when scrolling at
- // |viewport_point|. Can return nullptr if the hit test fails; see the
- // comment in IsInitialScrollHitTestReliable
- ScrollNode* HitTestScrollNode(const gfx::PointF& device_viewport_point) const;
+ // |viewport_point|. If no layer is hit, this falls back to the inner
+ // viewport scroll node. Returns:
+ // - If |hit_test_sucessful| is false, hit testing has failed and the
+ // compositor cannot determine the correct scroll node (e.g. see comments in
+ // IsInitialScrollHitTestReliable). |scroll_node| is always nullptr in this
+ // case.
+ // - If |hit_test_successful| is true, returns the ScrollNode to use in
+ // |scroll_node|. This can be nullptr if no layer was hit and there are no
+ // viewport nodes (e.g. OOPIF, UI compositor).
+ struct ScrollHitTestResult {
+ ScrollNode* scroll_node;
+ bool hit_test_successful;
+ };
+ ScrollHitTestResult HitTestScrollNode(
+ const gfx::PointF& device_viewport_point) const;
// Similar to above but includes complicated logic to determine whether the
// ScrollNode is able to be scrolled on the compositor or requires main
// thread scrolling. If main thread scrolling is required
// |scroll_on_main_thread| is set to true and the reason is given in
// |main_thread_scrolling_reason| to on of the enum values in
- // main_thread_scrolling_reason.h.
+ // main_thread_scrolling_reason.h. Can be removed after scroll unification
+ // https://crbug.com/476553.
ScrollNode* FindScrollNodeForCompositedScrolling(
const gfx::PointF& device_viewport_point,
LayerImpl* layer_hit_by_point,
bool* scroll_on_main_thread,
uint32_t* main_thread_scrolling_reason) const;
+
void StartScrollbarFadeRecursive(LayerImpl* layer);
void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy);
@@ -1226,7 +1259,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
std::unique_ptr<PageScaleAnimation> page_scale_animation_;
- std::unique_ptr<FrameRateCounter> fps_counter_;
+ DroppedFrameCounter dropped_frame_counter_;
std::unique_ptr<MemoryHistory> memory_history_;
std::unique_ptr<DebugRectHistory> debug_rect_history_;
@@ -1400,6 +1433,7 @@ class CC_EXPORT LayerTreeHostImpl : public InputHandler,
std::unique_ptr<LCDTextMetricsReporter> lcd_text_metrics_reporter_;
FrameRateEstimator frame_rate_estimator_;
+ bool has_observed_first_scroll_delay_ = false;
// Must be the last member to ensure this is destroyed first in the
// destruction order and invalidates all weak pointers.
diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc
index 3d1cfc76a5d..fd82323bf7e 100644
--- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc
@@ -27,10 +27,12 @@
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_id_provider.h"
#include "cc/animation/transform_operations.h"
+#include "cc/base/features.h"
#include "cc/base/histograms.h"
#include "cc/input/browser_controls_offset_manager.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/input/page_scale_animation.h"
+#include "cc/input/scroll_utils.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/heads_up_display_layer_impl.h"
#include "cc/layers/layer_impl.h"
@@ -262,7 +264,16 @@ class LayerTreeHostImplTest : public testing::Test,
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override {}
void NotifyThroughputTrackerResults(CustomTrackerResults results) override {}
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) override {}
+ void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) override {
+ first_scroll_observed++;
+ }
void set_reduce_memory_result(bool reduce_memory_result) {
reduce_memory_result_ = reduce_memory_result;
}
@@ -496,15 +507,21 @@ class LayerTreeHostImplTest : public testing::Test,
layer_tree_impl->DidBecomeActive();
// The point hits squash1 layer and also scroll layer, because scroll layer
- // is not an ancestor of squash1 layer, we cannot scroll on impl thread.
+ // is not an ancestor of squash1 layer, we cannot scroll on impl thread
+ // (without at least a hit test on the main thread).
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(230, 150), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
// The point hits squash1 layer and also scrollbar layer.
status = host_impl_->ScrollBegin(
@@ -512,9 +529,14 @@ class LayerTreeHostImplTest : public testing::Test,
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ ASSERT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ ASSERT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
// The point hits squash2 layer and also scroll layer, because scroll layer
// is an ancestor of squash2 layer, we should scroll on impl.
@@ -670,8 +692,8 @@ class LayerTreeHostImplTest : public testing::Test,
host_impl_ = nullptr;
}
- void WhiteListedTouchActionTestHelper(float device_scale_factor,
- float page_scale_factor) {
+ void AllowedTouchActionTestHelper(float device_scale_factor,
+ float page_scale_factor) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
DrawFrame();
@@ -825,6 +847,26 @@ class LayerTreeHostImplTest : public testing::Test,
scoped_refptr<AnimationTimeline> timeline_;
std::unique_ptr<base::Thread> image_worker_;
int next_layer_id_ = 2;
+ int first_scroll_observed = 0;
+};
+
+// Allows parameterizing the above class on the scroll unification flag. We
+// can't parameterize the base class itself because it serves as a base to
+// other parameterized descendants. Can be removed when unification ships.
+class ScrollUnifiedLayerTreeHostImplTest
+ : public LayerTreeHostImplTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ ScrollUnifiedLayerTreeHostImplTest() {
+ if (GetParam()) {
+ scoped_feature_list.InitAndEnableFeature(features::kScrollUnification);
+ } else {
+ scoped_feature_list.InitAndDisableFeature(features::kScrollUnification);
+ }
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list;
};
class CommitToPendingTreeLayerTreeHostImplTest : public LayerTreeHostImplTest {
@@ -901,7 +943,11 @@ class TestInputHandlerClient : public InputHandlerClient {
float max_page_scale_factor_;
};
-TEST_F(LayerTreeHostImplTest, LocalAndExternalPinchState) {
+INSTANTIATE_TEST_SUITE_P(All,
+ ScrollUnifiedLayerTreeHostImplTest,
+ testing::Bool());
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, LocalAndExternalPinchState) {
// PinchGestureBegin/End update pinch_gesture_active() properly.
EXPECT_FALSE(host_impl_->pinch_gesture_active());
host_impl_->PinchGestureBegin();
@@ -925,7 +971,7 @@ TEST_F(LayerTreeHostImplTest, LocalAndExternalPinchState) {
EXPECT_FALSE(host_impl_->pinch_gesture_active());
}
-TEST_F(LayerTreeHostImplTest, NotifyIfCanDrawChanged) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NotifyIfCanDrawChanged) {
// Note: It is not possible to disable the renderer once it has been set,
// so we do not need to test that disabling the renderer notifies us
// that can_draw changed.
@@ -961,7 +1007,7 @@ TEST_F(LayerTreeHostImplTest, NotifyIfCanDrawChanged) {
on_can_draw_state_changed_called_ = false;
}
-TEST_F(LayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) {
CreateHostImpl(DefaultSettings(), FakeLayerTreeFrameSink::CreateSoftware());
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -979,7 +1025,7 @@ TEST_F(LayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) {
ASSERT_EQ(fake_layer_tree_frame_sink->num_sent_frames(), 1u);
}
-TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaNoLayers) {
host_impl_->active_tree()->SetRootLayerForTesting(nullptr);
std::unique_ptr<ScrollAndScaleSet> scroll_info =
@@ -987,7 +1033,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) {
ASSERT_EQ(scroll_info->scrolls.size(), 0u);
}
-TEST_F(LayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
{
LayerImpl* child1 = AddLayer();
@@ -1015,7 +1061,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDeltaTreeButNoChanges) {
ExpectClearedScrollDeltasRecursive(root);
}
-TEST_F(LayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDeltaRepeatedScrolls) {
gfx::ScrollOffset scroll_offset(20, 30);
gfx::ScrollOffset scroll_delta(11, -15);
@@ -1075,7 +1121,9 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
"Compositing.Renderer.GPUMemoryForTilingsInKb", 1);
}
-TEST_F(LayerTreeHostImplTest, ScrollBeforeRootLayerAttached) {
+// This test verifies that we drop a scroll (and don't crash) if a scroll is
+// received before the root layer has been attached. https://crbug.com/895817.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeRootLayerAttached) {
InputHandler::ScrollStatus status =
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 1),
ui::ScrollInputType::kWheel)
@@ -1100,7 +1148,7 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeRootLayerAttached) {
// to pre-commit input deferral which causes some input events to be dropped
// before the first commit in a renderer has occurred. See the flag
// kAllowPreCommitInput and how it's used.
-TEST_F(LayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
// Simulate receiving a gesture mid-stream so that the Begin wasn't ever
@@ -1141,7 +1189,7 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateAndEndNoOpWithoutBegin) {
// Test that specifying a scroller to ScrollBegin (i.e. avoid hit testing)
// returns the correct status if the scroller cannot be scrolled on the
// compositor thread.
-TEST_F(LayerTreeHostImplTest, TargetMainThreadScroller) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, TargetMainThreadScroller) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
ScrollStateData scroll_state_data;
@@ -1160,22 +1208,32 @@ TEST_F(LayerTreeHostImplTest, TargetMainThreadScroller) {
}
// Now add a MainThreadScrollingReason. Trying to target the node this time
- // should result in a failed ScrollBegin with the appropriate return value.
+ // should result in a failed ScrollBegin with the appropriate return value,
+ // unless scroll unification is enabled - since all nodes can be scrolled
+ // from the compositor in that mode.
host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons =
MainThreadScrollingReason::kThreadedScrollingDisabled;
{
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
scroll_state.get(), ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(
- host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons,
- status.main_thread_scrolling_reasons);
- EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(
+ MainThreadScrollingReason::kThreadedScrollingDisabled,
+ host_impl_->CurrentlyScrollingNode()->main_thread_scrolling_reasons);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(
+ host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons,
+ status.main_thread_scrolling_reasons);
+ EXPECT_FALSE(host_impl_->CurrentlyScrollingNode());
+ }
}
}
-TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -1206,7 +1264,8 @@ TEST_F(LayerTreeHostImplTest, ScrollRootCallsCommitAndRedraw) {
// makes some sense since touchscreen/high-precision touchpad scrolling has a
// physical metaphor (movement sticks to finger) so smoothness should be
// prioritized.
-TEST_F(LayerTreeHostImplTest, ActivelyTouchScrollingOnlyAfterScrollMovement) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ActivelyTouchScrollingOnlyAfterScrollMovement) {
SetupViewportLayersOuterScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -1287,7 +1346,7 @@ TEST_F(LayerTreeHostImplTest, ActivelyTouchScrollingOnlyAfterScrollMovement) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutRootLayer) {
// We should not crash when trying to scroll an empty layer tree.
InputHandler::ScrollStatus status =
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
@@ -1299,7 +1358,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutRootLayer) {
status.main_thread_scrolling_reasons);
}
-TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutRenderer) {
auto gl_owned = std::make_unique<viz::TestGLES2Interface>();
gl_owned->LoseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
GL_INNOCENT_CONTEXT_RESET_ARB);
@@ -1323,7 +1382,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
status.main_thread_scrolling_reasons);
}
-TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -1356,7 +1415,8 @@ TEST_F(LayerTreeHostImplTest, ReplaceTreeWhileScrolling) {
scroll_delta));
}
-TEST_F(LayerTreeHostImplTest, ActivateTreeScrollingNodeDisappeared) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ActivateTreeScrollingNodeDisappeared) {
SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(1000, 1000));
auto status = host_impl_->ScrollBegin(
@@ -1385,7 +1445,7 @@ TEST_F(LayerTreeHostImplTest, ActivateTreeScrollingNodeDisappeared) {
EXPECT_FALSE(host_impl_->active_tree()->CurrentlyScrollingNode());
}
-TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll = InnerViewportScrollLayer();
scroll->SetWheelEventHandlerRegion(Region(gfx::Rect(20, 20)));
@@ -1415,7 +1475,7 @@ TEST_F(LayerTreeHostImplTest, ScrollBlocksOnWheelEventHandlers) {
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -1467,9 +1527,9 @@ TEST_F(LayerTreeHostImplTest, ScrollBlocksOnTouchEventHandlers) {
EXPECT_EQ(TouchAction::kPanX, touch_action);
}
-TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
- SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
- host_impl_->InnerViewportScrollNode()->main_thread_scrolling_reasons =
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ShouldScrollOnMainThread) {
+ SetupViewportLayersOuterScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
+ host_impl_->OuterViewportScrollNode()->main_thread_scrolling_reasons =
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
DrawFrame();
@@ -1478,30 +1538,48 @@ TEST_F(LayerTreeHostImplTest, ShouldScrollOnMainThread) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ host_impl_->CurrentlyScrollingNode()->main_thread_scrolling_reasons);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ status.main_thread_scrolling_reasons);
+ }
status =
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ host_impl_->CurrentlyScrollingNode()->main_thread_scrolling_reasons);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollWithOverlappingNonScrollableLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollWithOverlappingNonScrollableLayer) {
CreateAndTestNonScrollableLayers(false);
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ScrollWithOverlappingTransparentNonScrollableLayer) {
CreateAndTestNonScrollableLayers(true);
}
-TEST_F(LayerTreeHostImplTest, ScrolledOverlappingDrawnScrollbarLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrolledOverlappingDrawnScrollbarLayer) {
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
gfx::Size content_size = gfx::Size(360, 600);
gfx::Size scroll_content_size = gfx::Size(345, 3800);
@@ -1534,9 +1612,16 @@ TEST_F(LayerTreeHostImplTest, ScrolledOverlappingDrawnScrollbarLayer) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
// The point hits the drawn scrollbar layer completely and should scroll on
// the impl thread.
@@ -1623,7 +1708,7 @@ TEST_F(LayerTreeHostImplTestInvokeMainThreadCallbacks,
mock_details.presentation_feedback);
}
-TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionBasic) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
@@ -1631,24 +1716,40 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
DrawFrame();
- // All scroll types inside the non-fast scrollable region should fail.
+ // All scroll types inside the non-fast scrollable region should fail. When
+ // scroll unification is enabled, these scrolls succeed but request a main
+ // thread hit test.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(25, 25), gfx::Vector2d(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
status = host_impl_->ScrollBegin(
BeginState(gfx::Point(25, 25), gfx::Vector2d(0, 10),
ui::ScrollInputType::kTouchscreen)
.get(),
ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
// All scroll types outside this region should succeed.
status = host_impl_->ScrollBegin(
@@ -1679,7 +1780,7 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionBasic) {
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
@@ -1699,6 +1800,7 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
host_impl_->ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2d(0, 1),
ui::ScrollInputType::kWheel)
@@ -1711,12 +1813,19 @@ TEST_F(LayerTreeHostImplTest, NonFastScrollableRegionWithOffset) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHandlerNotPresent) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
EXPECT_FALSE(host_impl_->active_tree()->have_scroll_event_handlers());
DrawFrame();
@@ -1731,7 +1840,7 @@ TEST_F(LayerTreeHostImplTest, ScrollHandlerNotPresent) {
EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
}
-TEST_F(LayerTreeHostImplTest, ScrollHandlerPresent) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHandlerPresent) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
host_impl_->active_tree()->set_have_scroll_event_handlers(true);
DrawFrame();
@@ -1746,7 +1855,7 @@ TEST_F(LayerTreeHostImplTest, ScrollHandlerPresent) {
EXPECT_FALSE(host_impl_->scroll_affects_scroll_handler());
}
-TEST_F(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
DrawFrame();
@@ -1848,7 +1957,7 @@ TEST_F(LayerTreeHostImplTest, ScrollUpdateReturnsCorrectValue) {
// TODO(sunyunjia): Move scroll snap tests to a separate file.
// https://crbug.com/851690
-TEST_F(LayerTreeHostImplTest, ScrollSnapOnX) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnX) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -1893,7 +2002,7 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnX) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, ScrollSnapOnY) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnY) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -1938,7 +2047,7 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnY) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, ScrollSnapOnBoth) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapOnBoth) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -1984,7 +2093,7 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapOnBoth) {
// Simulate a ScrollBegin and ScrollEnd without any intervening ScrollUpdate.
// This test passes if it doesn't crash.
-TEST_F(LayerTreeHostImplTest, SnapAfterEmptyScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAfterEmptyScroll) {
CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -1999,7 +2108,7 @@ TEST_F(LayerTreeHostImplTest, SnapAfterEmptyScroll) {
host_impl_->ScrollEnd(true);
}
-TEST_F(LayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -2057,7 +2166,7 @@ TEST_F(LayerTreeHostImplTest, ScrollSnapAfterAnimatedScroll) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, SnapAnimationTargetUpdated) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationTargetUpdated) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -2120,7 +2229,7 @@ TEST_F(LayerTreeHostImplTest, SnapAnimationTargetUpdated) {
EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 50), CurrentScrollOffset(overflow));
}
-TEST_F(LayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
LayerImpl* overflow = CreateLayerForSnapping();
gfx::Point pointer_position(10, 10);
@@ -2183,7 +2292,7 @@ TEST_F(LayerTreeHostImplTest, SnapAnimationCancelledByScroll) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
SnapAnimationShouldNotStartWhenScrollEndsAtSnapTarget) {
LayerImpl* overflow = CreateLayerForSnapping();
@@ -2230,7 +2339,7 @@ TEST_F(LayerTreeHostImplTest,
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
GetSnapFlingInfoAndSetAnimatingSnapTargetWhenZoomed) {
LayerImpl* overflow = CreateLayerForSnapping();
// Scales the page to its 1/5.
@@ -2270,7 +2379,8 @@ TEST_F(LayerTreeHostImplTest,
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, SnapFlingAnimationEndWithoutFinishing) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SnapFlingAnimationEndWithoutFinishing) {
LayerImpl* overflow = CreateLayerForSnapping();
// Scales the page to its 1/5.
host_impl_->active_tree()->PushPageScaleFromMainThread(0.2f, 0.1f, 5.f);
@@ -2310,7 +2420,8 @@ TEST_F(LayerTreeHostImplTest, SnapFlingAnimationEndWithoutFinishing) {
GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
}
-TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ OverscrollBehaviorPreventsPropagation) {
const gfx::Size kViewportSize(100, 100);
const gfx::Size kContentSize(200, 200);
SetupViewportLayersOuterScrolls(kViewportSize, kContentSize);
@@ -2508,7 +2619,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollBehaviorPreventsPropagation) {
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 10), CurrentScrollOffset(overflow));
}
-TEST_F(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
const gfx::Size kViewportSize(100, 100);
const gfx::Size kContentSize(200, 200);
SetupViewportLayersOuterScrolls(kViewportSize, kContentSize);
@@ -2582,7 +2693,10 @@ TEST_F(LayerTreeHostImplTest, ScrollWithUserUnscrollableLayers) {
EXPECT_VECTOR_EQ(gfx::Vector2dF(10, 20), CurrentScrollOffset(overflow));
}
-TEST_F(LayerTreeHostImplTest, ForceMainThreadScrollWithoutScrollLayer) {
+// Test that if a scroll node doesn't have an associated Layer, scrolling
+// is forced to the main thread. With scroll unification, this kind of scroll
+// is now handled on the impl thread but will force a repaint on each scroll.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNodeWithoutScrollLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
ScrollNode* scroll_node = host_impl_->OuterViewportScrollNode();
// Change the scroll node so that it no longer has an associated layer.
@@ -2595,9 +2709,20 @@ TEST_F(LayerTreeHostImplTest, ForceMainThreadScrollWithoutScrollLayer) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ // We don't have a layer for the scroller but we didn't hit a non-fast
+ // scrolling region or fail hit testing the layer - we don't need a main
+ // thread hit test in this case.
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
}
TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
@@ -2712,7 +2837,8 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
EXPECT_FALSE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, AnimationSchedulingCommitToActiveTree) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AnimationSchedulingCommitToActiveTree) {
EXPECT_TRUE(host_impl_->CommitToActiveTree());
auto* root = SetupDefaultRootLayer(gfx::Size(50, 50));
@@ -2749,7 +2875,8 @@ TEST_F(LayerTreeHostImplTest, AnimationSchedulingCommitToActiveTree) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, AnimationSchedulingOnLayerDestruction) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AnimationSchedulingOnLayerDestruction) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(50, 50));
LayerImpl* child = AddLayer();
@@ -2828,7 +2955,7 @@ class MissingTilesLayer : public LayerImpl {
bool has_missing_tiles_;
};
-TEST_F(LayerTreeHostImplTest, ImplPinchZoom) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ImplPinchZoom) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -2909,7 +3036,7 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoom) {
}
}
-TEST_F(LayerTreeHostImplTest, ViewportScrollbarGeometry) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ViewportScrollbarGeometry) {
// Tests for correct behavior of solid color scrollbars on unscrollable pages
// under tricky fractional scale/size issues.
@@ -2966,7 +3093,7 @@ TEST_F(LayerTreeHostImplTest, ViewportScrollbarGeometry) {
EXPECT_TRUE(h_scrollbar->CanScrollOrientation());
}
-TEST_F(LayerTreeHostImplTest, ViewportScrollOrder) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ViewportScrollOrder) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.25f, 4);
@@ -3036,7 +3163,8 @@ TEST_F(LayerTreeHostImplTest, ViewportScrollOrder) {
// Make sure scrolls smaller than a unit applied to the viewport don't get
// dropped. crbug.com/539334.
-TEST_F(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollViewportWithFractionalAmounts) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
const gfx::Size content_size(1000, 1000);
@@ -3093,7 +3221,7 @@ TEST_F(LayerTreeHostImplTest, ScrollViewportWithFractionalAmounts) {
// Tests that scrolls during a pinch gesture (i.e. "two-finger" scrolls) work
// as expected. That is, scrolling during a pinch should bubble from the inner
// to the outer viewport.
-TEST_F(LayerTreeHostImplTest, ScrollDuringPinchGesture) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDuringPinchGesture) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
@@ -3153,7 +3281,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDuringPinchGesture) {
// Tests the "snapping" of pinch-zoom gestures to the screen edge. That is, when
// a pinch zoom is anchored within a certain margin of the screen edge, we
// should assume the user means to scroll into the edge of the screen.
-TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
@@ -3242,7 +3370,8 @@ TEST_F(LayerTreeHostImplTest, PinchZoomSnapsToScreenEdge) {
CurrentScrollOffset(InnerViewportScrollLayer()));
}
-TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ImplPinchZoomWheelBubbleBetweenViewports) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -3307,7 +3436,7 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoomWheelBubbleBetweenViewports) {
CurrentScrollOffset(inner_scroll_layer));
}
-TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithSwapPromises) {
ui::LatencyInfo latency_info;
latency_info.set_trace_id(5);
latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
@@ -3337,7 +3466,7 @@ TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) {
// Test that scrolls targeting a layer with a non-null scroll_parent() don't
// bubble up.
-TEST_F(LayerTreeHostImplTest, ScrollDoesntBubble) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollDoesntBubble) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
LayerImpl* viewport_scroll = InnerViewportScrollLayer();
@@ -3420,7 +3549,7 @@ TEST_F(LayerTreeHostImplTest, ScrollDoesntBubble) {
}
}
-TEST_F(LayerTreeHostImplTest, PinchGesture) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PinchGesture) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3610,7 +3739,7 @@ TEST_F(LayerTreeHostImplTest, PinchGesture) {
}
}
-TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollDelta) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SyncSubpixelScrollDelta) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3660,7 +3789,8 @@ TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollDelta) {
scroll_layer->ScreenSpaceTransform().To2dTranslation());
}
-TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollFromFractionalActiveBase) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SyncSubpixelScrollFromFractionalActiveBase) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3699,7 +3829,8 @@ TEST_F(LayerTreeHostImplTest, SyncSubpixelScrollFromFractionalActiveBase) {
gfx::Vector2dF(0, -1));
}
-TEST_F(LayerTreeHostImplTest, PinchZoomTriggersPageScaleAnimation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ PinchZoomTriggersPageScaleAnimation) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3821,7 +3952,7 @@ TEST_F(LayerTreeHostImplTest, PinchZoomTriggersPageScaleAnimation) {
}
}
-TEST_F(LayerTreeHostImplTest, PageScaleAnimation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimation) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -3950,7 +4081,7 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimation) {
}
}
-TEST_F(LayerTreeHostImplTest, PageScaleAnimationNoOp) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PageScaleAnimationNoOp) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -4008,7 +4139,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationNoOp) {
}
}
-TEST_F(LayerTreeHostImplTest, PageScaleAnimationTransferedOnSyncTreeActivate) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ PageScaleAnimationTransferedOnSyncTreeActivate) {
CreatePendingTree();
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1, 1);
SetupViewportLayers(host_impl_->pending_tree(), gfx::Size(50, 50),
@@ -4132,7 +4264,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationTransferedOnSyncTreeActivate) {
gfx::ScrollOffset(-50, -50)));
}
-TEST_F(LayerTreeHostImplTest, PageScaleAnimationCompletedNotification) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ PageScaleAnimationCompletedNotification) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
DrawFrame();
@@ -4181,7 +4314,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationCompletedNotification) {
host_impl_->DidFinishImplFrame(begin_frame_args);
}
-TEST_F(LayerTreeHostImplTest, MaxScrollOffsetAffectedByViewportBoundsDelta) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ MaxScrollOffsetAffectedByViewportBoundsDelta) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
@@ -4207,7 +4341,8 @@ TEST_F(LayerTreeHostImplTest, MaxScrollOffsetAffectedByViewportBoundsDelta) {
// Ensures scroll gestures coming from scrollbars cause animations in the
// appropriate scenarios.
-TEST_F(LayerTreeHostImplTest, AnimatedGranularityCausesSmoothScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AnimatedGranularityCausesSmoothScroll) {
gfx::Size viewport_size(300, 200);
gfx::Size content_size(1000, 1000);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -4275,7 +4410,8 @@ TEST_F(LayerTreeHostImplTest, AnimatedGranularityCausesSmoothScroll) {
// Ensures scroll gestures coming from scrollbars don't cause animations if
// smooth scrolling is disabled.
-TEST_F(LayerTreeHostImplTest, NonAnimatedGranularityCausesInstantScroll) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ NonAnimatedGranularityCausesInstantScroll) {
// Disable animated scrolling
LayerTreeSettings settings = DefaultSettings();
settings.enable_smooth_scroll = false;
@@ -4574,7 +4710,8 @@ TEST_F(LayerTreeHostImplTestScrollbarAnimation, NoAnimator) {
RunTest(LayerTreeSettings::NO_ANIMATOR);
}
-class LayerTreeHostImplTestScrollbarOpacity : public LayerTreeHostImplTest {
+class LayerTreeHostImplTestScrollbarOpacity
+ : public ScrollUnifiedLayerTreeHostImplTest {
protected:
void RunTest(LayerTreeSettings::ScrollbarAnimator animator) {
LayerTreeSettings settings = DefaultSettings();
@@ -4663,19 +4800,24 @@ class LayerTreeHostImplTestScrollbarOpacity : public LayerTreeHostImplTest {
}
};
-TEST_F(LayerTreeHostImplTestScrollbarOpacity, Android) {
+INSTANTIATE_TEST_SUITE_P(All,
+ LayerTreeHostImplTestScrollbarOpacity,
+ testing::Bool());
+
+TEST_P(LayerTreeHostImplTestScrollbarOpacity, Android) {
RunTest(LayerTreeSettings::ANDROID_OVERLAY);
}
-TEST_F(LayerTreeHostImplTestScrollbarOpacity, AuraOverlay) {
+TEST_P(LayerTreeHostImplTestScrollbarOpacity, AuraOverlay) {
RunTest(LayerTreeSettings::AURA_OVERLAY);
}
-TEST_F(LayerTreeHostImplTestScrollbarOpacity, NoAnimator) {
+TEST_P(LayerTreeHostImplTestScrollbarOpacity, NoAnimator) {
RunTest(LayerTreeSettings::NO_ANIMATOR);
}
-class LayerTreeHostImplTestMultiScrollable : public LayerTreeHostImplTest {
+class LayerTreeHostImplTestMultiScrollable
+ : public ScrollUnifiedLayerTreeHostImplTest {
public:
void SetUpLayers(LayerTreeSettings settings) {
is_aura_scrollbar_ =
@@ -4732,7 +4874,11 @@ class LayerTreeHostImplTestMultiScrollable : public LayerTreeHostImplTest {
SolidColorScrollbarLayerImpl* scrollbar_2_;
};
-TEST_F(LayerTreeHostImplTestMultiScrollable,
+INSTANTIATE_TEST_SUITE_P(All,
+ LayerTreeHostImplTestMultiScrollable,
+ testing::Bool());
+
+TEST_P(LayerTreeHostImplTestMultiScrollable,
ScrollbarFlashAfterAnyScrollUpdate) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(500);
@@ -4777,7 +4923,7 @@ TEST_F(LayerTreeHostImplTestMultiScrollable,
EXPECT_FALSE(animation_task_.is_null());
}
-TEST_F(LayerTreeHostImplTestMultiScrollable, ScrollbarFlashWhenMouseEnter) {
+TEST_P(LayerTreeHostImplTestMultiScrollable, ScrollbarFlashWhenMouseEnter) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(500);
settings.scrollbar_fade_duration = base::TimeDelta::FromMilliseconds(300);
@@ -4803,7 +4949,7 @@ TEST_F(LayerTreeHostImplTestMultiScrollable, ScrollbarFlashWhenMouseEnter) {
EXPECT_FALSE(animation_task_.is_null());
}
-TEST_F(LayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(500);
settings.scrollbar_fade_duration = base::TimeDelta::FromMilliseconds(300);
@@ -4892,7 +5038,8 @@ TEST_F(LayerTreeHostImplTest, ScrollHitTestOnScrollbar) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollbarVisibilityChangeCausesRedrawAndCommit) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollbarVisibilityChangeCausesRedrawAndCommit) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(20);
@@ -4961,7 +5108,7 @@ TEST_F(LayerTreeHostImplTest, ScrollbarVisibilityChangeCausesRedrawAndCommit) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollbarInnerLargerThanOuter) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollbarInnerLargerThanOuter) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -4983,7 +5130,7 @@ TEST_F(LayerTreeHostImplTest, ScrollbarInnerLargerThanOuter) {
EXPECT_EQ(300, horiz_scrollbar->clip_layer_length());
}
-TEST_F(LayerTreeHostImplTest, ScrollbarRegistration) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollbarRegistration) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = LayerTreeSettings::ANDROID_OVERLAY;
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(20);
@@ -5077,7 +5224,7 @@ TEST_F(LayerTreeHostImplTest, ScrollbarRegistration) {
root_scroll_element_id));
}
-TEST_F(LayerTreeHostImplTest, ScrollBeforeMouseMove) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeMouseMove) {
LayerTreeSettings settings = DefaultSettings();
settings.scrollbar_animator = LayerTreeSettings::AURA_OVERLAY;
settings.scrollbar_fade_delay = base::TimeDelta::FromMilliseconds(20);
@@ -5218,18 +5365,18 @@ void LayerTreeHostImplTest::SetupMouseMoveAtWithDeviceScale(
scrollbar_animation_controller->MouseIsOverScrollbarThumb(VERTICAL));
}
-TEST_F(LayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf1) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf1) {
SetupMouseMoveAtWithDeviceScale(1);
}
-TEST_F(LayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf2) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MouseMoveAtWithDeviceScaleOf2) {
SetupMouseMoveAtWithDeviceScale(2);
}
// This test verifies that only SurfaceLayers in the viewport and have fallbacks
// that are different are included in viz::CompositorFrameMetadata's
// |activation_dependencies|.
-TEST_F(LayerTreeHostImplTest, ActivationDependenciesInMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ActivationDependenciesInMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
LayerImpl* root = root_layer();
@@ -5308,7 +5455,8 @@ TEST_F(LayerTreeHostImplTest, ActivationDependenciesInMetadata) {
// Verify that updating the set of referenced surfaces for the active tree
// causes a new CompositorFrame to be submitted, even if there is no other
// damage.
-TEST_F(LayerTreeHostImplTest, SurfaceReferencesChangeCausesDamage) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SurfaceReferencesChangeCausesDamage) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* fake_layer_tree_frame_sink =
static_cast<FakeLayerTreeFrameSink*>(host_impl_->layer_tree_frame_sink());
@@ -5340,7 +5488,7 @@ TEST_F(LayerTreeHostImplTest, SurfaceReferencesChangeCausesDamage) {
}
}
-TEST_F(LayerTreeHostImplTest, CompositorFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, CompositorFrameMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
DrawFrame();
@@ -5467,7 +5615,8 @@ class DidDrawCheckLayer : public LayerImpl {
bool did_draw_called_;
};
-TEST_F(LayerTreeHostImplTest, DamageShouldNotCareAboutContributingLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ DamageShouldNotCareAboutContributingLayers) {
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
@@ -5551,7 +5700,7 @@ TEST_F(LayerTreeHostImplTest, DamageShouldNotCareAboutContributingLayers) {
}
}
-TEST_F(LayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) {
// The root layer is always drawn, so run this test on a child layer that
// will be masked out by the root layer's bounds.
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
@@ -5574,7 +5723,7 @@ TEST_F(LayerTreeHostImplTest, WillDrawReturningFalseDoesNotCall) {
EXPECT_FALSE(layer->did_draw_called());
}
-TEST_F(LayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) {
// The root layer is always drawn, so run this test on a child layer that
// will be masked out by the root layer's bounds.
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
@@ -5613,7 +5762,7 @@ TEST_F(LayerTreeHostImplTest, DidDrawNotCalledOnHiddenLayer) {
EXPECT_FALSE(layer->visible_layer_rect().IsEmpty());
}
-TEST_F(LayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) {
gfx::Size big_size(1000, 1000);
auto* root =
SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(), big_size);
@@ -5641,7 +5790,7 @@ TEST_F(LayerTreeHostImplTest, WillDrawNotCalledOnOccludedLayer) {
EXPECT_TRUE(top_layer->did_draw_called());
}
-TEST_F(LayerTreeHostImplTest, DidDrawCalledOnAllLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DidDrawCalledOnAllLayers) {
auto* root = SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(),
gfx::Size(10, 10));
auto* layer1 = AddLayer<DidDrawCheckLayer>(host_impl_->active_tree());
@@ -5906,11 +6055,12 @@ TEST_F(LayerTreeHostImplPrepareToDrawTest,
}
}
-TEST_F(LayerTreeHostImplTest, ScrollRootIgnored) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollRootIgnored) {
SetupDefaultRootLayer(gfx::Size(10, 10));
DrawFrame();
- // Scroll event is ignored because layer is not scrollable.
+ // Scroll event is ignored because layer is not scrollable and there is no
+ // viewport.
InputHandler::ScrollStatus status =
host_impl_->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
@@ -5923,7 +6073,7 @@ TEST_F(LayerTreeHostImplTest, ScrollRootIgnored) {
EXPECT_FALSE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ClampingAfterActivation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ClampingAfterActivation) {
CreatePendingTree();
host_impl_->pending_tree()->PushPageScaleFromMainThread(1, 1, 1);
SetupViewportLayers(host_impl_->pending_tree(), gfx::Size(50, 50),
@@ -5945,7 +6095,8 @@ TEST_F(LayerTreeHostImplTest, ClampingAfterActivation) {
EXPECT_EQ(CurrentScrollOffset(active_outer_layer), gfx::ScrollOffset(0, 0));
}
-class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest {
+class LayerTreeHostImplBrowserControlsTest
+ : public ScrollUnifiedLayerTreeHostImplTest {
public:
LayerTreeHostImplBrowserControlsTest()
// Make the clip size the same as the layer (content) size so the layer is
@@ -6002,6 +6153,10 @@ class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest {
LayerTreeSettings settings_;
}; // class LayerTreeHostImplBrowserControlsTest
+INSTANTIATE_TEST_SUITE_P(All,
+ LayerTreeHostImplBrowserControlsTest,
+ testing::Bool());
+
#define EXPECT_VIEWPORT_GEOMETRIES(expected_browser_controls_shown_ratio) \
do { \
auto* tree = host_impl_->active_tree(); \
@@ -6031,7 +6186,7 @@ class LayerTreeHostImplBrowserControlsTest : public LayerTreeHostImplTest {
// size). Since the viewport got larger, the effective scrollable "content" also
// did. This ensures, for one thing, that the overscroll glow is shown in the
// right place.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
HidingBrowserControlsExpandsScrollableSize) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(50, 50), gfx::Size(50, 50), gfx::Size(50, 50));
@@ -6074,11 +6229,108 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
host_impl_->ScrollEnd();
}
+TEST_P(LayerTreeHostImplBrowserControlsTest,
+ HidingBrowserControlsExpandsClipAncestorsOfReplacedOuterScroller) {
+ SetupBrowserControlsAndScrollLayerWithVirtualViewport(
+ gfx::Size(180, 180), gfx::Size(180, 180), gfx::Size(180, 180));
+
+ LayerTreeImpl* active_tree = host_impl_->active_tree();
+ PropertyTrees* property_trees = active_tree->property_trees();
+ LayerImpl* original_outer_scroll = OuterViewportScrollLayer();
+
+ LayerImpl* parent_clip_layer = AddLayer();
+ CopyProperties(original_outer_scroll, parent_clip_layer);
+ parent_clip_layer->SetBounds(gfx::Size(160, 160));
+ CreateClipNode(parent_clip_layer);
+ LayerImpl* clip_layer = AddLayer();
+ clip_layer->SetBounds(gfx::Size(150, 150));
+ CopyProperties(parent_clip_layer, clip_layer);
+ CreateClipNode(clip_layer);
+ LayerImpl* scroll_layer =
+ AddScrollableLayer(clip_layer, gfx::Size(150, 150), gfx::Size(300, 300));
+ GetScrollNode(scroll_layer)->scrolls_outer_viewport = true;
+ ClipNode* original_outer_clip = GetClipNode(original_outer_scroll);
+ ClipNode* parent_clip = GetClipNode(parent_clip_layer);
+ ClipNode* scroll_clip = GetClipNode(clip_layer);
+
+ auto viewport_property_ids = active_tree->ViewportPropertyIdsForTesting();
+ viewport_property_ids.outer_clip = clip_layer->clip_tree_index();
+ viewport_property_ids.outer_scroll = scroll_layer->scroll_tree_index();
+ active_tree->SetViewportPropertyIds(viewport_property_ids);
+ UpdateDrawProperties(active_tree);
+
+ EXPECT_EQ(scroll_layer, OuterViewportScrollLayer());
+ EXPECT_EQ(GetScrollNode(scroll_layer),
+ active_tree->OuterViewportScrollNode());
+
+ EXPECT_EQ(1, active_tree->CurrentTopControlsShownRatio());
+ EXPECT_EQ(50, host_impl_->browser_controls_manager()->ContentTopOffset());
+ EXPECT_EQ(gfx::Vector2dF(),
+ property_trees->inner_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::Vector2dF(),
+ property_trees->outer_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
+ EXPECT_EQ(gfx::RectF(0, 0, 180, 180), original_outer_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 160, 160), parent_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 150, 150), scroll_clip->clip);
+
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
+ host_impl_
+ ->ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 25),
+ ui::ScrollInputType::kTouchscreen)
+ .get(),
+ ui::ScrollInputType::kTouchscreen)
+ .thread);
+
+ // Hide the browser controls by a bit, the scrollable size should increase but
+ // the actual content bounds shouldn't.
+ host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
+ EXPECT_EQ(0.5f, active_tree->CurrentTopControlsShownRatio());
+ EXPECT_EQ(25, host_impl_->browser_controls_manager()->ContentTopOffset());
+ EXPECT_EQ(gfx::Vector2dF(0, 25),
+ property_trees->inner_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::Vector2dF(0, 25),
+ property_trees->outer_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
+ EXPECT_EQ(gfx::RectF(0, 0, 150, 175), scroll_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 160, 175), parent_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 180, 180), original_outer_clip->clip);
+
+ // Fully hide the browser controls.
+ host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
+ EXPECT_EQ(0, active_tree->CurrentTopControlsShownRatio());
+ EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
+ EXPECT_EQ(gfx::Vector2dF(0, 50),
+ property_trees->inner_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::Vector2dF(0, 50),
+ property_trees->outer_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
+ EXPECT_EQ(gfx::RectF(0, 0, 150, 200), scroll_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 160, 200), parent_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 180, 200), original_outer_clip->clip);
+
+ // Scrolling additionally shouldn't have any effect.
+ host_impl_->browser_controls_manager()->ScrollBy(gfx::Vector2dF(0, 25));
+ EXPECT_EQ(0, active_tree->CurrentTopControlsShownRatio());
+ EXPECT_EQ(0, host_impl_->browser_controls_manager()->ContentTopOffset());
+ EXPECT_EQ(gfx::Vector2dF(0, 50),
+ property_trees->inner_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::Vector2dF(0, 50),
+ property_trees->outer_viewport_container_bounds_delta());
+ EXPECT_EQ(gfx::SizeF(300, 300), active_tree->ScrollableSize());
+ EXPECT_EQ(gfx::RectF(0, 0, 150, 200), scroll_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 160, 200), parent_clip->clip);
+ EXPECT_EQ(gfx::RectF(0, 0, 180, 200), original_outer_clip->clip);
+
+ host_impl_->browser_controls_manager()->ScrollEnd();
+ host_impl_->ScrollEnd();
+}
+
// Ensure that moving the browser controls (i.e. omnibox/url-bar on mobile) on
// pages with a non-1 minimum page scale factor (e.g. legacy desktop page)
// correctly scales the clipping adjustment performed to show the newly exposed
// region of the page.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
MovingBrowserControlsOuterClipDeltaScaled) {
gfx::Size inner_size = gfx::Size(100, 100);
gfx::Size outer_size = gfx::Size(100, 100);
@@ -6122,7 +6374,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
}
// Tests that browser controls affect the position of horizontal scrollbars.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
HidingBrowserControlsAdjustsScrollbarPosition) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(50, 50), gfx::Size(50, 50), gfx::Size(50, 50));
@@ -6198,7 +6450,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
ScrollBrowserControlsByFractionalAmount) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(10, 10), gfx::Size(10, 10), gfx::Size(10, 10));
@@ -6231,7 +6483,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// scroll initiated on the inner viewport, causing the browser controls to show
// and thus making the outer viewport scrollable, still scrolls the outer
// viewport.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsOuterViewportBecomesScrollable) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(10, 50), gfx::Size(10, 50), gfx::Size(10, 100));
@@ -6322,7 +6574,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Test that the fixed position container delta is appropriately adjusted
// by the browser controls showing/hiding and page scale doesn't affect it.
-TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
+TEST_P(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(100, 100), gfx::Size(100, 100));
DrawFrame();
@@ -6396,7 +6648,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, FixedContainerDelta) {
// Push a browser controls ratio from the main thread that we didn't send as a
// delta and make sure that the ratio is clamped to the [0, 1] range.
-TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsPushUnsentRatio) {
+TEST_P(LayerTreeHostImplBrowserControlsTest, BrowserControlsPushUnsentRatio) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(10, 50), gfx::Size(10, 50), gfx::Size(10, 100));
DrawFrame();
@@ -6420,7 +6672,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsPushUnsentRatio) {
// Test that if a scrollable sublayer doesn't consume the scroll,
// browser controls should hide when scrolling down.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsScrollableSublayer) {
gfx::Size sub_content_size(100, 400);
gfx::Size sub_content_layer_size(100, 300);
@@ -6470,7 +6722,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Ensure setting the browser controls position explicitly using the setters on
// the TreeImpl correctly affects the browser controls manager and viewport
// bounds for the active tree.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
PositionBrowserControlsToActiveTreeExplicitly) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
@@ -6501,7 +6753,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Ensure setting the browser controls position explicitly using the setters on
// the TreeImpl correctly affects the browser controls manager and viewport
// bounds for the pending tree.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
PositionBrowserControlsToPendingTreeExplicitly) {
CreatePendingTree();
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
@@ -6546,7 +6798,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Test that the top_controls delta and sent delta are appropriately
// applied on sync tree activation. The total browser controls offset shouldn't
// change after the activation.
-TEST_F(LayerTreeHostImplBrowserControlsTest, ApplyDeltaOnTreeActivation) {
+TEST_P(LayerTreeHostImplBrowserControlsTest, ApplyDeltaOnTreeActivation) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
DrawFrame();
@@ -6586,7 +6838,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, ApplyDeltaOnTreeActivation) {
// the inner viewport container bounds. That is, the browser controls layout
// height is the amount that the inner viewport container was shrunk outside
// the compositor to accommodate the browser controls.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsLayoutHeightChanged) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
@@ -6629,7 +6881,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Test that showing/hiding the browser controls when the viewport is fully
// scrolled doesn't incorrectly change the viewport offset due to clamping from
// changing viewport bounds.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsViewportOffsetClamping) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(200, 200), gfx::Size(200, 400));
@@ -6705,7 +6957,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// Test that the browser controls coming in and out maintains the same aspect
// ratio between the inner and outer viewports.
-TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsAspectRatio) {
+TEST_P(LayerTreeHostImplBrowserControlsTest, BrowserControlsAspectRatio) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(200, 200), gfx::Size(200, 400));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 2);
@@ -6745,7 +6997,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsAspectRatio) {
}
// Test that scrolling the outer viewport affects the browser controls.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
BrowserControlsScrollOuterViewport) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
gfx::Size(100, 100), gfx::Size(200, 200), gfx::Size(200, 400));
@@ -6821,7 +7073,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
ScrollNonScrollableRootWithBrowserControls) {
SetupBrowserControlsAndScrollLayerWithVirtualViewport(
layer_size_, layer_size_, layer_size_);
@@ -6892,7 +7144,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
// was occurring because the UpdateViewportContainerSizes was being called
// before the property trees were updated with the bounds_delta.
// crbug.com/597266.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
ViewportBoundsDeltaOnTreeActivation) {
const gfx::Size inner_viewport_size(1000, 1000);
const gfx::Size outer_viewport_size(1000, 1000);
@@ -7016,7 +7268,7 @@ TEST_F(LayerTreeHostImplBrowserControlsTest,
}
}
-TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonCompositedRoot) {
// Test the configuration where a non-composited root layer is embedded in a
// scrollable outer layer.
gfx::Size surface_size(10, 10);
@@ -7054,7 +7306,7 @@ TEST_F(LayerTreeHostImplTest, ScrollNonCompositedRoot) {
EXPECT_TRUE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
gfx::Size surface_size(10, 10);
gfx::Size contents_size(20, 20);
@@ -7083,33 +7335,35 @@ TEST_F(LayerTreeHostImplTest, ScrollChildCallsCommitAndRedraw) {
EXPECT_TRUE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ScrollMissesChild) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollMissesChild) {
gfx::Size viewport_size(5, 5);
gfx::Size surface_size(10, 10);
- LayerImpl* root = SetupDefaultRootLayer(surface_size);
- AddScrollableLayer(root, viewport_size, surface_size);
+ SetupViewportLayersOuterScrolls(viewport_size, surface_size);
+ AddScrollableLayer(OuterViewportScrollLayer(), viewport_size, surface_size);
DrawFrame();
- // Scroll event is ignored because the input coordinate is outside the layer
- // boundaries.
+ // A scroll that doesn't hit any layers should fall back to viewport
+ // scrolling.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(15, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
- status.main_thread_scrolling_reasons);
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
+ host_impl_->OuterViewportScrollNode());
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ScrollMissesBackfacingChild) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollMissesBackfacingChild) {
gfx::Size viewport_size(5, 5);
gfx::Size surface_size(10, 10);
- LayerImpl* root = SetupDefaultRootLayer(viewport_size);
- LayerImpl* child = AddScrollableLayer(root, viewport_size, surface_size);
+
+ SetupViewportLayersOuterScrolls(viewport_size, surface_size);
+ LayerImpl* child = AddScrollableLayer(OuterViewportScrollLayer(),
+ viewport_size, surface_size);
gfx::Transform matrix;
matrix.RotateAboutXAxis(180.0);
@@ -7118,22 +7372,22 @@ TEST_F(LayerTreeHostImplTest, ScrollMissesBackfacingChild) {
CreateEffectNode(child).double_sided = false;
DrawFrame();
- // Scroll event is ignored because the scrollable layer is not facing the
- // viewer and there is nothing scrollable behind it.
+ // The scroll shouldn't hit the child layer because the it isn't facing the
+ // viewer. The hit test should go through it and hit the outer viewport.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
- status.main_thread_scrolling_reasons);
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
+ host_impl_->OuterViewportScrollNode());
EXPECT_FALSE(did_request_redraw_);
EXPECT_FALSE(did_request_commit_);
}
-TEST_F(LayerTreeHostImplTest, ScrollBlockedByContentLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollLayerWithMainThreadReason) {
gfx::Size scroll_container_size(5, 5);
gfx::Size surface_size(10, 10);
LayerImpl* root = SetupDefaultRootLayer(surface_size);
@@ -7148,19 +7402,28 @@ TEST_F(LayerTreeHostImplTest, ScrollBlockedByContentLayer) {
MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
DrawFrame();
- // Scrolling fails because the content layer is asking to be scrolled on the
- // main thread.
InputHandler::ScrollStatus status = host_impl_->ScrollBegin(
BeginState(gfx::Point(5, 5), gfx::Vector2dF(0, 10),
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ host_impl_->CurrentlyScrollingNode()->main_thread_scrolling_reasons);
+ } else {
+ // Scrolling fails because the content layer is asking to be scrolled on the
+ // main thread.
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollRootAndChangePageScaleOnMainThread) {
gfx::Size inner_viewport_size(20, 20);
gfx::Size outer_viewport_size(40, 40);
gfx::Size content_size(80, 80);
@@ -7203,7 +7466,8 @@ TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnMainThread) {
EXPECT_EQ(1, host_impl_->active_tree()->page_scale_delta());
}
-TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnImplThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollRootAndChangePageScaleOnImplThread) {
gfx::Size inner_viewport_size(20, 20);
gfx::Size outer_viewport_size(40, 40);
gfx::Size content_size(80, 80);
@@ -7255,7 +7519,8 @@ TEST_F(LayerTreeHostImplTest, ScrollRootAndChangePageScaleOnImplThread) {
EXPECT_EQ(page_scale, host_impl_->active_tree()->current_page_scale_factor());
}
-TEST_F(LayerTreeHostImplTest, PageScaleDeltaAppliedToRootScrollLayerOnly) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ PageScaleDeltaAppliedToRootScrollLayerOnly) {
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 2);
gfx::Size viewport_size(5, 5);
gfx::Size surface_size(10, 10);
@@ -7305,7 +7570,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleDeltaAppliedToRootScrollLayerOnly) {
outer_scroll->DrawTransform().matrix().getDouble(1, 1));
}
-TEST_F(LayerTreeHostImplTest, ScrollChildAndChangePageScaleOnMainThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollChildAndChangePageScaleOnMainThread) {
SetupViewportLayers(host_impl_->active_tree(), gfx::Size(15, 15),
gfx::Size(30, 30), gfx::Size(50, 50));
LayerImpl* outer_scroll = OuterViewportScrollLayer();
@@ -7345,7 +7611,7 @@ TEST_F(LayerTreeHostImplTest, ScrollChildAndChangePageScaleOnMainThread) {
EXPECT_EQ(1, host_impl_->active_tree()->page_scale_delta());
}
-TEST_F(LayerTreeHostImplTest, ScrollChildBeyondLimit) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollChildBeyondLimit) {
// Scroll a child layer beyond its maximum scroll range and make sure the
// parent layer isn't scrolled.
gfx::Size surface_size(10, 10);
@@ -7490,7 +7756,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedLatchToChild) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollWithoutBubbling) {
// Scroll a child layer beyond its maximum scroll range and make sure the
// the scroll doesn't bubble up to the parent layer.
gfx::Size surface_size(20, 20);
@@ -7629,7 +7895,8 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutBubbling) {
// Ensure that layers who's scroll parent is the InnerViewportScrollNode are
// still able to scroll on the compositor.
-TEST_F(LayerTreeHostImplTest, ChildrenOfInnerScrollNodeCanScrollOnThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ChildrenOfInnerScrollNodeCanScrollOnThread) {
gfx::Size viewport_size(10, 10);
gfx::Size content_size(20, 20);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -7673,7 +7940,7 @@ TEST_F(LayerTreeHostImplTest, ChildrenOfInnerScrollNodeCanScrollOnThread) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollEventBubbling) {
// When we try to scroll a non-scrollable child layer, the scroll delta
// should be applied to one of its ancestors if possible.
gfx::Size viewport_size(10, 10);
@@ -7716,7 +7983,7 @@ TEST_F(LayerTreeHostImplTest, ScrollEventBubbling) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollBeforeRedraw) {
gfx::Size surface_size(10, 10);
SetupViewportLayersNoScrolls(surface_size);
UpdateDrawProperties(host_impl_->active_tree());
@@ -7740,7 +8007,7 @@ TEST_F(LayerTreeHostImplTest, ScrollBeforeRedraw) {
.thread);
}
-TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
scroll_layer->SetDrawsContent(true);
@@ -7798,7 +8065,7 @@ TEST_F(LayerTreeHostImplTest, ScrollAxisAlignedRotatedLayer) {
wheel_scroll_delta));
}
-TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
float child_layer_angle = -20;
@@ -7891,7 +8158,7 @@ TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) {
// When scrolling an element with perspective, the distance scrolled
// depends on the point at which the scroll begins.
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -7973,7 +8240,7 @@ TEST_F(LayerTreeHostImplTest, ScrollPerspectiveTransformedLayer) {
}
}
-TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollScaledLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
@@ -8032,7 +8299,7 @@ TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) {
wheel_scroll_delta));
}
-TEST_F(LayerTreeHostImplTest, ScrollViewportRounding) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollViewportRounding) {
int width = 332;
int height = 20;
int scale = 3;
@@ -8048,7 +8315,7 @@ TEST_F(LayerTreeHostImplTest, ScrollViewportRounding) {
MaxScrollOffset(inner_viewport_scroll_layer));
}
-TEST_F(LayerTreeHostImplTest, RootLayerScrollOffsetDelegation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RootLayerScrollOffsetDelegation) {
TestInputHandlerClient scroll_watcher;
SetupViewportLayersInnerScrolls(gfx::Size(10, 20), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
@@ -8166,7 +8433,7 @@ void CheckLayerScrollDelta(LayerImpl* layer, gfx::Vector2dF scroll_delta) {
EXPECT_EQ(expected_point.ToString(), translated_point.ToString());
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ExternalRootLayerScrollOffsetDelegationReflectedInNextDraw) {
SetupViewportLayersInnerScrolls(gfx::Size(10, 20), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
@@ -8201,7 +8468,7 @@ TEST_F(LayerTreeHostImplTest,
// Ensure the viewport correctly handles the user_scrollable bits. That is, if
// the outer viewport disables user scrolling, we should still be able to
// scroll the inner viewport.
-TEST_F(LayerTreeHostImplTest, ViewportUserScrollable) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ViewportUserScrollable) {
gfx::Size viewport_size(100, 100);
gfx::Size content_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -8367,7 +8634,7 @@ TEST_F(LayerTreeHostImplTest, ViewportUserScrollable) {
// Ensure that the SetSynchronousInputHandlerRootScrollOffset method used by
// the WebView API correctly respects the user_scrollable bits on both of the
// inner and outer viewport scroll nodes.
-TEST_F(LayerTreeHostImplTest, SetRootScrollOffsetUserScrollable) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SetRootScrollOffsetUserScrollable) {
gfx::Size viewport_size(100, 100);
gfx::Size content_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -8480,14 +8747,14 @@ TEST_F(LayerTreeHostImplTest, SetRootScrollOffsetUserScrollable) {
// The SetSynchronousInputHandlerRootScrollOffset API can be called while there
// is no inner viewport set. This test passes if we don't crash.
-TEST_F(LayerTreeHostImplTest, SetRootScrollOffsetNoViewportCrash) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SetRootScrollOffsetNoViewportCrash) {
auto* inner_scroll = InnerViewportScrollLayer();
ASSERT_FALSE(inner_scroll);
gfx::ScrollOffset scroll_offset(25, 30);
host_impl_->SetSynchronousInputHandlerRootScrollOffset(scroll_offset);
}
-TEST_F(LayerTreeHostImplTest, OverscrollRoot) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollRoot) {
InputHandlerScrollResult scroll_result;
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -8641,7 +8908,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollRoot) {
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
// Scroll child layers beyond their maximum scroll range and make sure root
// overscroll does not accumulate.
InputHandlerScrollResult scroll_result;
@@ -8746,7 +9013,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildWithoutBubbling) {
}
}
-TEST_F(LayerTreeHostImplTest, OverscrollChildEventBubbling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollChildEventBubbling) {
// When we try to scroll a non-scrollable child layer, the scroll delta
// should be applied to one of its ancestors if possible. Overscroll should
// be reflected only when it has bubbled up to the root scrolling layer.
@@ -8784,7 +9051,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollChildEventBubbling) {
}
}
-TEST_F(LayerTreeHostImplTest, OverscrollAlways) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollAlways) {
InputHandlerScrollResult scroll_result;
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -8813,7 +9080,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollAlways) {
EXPECT_EQ(gfx::Vector2dF(0, 10), host_impl_->accumulated_root_overscroll());
}
-TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
InputHandlerScrollResult scroll_result;
gfx::Size viewport_size(100, 100);
gfx::Size content_size(200, 200);
@@ -8898,7 +9165,7 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollWhenNotAtEdge) {
}
}
-TEST_F(LayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
@@ -8963,7 +9230,15 @@ TEST_F(LayerTreeHostImplTest, NoOverscrollOnNonViewportLayers) {
host_impl_->ScrollEnd();
}
-TEST_F(LayerTreeHostImplTest, OverscrollOnMainThread) {
+// This ensures that the --disable-threaded-scrolling flag is respected even
+// when a scroll occurs outside of a layer which normally falls back to the
+// inner viewport layer. https://crbug.com/625100.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollOnMainThread) {
+ // TODO(bokan): The meaning of this flag is lost with scroll unification. We
+ // need to decide what we should do with it. https://crbug.com/1086625.
+ if (base::FeatureList::IsEnabled(features::kScrollUnification))
+ return;
+
InputHandlerScrollResult scroll_result;
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -9005,7 +9280,7 @@ TEST_F(LayerTreeHostImplTest, OverscrollOnMainThread) {
// Test that scrolling the inner viewport directly works, as can happen when the
// scroll chains up to it from an sibling of the outer viewport.
-TEST_F(LayerTreeHostImplTest, ScrollFromOuterViewportSibling) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollFromOuterViewportSibling) {
const gfx::Size viewport_size(100, 100);
SetupViewportLayersNoScrolls(viewport_size);
@@ -9081,7 +9356,8 @@ TEST_F(LayerTreeHostImplTest, ScrollFromOuterViewportSibling) {
// Test that scrolls chain correctly when a child scroller on the page (e.g. a
// scrolling div) is set as the outer viewport. This happens in the
// rootScroller proposal.
-TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollChainingWithReplacedOuterViewport) {
const gfx::Size content_size(200, 200);
const gfx::Size viewport_size(100, 100);
@@ -9211,7 +9487,7 @@ TEST_F(LayerTreeHostImplTest, ScrollChainingWithReplacedOuterViewport) {
// scrolling div) is set as the outer viewport but scrolls start from a layer
// that's not a descendant of the outer viewport. This happens in the
// rootScroller proposal.
-TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RootScrollerScrollNonDescendant) {
const gfx::Size content_size(300, 300);
const gfx::Size viewport_size(300, 300);
@@ -9381,7 +9657,7 @@ TEST_F(LayerTreeHostImplTest, RootScrollerScrollNonDescendant) {
}
}
-TEST_F(LayerTreeHostImplTest, OverscrollOnImplThread) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OverscrollOnImplThread) {
InputHandlerScrollResult scroll_result;
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -9514,7 +9790,7 @@ class BlendStateCheckLayer : public LayerImpl {
viz::ResourceId resource_id_;
};
-TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
root->SetDrawsContent(false);
@@ -9710,7 +9986,7 @@ static bool MayContainVideoBitSetOnFrameData(LayerTreeHostImpl* host_impl) {
return frame.may_contain_video;
}
-TEST_F(LayerTreeHostImplTest, MayContainVideo) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MayContainVideo) {
gfx::Size big_size(1000, 1000);
auto* root =
SetupRootLayer<DidDrawCheckLayer>(host_impl_->active_tree(), big_size);
@@ -10020,7 +10296,7 @@ class FakeDrawableLayerImpl : public LayerImpl {
// Make sure damage tracking propagates all the way to the viz::CompositorFrame
// submitted to the LayerTreeFrameSink, where it should request to swap only
// the sub-buffer that is damaged.
-TEST_F(LayerTreeHostImplTest, PartialSwapReceivesDamageRect) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PartialSwapReceivesDamageRect) {
auto gl_owned = std::make_unique<viz::TestGLES2Interface>();
gl_owned->set_have_post_sub_buffer(true);
scoped_refptr<viz::TestContextProvider> context_provider(
@@ -10108,7 +10384,7 @@ TEST_F(LayerTreeHostImplTest, PartialSwapReceivesDamageRect) {
layer_tree_host_impl->ReleaseLayerTreeFrameSink();
}
-TEST_F(LayerTreeHostImplTest, RootLayerDoesntCreateExtraSurface) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RootLayerDoesntCreateExtraSurface) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
LayerImpl* child = AddLayer();
child->SetBounds(gfx::Size(10, 10));
@@ -10153,7 +10429,7 @@ class FakeLayerWithQuads : public LayerImpl {
: LayerImpl(tree_impl, id) {}
};
-TEST_F(LayerTreeHostImplTest, LayersFreeTextures) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, LayersFreeTextures) {
scoped_refptr<viz::TestContextProvider> context_provider =
viz::TestContextProvider::Create();
viz::TestSharedImageInterface* sii = context_provider->SharedImageInterface();
@@ -10187,7 +10463,7 @@ TEST_F(LayerTreeHostImplTest, LayersFreeTextures) {
EXPECT_EQ(0u, sii->shared_image_count());
}
-TEST_F(LayerTreeHostImplTest, HasTransparentBackground) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, HasTransparentBackground) {
SetupDefaultRootLayer(gfx::Size(10, 10));
host_impl_->active_tree()->set_background_color(SK_ColorWHITE);
UpdateDrawProperties(host_impl_->active_tree());
@@ -10332,7 +10608,7 @@ class GLRendererWithSetupQuadForAntialiasing : public viz::GLRenderer {
using viz::GLRenderer::ShouldAntialiasQuad;
};
-TEST_F(LayerTreeHostImplTest, FarAwayQuadsDontNeedAA) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, FarAwayQuadsDontNeedAA) {
// Due to precision issues (especially on Android), sometimes far
// away quads can end up thinking they need AA.
float device_scale_factor = 4 / 3;
@@ -10433,7 +10709,7 @@ class CountingSoftwareDevice : public viz::SoftwareOutputDevice {
int frames_began_, frames_ended_;
};
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
ForcedDrawToSoftwareDeviceSkipsUnsupportedLayers) {
set_reduce_memory_result(false);
EXPECT_TRUE(CreateHostImpl(DefaultSettings(),
@@ -10468,7 +10744,7 @@ TEST_F(LayerTreeHostImplTest,
}
// Checks that we use the memory limits provided.
-TEST_F(LayerTreeHostImplTest, MemoryLimits) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MemoryLimits) {
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
@@ -10628,7 +10904,7 @@ TEST_F(LayerTreeHostImplTestPrepareTiles, PrepareTilesWhenInvisible) {
EXPECT_TRUE(fake_host_impl_->prepare_tiles_needed());
}
-TEST_F(LayerTreeHostImplTest, UIResourceManagement) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, UIResourceManagement) {
auto test_context_provider = viz::TestContextProvider::Create();
viz::TestSharedImageInterface* sii =
test_context_provider->SharedImageInterface();
@@ -10671,7 +10947,7 @@ TEST_F(LayerTreeHostImplTest, UIResourceManagement) {
EXPECT_EQ(0u, sii->shared_image_count());
}
-TEST_F(LayerTreeHostImplTest, CreateETC1UIResource) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, CreateETC1UIResource) {
auto test_context_provider = viz::TestContextProvider::Create();
viz::TestSharedImageInterface* sii =
test_context_provider->SharedImageInterface();
@@ -10696,7 +10972,7 @@ TEST_F(LayerTreeHostImplTest, CreateETC1UIResource) {
EXPECT_NE(0u, id1);
}
-TEST_F(LayerTreeHostImplTest, ObeyMSAACaps) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ObeyMSAACaps) {
LayerTreeSettings msaaSettings = DefaultSettings();
msaaSettings.gpu_rasterization_msaa_sample_count = 4;
@@ -10806,22 +11082,27 @@ TEST_P(LayerTreeHostImplTestWithRenderer, ShutdownReleasesContext) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
struct Helper {
std::unique_ptr<viz::CopyOutputResult> unprocessed_result;
- void OnResult(std::unique_ptr<viz::CopyOutputResult> result) {
+ void OnResult(base::OnceClosure finished,
+ std::unique_ptr<viz::CopyOutputResult> result) {
unprocessed_result = std::move(result);
+ std::move(finished).Run();
}
} helper;
GetEffectNode(root)->has_copy_request = true;
+ base::RunLoop copy_request_run_loop;
GetPropertyTrees(root)->effect_tree.AddCopyRequest(
root->effect_tree_index(),
std::make_unique<viz::CopyOutputRequest>(
viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
- base::BindOnce(&Helper::OnResult, base::Unretained(&helper))));
+ base::BindOnce(&Helper::OnResult, base::Unretained(&helper),
+ copy_request_run_loop.QuitClosure())));
DrawFrame();
auto* sii = context_provider->SharedImageInterface();
// The CopyOutputResult has a ref on the viz::ContextProvider and a shared
// image allocated.
+ copy_request_run_loop.Run();
EXPECT_TRUE(helper.unprocessed_result);
EXPECT_FALSE(context_provider->HasOneRef());
EXPECT_EQ(1u, sii->shared_image_count());
@@ -10839,12 +11120,15 @@ TEST_P(LayerTreeHostImplTestWithRenderer, ShutdownReleasesContext) {
helper.unprocessed_result.reset();
}
-TEST_F(LayerTreeHostImplTest, ScrollUnknownNotOnAncestorChain) {
+// This tests the case where hit testing only on scrollable layers returns a
+// layer that's outside the scroll chain of the first hit test *any* layer. See
+// LayerTreeHostImpl::IsInitialScrollHitTestReliable for details.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestIsNotReliable) {
// If we ray cast a scroller that is not on the first layer's ancestor chain,
// we should return SCROLL_UNKNOWN.
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
- SetupViewportLayersInnerScrolls(viewport_size, content_size);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
LayerImpl* occluder_layer = AddLayer();
occluder_layer->SetDrawsContent(true);
@@ -10863,20 +11147,27 @@ TEST_F(LayerTreeHostImplTest, ScrollUnknownNotOnAncestorChain) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollUnknownScrollAncestorMismatch) {
+// Similar but different case to above. See
+// LayerTreeHostImpl::IsInitialScrollHitTestReliable for details.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollHitTestAncestorMismatch) {
// If we ray cast a scroller this is on the first layer's ancestor chain, but
// is not the first scroller we encounter when walking up from the layer, we
// should also return SCROLL_UNKNOWN.
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
- SetupViewportLayersInnerScrolls(viewport_size, content_size);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
- LayerImpl* scroll_layer = InnerViewportScrollLayer();
+ LayerImpl* scroll_layer = OuterViewportScrollLayer();
LayerImpl* child_scroll_clip = AddLayer();
CopyProperties(scroll_layer, child_scroll_clip);
@@ -10899,12 +11190,17 @@ TEST_F(LayerTreeHostImplTest, ScrollUnknownScrollAncestorMismatch) {
ui::ScrollInputType::kWheel)
.get(),
ui::ScrollInputType::kWheel);
- EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
- status.main_thread_scrolling_reasons);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_UNKNOWN, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kFailedHitTest,
+ status.main_thread_scrolling_reasons);
+ }
}
-TEST_F(LayerTreeHostImplTest, ScrollInvisibleScroller) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollInvisibleScroller) {
gfx::Size viewport_size(50, 50);
gfx::Size content_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, content_size);
@@ -10932,7 +11228,8 @@ TEST_F(LayerTreeHostImplTest, ScrollInvisibleScroller) {
// Make sure LatencyInfo carried by LatencyInfoSwapPromise are passed
// in viz::CompositorFrameMetadata.
-TEST_F(LayerTreeHostImplTest, LatencyInfoPassedToCompositorFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ LatencyInfoPassedToCompositorFrameMetadata) {
LayerTreeSettings settings = DefaultSettings();
settings.commit_to_active_tree = false;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -10962,7 +11259,8 @@ TEST_F(LayerTreeHostImplTest, LatencyInfoPassedToCompositorFrameMetadata) {
}
#if defined(OS_ANDROID)
-TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToCompositorFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SelectionBoundsPassedToCompositorFrameMetadata) {
LayerImpl* root = SetupRootLayer<SolidColorLayerImpl>(
host_impl_->active_tree(), gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
@@ -10992,7 +11290,7 @@ TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToCompositorFrameMetadata) {
EXPECT_TRUE(selection_after.end.visible());
}
-TEST_F(LayerTreeHostImplTest, HiddenSelectionBoundsStayHidden) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, HiddenSelectionBoundsStayHidden) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
@@ -11051,7 +11349,7 @@ class SimpleSwapPromiseMonitor : public SwapPromiseMonitor {
int* set_needs_redraw_count_;
};
-TEST_F(LayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SimpleSwapPromiseMonitor) {
int set_needs_commit_count = 0;
int set_needs_redraw_count = 0;
@@ -11622,7 +11920,7 @@ TEST_F(LayerTreeHostImplWithBrowserControlsTest,
// Tests that when we set a child scroller (e.g. a scrolling div) as the outer
// viewport, scrolling it controls the browser controls.
-TEST_F(LayerTreeHostImplBrowserControlsTest,
+TEST_P(LayerTreeHostImplBrowserControlsTest,
ReplacedOuterViewportScrollsBrowserControls) {
const gfx::Size scroll_content_size(400, 400);
const gfx::Size root_layer_size(200, 200);
@@ -11859,7 +12157,8 @@ TEST_F(LayerTreeHostImplWithImplicitLimitsTest, ImplicitMemoryLimits) {
150u * 1024u * 1024u);
}
-TEST_F(LayerTreeHostImplTest, ExternalTransformReflectedInNextDraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ExternalTransformReflectedInNextDraw) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
gfx::Transform external_transform;
@@ -11885,7 +12184,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformReflectedInNextDraw) {
external_transform, layer->draw_properties().target_space_transform);
}
-TEST_F(LayerTreeHostImplTest, ExternalTransformSetNeedsRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ExternalTransformSetNeedsRedraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
@@ -11914,7 +12213,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformSetNeedsRedraw) {
EXPECT_FALSE(last_on_draw_frame_->has_no_damage);
}
-TEST_F(LayerTreeHostImplTest, OnMemoryPressure) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OnMemoryPressure) {
gfx::Size size(200, 200);
viz::ResourceFormat format = viz::RGBA_8888;
gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
@@ -11936,7 +12235,7 @@ TEST_F(LayerTreeHostImplTest, OnMemoryPressure) {
EXPECT_LT(memory_usage_after_memory_pressure, current_memory_usage);
}
-TEST_F(LayerTreeHostImplTest, OnDrawConstraintSetNeedsRedraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OnDrawConstraintSetNeedsRedraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
@@ -11969,7 +12268,7 @@ TEST_F(LayerTreeHostImplTest, OnDrawConstraintSetNeedsRedraw) {
// This test verifies that the viewport damage rect is the full viewport and not
// just part of the viewport in the presence of an external viewport.
-TEST_F(LayerTreeHostImplTest, FullViewportDamageAfterOnDraw) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, FullViewportDamageAfterOnDraw) {
const gfx::Size viewport_size(100, 100);
SetupDefaultRootLayer(viewport_size);
UpdateDrawProperties(host_impl_->active_tree());
@@ -12089,7 +12388,8 @@ TEST_F(CommitToPendingTreeLayerTreeHostImplTest,
EXPECT_TRUE(host_impl_->active_tree()->needs_update_draw_properties());
}
-TEST_F(LayerTreeHostImplTest, ExternalViewportAffectsVisibleRects) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ExternalViewportAffectsVisibleRects) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
@@ -12118,7 +12418,8 @@ TEST_F(LayerTreeHostImplTest, ExternalViewportAffectsVisibleRects) {
EXPECT_EQ(gfx::Rect(90, 90), content_layer->visible_layer_rect());
}
-TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsVisibleRects) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ExternalTransformAffectsVisibleRects) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
@@ -12152,7 +12453,8 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsVisibleRects) {
EXPECT_EQ(gfx::Rect(50, 50), content_layer->visible_layer_rect());
}
-TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsSublayerScaleFactor) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ExternalTransformAffectsSublayerScaleFactor) {
const gfx::Size viewport_size(50, 50);
const gfx::Size layer_size(100, 100);
SetupViewportLayersInnerScrolls(viewport_size, layer_size);
@@ -12196,7 +12498,7 @@ TEST_F(LayerTreeHostImplTest, ExternalTransformAffectsSublayerScaleFactor) {
#if defined(OS_MACOSX)
// Ensure Mac wheel scrolling causes instant scrolling. This test can be removed
// once https://crbug.com/574283 is fixed.
-TEST_F(LayerTreeHostImplTest, MacWheelIsNonAnimated) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MacWheelIsNonAnimated) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12219,7 +12521,116 @@ TEST_F(LayerTreeHostImplTest, MacWheelIsNonAnimated) {
}
#endif
-TEST_F(LayerTreeHostImplTest, ScrollAnimated) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OneScrollForFirstScrollDelay) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.commit_to_active_tree = false;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+ SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
+ gfx::Size(10, 10));
+ UpdateDrawProperties(host_impl_->active_tree());
+ EXPECT_EQ(first_scroll_observed, 0);
+
+ // LatencyInfo for the first scroll.
+ ui::LatencyInfo latency_info;
+ latency_info.set_trace_id(5);
+ latency_info.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
+ std::unique_ptr<SwapPromise> swap_promise(
+ new LatencyInfoSwapPromise(latency_info));
+ host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
+
+ host_impl_->SetFullViewportDamage();
+ host_impl_->SetNeedsRedraw();
+ DrawFrame();
+
+ constexpr uint32_t frame_token_1 = 1;
+ viz::FrameTimingDetails mock_details;
+ mock_details.presentation_feedback = ExampleFeedback();
+ // When the LayerTreeHostImpl receives presentation feedback, the callback
+ // will be fired.
+ host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
+
+ EXPECT_EQ(first_scroll_observed, 1);
+}
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, OtherInputsForFirstScrollDelay) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.commit_to_active_tree = false;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+ SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
+ gfx::Size(10, 10));
+ UpdateDrawProperties(host_impl_->active_tree());
+ EXPECT_EQ(first_scroll_observed, 0);
+
+ // LatencyInfo for the first input, which is not scroll.
+ ui::LatencyInfo latency_info;
+ latency_info.set_trace_id(5);
+ latency_info.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT);
+ std::unique_ptr<SwapPromise> swap_promise(
+ new LatencyInfoSwapPromise(latency_info));
+ host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
+
+ host_impl_->SetFullViewportDamage();
+ host_impl_->SetNeedsRedraw();
+ DrawFrame();
+
+ constexpr uint32_t frame_token_1 = 1;
+ viz::FrameTimingDetails mock_details;
+ mock_details.presentation_feedback = ExampleFeedback();
+ // When the LayerTreeHostImpl receives presentation feedback, the callback
+ // will be fired.
+ host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
+
+ EXPECT_EQ(first_scroll_observed, 0);
+}
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MultipleScrollsForFirstScrollDelay) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.commit_to_active_tree = false;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+ SetupRootLayer<SolidColorLayerImpl>(host_impl_->active_tree(),
+ gfx::Size(10, 10));
+ UpdateDrawProperties(host_impl_->active_tree());
+ EXPECT_EQ(first_scroll_observed, 0);
+
+ // LatencyInfo for the first scroll.
+ ui::LatencyInfo latency_info;
+ latency_info.set_trace_id(5);
+ latency_info.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
+ std::unique_ptr<SwapPromise> swap_promise(
+ new LatencyInfoSwapPromise(latency_info));
+ host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise));
+ DrawFrame();
+ constexpr uint32_t frame_token_1 = 1;
+ viz::FrameTimingDetails mock_details;
+ mock_details.presentation_feedback = ExampleFeedback();
+ // When the LayerTreeHostImpl receives presentation feedback, the callback
+ // will be fired.
+ host_impl_->DidPresentCompositorFrame(frame_token_1, mock_details);
+ EXPECT_EQ(first_scroll_observed, 1);
+
+ // LatencyInfo for the second scroll.
+ ui::LatencyInfo latency_info2;
+ latency_info2.set_trace_id(6);
+ latency_info2.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT);
+ std::unique_ptr<SwapPromise> swap_promise2(
+ new LatencyInfoSwapPromise(latency_info2));
+ host_impl_->active_tree()->QueuePinnedSwapPromise(std::move(swap_promise2));
+ host_impl_->SetFullViewportDamage();
+ host_impl_->SetNeedsRedraw();
+ DrawFrame();
+ constexpr uint32_t frame_token_2 = 2;
+ viz::FrameTimingDetails mock_details2;
+ mock_details2.presentation_feedback = ExampleFeedback();
+ // When the LayerTreeHostImpl receives presentation feedback, the callback
+ // will be fired.
+ host_impl_->DidPresentCompositorFrame(frame_token_2, mock_details2);
+ EXPECT_EQ(first_scroll_observed, 1);
+}
+
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimated) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12327,7 +12738,7 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimated) {
// Tests latching behavior, in particular when a ScrollEnd is received but a
// new ScrollBegin is received before the animation from the previous gesture
// stream is finished.
-TEST_F(LayerTreeHostImplTest, ScrollAnimatedLatching) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedLatching) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12426,7 +12837,7 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedLatching) {
// factor. That is, if you zoom into the page, a wheel scroll should scroll the
// content *less* than before so that it appears to move the same distance when
// zoomed in.
-TEST_F(LayerTreeHostImplTest, ScrollAnimatedWhileZoomed) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedWhileZoomed) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12503,7 +12914,7 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedWhileZoomed) {
// inner viewport is animating. Specifically this test makes sure the animation
// update doesn't get confused between the currently scrolling node and the
// currently animating node which are different. See https://crbug.com/1070561.
-TEST_F(LayerTreeHostImplTest, ScrollAnimatedUpdateInnerViewport) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedUpdateInnerViewport) {
const gfx::Size content_size(210, 1000);
const gfx::Size viewport_size(200, 200);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -12602,7 +13013,8 @@ TEST_F(LayerTreeHostImplTest, ScrollAnimatedUpdateInnerViewport) {
}
// This tests that faded-out Aura scrollbars can't be interacted with.
-TEST_F(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ FadedOutPaintedOverlayScrollbarHitTest) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12645,7 +13057,7 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) {
auto& scrollbar_effect_node = CreateEffectNode(scrollbar);
scrollbar_effect_node.opacity = 0.8;
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
InputHandlerPointerResult result =
host_impl_->MouseMoveAt(gfx::Point(350, 28));
EXPECT_GT(result.scroll_offset.y(), 0u);
@@ -12654,7 +13066,7 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) {
// Scrolling shouldn't occur at opacity = 0.
scrollbar_effect_node.opacity = 0;
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
result = host_impl_->MouseMoveAt(gfx::Point(350, 28));
EXPECT_EQ(result.scroll_offset.y(), 0u);
host_impl_->MouseUp(gfx::PointF(350, 28));
@@ -12664,11 +13076,73 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedOverlayScrollbarHitTest) {
host_impl_ = nullptr;
}
+// Tests that deleting a horizontal scrollbar doesn't affect the autoscroll task
+// for the vertical scrollbar.
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AutoscrollOnDeletedScrollbar) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.compositor_threaded_scrollbar_scrolling = true;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+
+ // Setup the viewport.
+ const gfx::Size viewport_size = gfx::Size(360, 600);
+ const gfx::Size content_size = gfx::Size(345, 3800);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
+ LayerImpl* scroll_layer = OuterViewportScrollLayer();
+
+ // Set up the scrollbar and its dimensions.
+ LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
+ auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
+ layer_tree_impl, ScrollbarOrientation::VERTICAL, false, true);
+ SetupScrollbarLayerCommon(scroll_layer, scrollbar);
+ scrollbar->SetHitTestable(true);
+
+ const gfx::Size scrollbar_size = gfx::Size(15, 600);
+ scrollbar->SetBounds(scrollbar_size);
+
+ // Set up the thumb dimensions.
+ scrollbar->SetThumbThickness(15);
+ scrollbar->SetThumbLength(50);
+ scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
+ scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
+
+ TestInputHandlerClient input_handler_client;
+ host_impl_->BindToClient(&input_handler_client);
+
+ // PointerDown on the scrollbar schedules an autoscroll task.
+ host_impl_->MouseDown(gfx::PointF(350, 580), /*jump_key_modifier*/ false);
+ EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
+
+ // Now, unregister the horizontal scrollbar and test that the autoscroll task
+ // that was scheduled for the vertical scrollbar still exists. (Note that
+ // adding a horizontal scrollbar layer is not needed. This test merely checks
+ // the logic inside ScrollbarController::DidUnregisterScrollbar)
+ host_impl_->DidUnregisterScrollbarLayer(scroll_layer->element_id(),
+ ScrollbarOrientation::HORIZONTAL);
+ EXPECT_TRUE(host_impl_->scrollbar_controller_for_testing()
+ ->AutoscrollTaskIsScheduled());
+
+ // If a call comes in to delete the scrollbar layer for which the autoscroll
+ // was scheduled, the autoscroll task should be cancelled.
+ host_impl_->DidUnregisterScrollbarLayer(scroll_layer->element_id(),
+ ScrollbarOrientation::VERTICAL);
+ EXPECT_FALSE(host_impl_->scrollbar_controller_for_testing()
+ ->AutoscrollTaskIsScheduled());
+
+ // End the scroll.
+ host_impl_->MouseUp(gfx::PointF(350, 580));
+ host_impl_->ScrollEnd();
+ EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
+
+ // Tear down the LayerTreeHostImpl before the InputHandlerClient.
+ host_impl_->ReleaseLayerTreeFrameSink();
+ host_impl_ = nullptr;
+}
+
// Tests that a pointerdown followed by pointermove(s) produces
// InputHandlerPointerResult with scroll_offset > 0 even though the GSB might
// have been dispatched *after* the first pointermove was handled by the
// ScrollbarController.
-TEST_F(LayerTreeHostImplTest, PointerMoveOutOfSequence) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PointerMoveOutOfSequence) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12701,7 +13175,7 @@ TEST_F(LayerTreeHostImplTest, PointerMoveOutOfSequence) {
// PointerDown sets up the state needed to initiate a drag. Although, the
// resulting GSB won't be dispatched until the next VSync. Hence, the
// CurrentlyScrollingNode is expected to be null.
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
EXPECT_TRUE(!host_impl_->CurrentlyScrollingNode());
// PointerMove arrives before the next VSync. This still needs to be handled
@@ -12740,7 +13214,7 @@ TEST_F(LayerTreeHostImplTest, PointerMoveOutOfSequence) {
}
// This tests that faded-out Mac scrollbars can't be interacted with.
-TEST_F(LayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12774,14 +13248,14 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) {
// scroll.
scrollbar->set_scrollbar_painted_opacity(0);
InputHandlerPointerResult result =
- host_impl_->MouseDown(gfx::PointF(350, 100), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 100), /*jump_key_modifier*/ false);
EXPECT_EQ(result.scroll_offset.y(), 0u);
// MouseDown on the track of a scrollbar with opacity > 0 should produce a
// scroll.
scrollbar->set_scrollbar_painted_opacity(1);
result =
- host_impl_->MouseDown(gfx::PointF(350, 100), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 100), /*jump_key_modifier*/ false);
EXPECT_GT(result.scroll_offset.y(), 0u);
// Tear down the LayerTreeHostImpl before the InputHandlerClient.
@@ -12789,7 +13263,8 @@ TEST_F(LayerTreeHostImplTest, FadedOutPaintedScrollbarHitTest) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, SingleGSUForScrollbarThumbDragPerFrame) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SingleGSUForScrollbarThumbDragPerFrame) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12839,7 +13314,7 @@ TEST_F(LayerTreeHostImplTest, SingleGSUForScrollbarThumbDragPerFrame) {
// MouseDown on the thumb should not produce a scroll.
InputHandlerPointerResult result =
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
EXPECT_EQ(result.scroll_offset.y(), 0u);
// The first request for a GSU should be processed as expected.
@@ -12875,9 +13350,176 @@ TEST_F(LayerTreeHostImplTest, SingleGSUForScrollbarThumbDragPerFrame) {
host_impl_ = nullptr;
}
+// Tests that the scheduled autoscroll task aborts if a 2nd mousedown occurs in
+// the same frame.
+TEST_F(LayerTreeHostImplTest, AutoscrollTaskAbort) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.compositor_threaded_scrollbar_scrolling = true;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+
+ // Setup the viewport.
+ const gfx::Size viewport_size = gfx::Size(360, 600);
+ const gfx::Size content_size = gfx::Size(345, 3800);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
+ LayerImpl* scroll_layer = OuterViewportScrollLayer();
+
+ // Set up the scrollbar and its dimensions.
+ LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
+ auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
+ layer_tree_impl, VERTICAL, /*is_left_side_vertical_scrollbar*/ false,
+ /*is_overlay*/ false);
+
+ SetupScrollbarLayer(scroll_layer, scrollbar);
+ const gfx::Size scrollbar_size = gfx::Size(15, 600);
+ scrollbar->SetBounds(scrollbar_size);
+ host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
+
+ // Set up the thumb dimensions.
+ scrollbar->SetThumbThickness(15);
+ scrollbar->SetThumbLength(50);
+ scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
+
+ // Set up scrollbar arrows.
+ scrollbar->SetBackButtonRect(
+ gfx::Rect(gfx::Point(345, 0), gfx::Size(15, 15)));
+ scrollbar->SetForwardButtonRect(
+ gfx::Rect(gfx::Point(345, 570), gfx::Size(15, 15)));
+ scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
+
+ TestInputHandlerClient input_handler_client;
+ host_impl_->BindToClient(&input_handler_client);
+
+ {
+ // An autoscroll task gets scheduled on mousedown.
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 575), /*jump_key_modifier*/ false);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ auto begin_state = BeginState(gfx::Point(350, 575), gfx::Vector2d(0, 40),
+ ui::ScrollInputType::kScrollbar);
+ EXPECT_EQ(
+ InputHandler::SCROLL_ON_IMPL_THREAD,
+ host_impl_
+ ->ScrollBegin(begin_state.get(), ui::ScrollInputType::kScrollbar)
+ .thread);
+ EXPECT_TRUE(host_impl_->scrollbar_controller_for_testing()
+ ->AutoscrollTaskIsScheduled());
+ }
+
+ {
+ // Another mousedown occurs in the same frame. InputHandlerProxy calls
+ // LayerTreeHostImpl::ScrollEnd and the autoscroll task should be cancelled.
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 575), /*jump_key_modifier*/ false);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ host_impl_->ScrollEnd();
+ EXPECT_FALSE(host_impl_->scrollbar_controller_for_testing()
+ ->AutoscrollTaskIsScheduled());
+ }
+
+ // Tear down the LayerTreeHostImpl before the InputHandlerClient.
+ host_impl_->ReleaseLayerTreeFrameSink();
+ host_impl_ = nullptr;
+}
+
+// Tests that the ScrollbarController handles jump clicks.
+TEST_F(LayerTreeHostImplTest, JumpOnScrollbarClick) {
+ LayerTreeSettings settings = DefaultSettings();
+ settings.compositor_threaded_scrollbar_scrolling = true;
+ CreateHostImpl(settings, CreateLayerTreeFrameSink());
+
+ // Setup the viewport.
+ const gfx::Size viewport_size = gfx::Size(360, 600);
+ const gfx::Size content_size = gfx::Size(345, 3800);
+ SetupViewportLayersOuterScrolls(viewport_size, content_size);
+ LayerImpl* scroll_layer = OuterViewportScrollLayer();
+
+ // Set up the scrollbar and its dimensions.
+ LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
+ auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
+ layer_tree_impl, VERTICAL, /*is_left_side_vertical_scrollbar*/ false,
+ /*is_overlay*/ false);
+
+ SetupScrollbarLayer(scroll_layer, scrollbar);
+ const gfx::Size scrollbar_size = gfx::Size(15, 600);
+ scrollbar->SetBounds(scrollbar_size);
+ host_impl_->set_force_smooth_wheel_scrolling_for_testing(true);
+
+ // Set up the thumb dimensions.
+ scrollbar->SetThumbThickness(15);
+ scrollbar->SetThumbLength(50);
+ scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 575));
+
+ // Set up scrollbar arrows.
+ scrollbar->SetBackButtonRect(
+ gfx::Rect(gfx::Point(345, 0), gfx::Size(15, 15)));
+ scrollbar->SetForwardButtonRect(
+ gfx::Rect(gfx::Point(345, 570), gfx::Size(15, 15)));
+ scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(345, 0));
+
+ TestInputHandlerClient input_handler_client;
+ host_impl_->BindToClient(&input_handler_client);
+
+ // Verify all 4 combinations of JumpOnTrackClick and jump_key_modifier.
+ {
+ // Click on track when JumpOnTrackClick is false and jump_key_modifier is
+ // false. Expected to perform a regular track scroll.
+ scrollbar->SetJumpOnTrackClick(false);
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 400), /*jump_key_modifier*/ false);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 525);
+ result = host_impl_->MouseUp(gfx::PointF(350, 400));
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 0);
+ }
+
+ {
+ // Click on track when JumpOnTrackClick is false and jump_key_modifier is
+ // true. Expected to perform scroller jump to the clicked location.
+ scrollbar->SetJumpOnTrackClick(false);
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 400), /*jump_key_modifier*/ true);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 2194);
+ result = host_impl_->MouseUp(gfx::PointF(350, 400));
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 0);
+ }
+
+ {
+ // Click on track when JumpOnTrackClick is true and jump_key_modifier is
+ // false. Expected to perform scroller jump to the clicked location.
+ scrollbar->SetJumpOnTrackClick(true);
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 400), /*jump_key_modifier*/ false);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 2194);
+ result = host_impl_->MouseUp(gfx::PointF(350, 400));
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 0);
+ }
+
+ {
+ // Click on track when JumpOnTrackClick is true and jump_key_modifier is
+ // true. Expected to perform a regular track scroll.
+ scrollbar->SetJumpOnTrackClick(true);
+ InputHandlerPointerResult result = host_impl_->MouseDown(
+ gfx::PointF(350, 400), /*jump_key_modifier*/ true);
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 525);
+ result = host_impl_->MouseUp(gfx::PointF(350, 400));
+ EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
+ EXPECT_EQ(result.scroll_offset.y(), 0);
+ }
+
+ // Tear down the LayerTreeHostImpl before the InputHandlerClient.
+ host_impl_->ReleaseLayerTreeFrameSink();
+ host_impl_ = nullptr;
+}
+
// Tests that an animated scrollbar scroll aborts when a different device (like
// a mousewheel) wants to animate the scroll offset.
-TEST_F(LayerTreeHostImplTest, AnimatedScrollYielding) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AnimatedScrollYielding) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -12919,7 +13561,7 @@ TEST_F(LayerTreeHostImplTest, AnimatedScrollYielding) {
{
// Set up an animated scrollbar autoscroll.
host_impl_->scrollbar_controller_for_testing()->HandlePointerDown(
- gfx::PointF(350, 560), /*shift_modifier*/ false);
+ gfx::PointF(350, 560), /*jump_key_modifier*/ false);
auto begin_state = BeginState(gfx::Point(350, 560), gfx::Vector2d(0, 40),
ui::ScrollInputType::kScrollbar);
EXPECT_EQ(
@@ -12981,7 +13623,7 @@ TEST_F(LayerTreeHostImplTest, AnimatedScrollYielding) {
// Tests that changing the scroller length in the middle of a thumb drag doesn't
// cause the scroller to jump.
-TEST_F(LayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -13028,7 +13670,7 @@ TEST_F(LayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) {
begin_frame_args.frame_id.sequence_number++;
host_impl_->WillBeginImplFrame(begin_frame_args);
InputHandlerPointerResult result =
- host_impl_->MouseDown(gfx::PointF(350, 18), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 18), /*jump_key_modifier*/ false);
EXPECT_EQ(result.scroll_offset.y(), 0);
EXPECT_EQ(result.type, PointerResultType::kScrollbarScroll);
host_impl_->DidFinishImplFrame(begin_frame_args);
@@ -13082,7 +13724,7 @@ TEST_F(LayerTreeHostImplTest, ThumbDragScrollerLengthIncrease) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, MainThreadFallback) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, MainThreadFallback) {
LayerTreeSettings settings = DefaultSettings();
settings.compositor_threaded_scrollbar_scrolling = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -13118,7 +13760,7 @@ TEST_F(LayerTreeHostImplTest, MainThreadFallback) {
// Clicking on the track should produce a scroll on the compositor thread.
InputHandlerPointerResult compositor_threaded_scrolling_result =
- host_impl_->MouseDown(gfx::PointF(350, 500), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 500), /*jump_key_modifier*/ false);
host_impl_->MouseUp(gfx::PointF(350, 500));
EXPECT_EQ(compositor_threaded_scrolling_result.scroll_offset.y(), 525u);
EXPECT_FALSE(GetScrollNode(scroll_layer)->main_thread_scrolling_reasons);
@@ -13129,7 +13771,7 @@ TEST_F(LayerTreeHostImplTest, MainThreadFallback) {
GetScrollNode(scroll_layer)->main_thread_scrolling_reasons =
MainThreadScrollingReason::kHasNonLayerViewportConstrainedObjects;
compositor_threaded_scrolling_result =
- host_impl_->MouseDown(gfx::PointF(350, 500), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(350, 500), /*jump_key_modifier*/ false);
host_impl_->MouseUp(gfx::PointF(350, 500));
EXPECT_EQ(compositor_threaded_scrolling_result.scroll_offset.y(), 0u);
@@ -13138,7 +13780,8 @@ TEST_F(LayerTreeHostImplTest, MainThreadFallback) {
host_impl_ = nullptr;
}
-TEST_F(LayerTreeHostImplTest, SecondScrollAnimatedBeginNotIgnored) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SecondScrollAnimatedBeginNotIgnored) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -13165,7 +13808,8 @@ TEST_F(LayerTreeHostImplTest, SecondScrollAnimatedBeginNotIgnored) {
// Verfify that a smooth scroll animation doesn't jump when UpdateTarget gets
// called before the animation is started.
-TEST_F(LayerTreeHostImplTest, AnimatedScrollUpdateTargetBeforeStarting) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AnimatedScrollUpdateTargetBeforeStarting) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -13223,7 +13867,7 @@ TEST_F(LayerTreeHostImplTest, AnimatedScrollUpdateTargetBeforeStarting) {
EXPECT_TRUE(y > 1 && y < 49);
}
-TEST_F(LayerTreeHostImplTest, ScrollAnimatedWithDelay) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, ScrollAnimatedWithDelay) {
const gfx::Size content_size(1000, 1000);
const gfx::Size viewport_size(50, 100);
SetupViewportLayersOuterScrolls(viewport_size, content_size);
@@ -13752,7 +14396,7 @@ TEST_F(LayerTreeHostImplTimelinesTest, ScrollAnimatedChangingBounds) {
EXPECT_EQ(gfx::ScrollOffset(250, 250), CurrentScrollOffset(scrolling_layer));
}
-TEST_F(LayerTreeHostImplTest, InvalidLayerNotAddedToRasterQueue) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, InvalidLayerNotAddedToRasterQueue) {
CreatePendingTree();
Region empty_invalidation;
@@ -13786,7 +14430,7 @@ TEST_F(LayerTreeHostImplTest, InvalidLayerNotAddedToRasterQueue) {
EXPECT_TRUE(empty_raster_priority_queue_all->IsEmpty());
}
-TEST_F(LayerTreeHostImplTest, DidBecomeActive) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DidBecomeActive) {
CreatePendingTree();
host_impl_->ActivateSyncTree();
CreatePendingTree();
@@ -13819,7 +14463,8 @@ TEST_F(LayerTreeHostImplTest, DidBecomeActive) {
EXPECT_EQ(2u, raw_mask_layer->did_become_active_call_count());
}
-TEST_F(LayerTreeHostImplTest, WheelScrollWithPageScaleFactorOnInnerLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ WheelScrollWithPageScaleFactorOnInnerLayer) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
auto* scroll_layer = InnerViewportScrollLayer();
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(50, 50));
@@ -13892,7 +14537,7 @@ size_t CountRenderPassesWithId(const viz::RenderPassList& list,
[id](const std::unique_ptr<viz::RenderPass>& p) { return p->id == id; });
}
-TEST_F(LayerTreeHostImplTest, RemoveUnreferencedRenderPass) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RemoveUnreferencedRenderPass) {
TestFrameData frame;
frame.render_passes.push_back(viz::RenderPass::Create());
viz::RenderPass* pass3 = frame.render_passes.back().get();
@@ -13927,7 +14572,7 @@ TEST_F(LayerTreeHostImplTest, RemoveUnreferencedRenderPass) {
EXPECT_EQ(1u, frame.render_passes[0]->id);
}
-TEST_F(LayerTreeHostImplTest, RemoveEmptyRenderPass) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RemoveEmptyRenderPass) {
TestFrameData frame;
frame.render_passes.push_back(viz::RenderPass::Create());
viz::RenderPass* pass3 = frame.render_passes.back().get();
@@ -13968,7 +14613,7 @@ TEST_F(LayerTreeHostImplTest, RemoveEmptyRenderPass) {
pass1->quad_list.ElementAt(0)->material);
}
-TEST_F(LayerTreeHostImplTest, DoNotRemoveEmptyRootRenderPass) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DoNotRemoveEmptyRootRenderPass) {
TestFrameData frame;
frame.render_passes.push_back(viz::RenderPass::Create());
viz::RenderPass* pass3 = frame.render_passes.back().get();
@@ -14024,7 +14669,7 @@ class FakeVideoFrameController : public VideoFrameController {
bool did_draw_frame_ = false;
};
-TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerInsideFrame) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AddVideoFrameControllerInsideFrame) {
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
FakeVideoFrameController controller;
@@ -14047,7 +14692,8 @@ TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerInsideFrame) {
EXPECT_FALSE(controller.did_draw_frame());
}
-TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerOutsideFrame) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ AddVideoFrameControllerOutsideFrame) {
viz::BeginFrameArgs begin_frame_args =
viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2);
FakeVideoFrameController controller;
@@ -14078,7 +14724,8 @@ TEST_F(LayerTreeHostImplTest, AddVideoFrameControllerOutsideFrame) {
}
// Tests that SetDeviceScaleFactor correctly impacts GPU rasterization.
-TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusDeviceScaleFactor) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ GpuRasterizationStatusDeviceScaleFactor) {
// Create a host impl with MSAA support (4 samples).
LayerTreeSettings msaaSettings = DefaultSettings();
msaaSettings.gpu_rasterization_msaa_sample_count = -1;
@@ -14108,7 +14755,8 @@ TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusDeviceScaleFactor) {
}
// Tests that explicit MSAA sample count correctly impacts GPU rasterization.
-TEST_F(LayerTreeHostImplTest, GpuRasterizationStatusExplicitMSAACount) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ GpuRasterizationStatusExplicitMSAACount) {
// Create a host impl with MSAA support and a forced sample count of 4.
LayerTreeSettings msaaSettings = DefaultSettings();
msaaSettings.gpu_rasterization_msaa_sample_count = 4;
@@ -14245,7 +14893,7 @@ TEST_F(MsaaCompatibilityLayerTreeHostImplTest,
0);
}
-TEST_F(LayerTreeHostImplTest, UpdatePageScaleFactorOnActiveTree) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, UpdatePageScaleFactorOnActiveTree) {
// Check page scale factor update in property trees when an update is made
// on the active tree.
CreatePendingTree();
@@ -14293,7 +14941,8 @@ TEST_F(LayerTreeHostImplTest, UpdatePageScaleFactorOnActiveTree) {
EXPECT_EQ(gfx::Point3F(), active_tree_node->origin);
}
-TEST_F(LayerTreeHostImplTest, SubLayerScaleForNodeInSubtreeOfPageScaleLayer) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SubLayerScaleForNodeInSubtreeOfPageScaleLayer) {
// Checks that the sublayer scale of a transform node in the subtree of the
// page scale layer is updated without a property tree rebuild.
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 1, 3);
@@ -14321,7 +14970,8 @@ TEST_F(LayerTreeHostImplTest, SubLayerScaleForNodeInSubtreeOfPageScaleLayer) {
// Checks that if we lose a GPU raster enabled LayerTreeFrameSink and replace
// it with a software LayerTreeFrameSink, LayerTreeHostImpl correctly
// re-computes GPU rasterization status.
-TEST_F(LayerTreeHostImplTest, RecomputeGpuRasterOnLayerTreeFrameSinkChange) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ RecomputeGpuRasterOnLayerTreeFrameSinkChange) {
host_impl_->ReleaseLayerTreeFrameSink();
host_impl_ = nullptr;
@@ -14533,7 +15183,7 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates(
animation_task_.Reset();
// Only the MouseMove's location will affect the overlay scrollbar.
- host_impl_->MouseDown(gfx::PointF(60, 50), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(60, 50), /*jump_key_modifier*/ false);
host_impl_->MouseMoveAt(gfx::Point(60, 50));
host_impl_->MouseUp(gfx::PointF(60, 50));
@@ -14544,29 +15194,30 @@ void LayerTreeHostImplTest::SetupMouseMoveAtTestScrollbarStates(
host_impl_->MouseMoveAt(gfx::Point(40, 150));
animation_task_.Reset();
- host_impl_->MouseDown(gfx::PointF(40, 150), /*shift_modifier*/ false);
+ host_impl_->MouseDown(gfx::PointF(40, 150), /*jump_key_modifier*/ false);
host_impl_->MouseUp(gfx::PointF(40, 150));
EXPECT_TRUE(animation_task_.is_null());
// Near scrollbar_1, then mouse down and unregister
// scrollbar_2_animation_controller, then mouse up should not cause crash.
host_impl_->MouseMoveAt(gfx::Point(40, 150));
- host_impl_->MouseDown(gfx::PointF(40, 150), /*shift_modifier*/ false);
- host_impl_->DidUnregisterScrollbarLayer(root_scroll->element_id());
+ host_impl_->MouseDown(gfx::PointF(40, 150), /*jump_key_modifier*/ false);
+ host_impl_->DidUnregisterScrollbarLayer(root_scroll->element_id(),
+ ScrollbarOrientation::VERTICAL);
host_impl_->MouseUp(gfx::PointF(40, 150));
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
LayerTreeHostImplTestScrollbarStatesInMainThreadScrolling) {
SetupMouseMoveAtTestScrollbarStates(true);
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
LayerTreeHostImplTestScrollbarStatesInNotMainThreadScrolling) {
SetupMouseMoveAtTestScrollbarStates(false);
}
-TEST_F(LayerTreeHostImplTest, CheckerImagingTileInvalidation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, CheckerImagingTileInvalidation) {
LayerTreeSettings settings = LegacySWSettings();
settings.commit_to_active_tree = false;
settings.enable_checker_imaging = true;
@@ -14651,7 +15302,7 @@ TEST_F(LayerTreeHostImplTest, CheckerImagingTileInvalidation) {
EXPECT_EQ(expected_invalidation, *(root->GetPendingInvalidation()));
}
-TEST_F(LayerTreeHostImplTest, RasterColorSpace) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorSpace) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
// The default raster color space should be sRGB.
@@ -14666,7 +15317,7 @@ TEST_F(LayerTreeHostImplTest, RasterColorSpace) {
gfx::ColorSpace::CreateDisplayP3D65());
}
-TEST_F(LayerTreeHostImplTest, RasterColorSpaceSoftware) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorSpaceSoftware) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, FakeLayerTreeFrameSink::CreateSoftware());
// Software composited resources should always use sRGB as their color space.
@@ -14680,7 +15331,7 @@ TEST_F(LayerTreeHostImplTest, RasterColorSpaceSoftware) {
gfx::ColorSpace::CreateSRGB());
}
-TEST_F(LayerTreeHostImplTest, RasterColorPrefersSRGB) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RasterColorPrefersSRGB) {
auto p3 = gfx::ColorSpace::CreateDisplayP3D65();
LayerTreeSettings settings = DefaultSettings();
@@ -14697,7 +15348,7 @@ TEST_F(LayerTreeHostImplTest, RasterColorPrefersSRGB) {
EXPECT_EQ(host_impl_->GetRasterColorSpace(gfx::ContentColorUsage::kSRGB), p3);
}
-TEST_F(LayerTreeHostImplTest, UpdatedTilingsForNonDrawingLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, UpdatedTilingsForNonDrawingLayers) {
gfx::Size layer_bounds(500, 500);
CreatePendingTree();
@@ -14749,7 +15400,8 @@ TEST_F(LayerTreeHostImplTest, UpdatedTilingsForNonDrawingLayers) {
->can_require_tiles_for_activation());
}
-TEST_F(LayerTreeHostImplTest, RasterTilePrioritizationForNonDrawingLayers) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ RasterTilePrioritizationForNonDrawingLayers) {
gfx::Size layer_bounds(500, 500);
CreatePendingTree();
auto* root =
@@ -14811,7 +15463,7 @@ TEST_F(LayerTreeHostImplTest, RasterTilePrioritizationForNonDrawingLayers) {
EXPECT_EQ(queue->Top().tile()->layer_id(), 3);
}
-TEST_F(LayerTreeHostImplTest, DrawAfterDroppingTileResources) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, DrawAfterDroppingTileResources) {
LayerTreeSettings settings = DefaultSettings();
settings.using_synchronous_renderer_compositor = true;
CreateHostImpl(settings, CreateLayerTreeFrameSink());
@@ -14845,20 +15497,20 @@ TEST_F(LayerTreeHostImplTest, DrawAfterDroppingTileResources) {
EXPECT_GT(layer->tilings()->num_tilings(), 0u);
}
-TEST_F(LayerTreeHostImplTest, WhiteListedTouchActionTest1) {
- WhiteListedTouchActionTestHelper(1.0f, 1.0f);
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AllowedTouchActionTest1) {
+ AllowedTouchActionTestHelper(1.0f, 1.0f);
}
-TEST_F(LayerTreeHostImplTest, WhiteListedTouchActionTest2) {
- WhiteListedTouchActionTestHelper(1.0f, 0.789f);
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AllowedTouchActionTest2) {
+ AllowedTouchActionTestHelper(1.0f, 0.789f);
}
-TEST_F(LayerTreeHostImplTest, WhiteListedTouchActionTest3) {
- WhiteListedTouchActionTestHelper(2.345f, 1.0f);
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AllowedTouchActionTest3) {
+ AllowedTouchActionTestHelper(2.345f, 1.0f);
}
-TEST_F(LayerTreeHostImplTest, WhiteListedTouchActionTest4) {
- WhiteListedTouchActionTestHelper(2.654f, 0.678f);
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, AllowedTouchActionTest4) {
+ AllowedTouchActionTestHelper(2.654f, 0.678f);
}
// Test implementation of RenderFrameMetadataObserver which can optionally
@@ -14893,7 +15545,7 @@ class TestRenderFrameMetadataObserver : public RenderFrameMetadataObserver {
base::Optional<RenderFrameMetadata> last_metadata_;
};
-TEST_F(LayerTreeHostImplTest, RenderFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, RenderFrameMetadata) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
host_impl_->active_tree()->SetDeviceViewportRect(gfx::Rect(50, 50));
host_impl_->active_tree()->PushPageScaleFromMainThread(1, 0.5f, 4);
@@ -15026,7 +15678,8 @@ TEST_F(LayerTreeHostImplTest, RenderFrameMetadata) {
}
}
-TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToRenderFrameMetadata) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ SelectionBoundsPassedToRenderFrameMetadata) {
LayerImpl* root = SetupDefaultRootLayer(gfx::Size(10, 10));
UpdateDrawProperties(host_impl_->active_tree());
@@ -15078,7 +15731,7 @@ TEST_F(LayerTreeHostImplTest, SelectionBoundsPassedToRenderFrameMetadata) {
EXPECT_TRUE(selection_2.end.visible());
}
-TEST_F(LayerTreeHostImplTest,
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
VerticalScrollDirectionChangesPassedToRenderFrameMetadata) {
// Set up the viewport.
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -15166,7 +15819,8 @@ TEST_F(LayerTreeHostImplTest,
// Tests ScrollUpdate() to see if the method sets the scroll tree's currently
// scrolling node.
-TEST_F(LayerTreeHostImplTest, ScrollUpdateDoesNotSetScrollingNode) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ScrollUpdateDoesNotSetScrollingNode) {
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
UpdateDrawProperties(host_impl_->active_tree());
@@ -15531,7 +16185,8 @@ TEST_F(HitTestRegionListGeneratingLayerTreeHostImplTest, InvalidFrameSinkId) {
hit_test_region_list->regions[0].rect.ToString());
}
-TEST_F(LayerTreeHostImplTest, ImplThreadPhaseUponImplSideInvalidation) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest,
+ ImplThreadPhaseUponImplSideInvalidation) {
LayerTreeSettings settings = DefaultSettings();
CreateHostImpl(settings, CreateLayerTreeFrameSink());
// In general invalidation should never be ran outside the impl frame.
@@ -15552,7 +16207,7 @@ TEST_F(LayerTreeHostImplTest, ImplThreadPhaseUponImplSideInvalidation) {
// Test passes when there is no crash.
}
-TEST_F(LayerTreeHostImplTest, SkipOnDrawDoesNotUpdateDrawParams) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, SkipOnDrawDoesNotUpdateDrawParams) {
EXPECT_TRUE(CreateHostImpl(DefaultSettings(),
FakeLayerTreeFrameSink::CreateSoftware()));
SetupViewportLayersInnerScrolls(gfx::Size(50, 50), gfx::Size(100, 100));
@@ -15581,7 +16236,7 @@ TEST_F(LayerTreeHostImplTest, SkipOnDrawDoesNotUpdateDrawParams) {
// Test that a touch scroll over a SolidColorScrollbarLayer, the scrollbar used
// on Android, does not register as a scrollbar scroll and result in main
// threaded scrolling.
-TEST_F(LayerTreeHostImplTest, TouchScrollOnAndroidScrollbar) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, TouchScrollOnAndroidScrollbar) {
LayerTreeImpl* layer_tree_impl = host_impl_->active_tree();
gfx::Size viewport_size = gfx::Size(360, 600);
gfx::Size scroll_content_size = gfx::Size(360, 3800);
@@ -15786,7 +16441,7 @@ TEST_F(ForceActivateAfterPaintWorkletPaintLayerTreeHostImplTest,
// Verify that the device scale factor is not used to rescale scrollbar deltas
// in percent-based scrolling.
-TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
+TEST_P(ScrollUnifiedLayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
LayerTreeSettings settings = DefaultSettings();
settings.percent_based_scrolling = true;
settings.use_zoom_for_dsf = true;
@@ -15798,21 +16453,21 @@ TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
LayerImpl* content_layer = AddContentLayer();
LayerImpl* scroll_layer = AddScrollableLayer(
- content_layer, gfx::Size(185, 200), gfx::Size(185, 3800));
+ content_layer, gfx::Size(185, 500), gfx::Size(185, 3800));
auto* scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
host_impl_->active_tree(), VERTICAL, false, true);
SetupScrollbarLayer(scroll_layer, scrollbar);
- scrollbar->SetBounds(gfx::Size(15, 200));
+ scrollbar->SetBounds(gfx::Size(15, 500));
scrollbar->SetThumbThickness(15);
scrollbar->SetThumbLength(50);
- scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 185));
+ scrollbar->SetTrackRect(gfx::Rect(0, 15, 15, 485));
scrollbar->SetBackButtonRect(gfx::Rect(gfx::Point(0, 0), gfx::Size(15, 15)));
scrollbar->SetForwardButtonRect(
- gfx::Rect(gfx::Point(0, 185), gfx::Size(15, 15)));
+ gfx::Rect(gfx::Point(0, 485), gfx::Size(15, 15)));
scrollbar->SetOffsetToTransformParent(gfx::Vector2dF(185, 0));
DrawFrame();
@@ -15821,13 +16476,13 @@ TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
host_impl_->BindToClient(&input_handler_client);
// Test scrolling with device scale factor = 3.
- const float expected_delta = kPercentDeltaForDirectionalScroll * 200u;
+ const float expected_delta = floorf(kPercentDeltaForDirectionalScroll * 500);
host_impl_->active_tree()->set_painted_device_scale_factor(3);
InputHandlerPointerResult scroll_result =
- host_impl_->MouseDown(gfx::PointF(190, 190), false);
- host_impl_->MouseUp(gfx::PointF(190, 190));
+ host_impl_->MouseDown(gfx::PointF(190, 490), false);
+ host_impl_->MouseUp(gfx::PointF(190, 490));
EXPECT_EQ(scroll_result.scroll_offset.y(), expected_delta);
EXPECT_FALSE(GetScrollNode(scroll_layer)->main_thread_scrolling_reasons);
@@ -15837,8 +16492,8 @@ TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
host_impl_->active_tree()->set_painted_device_scale_factor(1);
InputHandlerPointerResult scroll_with_dsf_1 =
- host_impl_->MouseDown(gfx::PointF(190, 190), false);
- host_impl_->MouseUp(gfx::PointF(190, 190));
+ host_impl_->MouseDown(gfx::PointF(190, 490), false);
+ host_impl_->MouseUp(gfx::PointF(190, 490));
EXPECT_EQ(scroll_with_dsf_1.scroll_offset.y(), expected_delta);
EXPECT_FALSE(GetScrollNode(scroll_layer)->main_thread_scrolling_reasons);
@@ -15848,5 +16503,497 @@ TEST_F(LayerTreeHostImplTest, PercentBasedScrollbarDeltasDSF3) {
host_impl_ = nullptr;
}
+class UnifiedScrollingTest : public LayerTreeHostImplTest {
+ public:
+ using Point = gfx::Point;
+ using ScrollInputType = ui::ScrollInputType;
+ using ScrollOffset = gfx::ScrollOffset;
+ using ScrollStatus = InputHandler::ScrollStatus;
+ using Size = gfx::Size;
+ using Rect = gfx::Rect;
+ using Vector2d = gfx::Vector2d;
+
+ void SetUp() override {
+ scoped_feature_list.InitAndEnableFeature(features::kScrollUnification);
+ LayerTreeHostImplTest::SetUp();
+
+ cur_time_ = base::TimeTicks() + base::TimeDelta::FromMilliseconds(100);
+ begin_frame_args_ =
+ viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1);
+
+ SetupViewportLayersOuterScrolls(gfx::Size(100, 100), gfx::Size(200, 200));
+ }
+
+ void CreateUncompositedScrollerAndNonFastScrollableRegion() {
+ // Create an uncompositd scroll node that corresponds to a non fast
+ // scrollable region on the outer viewport scroll layer.
+ Size scrollable_content_bounds(100, 100);
+ Size container_bounds(50, 50);
+ CreateScrollNodeForUncompositedScroller(
+ GetPropertyTrees(), host_impl_->OuterViewportScrollNode()->id,
+ ScrollerElementId(), scrollable_content_bounds, container_bounds);
+ OuterViewportScrollLayer()->SetNonFastScrollableRegion(Rect(0, 0, 50, 50));
+
+ host_impl_->active_tree()->set_needs_update_draw_properties();
+ UpdateDrawProperties(host_impl_->active_tree());
+ host_impl_->active_tree()->DidBecomeActive();
+ DrawFrame();
+ }
+
+ void CreateScroller(uint32_t main_thread_scrolling_reasons) {
+ // Creates a regular compositeds scroller that comes with a ScrollNode and
+ // Layer.
+ Size scrollable_content_bounds(100, 100);
+ Size container_bounds(50, 50);
+
+ LayerImpl* layer =
+ AddScrollableLayer(OuterViewportScrollLayer(), container_bounds,
+ scrollable_content_bounds);
+ scroller_layer_ = layer;
+ GetScrollNode(layer)->main_thread_scrolling_reasons =
+ main_thread_scrolling_reasons;
+ GetScrollNode(layer)->is_composited = true;
+
+ UpdateDrawProperties(host_impl_->active_tree());
+ host_impl_->active_tree()->DidBecomeActive();
+ DrawFrame();
+ }
+
+ void CreateSuashingLayerCoveringWholeViewport() {
+ // Add a (simulated) "squashing layer". It's scroll parent is the outer
+ // viewport but it covers the entire viewport and any scrollers underneath
+ // it.
+ LayerImpl* squashing_layer = AddLayer();
+ squashing_layer->SetBounds(gfx::Size(100, 100));
+ squashing_layer->SetDrawsContent(true);
+ squashing_layer->SetHitTestable(true);
+ CopyProperties(OuterViewportScrollLayer(), squashing_layer);
+ UpdateDrawProperties(host_impl_->active_tree());
+ }
+
+ ScrollStatus ScrollBegin(const Vector2d& delta) {
+ auto scroll_state =
+ BeginState(Point(25, 25), delta, ScrollInputType::kWheel);
+ ScrollStatus status =
+ host_impl_->ScrollBegin(scroll_state.get(), ScrollInputType::kWheel);
+
+ if (status.needs_main_thread_hit_test)
+ to_be_continued_scroll_begin_ = std::move(scroll_state);
+
+ return status;
+ }
+
+ ScrollStatus ContinuedScrollBegin(ElementId element_id) {
+ DCHECK(to_be_continued_scroll_begin_)
+ << "ContinuedScrollBegin needs to come after a ScrollBegin that "
+ "requested a main frame";
+ std::unique_ptr<ScrollState> scroll_state =
+ std::move(to_be_continued_scroll_begin_);
+
+ scroll_state->data()->set_current_native_scrolling_element(element_id);
+ scroll_state->data()->is_main_thread_hit_tested = true;
+
+ return host_impl_->ScrollBegin(scroll_state.get(), ScrollInputType::kWheel);
+ }
+
+ InputHandlerScrollResult ScrollUpdate(const Vector2d& delta) {
+ auto scroll_state =
+ UpdateState(Point(25, 25), delta, ScrollInputType::kWheel);
+ return host_impl_->ScrollUpdate(scroll_state.get());
+ }
+
+ InputHandlerScrollResult AnimatedScrollUpdate(const Vector2d& delta) {
+ auto scroll_state = AnimatedUpdateState(Point(25, 25), delta);
+ return host_impl_->ScrollUpdate(scroll_state.get());
+ }
+
+ void ScrollEnd() { return host_impl_->ScrollEnd(); }
+
+ // An animation is setup in the first BeginFrame so it won't actually update
+ // in the first one. Use a named method for that so it's clear rather than
+ // mysteriously calling BeginFrame twice.
+ void StartAnimation() { BeginFrame(base::TimeDelta()); }
+
+ void BeginFrame(base::TimeDelta forward) {
+ cur_time_ += forward;
+ begin_frame_args_.frame_time = cur_time_;
+ begin_frame_args_.frame_id.sequence_number++;
+ host_impl_->WillBeginImplFrame(begin_frame_args_);
+ host_impl_->Animate();
+ host_impl_->UpdateAnimationState(true);
+ host_impl_->DidFinishImplFrame(begin_frame_args_);
+ }
+
+ ScrollOffset GetScrollOffset(ScrollNode* node) {
+ return GetPropertyTrees()->scroll_tree.current_scroll_offset(
+ node->element_id);
+ }
+
+ ScrollOffset ScrollerOffset() {
+ return GetPropertyTrees()->scroll_tree.current_scroll_offset(
+ ScrollerElementId());
+ }
+
+ PropertyTrees* GetPropertyTrees() {
+ return host_impl_->active_tree()->property_trees();
+ }
+
+ ScrollNode* CurrentlyScrollingNode() {
+ return host_impl_->CurrentlyScrollingNode();
+ }
+
+ ScrollNode* ScrollerNode() {
+ ScrollNode* node = GetPropertyTrees()->scroll_tree.FindNodeFromElementId(
+ ScrollerElementId());
+ DCHECK(node);
+ return node;
+ }
+ ElementId ScrollerElementId() const {
+ if (scroller_layer_)
+ return scroller_layer_->element_id();
+
+ return ElementId(1234);
+ }
+
+ base::TimeDelta kFrameInterval = base::TimeDelta::FromMilliseconds(16);
+
+ // Parameterized test body. Defined inline with tests.
+ void TestUncompositedScrollingState(bool mutates_transform_tree);
+
+ private:
+ LayerImpl* scroller_layer_ = nullptr;
+
+ base::TimeTicks cur_time_;
+ viz::BeginFrameArgs begin_frame_args_;
+
+ std::unique_ptr<ScrollState> to_be_continued_scroll_begin_;
+ base::test::ScopedFeatureList scoped_feature_list;
+};
+
+// A ScrollBegin that hits a non fast scrollable region must return a request
+// for a main thread hit test.
+TEST_F(UnifiedScrollingTest, UnifiedScrollNonFastScrollableRegion) {
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+
+ // When ScrollUnification is turned on, scrolling inside a non-fast
+ // scrollable region should request a main thread hit test. It's the client's
+ // responsibility to request a hit test from Blink. It can then call
+ // ScrollBegin again, providing the element_id to scroll.
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+
+ // The scroll hasn't started yet though.
+ EXPECT_FALSE(CurrentlyScrollingNode());
+ }
+
+ // Simulate the scroll hit test coming back from the main thread. This time
+ // ScrollBegin will be called with an element id provided so that a hit test
+ // is unnecessary.
+ {
+ ScrollStatus status = ContinuedScrollBegin(ScrollerElementId());
+
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+
+ EXPECT_TRUE(CurrentlyScrollingNode());
+ EXPECT_EQ(ScrollerNode(), CurrentlyScrollingNode());
+ }
+
+ // Ensure ScrollUpdates can successfully scroll this node. They shouldn't
+ // mutate the associated transform node.
+ {
+ EXPECT_TRUE(ScrollUpdate(Vector2d(0, 10)).did_scroll);
+ EXPECT_EQ(ScrollOffset(0, 10), ScrollerOffset());
+ }
+
+ // Try to scroll past the end. Ensure the max scrolling bounds are respected.
+ {
+ EXPECT_TRUE(ScrollUpdate(Vector2d(0, 1000)).did_scroll);
+ EXPECT_EQ(ScrollOffset(0, 50), ScrollerOffset());
+ }
+
+ // Overscrolling should cause the scroll update to be dropped.
+ {
+ EXPECT_FALSE(ScrollUpdate(Vector2d(0, 10)).did_scroll);
+ EXPECT_EQ(ScrollOffset(0, 50), ScrollerOffset());
+ }
+
+ host_impl_->ScrollEnd();
+}
+
+// A main thread hit test should still go through latch bubbling. That is, if
+// the hit tested scroller is fully scrolled and cannot consume the scroll, we
+// should chain up to its ancestor.
+TEST_F(UnifiedScrollingTest, MainThreadHitTestLatchBubbling) {
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+
+ // Start with the scroller fully scrolled.
+ {
+ ScrollBegin(Vector2d(0, 1000));
+ ContinuedScrollBegin(ScrollerElementId());
+ ScrollUpdate(Vector2d(0, 1000));
+ ScrollEnd();
+ ASSERT_EQ(ScrollOffset(0, 50), ScrollerOffset());
+ }
+
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ ASSERT_TRUE(status.needs_main_thread_hit_test);
+ status = ContinuedScrollBegin(ScrollerElementId());
+
+ // Since the hit tested scroller in ContinuedScrollBegin was fully
+ // scrolled, we should latch to the viewport instead.
+ EXPECT_TRUE(CurrentlyScrollingNode());
+ EXPECT_EQ(host_impl_->OuterViewportScrollNode(), CurrentlyScrollingNode());
+ }
+}
+
+using UnifiedScrollingDeathTest = UnifiedScrollingTest;
+
+// A main thread hit test that with an empty target id should be dropped.
+TEST_F(UnifiedScrollingDeathTest, EmptyMainThreadHitTest) {
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+ {
+ ElementId kInvalidId;
+ DCHECK(!kInvalidId);
+
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+
+ // Note, we have a NOTREACHED here to make sure this cannot happen. If
+ // DCHECKs are enabled we can just make sure we get killed when we end up
+ // in this situation.
+#if DCHECK_IS_ON()
+ EXPECT_DEATH_IF_SUPPORTED({ status = ContinuedScrollBegin(kInvalidId); },
+ "");
+#else
+ status = ContinuedScrollBegin(kInvalidId);
+ EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
+ status.main_thread_scrolling_reasons);
+#endif
+ }
+}
+
+// A main thread hit test that returns a scroll node we can't find should be
+// dropped.
+TEST_F(UnifiedScrollingTest, MainThreadHitTestScrollNodeNotFound) {
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+
+ {
+ ElementId kUnknown(42);
+ DCHECK(!GetPropertyTrees()->scroll_tree.FindNodeFromElementId(kUnknown));
+
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ status = ContinuedScrollBegin(kUnknown);
+ EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNoScrollingLayer,
+ status.main_thread_scrolling_reasons);
+ }
+}
+
+// The presence of a squashing layer is one reason we might "fail to hit test"
+// on the compositor. A heuristic is used that if the first hit scrollable
+// layer isn't a scrolling ancestor of the first hit layer, we probably hit a
+// squashing layer which might have empty regions we don't know how to hit
+// test. This requires falling back to the main thread. However, with scroll
+// unification, we may still be able to scroll the final scroller entirely on
+// the compositor. See LayerTreeHostImpl::IsInitialScrollHitTestReliable.
+TEST_F(UnifiedScrollingTest, SquashingLayerCausesMainThreadHitTest) {
+ // Create a scroller that should scroll on the compositor thread and the add
+ // "squashing" layer over top of it. This simulates the case where a
+ // squashing layer obscuring a scroller makes the hit test unreliable.
+ CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
+ CreateSuashingLayerCoveringWholeViewport();
+
+ // When ScrollUnification is turned on, scrolling over a squashing-like layer
+ // that cannot be reliably hit tested on the compositor should request a main
+ // thread hit test.
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ }
+
+ // Resolving the hit test should allow the scroller underneath to scroll as
+ // normal on the impl thread.
+ {
+ ScrollStatus status = ContinuedScrollBegin(ScrollerElementId());
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+
+ EXPECT_TRUE(CurrentlyScrollingNode());
+ EXPECT_EQ(ScrollerNode(), CurrentlyScrollingNode());
+ }
+}
+
+// Under unified scroling, a composited scroller with a main thread scrolling
+// reason should be scrolled on the compositor. Ensure ScrollBegin returns
+// success without needing a main thread hit test.
+TEST_F(UnifiedScrollingTest, MainThreadScrollingReasonsScrollOnCompositor) {
+ CreateScroller(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
+
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ }
+}
+
+// This tests whether or not various kinds of scrolling mutates the transform
+// tree or not. It is parameterized and used by tests below.
+void UnifiedScrollingTest::TestUncompositedScrollingState(
+ bool mutates_transform_tree) {
+ TransformTree& tree = GetPropertyTrees()->transform_tree;
+ TransformNode* transform_node = tree.Node(ScrollerNode()->transform_id);
+
+ // Ensure we're in a clean state to start.
+ {
+ ASSERT_EQ(transform_node->element_id, ScrollerElementId());
+ ASSERT_TRUE(transform_node->scrolls);
+
+ ASSERT_EQ(ScrollOffset(0, 0), transform_node->scroll_offset);
+ ASSERT_FALSE(transform_node->transform_changed);
+ ASSERT_FALSE(transform_node->needs_local_transform_update);
+ ASSERT_FALSE(tree.needs_update());
+ }
+
+ // Start a scroll, ensure the scroll tree was updated and a commit was
+ // requested. Check that the transform tree mutation was as expected for the
+ // test parameter.
+ {
+ ScrollStatus status = ScrollBegin(Vector2d(0, 10));
+ if (status.needs_main_thread_hit_test)
+ ContinuedScrollBegin(ScrollerElementId());
+
+ ASSERT_EQ(ScrollerNode(), CurrentlyScrollingNode());
+
+ did_request_commit_ = false;
+
+ ScrollUpdate(Vector2d(0, 10));
+ ASSERT_EQ(ScrollOffset(0, 10), ScrollerOffset());
+ EXPECT_TRUE(did_request_commit_);
+
+ // Ensure the transform tree was updated only if expected.
+ if (mutates_transform_tree)
+ EXPECT_EQ(ScrollOffset(0, 10), transform_node->scroll_offset);
+ else
+ EXPECT_EQ(ScrollOffset(0, 0), transform_node->scroll_offset);
+ EXPECT_EQ(mutates_transform_tree, transform_node->transform_changed);
+ EXPECT_EQ(mutates_transform_tree,
+ transform_node->needs_local_transform_update);
+ EXPECT_EQ(mutates_transform_tree, tree.needs_update());
+ }
+
+ // Perform animated scroll update. Ensure the same things.
+ {
+ did_request_commit_ = false;
+
+ AnimatedScrollUpdate(Vector2d(0, 10));
+ ASSERT_TRUE(host_impl_->mutator_host()->ImplOnlyScrollAnimatingElement());
+ ASSERT_EQ(ScrollOffset(0, 10), ScrollerOffset());
+
+ StartAnimation();
+ BeginFrame(kFrameInterval);
+ BeginFrame(base::TimeDelta::FromMilliseconds(500));
+ BeginFrame(kFrameInterval);
+
+ ASSERT_EQ(ScrollOffset(0, 20), ScrollerOffset());
+ EXPECT_TRUE(did_request_commit_);
+
+ if (mutates_transform_tree)
+ EXPECT_EQ(ScrollOffset(0, 20), transform_node->scroll_offset);
+ else
+ EXPECT_EQ(ScrollOffset(0, 0), transform_node->scroll_offset);
+ EXPECT_EQ(mutates_transform_tree, transform_node->transform_changed);
+ EXPECT_EQ(mutates_transform_tree,
+ transform_node->needs_local_transform_update);
+ EXPECT_EQ(mutates_transform_tree, tree.needs_update());
+ }
+}
+
+// When scrolling a main-thread hit tested scroller with main thread reasons,
+// we should update the scroll node but the transform tree shouldn't be
+// mutated. Also ensure NeedsCommit is set. A nice additional benefit of scroll
+// unification should be seamless upgrade to a full compositor scroll if a main
+// thread reason is removed.
+TEST_F(UnifiedScrollingTest, MainThreadReasonsScrollDoesntAffectTransform) {
+ CreateScroller(
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
+
+ TestUncompositedScrollingState(/*mutates_transform_tree=*/false);
+
+ ASSERT_EQ(ScrollerNode()->main_thread_scrolling_reasons,
+ MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects);
+ TransformTree& tree = GetPropertyTrees()->transform_tree;
+ TransformNode* transform_node = tree.Node(ScrollerNode()->transform_id);
+
+ // Removing the main thread reason bit should start mutating the transform
+ // tree.
+ {
+ ScrollerNode()->main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNotScrollingOnMain;
+ UpdateDrawProperties(host_impl_->active_tree());
+ host_impl_->active_tree()->DidBecomeActive();
+
+ ScrollUpdate(Vector2d(0, 10));
+ ASSERT_EQ(ScrollOffset(0, 30), ScrollerOffset());
+
+ // The transform node should now be updated by the scroll.
+ EXPECT_EQ(ScrollOffset(0, 30), transform_node->scroll_offset);
+ EXPECT_TRUE(transform_node->transform_changed);
+ EXPECT_TRUE(transform_node->needs_local_transform_update);
+ EXPECT_TRUE(tree.needs_update());
+ }
+
+ ScrollEnd();
+}
+
+// When scrolling an uncomposited scroller, we shouldn't modify the transform
+// tree. If a scroller is promoted mid-scroll it should start mutating the
+// transform tree.
+TEST_F(UnifiedScrollingTest, UncompositedScrollerDoesntAffectTransform) {
+ CreateUncompositedScrollerAndNonFastScrollableRegion();
+
+ TestUncompositedScrollingState(/*mutates_transform_tree=*/false);
+
+ ASSERT_FALSE(ScrollerNode()->is_composited);
+ TransformTree& tree = GetPropertyTrees()->transform_tree;
+ TransformNode* transform_node = tree.Node(ScrollerNode()->transform_id);
+
+ // Marking the node as composited should start updating the transform tree.
+ {
+ ScrollerNode()->is_composited = true;
+ UpdateDrawProperties(host_impl_->active_tree());
+ host_impl_->active_tree()->DidBecomeActive();
+
+ ScrollUpdate(Vector2d(0, 10));
+ ASSERT_EQ(ScrollOffset(0, 30), ScrollerOffset());
+
+ // The transform node should now be updated by the scroll.
+ EXPECT_EQ(ScrollOffset(0, 30), transform_node->scroll_offset);
+ EXPECT_TRUE(transform_node->transform_changed);
+ EXPECT_TRUE(transform_node->needs_local_transform_update);
+ EXPECT_TRUE(tree.needs_update());
+ }
+
+ ScrollEnd();
+}
+
+// When scrolling a composited scroller that just happens to have needed a main
+// thread hit test first, we should modify the transform tree as usual.
+TEST_F(UnifiedScrollingTest, CompositedWithSquashedLayerMutatesTransform) {
+ CreateScroller(MainThreadScrollingReason::kNotScrollingOnMain);
+ CreateSuashingLayerCoveringWholeViewport();
+
+ TestUncompositedScrollingState(/*mutates_transform_tree=*/true);
+
+ ScrollEnd();
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc
index b55a4b3b733..0a6809e778a 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_blending.cc
@@ -6,12 +6,13 @@
#include "base/stl_util.h"
#include "build/build_config.h"
-#include "cc/layers/picture_image_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/render_surface_filters.h"
#include "cc/paint/skia_paint_canvas.h"
+#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_pixel_resource_test.h"
#include "cc/test/pixel_comparator.h"
#include "cc/test/test_layer_tree_frame_sink.h"
@@ -118,23 +119,19 @@ class LayerTreeHostBlendingPixelTest
scoped_refptr<Layer> CreateColorfulBackdropLayer(int width, int height) {
sk_sp<SkSurface> backing_store = CreateColorfulSurface(width, height);
- scoped_refptr<PictureImageLayer> layer = PictureImageLayer::Create();
+ gfx::Size bounds(width, height);
+ backdrop_client_.set_bounds(bounds);
+ backdrop_client_.add_draw_image(backing_store->makeImageSnapshot(),
+ gfx::Point(), PaintFlags());
+ scoped_refptr<FakePictureLayer> layer =
+ FakePictureLayer::Create(&backdrop_client_);
layer->SetIsDrawable(true);
- layer->SetBounds(gfx::Size(width, height));
- layer->SetImage(PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(backing_store->makeImageSnapshot(),
- PaintImage::GetNextContentId())
- .TakePaintImage(),
- SkMatrix::I(), false);
+ layer->SetBounds(bounds);
return layer;
}
void SetupMaskLayer(scoped_refptr<Layer> layer) {
gfx::Size bounds = layer->bounds();
- scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create();
- mask->SetIsDrawable(true);
- mask->SetBounds(bounds);
sk_sp<SkSurface> surface =
SkSurface::MakeRasterN32Premul(bounds.width(), bounds.height());
@@ -146,12 +143,15 @@ class LayerTreeHostBlendingPixelTest
// cover the right half of it
canvas->drawRect(
SkRect::MakeXYWH(1, 0, bounds.width() - 1, bounds.height()), paint);
- mask->SetImage(PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(surface->makeImageSnapshot(),
- PaintImage::GetNextContentId())
- .TakePaintImage(),
- SkMatrix::I(), false);
+
+ mask_client_.set_bounds(bounds);
+ mask_client_.add_draw_image(surface->makeImageSnapshot(), gfx::Point(),
+ PaintFlags());
+
+ scoped_refptr<FakePictureLayer> mask =
+ FakePictureLayer::Create(&mask_client_);
+ mask->SetIsDrawable(true);
+ mask->SetBounds(bounds);
layer->SetMaskLayer(mask);
}
@@ -254,6 +254,8 @@ class LayerTreeHostBlendingPixelTest
bool force_antialiasing_;
bool force_blending_with_shaders_;
+ FakeContentLayerClient mask_client_;
+ FakeContentLayerClient backdrop_client_;
SkColor misc_opaque_color_ = 0xffc86464;
};
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
index c93b84e3271..4042e97367c 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -309,55 +309,6 @@ TEST_P(LayerTreeHostFiltersPixelTest, BackdropFilterBlurOutsets) {
base::FilePath(FILE_PATH_LITERAL("backdrop_filter_blur_outsets.png")));
}
-class LayerTreeHostImageFiltersPixelTestLayerList
- : public LayerTreeHostFiltersPixelTest {
- public:
- LayerTreeHostImageFiltersPixelTestLayerList() { SetUseLayerLists(); }
-
- void SetupTree() override {
- SetInitialRootBounds(gfx::Size(200, 200));
- LayerTreePixelTest::SetupTree();
-
- Layer* root = layer_tree_host()->root_layer();
- scoped_refptr<SolidColorLayer> background =
- CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorYELLOW);
- CopyProperties(root, background.get());
- root->AddChild(background);
-
- scoped_refptr<SolidColorLayer> foreground =
- CreateSolidColorLayer(gfx::Rect(200, 200), SK_ColorRED);
- CopyProperties(root, foreground.get());
- root->AddChild(foreground);
-
- EffectNode& effect_node = CreateEffectNode(foreground.get());
- float matrix[20] = {0};
- // This filter does a red-blue swap, so the foreground becomes blue.
- matrix[2] = matrix[6] = matrix[10] = matrix[18] = 1.0f;
- // Set up a crop rect to filter the bottom 200x100 pixels of the foreground.
- SkImageFilter::CropRect crop_rect(SkRect::MakeXYWH(0, 100, 200, 100));
- FilterOperations filters;
- filters.Append(FilterOperation::CreateReferenceFilter(
- sk_make_sp<ColorFilterPaintFilter>(SkColorFilters::Matrix(matrix),
- nullptr, &crop_rect)));
-
- effect_node.filters = filters;
- effect_node.render_surface_reason = RenderSurfaceReason::kFilter;
-
- // Move the filters origin up by 100 pixels so the crop rect is applied
- // only to the top 100 pixels, not the bottom.
- effect_node.filters_origin = gfx::PointF(0.0f, -100.0f);
- }
-};
-
-INSTANTIATE_TEST_SUITE_P(All,
- LayerTreeHostImageFiltersPixelTestLayerList,
- ::testing::ValuesIn(kRendererTypes));
-
-TEST_P(LayerTreeHostImageFiltersPixelTestLayerList, NonZeroOrigin) {
- RunPixelTestWithLayerList(
- base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")));
-}
-
class LayerTreeHostBlurFiltersPixelTestGPULayerList
: public LayerTreeHostFiltersPixelTest {
public:
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc
index 7e39b785622..702d4e5dbb7 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -7,13 +7,13 @@
#include "base/stl_util.h"
#include "build/build_config.h"
#include "cc/layers/content_layer_client.h"
-#include "cc/layers/picture_image_layer.h"
#include "cc/layers/picture_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_op_buffer.h"
+#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_pixel_resource_test.h"
#include "cc/test/pixel_comparator.h"
@@ -318,22 +318,20 @@ TEST_P(LayerTreeHostMaskPixelTestWithLayerList, MaskWithEffectDifferentSize) {
}
TEST_P(LayerTreeHostMaskPixelTestWithLayerList, ImageMaskWithEffect) {
- MaskContentLayerClient client(mask_bounds_);
- scoped_refptr<PictureImageLayer> mask_layer = PictureImageLayer::Create();
+ MaskContentLayerClient mask_client(mask_bounds_);
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(50, 50);
SkCanvas* canvas = surface->getCanvas();
scoped_refptr<DisplayItemList> mask_display_list =
- client.PaintContentsToDisplayList(
+ mask_client.PaintContentsToDisplayList(
ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
mask_display_list->Raster(canvas);
- mask_layer->SetImage(PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(surface->makeImageSnapshot(),
- PaintImage::GetNextContentId())
- .TakePaintImage(),
- SkMatrix::I(), false);
- mask_layer_ = mask_layer;
+
+ FakeContentLayerClient layer_client;
+ layer_client.set_bounds(mask_bounds_);
+ layer_client.add_draw_image(surface->makeImageSnapshot(), gfx::Point(),
+ PaintFlags());
+ mask_layer_ = FakePictureLayer::Create(&layer_client);
pixel_comparator_ =
std::make_unique<FuzzyPixelOffByOneComparator>(true /* discard_alpha */);
@@ -350,10 +348,6 @@ TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) {
gfx::Size mask_bounds(50, 50);
- scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create();
- mask->SetIsDrawable(true);
- mask->SetBounds(mask_bounds);
-
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(50, 50);
SkCanvas* canvas = surface->getCanvas();
MaskContentLayerClient client(mask_bounds);
@@ -361,12 +355,14 @@ TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) {
client.PaintContentsToDisplayList(
ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
mask_display_list->Raster(canvas);
- mask->SetImage(PaintImageBuilder::WithDefault()
- .set_id(PaintImage::GetNextId())
- .set_image(surface->makeImageSnapshot(),
- PaintImage::GetNextContentId())
- .TakePaintImage(),
- SkMatrix::I(), false);
+
+ FakeContentLayerClient mask_client;
+ mask_client.set_bounds(mask_bounds);
+ mask_client.add_draw_image(surface->makeImageSnapshot(), gfx::Point(),
+ PaintFlags());
+ scoped_refptr<FakePictureLayer> mask = FakePictureLayer::Create(&mask_client);
+ mask->SetIsDrawable(true);
+ mask->SetBounds(mask_bounds);
scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder(
gfx::Rect(25, 25, 50, 50), kCSSGreen, 1, SK_ColorBLACK);
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc
index 2739d9880bb..34d9ef117a4 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_synchronous.cc
@@ -4,7 +4,6 @@
#include "build/build_config.h"
#include "cc/layers/content_layer_client.h"
-#include "cc/layers/picture_image_layer.h"
#include "cc/layers/picture_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/test/fake_content_layer_client.h"
diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc
index edbee8ea336..2d94507a792 100644
--- a/chromium/cc/trees/layer_tree_host_unittest.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest.cc
@@ -52,7 +52,6 @@
#include "cc/test/test_layer_tree_frame_sink.h"
#include "cc/trees/clip_node.h"
#include "cc/trees/effect_node.h"
-#include "cc/trees/frame_rate_counter.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/scroll_and_scale_set.h"
@@ -6187,9 +6186,9 @@ class LayerTreeHostTestSwapPromiseDuringCommit : public LayerTreeHostTest {
&set_needs_commit_count,
&set_needs_redraw_count));
layer_tree_host()->QueueSwapPromise(std::move(swap_promise));
- // Queueing a swap promise from DidBeginMainFrame should cause a
+ // Queueing a swap promise from DidBeginMainFrame should not cause a
// subsequent main frame to be scheduled.
- EXPECT_EQ(1, set_needs_commit_count);
+ EXPECT_EQ(0, set_needs_commit_count);
}
EndTest();
@@ -8902,28 +8901,11 @@ class LayerTreeHostCustomThrougputTrackerTest : public LayerTreeHostTest {
}
void NotifyThroughputTrackerResults(CustomTrackerResults results) override {
+ // Check that data for kSequenceId is captured. Ideally, we should get
+ // 2 frame_expected and 2 frame_produced. But on slow bots, it is difficult
+ // to infer the correct numbers. Both frame_expected and frame_produced
+ // could drop to 1 (or even below). So no sanity check on data itself.
ASSERT_TRUE(base::Contains(results, kSequenceId));
- const auto& throughput = results[kSequenceId];
- // Frame 3 and 4 are counted. See the sequence in DidCommit comment for
- // normal case that expects 2 for both frames_expected and frames_produced.
- //
- // However, on slow bots, things could be different.
- // - Begin frame could be skipped but still counted as expected frames,
- //
- // e(5,5)b(8)B(0,8)E(8)s(3)S(8)e(8,8)b(11)
- // B(8,11)E(11)ts(4)S(11)e(11,11)P(3)e(14,14)P(4)
- //
- // B(0, 8) and B(8, 11) make frame_expected to be 4, more than 2 expected
- // by test.
- //
- // - Finish before frame 4 is presented in multi-thread mode.
- //
- // e(3,3)b(4)B(0,4)E(4)s(2)e(4,4)b(6)B(4,6)E(6)s(3)S(4)e(6,6)
- // P(2)e(7,7)P(3)
- //
- // Only P(3) is counted thus frames_produced is 1.
- EXPECT_GE(throughput.frames_expected, 2u);
- EXPECT_GE(throughput.frames_produced, 1u);
EndTest();
}
@@ -8931,5 +8913,112 @@ class LayerTreeHostCustomThrougputTrackerTest : public LayerTreeHostTest {
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostCustomThrougputTrackerTest);
+// Confirm that DelegatedInkMetadata set on the LTH propagates to the
+// CompositorFrameMetadata and RenderFrameMetadata, and then both are correctly
+// reset when another frame is drawn without DelegatedInkMetadata.
+class LayerTreeHostTestDelegatedInkMetadataOnAndOff
+ : public LayerTreeHostTest,
+ public RenderFrameMetadataObserver {
+ public:
+ // Provides a wrapper which can be passed to LayerTreeHost, but just forwards
+ // to the test class.
+ class ForwardingRenderFrameMetadataObserver
+ : public RenderFrameMetadataObserver {
+ public:
+ explicit ForwardingRenderFrameMetadataObserver(
+ RenderFrameMetadataObserver* target)
+ : target_(target) {}
+
+ // RenderFrameMetadataObserver implementation.
+ void BindToCurrentThread() override { target_->BindToCurrentThread(); }
+ void OnRenderFrameSubmission(
+ const RenderFrameMetadata& render_frame_metadata,
+ viz::CompositorFrameMetadata* compositor_frame_metadata,
+ bool force_send) override {
+ target_->OnRenderFrameSubmission(render_frame_metadata,
+ compositor_frame_metadata, force_send);
+ }
+
+ private:
+ RenderFrameMetadataObserver* target_ = nullptr;
+ };
+
+ void BeginTest() override {
+ // Set up a basic render frame observer for the LTH/LTHI to forward to.
+ layer_tree_host()->SetRenderFrameObserver(
+ std::make_unique<ForwardingRenderFrameMetadataObserver>(this));
+
+ // Setting up a basic frame that can be redrawn.
+ layer_tree_host()->SetViewportRectAndScale(gfx::Rect(10, 10), 1.f,
+ viz::LocalSurfaceIdAllocation());
+ layer_tree_host()->root_layer()->SetBounds(gfx::Size(10, 10));
+ layer_ = FakePictureLayer::Create(&client_);
+ layer_tree_host()->root_layer()->AddChild(layer_);
+ client_.set_bounds(layer_->bounds());
+
+ // Values chosen arbitrarily
+ SkColor color = SK_ColorDKGRAY;
+ double diameter = 1.000002;
+ gfx::PointF point = gfx::PointF(135, 45);
+ gfx::RectF area = gfx::RectF(173, 438);
+ base::TimeTicks timestamp = base::TimeTicks::Now();
+
+ expected_metadata_ =
+ viz::DelegatedInkMetadata(point, diameter, color, timestamp, area);
+ layer_tree_host()->SetDelegatedInkMetadata(
+ std::make_unique<viz::DelegatedInkMetadata>(
+ expected_metadata_.value()));
+ }
+
+ void DidCommitAndDrawFrame() override {
+ // Cause a redraw to occur.
+ layer_->SetNeedsDisplay();
+ }
+
+ void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
+ if (expected_metadata_.has_value()) {
+ // Now try again with no metadata to confirm everything is cleared out.
+ expected_metadata_.reset();
+ }
+ }
+
+ void ExpectMetadata(bool had_delegated_ink_metadata,
+ viz::DelegatedInkMetadata* actual_metadata) {
+ if (expected_metadata_.has_value()) {
+ EXPECT_TRUE(had_delegated_ink_metadata);
+ EXPECT_TRUE(actual_metadata);
+ EXPECT_EQ(expected_metadata_->point(), actual_metadata->point());
+ EXPECT_EQ(expected_metadata_->color(), actual_metadata->color());
+ EXPECT_EQ(expected_metadata_->diameter(), actual_metadata->diameter());
+ EXPECT_EQ(expected_metadata_->presentation_area(),
+ actual_metadata->presentation_area());
+ EXPECT_EQ(expected_metadata_->timestamp(), actual_metadata->timestamp());
+ } else {
+ EXPECT_FALSE(had_delegated_ink_metadata);
+ EXPECT_FALSE(actual_metadata);
+ EndTest();
+ }
+ }
+
+ void AfterTest() override {}
+
+ // RenderFrameMetadataObserver implementation.
+ void BindToCurrentThread() override {}
+ void OnRenderFrameSubmission(
+ const RenderFrameMetadata& render_frame_metadata,
+ viz::CompositorFrameMetadata* compositor_frame_metadata,
+ bool force_send) override {
+ ExpectMetadata(render_frame_metadata.has_delegated_ink_metadata,
+ compositor_frame_metadata->delegated_ink_metadata.get());
+ }
+
+ private:
+ base::Optional<viz::DelegatedInkMetadata> expected_metadata_;
+ FakeContentLayerClient client_;
+ scoped_refptr<Layer> layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestDelegatedInkMetadataOnAndOff);
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc
index bb64e9ef4a4..7a188881491 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_copyrequest.cc
@@ -10,6 +10,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "cc/layers/effect_tree_layer_list_iterator.h"
+#include "cc/test/cc_test_suite.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_test.h"
@@ -309,13 +310,19 @@ class LayerTreeHostCopyRequestTestLayerDestroyed
base::BindOnce(
&LayerTreeHostCopyRequestTestLayerDestroyed::CopyOutputCallback,
base::Unretained(this))));
+ // We expect that the RequestCopyOfOutput won't yet return results until
+ // the main is destroyed. So we RunUntilIdle to ensure no PostTask is
+ // currently queued to return the result.
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(0, callback_count_);
// Destroy the main thread layer right away.
main_destroyed_->RemoveFromParent();
main_destroyed_.reset();
- // Should callback with a NULL bitmap.
+ // Should callback with a NULL bitmap, result will be in a PostTask so
+ // RunUntilIdle().
+ CCTestSuite::RunUntilIdle();
EXPECT_EQ(1, callback_count_);
// Prevent drawing so we can't make a copy of the impl_destroyed layer.
@@ -350,7 +357,6 @@ class LayerTreeHostCopyRequestTestLayerDestroyed
}
void CopyOutputCallback(std::unique_ptr<viz::CopyOutputResult> result) {
- EXPECT_TRUE(layer_tree_host()->GetTaskRunnerProvider()->IsMainThread());
EXPECT_TRUE(result->IsEmpty());
++callback_count_;
}
diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
index a5f7b6e522a..3a95b182bf0 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -10,8 +10,10 @@
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
#include "cc/animation/animation_host.h"
#include "cc/base/completion_event.h"
+#include "cc/base/features.h"
#include "cc/input/main_thread_scrolling_reason.h"
#include "cc/input/scroll_elasticity_helper.h"
#include "cc/layers/layer.h"
@@ -42,11 +44,14 @@ using ::testing::Mock;
namespace cc {
namespace {
-std::unique_ptr<ScrollState> BeginState(const gfx::Point& point) {
+std::unique_ptr<ScrollState> BeginState(const gfx::Point& point,
+ const gfx::Vector2dF& delta_hint) {
ScrollStateData scroll_state_data;
scroll_state_data.is_beginning = true;
scroll_state_data.position_x = point.x();
scroll_state_data.position_y = point.y();
+ scroll_state_data.delta_x_hint = delta_hint.x();
+ scroll_state_data.delta_y_hint = delta_hint.y();
std::unique_ptr<ScrollState> scroll_state(new ScrollState(scroll_state_data));
return scroll_state;
}
@@ -685,8 +690,9 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest {
gfx::Point scroll_point = gfx::ToCeiledPoint(
gfx::PointF(-0.5f, -0.5f) +
GetTransformNode(expected_scroll_layer_impl)->post_translation);
- InputHandler::ScrollStatus status = impl->ScrollBegin(
- BeginState(scroll_point).get(), ui::ScrollInputType::kTouchscreen);
+ InputHandler::ScrollStatus status =
+ impl->ScrollBegin(BeginState(scroll_point, scroll_amount_).get(),
+ ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
impl->ScrollUpdate(UpdateState(gfx::Point(), scroll_amount_).get());
auto* scrolling_node = impl->CurrentlyScrollingNode();
@@ -708,8 +714,9 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest {
gfx::Point scroll_point = gfx::ToCeiledPoint(
gfx::PointF(0.5f, 0.5f) +
GetTransformNode(expected_scroll_layer_impl)->post_translation);
- InputHandler::ScrollStatus status = impl->ScrollBegin(
- BeginState(scroll_point).get(), ui::ScrollInputType::kWheel);
+ InputHandler::ScrollStatus status =
+ impl->ScrollBegin(BeginState(scroll_point, scroll_amount_).get(),
+ ui::ScrollInputType::kWheel);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
impl->ScrollUpdate(UpdateState(gfx::Point(), scroll_amount_).get());
impl->ScrollEnd();
@@ -1058,6 +1065,116 @@ class LayerTreeHostScrollTestImplOnlyScroll : public LayerTreeHostScrollTest {
// This tests scrolling on the impl side which is only possible with a thread.
MULTI_THREAD_TEST_F(LayerTreeHostScrollTestImplOnlyScroll);
+// TODO(crbug.com/574283): Mac currently doesn't support smooth scrolling wheel
+// events.
+#if !defined(OS_MACOSX)
+// This test simulates scrolling on the impl thread such that it starts a scroll
+// animation. It ensures that RequestScrollAnimationEndNotification() correctly
+// notifies the callback after the animation ends.
+class SmoothScrollAnimationEndNotification : public LayerTreeHostScrollTest {
+ public:
+ SmoothScrollAnimationEndNotification() = default;
+
+ void InitializeSettings(LayerTreeSettings* settings) override {
+ LayerTreeHostScrollTest::InitializeSettings(settings);
+ settings->enable_smooth_scroll = true;
+ }
+
+ void SetupTree() override {
+ LayerTreeHostScrollTest::SetupTree();
+
+ Layer* root_layer = layer_tree_host()->root_layer();
+ Layer* root_scroll_layer =
+ layer_tree_host()->OuterViewportScrollLayerForTesting();
+
+ child_layer_ = Layer::Create();
+ child_layer_->SetElementId(
+ LayerIdToElementIdForTesting(child_layer_->id()));
+ child_layer_->SetBounds(gfx::Size(110, 110));
+
+ child_layer_->SetIsDrawable(true);
+ child_layer_->SetHitTestable(true);
+ child_layer_->SetElementId(
+ LayerIdToElementIdForTesting(child_layer_->id()));
+ child_layer_->SetBounds(root_scroll_layer->bounds());
+ root_layer->AddChild(child_layer_);
+
+ CopyProperties(root_scroll_layer, child_layer_.get());
+ CreateTransformNode(child_layer_.get());
+ CreateScrollNode(child_layer_.get(), root_layer->bounds());
+ }
+
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+ void WillCommit() override {
+ // Keep the test committing (otherwise the early out for no update
+ // will stall the test).
+ if (layer_tree_host()->SourceFrameNumber() < 2) {
+ layer_tree_host()->SetNeedsCommit();
+ }
+ }
+
+ void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override {
+ if (host_impl->active_tree()->source_frame_number() < 0)
+ return;
+
+ if (host_impl->active_tree()->source_frame_number() == 0) {
+ const gfx::Point scroll_point(10, 10);
+ const gfx::Vector2dF scroll_amount(350, -350);
+ auto scroll_state = BeginState(scroll_point, scroll_amount);
+ scroll_state->data()->delta_granularity =
+ ui::ScrollGranularity::kScrollByPixel;
+ InputHandler::ScrollStatus status = host_impl->ScrollBegin(
+ scroll_state.get(), ui::ScrollInputType::kWheel);
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ scroll_state = UpdateState(scroll_point, scroll_amount);
+ scroll_state->data()->delta_granularity =
+ ui::ScrollGranularity::kScrollByPixel;
+ host_impl->ScrollUpdate(scroll_state.get());
+
+ EXPECT_TRUE(
+ !!host_impl->mutator_host()->ImplOnlyScrollAnimatingElement());
+ } else if (!scroll_end_requested_) {
+ host_impl->ScrollEnd(false);
+ scroll_end_requested_ = true;
+ }
+ PostSetNeedsCommitToMainThread();
+ }
+
+ void UpdateLayerTreeHost() override {
+ if (scroll_animation_started_)
+ return;
+
+ if (layer_tree_host()->HasCompositorDrivenScrollAnimationForTesting()) {
+ scroll_animation_started_ = true;
+ layer_tree_host()->RequestScrollAnimationEndNotification(
+ base::BindOnce(&SmoothScrollAnimationEndNotification::OnScrollEnd,
+ base::Unretained(this)));
+ }
+ }
+
+ void AfterTest() override {
+ EXPECT_TRUE(scroll_end_requested_);
+ EXPECT_TRUE(scroll_animation_started_);
+ EXPECT_TRUE(scroll_animation_ended_);
+ }
+
+ private:
+ void OnScrollEnd() {
+ scroll_animation_ended_ = true;
+ EndTest();
+ }
+
+ scoped_refptr<Layer> child_layer_;
+
+ bool scroll_end_requested_ = false;
+ bool scroll_animation_started_ = false;
+ bool scroll_animation_ended_ = false;
+};
+
+MULTI_THREAD_TEST_F(SmoothScrollAnimationEndNotification);
+#endif // !defined(OS_MACOSX)
+
void DoGestureScroll(LayerTreeHostImpl* host_impl,
const scoped_refptr<Layer>& scroller,
gfx::Vector2dF offset) {
@@ -1354,12 +1471,13 @@ class LayerTreeHostScrollTestScrollZeroMaxScrollOffset
void SetupTree() override {
LayerTreeHostScrollTest::SetupTree();
- // Add a sub-scroller to test TryScroll against. The outer viewport scroll
+ // Add a sub-scroller to test ScrollBegin against. The outer viewport scroll
// will be latched to for scrolling even if it doesn't have any scroll
// extent in the given direction to support overscroll actions.
scroller_ = Layer::Create();
scroller_->SetIsDrawable(true);
scroller_->SetHitTestable(true);
+ scroller_->SetBounds(gfx::Size(200, 200));
scroller_->SetElementId(LayerIdToElementIdForTesting(scroller_->id()));
CopyProperties(layer_tree_host()->OuterViewportScrollLayerForTesting(),
scroller_.get());
@@ -1369,61 +1487,91 @@ class LayerTreeHostScrollTestScrollZeroMaxScrollOffset
layer_tree_host()->root_layer()->AddChild(scroller_.get());
}
- void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+ void BeginTest() override { NextStep(); }
+
+ void NextStep() {
+ if (TestEnded())
+ return;
+
+ ++cur_step_;
- void UpdateLayerTreeHost() override {
ScrollTree& scroll_tree = layer_tree_host()->property_trees()->scroll_tree;
ScrollNode* scroll_node = scroll_tree.Node(scroller_->scroll_tree_index());
- switch (layer_tree_host()->SourceFrameNumber()) {
- case 0:
+ switch (cur_step_) {
+ case 1:
// Set max_scroll_offset = (100, 100).
scroll_node->bounds = scroll_node->container_bounds;
scroll_node->bounds.Enlarge(100, 100);
break;
- case 1:
+ case 2:
// Set max_scroll_offset = (0, 0).
scroll_node->bounds = scroll_node->container_bounds;
break;
- case 2:
+ case 3:
// Set max_scroll_offset = (-1, -1).
scroll_node->bounds = gfx::Size();
break;
}
+
+ layer_tree_host()->SetNeedsCommit();
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
+ if (TestEnded())
+ return;
+
ScrollTree& scroll_tree =
impl->active_tree()->property_trees()->scroll_tree;
ScrollNode* scroll_node = scroll_tree.Node(scroller_->scroll_tree_index());
+
+ ScrollStateData scroll_state_data;
+ scroll_state_data.is_beginning = true;
+ // The position has to be at (0, 0) since the viewport in this test has
+ // bounds (1, 1).
+ scroll_state_data.position_x = 0;
+ scroll_state_data.position_y = 0;
+ scroll_state_data.delta_x_hint = 10;
+ scroll_state_data.delta_y_hint = 10;
+ scroll_state_data.is_direct_manipulation = true;
+
+ ScrollState scroll_state(scroll_state_data);
InputHandler::ScrollStatus status =
- impl->TryScroll(scroll_tree, scroll_node);
- switch (impl->active_tree()->source_frame_number()) {
- case 0:
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread)
- << "In Frame 0";
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
- status.main_thread_scrolling_reasons)
- << "In Frame 0";
- PostSetNeedsCommitToMainThread();
- break;
+ impl->ScrollBegin(&scroll_state, ui::ScrollInputType::kTouchscreen);
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread)
+ << "In Frame " << impl->active_tree()->source_frame_number();
+
+ switch (cur_step_) {
case 1:
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread) << "In Frame 1";
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollable,
- status.main_thread_scrolling_reasons)
- << "In Frame 1";
- PostSetNeedsCommitToMainThread();
+ // Since the scroller has scroll extend and is scrollable, we should
+ // have targeted it.
+ EXPECT_EQ(scroll_node, impl->CurrentlyScrollingNode()) << "In Frame 0";
break;
case 2:
- EXPECT_EQ(InputHandler::SCROLL_IGNORED, status.thread) << "In Frame 2";
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollable,
- status.main_thread_scrolling_reasons)
+ // Since the max_scroll_offset is (0, 0) - we shouldn't target it and
+ // we should instead bubble up to the viewport.
+ EXPECT_EQ(impl->OuterViewportScrollNode(),
+ impl->CurrentlyScrollingNode())
+ << "In Frame 1";
+ break;
+ case 3:
+ // Since the max_scroll_offset is (-1, -1) - we shouldn't target it and
+ // we should instead bubble up to the viewport.
+ EXPECT_EQ(impl->OuterViewportScrollNode(),
+ impl->CurrentlyScrollingNode())
<< "In Frame 2";
EndTest();
break;
}
+ impl->ScrollEnd();
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &LayerTreeHostScrollTestScrollZeroMaxScrollOffset::NextStep,
+ base::Unretained(this)));
}
private:
+ int cur_step_ = 0;
scoped_refptr<Layer> scroller_;
};
@@ -1453,14 +1601,23 @@ class LayerTreeHostScrollTestScrollNonDrawnLayer
// checking whether the screen space point is inside the non-fast
// scrollable region.
InputHandler::ScrollStatus status = impl->ScrollBegin(
- BeginState(gfx::Point(0, 0)).get(), ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- status.main_thread_scrolling_reasons);
+ BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ impl->ScrollEnd();
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
- status = impl->ScrollBegin(BeginState(gfx::Point(21, 21)).get(),
- ui::ScrollInputType::kTouchscreen);
+ status = impl->ScrollBegin(
+ BeginState(gfx::Point(21, 21), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
status.main_thread_scrolling_reasons);
@@ -1481,36 +1638,88 @@ class LayerTreeHostScrollTestImplScrollUnderMainThreadScrollingParent
void SetupTree() override {
LayerTreeHostScrollTest::SetupTree();
- GetScrollNode(layer_tree_host()->InnerViewportScrollLayerForTesting())
+ GetScrollNode(layer_tree_host()->OuterViewportScrollLayerForTesting())
->main_thread_scrolling_reasons =
- MainThreadScrollingReason::kScrollbarScrolling;
+ MainThreadScrollingReason::kThreadedScrollingDisabled;
+
+ scroller_ = Layer::Create();
+ scroller_->SetIsDrawable(true);
+ scroller_->SetHitTestable(true);
+ scroller_->SetBounds(gfx::Size(200, 200));
+ scroller_->SetElementId(LayerIdToElementIdForTesting(scroller_->id()));
+ CopyProperties(layer_tree_host()->OuterViewportScrollLayerForTesting(),
+ scroller_.get());
+ CreateTransformNode(scroller_.get());
+ CreateScrollNode(scroller_.get(),
+ layer_tree_host()->root_layer()->bounds());
+ layer_tree_host()->root_layer()->AddChild(scroller_.get());
}
void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
- LayerImpl* inner_scroll_layer =
- impl->active_tree()->InnerViewportScrollLayerForTesting();
- LayerImpl* outer_scroll_layer =
- impl->active_tree()->OuterViewportScrollLayerForTesting();
-
ScrollTree& scroll_tree =
impl->active_tree()->property_trees()->scroll_tree;
- ScrollNode* inner_scroll_node =
- scroll_tree.Node(inner_scroll_layer->scroll_tree_index());
- ScrollNode* outer_scroll_node =
- scroll_tree.Node(outer_scroll_layer->scroll_tree_index());
+ ScrollNode* scroller_scroll_node =
+ scroll_tree.Node(scroller_->scroll_tree_index());
+
+ ScrollStateData scroll_state_data;
+ scroll_state_data.is_beginning = true;
+ // To hit the scroller, the position has to be at (0, 0) since the viewport
+ // in this test has bounds (1, 1) and would otherwise clip the hit test.
+ scroll_state_data.position_x = 0;
+ scroll_state_data.position_y = 0;
+ scroll_state_data.delta_x_hint = 10;
+ scroll_state_data.delta_y_hint = 10;
+ scroll_state_data.is_direct_manipulation = true;
+ ScrollState scroll_state(scroll_state_data);
+
+ // Scroll hitting the scroller layer.
+ {
+ InputHandler::ScrollStatus status =
+ impl->ScrollBegin(&scroll_state, ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(impl->CurrentlyScrollingNode(), scroller_scroll_node);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ } else {
+ // Despite the fact that we hit the scroller, which has no main thread
+ // scrolling reason, we still must fallback to main thread scrolling due
+ // to the fact that it has a main thread scrolling ancestor.
+ EXPECT_EQ(impl->CurrentlyScrollingNode(), nullptr);
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kThreadedScrollingDisabled,
+ status.main_thread_scrolling_reasons);
+ }
+ impl->ScrollEnd();
+ }
- InputHandler::ScrollStatus status =
- impl->TryScroll(scroll_tree, inner_scroll_node);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kScrollbarScrolling,
- status.main_thread_scrolling_reasons);
+ // Scroll hitting the viewport layer.
+ {
+ // A hit test outside the viewport should fallback to scrolling the
+ // viewport.
+ scroll_state.data()->position_y = 1000;
+
+ InputHandler::ScrollStatus status =
+ impl->ScrollBegin(&scroll_state, ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(impl->CurrentlyScrollingNode(),
+ impl->OuterViewportScrollNode());
+ } else {
+ // Since the viewport has a main thread scrolling reason, this
+ // too should fallback to the main thread.
+ EXPECT_EQ(impl->CurrentlyScrollingNode(), nullptr);
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kThreadedScrollingDisabled,
+ status.main_thread_scrolling_reasons);
+ }
+ impl->ScrollEnd();
+ }
- status = impl->TryScroll(scroll_tree, outer_scroll_node);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
- status.main_thread_scrolling_reasons);
EndTest();
}
+
+ private:
+ scoped_refptr<Layer> scroller_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
@@ -2422,27 +2631,28 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
void SetupTree() override {
SetInitialRootBounds(gfx::Size(800, 600));
LayerTreeHostScrollTest::SetupTree();
- Layer* root = layer_tree_host()->root_layer();
- fake_content_layer_client_.set_bounds(root->bounds());
+ Layer* outer_scroll =
+ layer_tree_host()->OuterViewportScrollLayerForTesting();
+ fake_content_layer_client_.set_bounds(outer_scroll->bounds());
bottom_ = FakePictureLayer::Create(&fake_content_layer_client_);
bottom_->SetElementId(LayerIdToElementIdForTesting(bottom_->id()));
bottom_->SetBounds(gfx::Size(100, 100));
bottom_->SetNonFastScrollableRegion(Region(gfx::Rect(50, 50, 50, 50)));
bottom_->SetHitTestable(true);
- CopyProperties(root, bottom_.get());
- root->AddChild(bottom_);
+ CopyProperties(outer_scroll, bottom_.get());
+ outer_scroll->AddChild(bottom_);
middle_scrollable_ = FakePictureLayer::Create(&fake_content_layer_client_);
middle_scrollable_->SetElementId(
LayerIdToElementIdForTesting(middle_scrollable_->id()));
- middle_scrollable_->SetBounds(gfx::Size(100, 100));
+ middle_scrollable_->SetBounds(gfx::Size(100, 200));
middle_scrollable_->SetIsDrawable(true);
middle_scrollable_->SetHitTestable(true);
CopyProperties(bottom_.get(), middle_scrollable_.get());
CreateTransformNode(middle_scrollable_.get());
- CreateScrollNode(middle_scrollable_.get(), gfx::Size(100, 200));
- root->AddChild(middle_scrollable_);
+ CreateScrollNode(middle_scrollable_.get(), gfx::Size(100, 100));
+ outer_scroll->AddChild(middle_scrollable_);
top_ = FakePictureLayer::Create(&fake_content_layer_client_);
top_->SetElementId(LayerIdToElementIdForTesting(top_->id()));
@@ -2450,7 +2660,7 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
top_->SetNonFastScrollableRegion(Region(gfx::Rect(0, 0, 50, 50)));
top_->SetHitTestable(true);
CopyProperties(middle_scrollable_.get(), top_.get());
- root->AddChild(top_);
+ outer_scroll->AddChild(top_);
}
void BeginTest() override { PostSetNeedsCommitToMainThread(); }
@@ -2459,35 +2669,70 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
if (TestEnded())
return;
- // The top-left hit should immediately hit the top layer's non-fast region
- // which forces main-thread scrolling.
- auto top_left_status =
- impl->ScrollBegin(BeginState(gfx::Point(20, 20)).get(),
- ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, top_left_status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- top_left_status.main_thread_scrolling_reasons);
-
- // The top-right hit should hit the top layer but not the non-fast region so
- // the scroll should continue to scroll on the impl.
- InputHandler::ScrollStatus top_right_status =
- impl->ScrollBegin(BeginState(gfx::Point(80, 20)).get(),
- ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, top_right_status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
- top_right_status.main_thread_scrolling_reasons);
- impl->ScrollEnd();
+ ScrollNode* scroll_node =
+ impl->active_tree()->property_trees()->scroll_tree.Node(
+ middle_scrollable_->scroll_tree_index());
- // The bottom-right should hit the bottom layer's non-fast region. Though
- // the middle layer is a composited scroller and is hit first, we cannot do
- // a fast scroll because an ancestor on the scroll chain has hit a non-fast
- // region.
- InputHandler::ScrollStatus bottom_right_status =
- impl->ScrollBegin(BeginState(gfx::Point(80, 80)).get(),
- ui::ScrollInputType::kTouchscreen);
- EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, bottom_right_status.thread);
- EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
- bottom_right_status.main_thread_scrolling_reasons);
+ // The top-left hit should immediately hit the top layer's non-fast region.
+ {
+ auto status = impl->ScrollBegin(
+ BeginState(gfx::Point(20, 20), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ // Hitting a non fast region should request a hit test from the main
+ // thread.
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_TRUE(status.needs_main_thread_hit_test);
+ impl->ScrollEnd();
+ } else {
+ // Prior to scroll unification, this forces scrolling to the main
+ // thread.
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
+ }
+
+ // The top-right hit should hit the top layer but not the non-fast region
+ // so the scroll can be handled without involving the main thread.
+ {
+ InputHandler::ScrollStatus status = impl->ScrollBegin(
+ BeginState(gfx::Point(80, 20), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ } else {
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain,
+ status.main_thread_scrolling_reasons);
+ }
+ EXPECT_EQ(scroll_node, impl->CurrentlyScrollingNode());
+ impl->ScrollEnd();
+ }
+
+ // The bottom-right should hit the bottom layer's non-fast region.
+ {
+ InputHandler::ScrollStatus status = impl->ScrollBegin(
+ BeginState(gfx::Point(80, 80), gfx::Vector2dF(0, 1)).get(),
+ ui::ScrollInputType::kTouchscreen);
+ if (base::FeatureList::IsEnabled(features::kScrollUnification)) {
+ // Even though the point intersects a non-fast region, the first hit
+ // layer is scrollable from the compositor thread so no need to involve
+ // the main thread.
+ EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ EXPECT_FALSE(status.needs_main_thread_hit_test);
+ EXPECT_EQ(scroll_node, impl->CurrentlyScrollingNode());
+ impl->ScrollEnd();
+ } else {
+ // Though the middle layer is a composited scroller and is hit first, we
+ // cannot do a fast scroll because an ancestor on the scroll chain has
+ // hit a non-fast region.
+ EXPECT_EQ(InputHandler::SCROLL_ON_MAIN_THREAD, status.thread);
+ EXPECT_EQ(MainThreadScrollingReason::kNonFastScrollableRegion,
+ status.main_thread_scrolling_reasons);
+ }
+ }
EndTest();
}
@@ -2501,5 +2746,70 @@ class NonScrollingNonFastScrollableRegion : public LayerTreeHostScrollTest {
SINGLE_THREAD_TEST_F(NonScrollingNonFastScrollableRegion);
+// This test verifies that scrolling in non layer list mode (used by UI
+// compositor) is always "compositor scrolled", i.e. property trees are mutated
+// and the updated layers redrawn. This test intentionally doesn't inherit
+// from LayerTreeHostScrollTest since that enables LayerLists.
+class UnifiedScrollingRepaintOnScroll : public LayerTreeTest {
+ public:
+ UnifiedScrollingRepaintOnScroll() {
+ scoped_feature_list.InitAndEnableFeature(features::kScrollUnification);
+ }
+
+ void BeginTest() override { PostSetNeedsCommitToMainThread(); }
+
+ void SetupTree() override {
+ LayerTreeTest::SetupTree();
+
+ layer_ = FakePictureLayer::Create(&client_);
+ layer_->SetScrollable(gfx::Size(10, 10));
+ layer_->SetBounds(gfx::Size(100, 100));
+ layer_->SetIsDrawable(true);
+ layer_->SetHitTestable(true);
+ layer_->SetElementId(LayerIdToElementIdForTesting(layer_->id()));
+ client_.set_bounds(layer_->bounds());
+ layer_tree_host()->root_layer()->AddChild(layer_);
+ }
+
+ void DrawLayersOnThread(LayerTreeHostImpl* impl) override {
+ if (is_done_)
+ return;
+ is_done_ = true;
+ EndTest();
+
+ TransformTree& transform_tree =
+ impl->active_tree()->property_trees()->transform_tree;
+ ASSERT_FALSE(transform_tree.needs_update());
+
+ // Perform a scroll over our FakePictureLayer.
+ {
+ InputHandler::ScrollStatus status = impl->ScrollBegin(
+ BeginState(gfx::Point(0, 0), gfx::Vector2dF(0, 10)).get(),
+ ui::ScrollInputType::kTouchscreen);
+
+ ASSERT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD, status.thread);
+ ASSERT_EQ(layer_->scroll_tree_index(),
+ impl->CurrentlyScrollingNode()->id);
+
+ impl->ScrollUpdate(
+ UpdateState(gfx::Point(), gfx::Vector2dF(0, 10)).get());
+ impl->ScrollEnd();
+ }
+
+ // All scrolling in non-layer-list mode (i.e. UI compositor) should be
+ // "compositor" scrolling so it should mutate the property tree and redraw,
+ // rather than relying on an update from the main thread.
+ ASSERT_TRUE(transform_tree.needs_update());
+ }
+
+ private:
+ bool is_done_ = false;
+ scoped_refptr<Layer> layer_;
+ FakeContentLayerClient client_;
+ base::test::ScopedFeatureList scoped_feature_list;
+};
+
+MULTI_THREAD_TEST_F(UnifiedScrollingRepaintOnScroll);
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc
index 600c987976c..7ab7bf797ef 100644
--- a/chromium/cc/trees/layer_tree_impl.cc
+++ b/chromium/cc/trees/layer_tree_impl.cc
@@ -10,7 +10,9 @@
#include <algorithm>
#include <iterator>
#include <limits>
+#include <memory>
#include <set>
+#include <utility>
#include "base/containers/adapters.h"
#include "base/debug/crash_logging.h"
@@ -24,6 +26,7 @@
#include "base/trace_event/trace_event.h"
#include "base/trace_event/traced_value.h"
#include "cc/base/devtools_instrumentation.h"
+#include "cc/base/features.h"
#include "cc/base/histograms.h"
#include "cc/base/math_util.h"
#include "cc/base/synced_property.h"
@@ -209,17 +212,38 @@ void LayerTreeImpl::DidUpdateScrollOffset(ElementId id) {
return;
}
+ // This bit controls whether we'll update the transform node based on a
+ // changed scroll offset. If scroll unification is off, we always do this
+ // because the scroll handling code will only invoke a scroll update on nodes
+ // that can compositor scroll. However, with scroll unification, we can
+ // mutate scroll nodes which have main thread scrolling reasons, or aren't
+ // backed by a layer at all. In those cases, we don't want to produce any
+ // immediate changes in the compositor, we want the scroll to propagate
+ // through Blink in a commit and have Blink update properties, paint,
+ // compositing, etc. Thus, we avoid mutating the transform tree in this case.
+ // TODO(bokan): We SetNeedsCommit in LTHI when a scroll happens but in a
+ // normal compositor scroll there isn't much urgency for a commit to be
+ // scheduled. We should look into what we can do to make sure this is
+ // proritized accordingly. https://crbug.com/1082618.
+ bool can_realize_scroll_on_compositor =
+ !base::FeatureList::IsEnabled(features::kScrollUnification) ||
+ (scroll_node->is_composited &&
+ !scroll_node->main_thread_scrolling_reasons);
+
DCHECK(scroll_node->transform_id != TransformTree::kInvalidNodeId);
TransformTree& transform_tree = property_trees()->transform_tree;
auto* transform_node = transform_tree.Node(scroll_node->transform_id);
- if (transform_node->scroll_offset != scroll_tree.current_scroll_offset(id)) {
- transform_node->scroll_offset = scroll_tree.current_scroll_offset(id);
- transform_node->needs_local_transform_update = true;
- transform_tree.set_needs_update(true);
+ if (can_realize_scroll_on_compositor) {
+ if (transform_node->scroll_offset !=
+ scroll_tree.current_scroll_offset(id)) {
+ transform_node->scroll_offset = scroll_tree.current_scroll_offset(id);
+ transform_node->needs_local_transform_update = true;
+ transform_tree.set_needs_update(true);
+ }
+ transform_node->transform_changed = true;
+ property_trees()->changed = true;
+ set_needs_update_draw_properties();
}
- transform_node->transform_changed = true;
- property_trees()->changed = true;
- set_needs_update_draw_properties();
if (IsActiveTree()) {
// Ensure the other trees are kept in sync.
@@ -419,6 +443,13 @@ void LayerTreeImpl::UpdateViewportContainerSizes() {
OuterViewportScrollNode()->container_bounds.height() +
scaled_bounds_delta.y();
outer_clip_node->clip.set_height(adjusted_container_height);
+
+ // Expand all clips between the outer viewport and the inner viewport.
+ auto* outer_ancestor = property_trees->clip_tree.parent(outer_clip_node);
+ while (outer_ancestor && outer_ancestor->id != ClipTree::kRootNodeId) {
+ outer_ancestor->clip.Union(outer_clip_node->clip);
+ outer_ancestor = property_trees->clip_tree.parent(outer_ancestor);
+ }
}
anchor.ResetViewportToAnchoredPosition();
@@ -477,6 +508,16 @@ OwnedLayerImplList LayerTreeImpl::DetachLayersKeepingRootLayerForTesting() {
}
void LayerTreeImpl::SetPropertyTrees(PropertyTrees* property_trees) {
+ // Updating the scroll tree shouldn't clobber the currently scrolling node so
+ // stash it and restore it at the end of this method. To maintain the
+ // current scrolling node we need to use element ids which are stable across
+ // the property tree update in SetPropertyTrees.
+ ElementId scrolling_element_id;
+ if (IsActiveTree()) {
+ if (ScrollNode* scrolling_node = CurrentlyScrollingNode())
+ scrolling_element_id = scrolling_node->element_id;
+ }
+
std::vector<std::unique_ptr<RenderSurfaceImpl>> old_render_surfaces;
property_trees_.effect_tree.TakeRenderSurfaces(&old_render_surfaces);
property_trees_ = *property_trees;
@@ -493,6 +534,13 @@ void LayerTreeImpl::SetPropertyTrees(PropertyTrees* property_trees) {
// effect tree.
if (IsActiveTree())
property_trees_.effect_tree.set_needs_update(true);
+
+ const ScrollNode* scrolling_node = nullptr;
+ if (scrolling_element_id) {
+ auto& scroll_tree = property_trees_.scroll_tree;
+ scrolling_node = scroll_tree.FindNodeFromElementId(scrolling_element_id);
+ }
+ SetCurrentlyScrollingNode(scrolling_node);
}
void LayerTreeImpl::PushPropertyTreesTo(LayerTreeImpl* target_tree) {
@@ -508,21 +556,8 @@ void LayerTreeImpl::PushPropertyTreesTo(LayerTreeImpl* target_tree) {
target_tree->MoveChangeTrackingToLayers();
}
- // To maintain the current scrolling node we need to use element ids which
- // are stable across the property tree update in SetPropertyTrees.
- ElementId scrolling_element_id;
- if (ScrollNode* scrolling_node = target_tree->CurrentlyScrollingNode())
- scrolling_element_id = scrolling_node->element_id;
-
target_tree->SetPropertyTrees(&property_trees_);
- const ScrollNode* scrolling_node = nullptr;
- if (scrolling_element_id) {
- auto& scroll_tree = target_tree->property_trees()->scroll_tree;
- scrolling_node = scroll_tree.FindNodeFromElementId(scrolling_element_id);
- }
- target_tree->SetCurrentlyScrollingNode(scrolling_node);
-
std::vector<EventMetrics> events_metrics;
events_metrics.swap(events_metrics_from_main_thread_);
target_tree->AppendEventsMetricsFromMainThread(std::move(events_metrics));
@@ -618,6 +653,13 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) {
target_tree->HandleScrollbarShowRequestsFromMain();
target_tree->AddPresentationCallbacks(std::move(presentation_callbacks_));
presentation_callbacks_.clear();
+
+ if (delegated_ink_metadata_) {
+ TRACE_EVENT_INSTANT1("cc", "Delegated ink metadata pushed to tree",
+ TRACE_EVENT_SCOPE_THREAD, "point",
+ delegated_ink_metadata_->point().ToString());
+ target_tree->set_delegated_ink_metadata(std::move(delegated_ink_metadata_));
+ }
}
void LayerTreeImpl::HandleTickmarksVisibilityChange() {
@@ -1581,12 +1623,8 @@ ImageAnimationController* LayerTreeImpl::image_animation_controller() const {
return host_impl_->image_animation_controller();
}
-FrameRateCounter* LayerTreeImpl::frame_rate_counter() const {
- return host_impl_->fps_counter();
-}
-
-base::Optional<int> LayerTreeImpl::current_universal_throughput() {
- return host_impl_->current_universal_throughput();
+DroppedFrameCounter* LayerTreeImpl::dropped_frame_counter() const {
+ return host_impl_->dropped_frame_counter();
}
MemoryHistory* LayerTreeImpl::memory_history() const {
@@ -1937,9 +1975,11 @@ void LayerTreeImpl::UnregisterScrollbar(
if (scrollbar_ids.horizontal == Layer::INVALID_ID &&
scrollbar_ids.vertical == Layer::INVALID_ID) {
element_id_to_scrollbar_layer_ids_.erase(scroll_element_id);
- if (IsActiveTree()) {
- host_impl_->DidUnregisterScrollbarLayer(scroll_element_id);
- }
+ }
+
+ if (IsActiveTree()) {
+ host_impl_->DidUnregisterScrollbarLayer(scroll_element_id,
+ scrollbar_layer->orientation());
}
}
@@ -2256,6 +2296,19 @@ LayerTreeImpl::FindLayersHitByPointInNonFastScrollableRegion(
return layers;
}
+bool LayerTreeImpl::PointHitsNonFastScrollableRegion(
+ const gfx::PointF& screen_space_point,
+ const LayerImpl& layer) const {
+ // We assume the layer has already been hit tested.
+ DCHECK(PointHitsLayer(&layer, screen_space_point, nullptr));
+
+ if (layer.non_fast_scrollable_region().IsEmpty())
+ return false;
+
+ return PointHitsRegion(screen_space_point, layer.ScreenSpaceTransform(),
+ layer.non_fast_scrollable_region(), &layer);
+}
+
struct HitTestFramedVisibleScrollableOrTouchableFunctor {
bool operator()(LayerImpl* layer) const {
return layer->HitTestable() && layer->frame_element_id();
diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h
index 5c2f586a0ea..d4583970c74 100644
--- a/chromium/cc/trees/layer_tree_impl.h
+++ b/chromium/cc/trees/layer_tree_impl.h
@@ -46,7 +46,7 @@ class ContextProvider;
namespace cc {
class DebugRectHistory;
-class FrameRateCounter;
+class DroppedFrameCounter;
class HeadsUpDisplayLayerImpl;
class ImageDecodeCache;
class LayerTreeDebugState;
@@ -127,8 +127,7 @@ class CC_EXPORT LayerTreeImpl {
TileManager* tile_manager() const;
ImageDecodeCache* image_decode_cache() const;
ImageAnimationController* image_animation_controller() const;
- FrameRateCounter* frame_rate_counter() const;
- base::Optional<int> current_universal_throughput();
+ DroppedFrameCounter* dropped_frame_counter() const;
MemoryHistory* memory_history() const;
DebugRectHistory* debug_rect_history() const;
bool IsActiveTree() const;
@@ -591,6 +590,8 @@ class CC_EXPORT LayerTreeImpl {
// Return all layers with a hit non-fast scrollable region.
std::vector<const LayerImpl*> FindLayersHitByPointInNonFastScrollableRegion(
const gfx::PointF& screen_space_point);
+ bool PointHitsNonFastScrollableRegion(const gfx::PointF& scree_space_point,
+ const LayerImpl& layer) const;
// Returns the ElementId representing a frame's document at the given point.
// In cases where cc doesn't have enough information to perform accurate
@@ -710,6 +711,18 @@ class CC_EXPORT LayerTreeImpl {
return host_impl_->DrawTransform();
}
+ // These functions are used for plumbing DelegatedInkMetadata from blink
+ // through the compositor and into viz via a compositor frame. They should
+ // only be called after the JS API |updateInkTrailStartPoint| has been
+ // called, which populates the metadata with provided information.
+ void set_delegated_ink_metadata(
+ std::unique_ptr<viz::DelegatedInkMetadata> metadata) {
+ delegated_ink_metadata_ = std::move(metadata);
+ }
+ std::unique_ptr<viz::DelegatedInkMetadata> take_delegated_ink_metadata() {
+ return std::move(delegated_ink_metadata_);
+ }
+
protected:
float ClampPageScaleFactorToLimits(float page_scale_factor) const;
void PushPageScaleFactorAndLimits(const float* page_scale_factor,
@@ -861,6 +874,8 @@ class CC_EXPORT LayerTreeImpl {
// Event metrics that are reported back from the main thread.
std::vector<EventMetrics> events_metrics_from_main_thread_;
+
+ std::unique_ptr<viz::DelegatedInkMetadata> delegated_ink_metadata_;
};
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h
index 95a901678c2..446d46764c5 100644
--- a/chromium/cc/trees/layer_tree_settings.h
+++ b/chromium/cc/trees/layer_tree_settings.h
@@ -187,10 +187,18 @@ class CC_EXPORT LayerTreeSettings {
// Whether experimental de-jelly effect is allowed.
bool allow_de_jelly_effect = false;
+ // Whether the compositor should attempt to sync with the scroll handlers
+ // before submitting a frame.
+ bool enable_synchronized_scrolling = true;
+
#if DCHECK_IS_ON()
// Whether to check if any double blur exists.
bool log_on_ui_double_background_blur = false;
#endif
+
+ // When enabled, enforces new interoperable semantics for 3D transforms.
+ // See crbug.com/1008483.
+ bool enable_transform_interop = false;
};
class CC_EXPORT LayerListSettings : public LayerTreeSettings {
diff --git a/chromium/cc/trees/mutator_host.h b/chromium/cc/trees/mutator_host.h
index 1292e0ec826..e3cf671314b 100644
--- a/chromium/cc/trees/mutator_host.h
+++ b/chromium/cc/trees/mutator_host.h
@@ -160,7 +160,6 @@ class MutatorHost {
// the scroller. Otherwise returns an invalid ElementId.
virtual ElementId ImplOnlyScrollAnimatingElement() const = 0;
- virtual size_t CompositedAnimationsCount() const = 0;
virtual size_t MainThreadAnimationsCount() const = 0;
virtual bool HasCustomPropertyAnimations() const = 0;
virtual bool CurrentFrameHadRAF() const = 0;
diff --git a/chromium/cc/trees/occlusion_tracker.h b/chromium/cc/trees/occlusion_tracker.h
index d2498676dec..c734beff463 100644
--- a/chromium/cc/trees/occlusion_tracker.h
+++ b/chromium/cc/trees/occlusion_tracker.h
@@ -59,7 +59,7 @@ class CC_EXPORT OcclusionTracker {
protected:
struct StackObject {
- StackObject() : target(0) {}
+ StackObject() : target(nullptr) {}
explicit StackObject(const RenderSurfaceImpl* target) : target(target) {}
const RenderSurfaceImpl* target;
SimpleEnclosedRegion occlusion_from_outside_target;
diff --git a/chromium/cc/trees/property_tree.cc b/chromium/cc/trees/property_tree.cc
index cbdb9d43e73..00ce2f2e65d 100644
--- a/chromium/cc/trees/property_tree.cc
+++ b/chromium/cc/trees/property_tree.cc
@@ -493,24 +493,27 @@ void TransformTree::UpdateSnapping(TransformNode* node) {
// rounded, then what we're after is the scroll delta X, where ST * X = ST'.
// I.e., we want a transform that will realize our snap. It follows that
// X = ST^-1 * ST'. We cache ST and ST^-1 to make this more efficient.
- gfx::Transform rounded = ToScreen(node->id);
- rounded.RoundTranslationComponents();
- gfx::Transform delta = FromScreen(node->id);
- delta *= rounded;
-
- DCHECK(delta.IsApproximatelyIdentityOrTranslation(SkDoubleToScalar(1e-4)))
+ DCHECK_LT(node->id, static_cast<int>(cached_data_.size()));
+ gfx::Transform& to_screen = cached_data_[node->id].to_screen;
+ to_screen.RoundTranslationComponents();
+ gfx::Transform& from_screen = cached_data_[node->id].from_screen;
+ gfx::Transform delta = from_screen;
+ delta *= to_screen;
+
+ constexpr float kTolerance = 1e-4f;
+ DCHECK(delta.IsApproximatelyIdentityOrTranslation(kTolerance))
<< delta.ToString();
gfx::Vector2dF translation = delta.To2dTranslation();
+ node->snap_amount = translation;
+ if (translation.IsZero())
+ return;
- // Now that we have our delta, we must apply it to each of our combined,
- // to/from matrices.
- SetToScreen(node->id, rounded);
- node->to_parent.Translate(translation.x(), translation.y());
- gfx::Transform from_screen = FromScreen(node->id);
from_screen.matrix().postTranslate(-translation.x(), -translation.y(), 0);
- SetFromScreen(node->id, from_screen);
- node->snap_amount = translation;
+ node->to_parent.Translate(translation.x(), translation.y());
+ // Avoid accumulation of errors in to_parent.
+ if (node->to_parent.IsApproximatelyIdentityOrIntegerTranslation(kTolerance))
+ node->to_parent.RoundTranslationComponents();
}
void TransformTree::UpdateTransformChanged(TransformNode* node,
@@ -745,6 +748,17 @@ void EffectTree::UpdateHasMaskingChild(EffectNode* node,
}
}
+void EffectTree::UpdateOnlyDrawsVisibleContent(EffectNode* node,
+ EffectNode* parent_node) {
+ node->only_draws_visible_content = !node->has_copy_request;
+ if (parent_node)
+ node->only_draws_visible_content &= parent_node->only_draws_visible_content;
+ if (!node->backdrop_filters.IsEmpty()) {
+ node->only_draws_visible_content &=
+ !node->backdrop_filters.HasFilterOfType(FilterOperation::ZOOM);
+ }
+}
+
void EffectTree::UpdateSurfaceContentsScale(EffectNode* effect_node) {
if (!effect_node->HasRenderSurface()) {
effect_node->surface_contents_scale = gfx::Vector2dF(1.0f, 1.0f);
@@ -829,6 +843,7 @@ void EffectTree::UpdateEffects(int id) {
UpdateEffectChanged(node, parent_node);
UpdateBackfaceVisibility(node, parent_node);
UpdateHasMaskingChild(node, parent_node);
+ UpdateOnlyDrawsVisibleContent(node, parent_node);
UpdateSurfaceContentsScale(node);
}
@@ -1273,6 +1288,8 @@ void ScrollTree::OnScrollOffsetAnimated(ElementId id,
if (!property_trees()->is_active)
return;
+ TRACE_EVENT2("cc", "ScrollTree::OnScrollOffsetAnimated", "x",
+ scroll_offset.x(), "y", scroll_offset.y());
ScrollNode* scroll_node = Node(scroll_tree_index);
if (SetScrollOffset(id,
ClampScrollOffsetToLimits(scroll_offset, *scroll_node)))
@@ -1570,6 +1587,9 @@ void ScrollTree::SetBaseScrollOffset(ElementId id,
bool ScrollTree::SetScrollOffset(ElementId id,
const gfx::ScrollOffset& scroll_offset) {
+ // TODO(crbug.com/1087088): Remove TRACE_EVENT call when the bug is fixed
+ TRACE_EVENT2("cc", "ScrollTree::SetScrollOffset", "x", scroll_offset.x(), "y",
+ scroll_offset.y());
if (property_trees()->is_main_thread) {
if (scroll_offset_map_[id] == scroll_offset)
return false;
diff --git a/chromium/cc/trees/property_tree.h b/chromium/cc/trees/property_tree.h
index 4dcf7d7a0bd..6c41097cc7a 100644
--- a/chromium/cc/trees/property_tree.h
+++ b/chromium/cc/trees/property_tree.h
@@ -356,6 +356,7 @@ class CC_EXPORT EffectTree final : public PropertyTree<EffectNode> {
void UpdateIsDrawn(EffectNode* node, EffectNode* parent_node);
void UpdateBackfaceVisibility(EffectNode* node, EffectNode* parent_node);
void UpdateHasMaskingChild(EffectNode* node, EffectNode* parent_node);
+ void UpdateOnlyDrawsVisibleContent(EffectNode* node, EffectNode* parent_node);
// Stores copy requests, keyed by node id.
std::unordered_multimap<int, std::unique_ptr<viz::CopyOutputRequest>>
diff --git a/chromium/cc/trees/property_tree_builder.cc b/chromium/cc/trees/property_tree_builder.cc
index b036be21cbd..fcf85d88d2d 100644
--- a/chromium/cc/trees/property_tree_builder.cc
+++ b/chromium/cc/trees/property_tree_builder.cc
@@ -612,6 +612,7 @@ void PropertyTreeBuilderContext::AddScrollNodeIfNeeded(
node.user_scrollable_vertical = layer->GetUserScrollableVertical();
node.element_id = layer->element_id();
node.transform_id = data_for_children->transform_tree_parent;
+ node.is_composited = true;
node_id = scroll_tree_.Insert(node, parent_id);
data_for_children->scroll_tree_parent = node_id;
diff --git a/chromium/cc/trees/property_tree_builder_unittest.cc b/chromium/cc/trees/property_tree_builder_unittest.cc
index af760bc828a..183c3342de0 100644
--- a/chromium/cc/trees/property_tree_builder_unittest.cc
+++ b/chromium/cc/trees/property_tree_builder_unittest.cc
@@ -201,7 +201,7 @@ TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForTransparentChild) {
EXPECT_EQ(1U, GetRenderSurfaceList().size());
EXPECT_EQ(static_cast<viz::RenderPassId>(root->id()),
GetRenderSurfaceList().at(0)->id());
- EXPECT_EQ(gfx::Rect(), ImplOf(root)->drawable_content_rect());
+ EXPECT_EQ(gfx::Rect(), ImplOf(root)->visible_drawable_content_rect());
}
TEST_F(PropertyTreeBuilderTest,
diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h
index 0fe7a90fe3f..9afdb987bfc 100644
--- a/chromium/cc/trees/proxy.h
+++ b/chromium/cc/trees/proxy.h
@@ -8,7 +8,6 @@
#include <memory>
#include <string>
-#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
diff --git a/chromium/cc/trees/proxy_impl.cc b/chromium/cc/trees/proxy_impl.cc
index 544ad6392e3..24e222ffbe0 100644
--- a/chromium/cc/trees/proxy_impl.cc
+++ b/chromium/cc/trees/proxy_impl.cc
@@ -370,26 +370,10 @@ void ProxyImpl::SetVideoNeedsBeginFrames(bool needs_begin_frames) {
scheduler_->SetVideoNeedsBeginFrames(needs_begin_frames);
}
-size_t ProxyImpl::CompositedAnimationsCount() const {
- return host_impl_->mutator_host()->CompositedAnimationsCount();
-}
-
-size_t ProxyImpl::MainThreadAnimationsCount() const {
- return host_impl_->mutator_host()->MainThreadAnimationsCount();
-}
-
bool ProxyImpl::HasCustomPropertyAnimations() const {
return host_impl_->mutator_host()->HasCustomPropertyAnimations();
}
-bool ProxyImpl::CurrentFrameHadRAF() const {
- return host_impl_->mutator_host()->CurrentFrameHadRAF();
-}
-
-bool ProxyImpl::NextFrameHasPendingRAF() const {
- return host_impl_->mutator_host()->NextFrameHasPendingRAF();
-}
-
bool ProxyImpl::IsInsideDraw() {
return inside_draw_;
}
@@ -542,14 +526,37 @@ void ProxyImpl::NotifyThroughputTrackerResults(CustomTrackerResults results) {
proxy_main_weak_ptr_, std::move(results)));
}
+void ProxyImpl::SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ DCHECK(IsImplThread());
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&ProxyMain::SubmitThroughputData, proxy_main_weak_ptr_,
+ source_id, aggregated_percent, impl_percent,
+ main_percent));
+}
+
+void ProxyImpl::DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) {
+ DCHECK(IsImplThread());
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE, base::BindOnce(&ProxyMain::DidObserveFirstScrollDelay,
+ proxy_main_weak_ptr_, first_scroll_delay,
+ first_scroll_timestamp));
+}
+
bool ProxyImpl::WillBeginImplFrame(const viz::BeginFrameArgs& args) {
DCHECK(IsImplThread());
return host_impl_->WillBeginImplFrame(args);
}
-void ProxyImpl::DidFinishImplFrame() {
+void ProxyImpl::DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) {
DCHECK(IsImplThread());
- host_impl_->DidFinishImplFrame(scheduler_->last_activate_origin_frame_args());
+ host_impl_->DidFinishImplFrame(last_activated_args);
}
void ProxyImpl::DidNotProduceFrame(const viz::BeginFrameAck& ack,
@@ -703,9 +710,6 @@ DrawResult ProxyImpl::DrawInternal(bool forced_draw) {
base::AutoReset<bool> mark_inside(&inside_draw_, true);
- if (host_impl_->pending_tree())
- host_impl_->pending_tree()->UpdateDrawProperties();
-
// This method is called on a forced draw, regardless of whether we are able
// to produce a frame, as the calling site on main thread is blocked until its
// request completes, and we signal completion here. If CanDraw() is false, we
@@ -756,6 +760,12 @@ DrawResult ProxyImpl::DrawInternal(bool forced_draw) {
proxy_main_weak_ptr_));
}
+ // The tile visibility/priority of the pending tree needs to be updated so
+ // that it doesn't get activated before the raster is complete. But this needs
+ // to happen after the draw, off of the critical path to draw.
+ if (host_impl_->pending_tree())
+ host_impl_->pending_tree()->UpdateDrawProperties();
+
DCHECK_NE(INVALID_RESULT, result);
return result;
}
diff --git a/chromium/cc/trees/proxy_impl.h b/chromium/cc/trees/proxy_impl.h
index 3050199e43d..dbe9b20d3ba 100644
--- a/chromium/cc/trees/proxy_impl.h
+++ b/chromium/cc/trees/proxy_impl.h
@@ -119,10 +119,18 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient,
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override;
void NotifyThroughputTrackerResults(CustomTrackerResults results) override;
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) override;
+ void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) override;
// SchedulerClient implementation
bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override;
- void DidFinishImplFrame() override;
+ void DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) override;
void DidNotProduceFrame(const viz::BeginFrameAck& ack,
FrameSkippedReason reason) override;
void WillNotReceiveBeginFrame() override;
@@ -140,11 +148,7 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient,
void ScheduledActionBeginMainFrameNotExpectedUntil(
base::TimeTicks time) override;
void FrameIntervalUpdated(base::TimeDelta interval) override {}
- size_t CompositedAnimationsCount() const override;
- size_t MainThreadAnimationsCount() const override;
bool HasCustomPropertyAnimations() const override;
- bool CurrentFrameHadRAF() const override;
- bool NextFrameHasPendingRAF() const override;
DrawResult DrawInternal(bool forced_draw);
diff --git a/chromium/cc/trees/proxy_main.cc b/chromium/cc/trees/proxy_main.cc
index 22976f62b02..1ad9795cd11 100644
--- a/chromium/cc/trees/proxy_main.cc
+++ b/chromium/cc/trees/proxy_main.cc
@@ -378,6 +378,22 @@ void ProxyMain::NotifyThroughputTrackerResults(CustomTrackerResults results) {
layer_tree_host_->NotifyThroughputTrackerResults(std::move(results));
}
+void ProxyMain::SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) {
+ DCHECK(!task_runner_provider_->IsImplThread());
+ layer_tree_host_->SubmitThroughputData(source_id, aggregated_percent,
+ impl_percent, main_percent);
+}
+
+void ProxyMain::DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) {
+ layer_tree_host_->DidObserveFirstScrollDelay(first_scroll_delay,
+ first_scroll_timestamp);
+}
+
bool ProxyMain::IsStarted() const {
DCHECK(IsMainThread());
return started_;
diff --git a/chromium/cc/trees/proxy_main.h b/chromium/cc/trees/proxy_main.h
index 8738cb1f38f..050af82306b 100644
--- a/chromium/cc/trees/proxy_main.h
+++ b/chromium/cc/trees/proxy_main.h
@@ -58,6 +58,12 @@ class CC_EXPORT ProxyMain : public Proxy {
std::vector<LayerTreeHost::PresentationTimeCallback> callbacks,
const gfx::PresentationFeedback& feedback);
void NotifyThroughputTrackerResults(CustomTrackerResults results);
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent);
+ void DidObserveFirstScrollDelay(base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp);
CommitPipelineStage max_requested_pipeline_stage() const {
return max_requested_pipeline_stage_;
diff --git a/chromium/cc/trees/render_frame_metadata.h b/chromium/cc/trees/render_frame_metadata.h
index 43af9536c47..2b884785a9d 100644
--- a/chromium/cc/trees/render_frame_metadata.h
+++ b/chromium/cc/trees/render_frame_metadata.h
@@ -41,8 +41,7 @@ class CC_EXPORT RenderFrameMetadata {
// specified.
SkColor root_background_color = SK_ColorWHITE;
- // Scroll offset of the root layer. This optional parameter is only valid
- // during tests.
+ // Scroll offset of the root layer.
base::Optional<gfx::Vector2dF> root_scroll_offset;
// Selection region relative to the current viewport. If the selection is
@@ -56,6 +55,11 @@ class CC_EXPORT RenderFrameMetadata {
// are the same).
bool is_mobile_optimized = false;
+ // Flag used to notify the browser process to start or stop forwarding points
+ // to viz for use in a delegated ink trail. True the entire time points should
+ // be forwarded, and forwarding stops as soon as it is false again.
+ bool has_delegated_ink_metadata = false;
+
// The device scale factor used to generate a CompositorFrame.
float device_scale_factor = 1.f;
diff --git a/chromium/cc/trees/scroll_and_scale_set.h b/chromium/cc/trees/scroll_and_scale_set.h
index cef0850f33b..b7e9d9ed91a 100644
--- a/chromium/cc/trees/scroll_and_scale_set.h
+++ b/chromium/cc/trees/scroll_and_scale_set.h
@@ -97,6 +97,10 @@ struct CC_EXPORT ScrollAndScaleSet {
// ended.
bool scroll_gesture_did_end;
+ // Tracks whether there is an ongoing compositor-driven animation for a
+ // scroll.
+ bool ongoing_scroll_animation = false;
+
// Tracks different methods of scrolling (e.g. wheel, touch, precision
// touchpad, etc.).
ManipulationInfo manipulation_info;
diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc
index 6f469a42c4e..c5449d5e85a 100644
--- a/chromium/cc/trees/single_thread_proxy.cc
+++ b/chromium/cc/trees/single_thread_proxy.cc
@@ -415,26 +415,10 @@ void SingleThreadProxy::SetVideoNeedsBeginFrames(bool needs_begin_frames) {
scheduler_on_impl_thread_->SetVideoNeedsBeginFrames(needs_begin_frames);
}
-size_t SingleThreadProxy::CompositedAnimationsCount() const {
- return 0;
-}
-
-size_t SingleThreadProxy::MainThreadAnimationsCount() const {
- return 0;
-}
-
bool SingleThreadProxy::HasCustomPropertyAnimations() const {
return false;
}
-bool SingleThreadProxy::CurrentFrameHadRAF() const {
- return false;
-}
-
-bool SingleThreadProxy::NextFrameHasPendingRAF() const {
- return false;
-}
-
bool SingleThreadProxy::IsInsideDraw() {
return inside_draw_;
}
@@ -635,7 +619,7 @@ void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time,
// another draw will never be scheduled, so break remaining promises.
host_impl_->active_tree()->BreakSwapPromises(SwapPromise::SWAP_FAILS);
- DidFinishImplFrame();
+ DidFinishImplFrame(begin_frame_args);
}
}
@@ -958,9 +942,9 @@ void SingleThreadProxy::ScheduledActionPerformImplSideInvalidation() {
NotifyReadyToActivate();
}
-void SingleThreadProxy::DidFinishImplFrame() {
- host_impl_->DidFinishImplFrame(
- scheduler_on_impl_thread_->last_activate_origin_frame_args());
+void SingleThreadProxy::DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) {
+ host_impl_->DidFinishImplFrame(last_activated_args);
#if DCHECK_IS_ON()
DCHECK(inside_impl_frame_)
<< "DidFinishImplFrame called while not inside an impl frame!";
diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h
index 96228af593c..ee1915c5222 100644
--- a/chromium/cc/trees/single_thread_proxy.h
+++ b/chromium/cc/trees/single_thread_proxy.h
@@ -77,7 +77,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
// SchedulerClient implementation
bool WillBeginImplFrame(const viz::BeginFrameArgs& args) override;
- void DidFinishImplFrame() override;
+ void DidFinishImplFrame(
+ const viz::BeginFrameArgs& last_activated_args) override;
void DidNotProduceFrame(const viz::BeginFrameAck& ack,
FrameSkippedReason reason) override;
void WillNotReceiveBeginFrame() override;
@@ -95,11 +96,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void ScheduledActionBeginMainFrameNotExpectedUntil(
base::TimeTicks time) override;
void FrameIntervalUpdated(base::TimeDelta interval) override;
- size_t CompositedAnimationsCount() const override;
- size_t MainThreadAnimationsCount() const override;
bool HasCustomPropertyAnimations() const override;
- bool CurrentFrameHadRAF() const override;
- bool NextFrameHasPendingRAF() const override;
// LayerTreeHostImplClient implementation
void DidLoseLayerTreeFrameSinkOnImplThread() override;
@@ -137,9 +134,21 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void NotifyPaintWorkletStateChange(
Scheduler::PaintWorkletState state) override;
void NotifyThroughputTrackerResults(CustomTrackerResults results) override;
+ void SubmitThroughputData(ukm::SourceId source_id,
+ int aggregated_percent,
+ int impl_percent,
+ base::Optional<int> main_percent) override {}
void RequestNewLayerTreeFrameSink();
+ void DidObserveFirstScrollDelay(
+ base::TimeDelta first_scroll_delay,
+ base::TimeTicks first_scroll_timestamp) override {
+ // Single-threaded mode is only for browser compositing and for renderers in
+ // layout tests. This will still get called in the latter case, but we don't
+ // need to record UKM in that case.
+ }
+
// Called by the legacy path where RenderWidget does the scheduling.
// Rasterization of tiles is only performed when |raster| is true.
void CompositeImmediately(base::TimeTicks frame_begin_time, bool raster);
diff --git a/chromium/cc/trees/task_runner_provider.h b/chromium/cc/trees/task_runner_provider.h
index 0bc19d31f38..232bc6da0f6 100644
--- a/chromium/cc/trees/task_runner_provider.h
+++ b/chromium/cc/trees/task_runner_provider.h
@@ -8,7 +8,7 @@
#include <memory>
#include <string>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
diff --git a/chromium/cc/trees/transform_node.cc b/chromium/cc/trees/transform_node.cc
index 780bba5ec67..b19c820baab 100644
--- a/chromium/cc/trees/transform_node.cc
+++ b/chromium/cc/trees/transform_node.cc
@@ -32,6 +32,7 @@ TransformNode::TransformNode()
moved_by_outer_viewport_bounds_delta_y(false),
in_subtree_of_page_scale_layer(false),
transform_changed(false),
+ delegates_to_parent_for_backface(false),
maximum_animation_scale(kNotScaled),
starting_animation_scale(kNotScaled) {}
@@ -62,6 +63,8 @@ bool TransformNode::operator==(const TransformNode& other) const {
other.moved_by_outer_viewport_bounds_delta_y &&
in_subtree_of_page_scale_layer ==
other.in_subtree_of_page_scale_layer &&
+ delegates_to_parent_for_backface ==
+ other.delegates_to_parent_for_backface &&
transform_changed == other.transform_changed &&
scroll_offset == other.scroll_offset &&
snap_amount == other.snap_amount &&
diff --git a/chromium/cc/trees/transform_node.h b/chromium/cc/trees/transform_node.h
index 401af01e617..47bb059ea1c 100644
--- a/chromium/cc/trees/transform_node.h
+++ b/chromium/cc/trees/transform_node.h
@@ -105,6 +105,10 @@ struct CC_EXPORT TransformNode {
// We need to track changes to to_screen transform to compute the damage rect.
bool transform_changed : 1;
+ // Whether the parent transform node should be used for checking backface
+ // visibility, not this transform one.
+ bool delegates_to_parent_for_backface : 1;
+
gfx::ScrollOffset scroll_offset;
// This value stores the snapped amount whenever we snap. If the snap is due
diff --git a/chromium/cc/trees/ukm_manager.cc b/chromium/cc/trees/ukm_manager.cc
index 2141cf1a21c..9d4eab1315f 100644
--- a/chromium/cc/trees/ukm_manager.cc
+++ b/chromium/cc/trees/ukm_manager.cc
@@ -4,7 +4,12 @@
#include "cc/trees/ukm_manager.h"
+#include <algorithm>
+#include <utility>
+
+#include "cc/metrics/compositor_frame_reporter.h"
#include "cc/metrics/throughput_ukm_reporter.h"
+#include "components/viz/common/quads/compositor_frame.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
@@ -187,11 +192,13 @@ void UkmManager::RecordAggregateThroughput(AggregationType aggregation_type,
builder.Record(recorder_.get());
}
-void UkmManager::RecordLatencyUKM(
+void UkmManager::RecordCompositorLatencyUKM(
CompositorFrameReporter::FrameReportType report_type,
const std::vector<CompositorFrameReporter::StageData>& stage_history,
const CompositorFrameReporter::ActiveTrackers& active_trackers,
const viz::FrameTimingDetails& viz_breakdown) const {
+ using StageType = CompositorFrameReporter::StageType;
+
ukm::builders::Graphics_Smoothness_Latency builder(source_id_);
if (report_type == CompositorFrameReporter::FrameReportType::kDroppedFrame) {
@@ -202,7 +209,7 @@ void UkmManager::RecordLatencyUKM(
for (const CompositorFrameReporter::StageData& stage : stage_history) {
switch (stage.stage_type) {
#define CASE_FOR_STAGE(name) \
- case CompositorFrameReporter::StageType::k##name: \
+ case StageType::k##name: \
builder.Set##name((stage.end_time - stage.start_time).InMicroseconds()); \
break;
CASE_FOR_STAGE(BeginImplFrameToSendBeginMainFrame);
@@ -215,8 +222,7 @@ void UkmManager::RecordLatencyUKM(
#undef CASE_FOR_STAGE
// Break out kSubmitCompositorFrameToPresentationCompositorFrame to report
// the viz breakdown.
- case CompositorFrameReporter::StageType::
- kSubmitCompositorFrameToPresentationCompositorFrame:
+ case StageType::kSubmitCompositorFrameToPresentationCompositorFrame:
builder.SetSubmitCompositorFrameToPresentationCompositorFrame(
(stage.end_time - stage.start_time).InMicroseconds());
if (viz_breakdown.received_compositor_frame_timestamp.is_null())
@@ -287,4 +293,80 @@ void UkmManager::RecordLatencyUKM(
builder.Record(recorder_.get());
}
+void UkmManager::RecordEventLatencyUKM(
+ const std::vector<EventMetrics>& events_metrics,
+ const std::vector<CompositorFrameReporter::StageData>& stage_history,
+ const viz::FrameTimingDetails& viz_breakdown) const {
+ using StageType = CompositorFrameReporter::StageType;
+
+ for (const EventMetrics& event_metrics : events_metrics) {
+ ukm::builders::Graphics_Smoothness_EventLatency builder(source_id_);
+
+ builder.SetEventType(static_cast<int64_t>(event_metrics.type()));
+
+ if (event_metrics.scroll_type()) {
+ builder.SetScrollInputType(
+ static_cast<int64_t>(*event_metrics.scroll_type()));
+
+ if (!viz_breakdown.swap_timings.is_null()) {
+ builder.SetTotalLatencyToSwapEnd(
+ (viz_breakdown.swap_timings.swap_end - event_metrics.time_stamp())
+ .InMicroseconds());
+ }
+ }
+
+ // It is possible for an event to arrive in the compositor in the middle of
+ // a frame (e.g. the browser received the event *after* renderer received a
+ // begin-impl, and the event reached the compositor before that frame
+ // ended). To handle such cases, find the first stage that happens after the
+ // event's arrival in the browser.
+ auto stage_it = std::find_if(
+ stage_history.begin(), stage_history.end(),
+ [&event_metrics](const CompositorFrameReporter::StageData& stage) {
+ return stage.start_time > event_metrics.time_stamp();
+ });
+ // TODO(crbug.com/1079116): Ideally, at least the start time of
+ // SubmitCompositorFrameToPresentationCompositorFrame stage should be
+ // greater than the event time stamp, but apparently, this is not always the
+ // case (see crbug.com/1093698). For now, skip to the next event in such
+ // cases. Hopefully, the work to reduce discrepancies between the new
+ // EventLatency and the old Event.Latency metrics would fix this issue. If
+ // not, we need to reconsider investigating this issue.
+ if (stage_it == stage_history.end())
+ continue;
+
+ builder.SetBrowserToRendererCompositor(
+ (stage_it->start_time - event_metrics.time_stamp()).InMicroseconds());
+
+ for (; stage_it != stage_history.end(); ++stage_it) {
+ // Total latency is calculated since the event timestamp.
+ const base::TimeTicks start_time =
+ stage_it->stage_type == StageType::kTotalLatency
+ ? event_metrics.time_stamp()
+ : stage_it->start_time;
+
+ switch (stage_it->stage_type) {
+#define CASE_FOR_STAGE(name) \
+ case StageType::k##name: \
+ builder.Set##name((stage_it->end_time - start_time).InMicroseconds()); \
+ break;
+ CASE_FOR_STAGE(BeginImplFrameToSendBeginMainFrame);
+ CASE_FOR_STAGE(SendBeginMainFrameToCommit);
+ CASE_FOR_STAGE(Commit);
+ CASE_FOR_STAGE(EndCommitToActivation);
+ CASE_FOR_STAGE(Activation);
+ CASE_FOR_STAGE(EndActivateToSubmitCompositorFrame);
+ CASE_FOR_STAGE(SubmitCompositorFrameToPresentationCompositorFrame);
+ CASE_FOR_STAGE(TotalLatency);
+#undef CASE_FOR_STAGE
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ builder.Record(recorder_.get());
+ }
+}
+
} // namespace cc
diff --git a/chromium/cc/trees/ukm_manager.h b/chromium/cc/trees/ukm_manager.h
index 50653814f85..6a2cc1c03db 100644
--- a/chromium/cc/trees/ukm_manager.h
+++ b/chromium/cc/trees/ukm_manager.h
@@ -5,9 +5,14 @@
#ifndef CC_TREES_UKM_MANAGER_H_
#define CC_TREES_UKM_MANAGER_H_
+#include <memory>
+#include <vector>
+
#include "cc/cc_export.h"
#include "cc/metrics/compositor_frame_reporter.h"
+#include "cc/metrics/event_metrics.h"
#include "cc/metrics/frame_sequence_metrics.h"
+#include "components/viz/common/frame_timing_details.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "url/gurl.h"
@@ -33,6 +38,7 @@ class CC_EXPORT UkmManager {
~UkmManager();
void SetSourceId(ukm::SourceId source_id);
+ ukm::SourceId source_id() const { return source_id_; }
// These metrics are recorded while a user interaction is in progress.
void SetUserInteractionInProgress(bool in_progress);
@@ -48,12 +54,17 @@ class CC_EXPORT UkmManager {
int64_t throughput) const;
void RecordAggregateThroughput(AggregationType aggregation_type,
int64_t throughput_percent) const;
- void RecordLatencyUKM(
+ void RecordCompositorLatencyUKM(
CompositorFrameReporter::FrameReportType report_type,
const std::vector<CompositorFrameReporter::StageData>& stage_history,
const CompositorFrameReporter::ActiveTrackers& active_trackers,
const viz::FrameTimingDetails& viz_breakdown) const;
+ void RecordEventLatencyUKM(
+ const std::vector<EventMetrics>& events_metrics,
+ const std::vector<CompositorFrameReporter::StageData>& stage_history,
+ const viz::FrameTimingDetails& viz_breakdown) const;
+
ukm::UkmRecorder* recorder_for_testing() { return recorder_.get(); }
private:
diff --git a/chromium/cc/trees/ukm_manager_unittest.cc b/chromium/cc/trees/ukm_manager_unittest.cc
index 9f4dd926f5b..2b55162d45f 100644
--- a/chromium/cc/trees/ukm_manager_unittest.cc
+++ b/chromium/cc/trees/ukm_manager_unittest.cc
@@ -4,7 +4,14 @@
#include "cc/trees/ukm_manager.h"
+#include <vector>
+
+#include "base/time/time.h"
+#include "cc/metrics/compositor_frame_reporter.h"
+#include "cc/metrics/event_metrics.h"
#include "components/ukm/test_ukm_recorder.h"
+#include "components/viz/common/frame_timing_details.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
@@ -23,6 +30,55 @@ const char kCheckerboardAreaRatio[] = "CheckerboardedContentAreaRatio";
const char kMissingTiles[] = "NumMissingTiles";
const char kCheckerboardedImagesCount[] = "CheckerboardedImagesCount";
+// Names of compositor/event latency UKM events.
+const char kCompositorLatency[] = "Graphics.Smoothness.Latency";
+const char kEventLatency[] = "Graphics.Smoothness.EventLatency";
+
+// Names of enum metrics used in compositor/event latency UKM metrics.
+const char kMissedFrame[] = "MissedFrame";
+const char kEventType[] = "EventType";
+const char kScrollInputType[] = "ScrollInputType";
+
+// Names of compositor stages and substages used in compositor/event latency UKM
+// metrics.
+const char kBrowserToRendererCompositor[] = "BrowserToRendererCompositor";
+const char kBeginImplFrameToSendBeginMainFrame[] =
+ "BeginImplFrameToSendBeginMainFrame";
+const char kSendBeginMainFrameToCommit[] = "SendBeginMainFrameToCommit";
+const char kCommit[] = "Commit";
+const char kEndCommitToActivation[] = "EndCommitToActivation";
+const char kActivation[] = "Activation";
+const char kEndActivateToSubmitCompositorFrame[] =
+ "EndActivateToSubmitCompositorFrame";
+const char kSubmitCompositorFrameToPresentationCompositorFrame[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame";
+const char kVizBreakdownSubmitToReceiveCompositorFrame[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame."
+ "SubmitToReceiveCompositorFrame";
+const char kVizBreakdownReceivedCompositorFrameToStartDraw[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame."
+ "ReceivedCompositorFrameToStartDraw";
+const char kVizBreakdownStartDrawToSwapStart[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame.StartDrawToSwapStart";
+const char kVizBreakdownSwapStartToSwapEnd[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame.SwapStartToSwapEnd";
+const char kVizBreakdownSwapEndToPresentationCompositorFrame[] =
+ "SubmitCompositorFrameToPresentationCompositorFrame."
+ "SwapEndToPresentationCompositorFrame";
+const char kTotalLatencyToSwapEnd[] = "TotalLatencyToSwapEnd";
+const char kTotalLatency[] = "TotalLatency";
+
+// Names of frame sequence types use in compositor latency UKM metrics (see
+// FrameSequenceTrackerType enum).
+const char kCompositorAnimation[] = "CompositorAnimation";
+const char kMainThreadAnimation[] = "MainThreadAnimation";
+const char kPinchZoom[] = "PinchZoom";
+const char kRAF[] = "RAF";
+const char kTouchScroll[] = "TouchScroll";
+const char kUniversal[] = "Universal";
+const char kVideo[] = "Video";
+const char kWheelScroll[] = "WheelScroll";
+
class UkmManagerTest : public testing::Test {
public:
UkmManagerTest() {
@@ -36,6 +92,8 @@ class UkmManagerTest : public testing::Test {
manager_->SetSourceId(kTestSourceId1);
}
+ ~UkmManagerTest() override = default;
+
protected:
ukm::TestUkmRecorder* test_ukm_recorder_;
std::unique_ptr<UkmManager> manager_;
@@ -99,5 +157,300 @@ TEST_F(UkmManagerTest, Basic) {
}
}
+class UkmManagerCompositorLatencyTest
+ : public UkmManagerTest,
+ public testing::WithParamInterface<
+ CompositorFrameReporter::FrameReportType> {
+ public:
+ UkmManagerCompositorLatencyTest() : report_type_(GetParam()) {}
+ ~UkmManagerCompositorLatencyTest() override = default;
+
+ protected:
+ CompositorFrameReporter::FrameReportType report_type() const {
+ return report_type_;
+ }
+
+ private:
+ CompositorFrameReporter::FrameReportType report_type_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ All,
+ UkmManagerCompositorLatencyTest,
+ testing::Values(
+ CompositorFrameReporter::FrameReportType::kNonDroppedFrame,
+ CompositorFrameReporter::FrameReportType::kMissedDeadlineFrame,
+ CompositorFrameReporter::FrameReportType::kDroppedFrame,
+ CompositorFrameReporter::FrameReportType::kCompositorOnlyFrame));
+
+TEST_P(UkmManagerCompositorLatencyTest, CompositorLatency) {
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ const base::TimeTicks begin_impl_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks begin_main_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks begin_commit_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks end_commit_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks begin_activate_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks end_activate_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks submit_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+
+ viz::FrameTimingDetails viz_breakdown;
+ viz_breakdown.received_compositor_frame_timestamp =
+ (now += base::TimeDelta::FromMicroseconds(1));
+ viz_breakdown.draw_start_timestamp =
+ (now += base::TimeDelta::FromMicroseconds(2));
+ viz_breakdown.swap_timings.swap_start =
+ (now += base::TimeDelta::FromMicroseconds(3));
+ viz_breakdown.swap_timings.swap_end =
+ (now += base::TimeDelta::FromMicroseconds(4));
+ viz_breakdown.presentation_feedback.timestamp =
+ (now += base::TimeDelta::FromMicroseconds(5));
+
+ std::vector<CompositorFrameReporter::StageData> stage_history = {
+ {
+ CompositorFrameReporter::StageType::
+ kBeginImplFrameToSendBeginMainFrame,
+ begin_impl_time,
+ begin_main_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit,
+ begin_main_time,
+ begin_commit_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kCommit,
+ begin_commit_time,
+ end_commit_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kEndCommitToActivation,
+ end_commit_time,
+ begin_activate_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kActivation,
+ begin_activate_time,
+ end_activate_time,
+ },
+ {
+ CompositorFrameReporter::StageType::
+ kEndActivateToSubmitCompositorFrame,
+ end_activate_time,
+ submit_time,
+ },
+ {
+ CompositorFrameReporter::StageType::
+ kSubmitCompositorFrameToPresentationCompositorFrame,
+ submit_time,
+ viz_breakdown.presentation_feedback.timestamp,
+ },
+ {
+ CompositorFrameReporter::StageType::kTotalLatency,
+ begin_impl_time,
+ viz_breakdown.presentation_feedback.timestamp,
+ },
+ };
+
+ CompositorFrameReporter::ActiveTrackers active_trackers;
+ active_trackers.set(
+ static_cast<size_t>(FrameSequenceTrackerType::kTouchScroll));
+ active_trackers.set(
+ static_cast<size_t>(FrameSequenceTrackerType::kCompositorAnimation));
+ active_trackers.set(
+ static_cast<size_t>(FrameSequenceTrackerType::kUniversal));
+
+ manager_->RecordCompositorLatencyUKM(report_type(), stage_history,
+ active_trackers, viz_breakdown);
+
+ const auto& entries =
+ test_ukm_recorder_->GetEntriesByName(kCompositorLatency);
+ EXPECT_EQ(1u, entries.size());
+ const auto* entry = entries[0];
+
+ EXPECT_NE(ukm::kInvalidSourceId, entry->source_id);
+ test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestUrl));
+
+ if (report_type() ==
+ CompositorFrameReporter::FrameReportType::kDroppedFrame) {
+ test_ukm_recorder_->ExpectEntryMetric(entry, kMissedFrame, true);
+ } else {
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kMissedFrame));
+ }
+
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kBeginImplFrameToSendBeginMainFrame,
+ (begin_main_time - begin_impl_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kSendBeginMainFrameToCommit,
+ (begin_commit_time - begin_main_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kCommit, (end_commit_time - begin_commit_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kEndCommitToActivation,
+ (begin_activate_time - end_commit_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kActivation,
+ (end_activate_time - begin_activate_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kEndActivateToSubmitCompositorFrame,
+ (submit_time - end_activate_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kSubmitCompositorFrameToPresentationCompositorFrame,
+ (viz_breakdown.presentation_feedback.timestamp - submit_time)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kVizBreakdownSubmitToReceiveCompositorFrame,
+ (viz_breakdown.received_compositor_frame_timestamp - submit_time)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kVizBreakdownReceivedCompositorFrameToStartDraw,
+ (viz_breakdown.draw_start_timestamp -
+ viz_breakdown.received_compositor_frame_timestamp)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(entry,
+ kVizBreakdownStartDrawToSwapStart,
+ (viz_breakdown.swap_timings.swap_start -
+ viz_breakdown.draw_start_timestamp)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(entry, kVizBreakdownSwapStartToSwapEnd,
+ (viz_breakdown.swap_timings.swap_end -
+ viz_breakdown.swap_timings.swap_start)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kVizBreakdownSwapEndToPresentationCompositorFrame,
+ (viz_breakdown.presentation_feedback.timestamp -
+ viz_breakdown.swap_timings.swap_end)
+ .InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kTotalLatency,
+ (viz_breakdown.presentation_feedback.timestamp - begin_impl_time)
+ .InMicroseconds());
+
+ test_ukm_recorder_->ExpectEntryMetric(entry, kCompositorAnimation, true);
+ test_ukm_recorder_->ExpectEntryMetric(entry, kTouchScroll, true);
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kMainThreadAnimation));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kPinchZoom));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kRAF));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kUniversal));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kVideo));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kWheelScroll));
+}
+
+TEST_F(UkmManagerTest, EventLatency) {
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ const base::TimeTicks event_time = now;
+ std::unique_ptr<EventMetrics> event_metrics_ptrs[] = {
+ EventMetrics::Create(ui::ET_GESTURE_SCROLL_BEGIN, event_time,
+ ui::ScrollInputType::kWheel),
+ EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, event_time,
+ ui::ScrollInputType::kWheel),
+ EventMetrics::Create(ui::ET_GESTURE_SCROLL_UPDATE, event_time,
+ ui::ScrollInputType::kWheel),
+ };
+ EXPECT_THAT(event_metrics_ptrs, ::testing::Each(::testing::NotNull()));
+ std::vector<EventMetrics> events_metrics = {
+ *event_metrics_ptrs[0], *event_metrics_ptrs[1], *event_metrics_ptrs[2]};
+
+ const base::TimeTicks begin_impl_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks end_activate_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+ const base::TimeTicks submit_time =
+ (now += base::TimeDelta::FromMicroseconds(10));
+
+ viz::FrameTimingDetails viz_breakdown;
+ viz_breakdown.received_compositor_frame_timestamp =
+ (now += base::TimeDelta::FromMicroseconds(1));
+ viz_breakdown.draw_start_timestamp =
+ (now += base::TimeDelta::FromMicroseconds(2));
+ viz_breakdown.swap_timings.swap_start =
+ (now += base::TimeDelta::FromMicroseconds(3));
+ viz_breakdown.swap_timings.swap_end =
+ (now += base::TimeDelta::FromMicroseconds(4));
+ viz_breakdown.presentation_feedback.timestamp =
+ (now += base::TimeDelta::FromMicroseconds(5));
+
+ const base::TimeTicks swap_end_time = viz_breakdown.swap_timings.swap_end;
+ const base::TimeTicks present_time =
+ viz_breakdown.presentation_feedback.timestamp;
+
+ std::vector<CompositorFrameReporter::StageData> stage_history = {
+ {
+ CompositorFrameReporter::StageType::
+ kBeginImplFrameToSendBeginMainFrame,
+ begin_impl_time,
+ end_activate_time,
+ },
+ {
+ CompositorFrameReporter::StageType::
+ kEndActivateToSubmitCompositorFrame,
+ end_activate_time,
+ submit_time,
+ },
+ {
+ CompositorFrameReporter::StageType::
+ kSubmitCompositorFrameToPresentationCompositorFrame,
+ submit_time,
+ present_time,
+ },
+ {
+ CompositorFrameReporter::StageType::kTotalLatency,
+ event_time,
+ present_time,
+ },
+ };
+
+ manager_->RecordEventLatencyUKM(events_metrics, stage_history, viz_breakdown);
+
+ const auto& entries = test_ukm_recorder_->GetEntriesByName(kEventLatency);
+ EXPECT_EQ(3u, entries.size());
+ for (size_t i = 0; i < entries.size(); i++) {
+ const auto* entry = entries[i];
+ const auto* event_metrics = event_metrics_ptrs[i].get();
+
+ EXPECT_NE(ukm::kInvalidSourceId, entry->source_id);
+ test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kTestUrl));
+
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kEventType, static_cast<int64_t>(event_metrics->type()));
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kScrollInputType,
+ static_cast<int64_t>(*event_metrics->scroll_type()));
+
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kBrowserToRendererCompositor,
+ (begin_impl_time - event_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kBeginImplFrameToSendBeginMainFrame,
+ (end_activate_time - begin_impl_time).InMicroseconds());
+ EXPECT_FALSE(
+ test_ukm_recorder_->EntryHasMetric(entry, kSendBeginMainFrameToCommit));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kCommit));
+ EXPECT_FALSE(
+ test_ukm_recorder_->EntryHasMetric(entry, kEndCommitToActivation));
+ EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(entry, kActivation));
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kEndActivateToSubmitCompositorFrame,
+ (submit_time - end_activate_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kSubmitCompositorFrameToPresentationCompositorFrame,
+ (present_time - submit_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kTotalLatencyToSwapEnd,
+ (swap_end_time - event_time).InMicroseconds());
+ test_ukm_recorder_->ExpectEntryMetric(
+ entry, kTotalLatency, (present_time - event_time).InMicroseconds());
+ }
+}
+
} // namespace
} // namespace cc