// Copyright 2013 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/layers/layer_position_constraint.h" #include #include "cc/layers/layer_impl.h" #include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/test_shared_bitmap_manager.h" #include "cc/test/test_task_graph_runner.h" #include "cc/trees/layer_tree_host_common.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { namespace { void SetLayerPropertiesForTesting(LayerImpl* layer, const gfx::Transform& transform, const gfx::Point3F& transform_origin, const gfx::PointF& position, const gfx::Size& bounds, bool flatten_transform) { layer->SetTransform(transform); layer->SetTransformOrigin(transform_origin); layer->SetPosition(position); layer->SetBounds(bounds); layer->SetShouldFlattenTransform(flatten_transform); layer->SetContentBounds(bounds); } void ExecuteCalculateDrawProperties(LayerImpl* root_layer, float device_scale_factor, float page_scale_factor, LayerImpl* page_scale_application_layer, bool can_use_lcd_text) { gfx::Transform identity_matrix; std::vector dummy_render_surface_layer_list; LayerImpl* scroll_layer = root_layer->children()[0]; gfx::Size device_viewport_size = gfx::Size(root_layer->bounds().width() * device_scale_factor, root_layer->bounds().height() * device_scale_factor); // We are probably not testing what is intended if the scroll_layer bounds are // empty. DCHECK(!scroll_layer->bounds().IsEmpty()); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root_layer, device_viewport_size, &dummy_render_surface_layer_list); inputs.device_scale_factor = device_scale_factor; inputs.page_scale_factor = page_scale_factor; inputs.page_scale_application_layer = page_scale_application_layer; inputs.can_use_lcd_text = can_use_lcd_text; LayerTreeHostCommon::CalculateDrawProperties(&inputs); } void ExecuteCalculateDrawProperties(LayerImpl* root_layer) { LayerImpl* page_scale_application_layer = nullptr; ExecuteCalculateDrawProperties( root_layer, 1.f, 1.f, page_scale_application_layer, false); } class LayerPositionConstraintTest : public testing::Test { public: LayerPositionConstraintTest() : host_impl_(&proxy_, &shared_bitmap_manager_, &task_graph_runner_) { root_ = CreateTreeForTest(); scroll_ = root_->children()[0]; fixed_to_top_left_.set_is_fixed_position(true); fixed_to_bottom_right_.set_is_fixed_position(true); fixed_to_bottom_right_.set_is_fixed_to_right_edge(true); fixed_to_bottom_right_.set_is_fixed_to_bottom_edge(true); } scoped_ptr CreateTreeForTest() { scoped_ptr root = LayerImpl::Create(host_impl_.active_tree(), 42); scoped_ptr scroll_layer = LayerImpl::Create(host_impl_.active_tree(), 1); scoped_ptr child = LayerImpl::Create(host_impl_.active_tree(), 2); scoped_ptr grand_child = LayerImpl::Create(host_impl_.active_tree(), 3); scoped_ptr great_grand_child = LayerImpl::Create(host_impl_.active_tree(), 4); root->SetHasRenderSurface(true); gfx::Transform IdentityMatrix; gfx::Point3F transform_origin; gfx::PointF position; gfx::Size bounds(200, 200); gfx::Size clip_bounds(100, 100); SetLayerPropertiesForTesting(scroll_layer.get(), IdentityMatrix, transform_origin, position, bounds, true); SetLayerPropertiesForTesting( child.get(), IdentityMatrix, transform_origin, position, bounds, true); SetLayerPropertiesForTesting(grand_child.get(), IdentityMatrix, transform_origin, position, bounds, true); SetLayerPropertiesForTesting(great_grand_child.get(), IdentityMatrix, transform_origin, position, bounds, true); root->SetBounds(clip_bounds); scroll_layer->SetScrollClipLayer(root->id()); child->SetScrollClipLayer(root->id()); grand_child->SetScrollClipLayer(root->id()); grand_child->AddChild(great_grand_child.Pass()); child->AddChild(grand_child.Pass()); scroll_layer->AddChild(child.Pass()); root->AddChild(scroll_layer.Pass()); return root.Pass(); } protected: FakeImplProxy proxy_; TestSharedBitmapManager shared_bitmap_manager_; TestTaskGraphRunner task_graph_runner_; FakeLayerTreeHostImpl host_impl_; scoped_ptr root_; LayerImpl* scroll_; LayerPositionConstraint fixed_to_top_left_; LayerPositionConstraint fixed_to_bottom_right_; }; namespace { void SetFixedContainerSizeDelta(LayerImpl* scroll_layer, const gfx::Vector2d& delta) { DCHECK(scroll_layer); DCHECK(scroll_layer->scrollable()); LayerImpl* container_layer = scroll_layer->scroll_clip_layer(); container_layer->SetBoundsDelta(delta); } } // namespace TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionLayerWithDirectContainer) { // This test checks for correct scroll compensation when the fixed-position // container is the direct parent of the fixed-position layer. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; child->SetIsContainerForFixedPositionLayers(true); grand_child->SetPositionConstraint(fixed_to_top_left_); // Case 1: scroll delta of 0, 0 child->SetScrollDelta(gfx::Vector2d(0, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_child_transform; gfx::Transform expected_grand_child_transform = expected_child_transform; EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 2: scroll delta of 10, 10 child->SetScrollDelta(gfx::Vector2d(10, 10)); ExecuteCalculateDrawProperties(root_.get()); // Here the child is affected by scroll delta, but the fixed position // grand_child should not be affected. expected_child_transform.MakeIdentity(); expected_child_transform.Translate(-10.0, -10.0); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 3: fixed-container size delta of 20, 20 SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); // Top-left fixed-position layer should not be affected by container size. EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 4: Bottom-right fixed-position layer. grand_child->SetPositionConstraint(fixed_to_bottom_right_); ExecuteCalculateDrawProperties(root_.get()); // Bottom-right fixed-position layer moves as container resizes. expected_grand_child_transform.MakeIdentity(); // Apply size delta from the child(container) layer. expected_grand_child_transform.Translate(20.0, 20.0); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionLayerWithTransformedDirectContainer) { // This test checks for correct scroll compensation when the fixed-position // container is the direct parent of the fixed-position layer, but that // container is transformed. In this case, the fixed position element // inherits the container's transform, but the scroll delta that has to be // undone should not be affected by that transform. // // Transforms are in general non-commutative; using something like a // non-uniform scale helps to verify that translations and non-uniform scales // are applied in the correct order. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; // This scale will cause child and grand_child to be effectively 200 x 800 // with respect to the render target. gfx::Transform non_uniform_scale; non_uniform_scale.Scale(2.0, 8.0); child->SetTransform(non_uniform_scale); child->SetIsContainerForFixedPositionLayers(true); grand_child->SetPositionConstraint(fixed_to_top_left_); // Case 1: scroll delta of 0, 0 child->SetScrollDelta(gfx::Vector2d(0, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_child_transform; expected_child_transform.PreconcatTransform(non_uniform_scale); gfx::Transform expected_grand_child_transform = expected_child_transform; EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 2: scroll delta of 10, 20 child->SetScrollDelta(gfx::Vector2d(10, 20)); ExecuteCalculateDrawProperties(root_.get()); // The child should be affected by scroll delta, but the fixed position // grand_child should not be affected. expected_child_transform.MakeIdentity(); expected_child_transform.Translate(-10.0, -20.0); // scroll delta expected_child_transform.PreconcatTransform(non_uniform_scale); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 3: fixed-container size delta of 20, 20 SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); // Top-left fixed-position layer should not be affected by container size. EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 4: Bottom-right fixed-position layer. grand_child->SetPositionConstraint(fixed_to_bottom_right_); ExecuteCalculateDrawProperties(root_.get()); // Bottom-right fixed-position layer moves as container resizes. expected_grand_child_transform.MakeIdentity(); // Apply child layer transform. expected_grand_child_transform.PreconcatTransform(non_uniform_scale); // Apply size delta from the child(container) layer. expected_grand_child_transform.Translate(20.0, 20.0); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionLayerWithDistantContainer) { // This test checks for correct scroll compensation when the fixed-position // container is NOT the direct parent of the fixed-position layer. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; LayerImpl* great_grand_child = grand_child->children()[0]; child->SetIsContainerForFixedPositionLayers(true); grand_child->SetPosition(gfx::PointF(8.f, 6.f)); great_grand_child->SetPositionConstraint(fixed_to_top_left_); // Case 1: scroll delta of 0, 0 child->SetScrollDelta(gfx::Vector2d(0, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_child_transform; gfx::Transform expected_grand_child_transform; expected_grand_child_transform.Translate(8.0, 6.0); gfx::Transform expected_great_grand_child_transform = expected_grand_child_transform; EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 2: scroll delta of 10, 10 child->SetScrollDelta(gfx::Vector2d(10, 10)); ExecuteCalculateDrawProperties(root_.get()); // Here the child and grand_child are affected by scroll delta, but the fixed // position great_grand_child should not be affected. expected_child_transform.MakeIdentity(); expected_child_transform.Translate(-10.0, -10.0); expected_grand_child_transform.MakeIdentity(); expected_grand_child_transform.Translate(-2.0, -4.0); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 3: fixed-container size delta of 20, 20 SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); // Top-left fixed-position layer should not be affected by container size. EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 4: Bottom-right fixed-position layer. great_grand_child->SetPositionConstraint(fixed_to_bottom_right_); ExecuteCalculateDrawProperties(root_.get()); // Bottom-right fixed-position layer moves as container resizes. expected_great_grand_child_transform.MakeIdentity(); // Apply size delta from the child(container) layer. expected_great_grand_child_transform.Translate(20.0, 20.0); // Apply layer position from the grand child layer. expected_great_grand_child_transform.Translate(8.0, 6.0); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionLayerWithDistantContainerAndTransforms) { // This test checks for correct scroll compensation when the fixed-position // container is NOT the direct parent of the fixed-position layer, and the // hierarchy has various transforms that have to be processed in the correct // order. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; LayerImpl* great_grand_child = grand_child->children()[0]; gfx::Transform rotation_about_z; rotation_about_z.RotateAboutZAxis(90.0); child->SetIsContainerForFixedPositionLayers(true); child->SetTransform(rotation_about_z); grand_child->SetPosition(gfx::PointF(8.f, 6.f)); grand_child->SetTransform(rotation_about_z); // great_grand_child is positioned upside-down with respect to the render // target. great_grand_child->SetPositionConstraint(fixed_to_top_left_); // Case 1: scroll delta of 0, 0 child->SetScrollDelta(gfx::Vector2d(0, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_child_transform; expected_child_transform.PreconcatTransform(rotation_about_z); gfx::Transform expected_grand_child_transform; expected_grand_child_transform.PreconcatTransform( rotation_about_z); // child's local transform is inherited // translation because of position occurs before layer's local transform. expected_grand_child_transform.Translate(8.0, 6.0); expected_grand_child_transform.PreconcatTransform( rotation_about_z); // grand_child's local transform gfx::Transform expected_great_grand_child_transform = expected_grand_child_transform; EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 2: scroll delta of 10, 20 child->SetScrollDelta(gfx::Vector2d(10, 20)); ExecuteCalculateDrawProperties(root_.get()); // Here the child and grand_child are affected by scroll delta, but the fixed // position great_grand_child should not be affected. expected_child_transform.MakeIdentity(); expected_child_transform.Translate(-10.0, -20.0); // scroll delta expected_child_transform.PreconcatTransform(rotation_about_z); expected_grand_child_transform.MakeIdentity(); expected_grand_child_transform.Translate( -10.0, -20.0); // child's scroll delta is inherited expected_grand_child_transform.PreconcatTransform( rotation_about_z); // child's local transform is inherited // translation because of position occurs before layer's local transform. expected_grand_child_transform.Translate(8.0, 6.0); expected_grand_child_transform.PreconcatTransform( rotation_about_z); // grand_child's local transform EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 3: fixed-container size delta of 20, 20 SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); // Top-left fixed-position layer should not be affected by container size. EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 4: Bottom-right fixed-position layer. great_grand_child->SetPositionConstraint(fixed_to_bottom_right_); ExecuteCalculateDrawProperties(root_.get()); // Bottom-right fixed-position layer moves as container resizes. expected_great_grand_child_transform.MakeIdentity(); // Apply child layer transform. expected_great_grand_child_transform.PreconcatTransform(rotation_about_z); // Apply size delta from the child(container) layer. expected_great_grand_child_transform.Translate(20.0, 20.0); // Apply layer position from the grand child layer. expected_great_grand_child_transform.Translate(8.0, 6.0); // Apply grand child layer transform. expected_great_grand_child_transform.PreconcatTransform(rotation_about_z); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionLayerWithMultipleScrollDeltas) { // This test checks for correct scroll compensation when the fixed-position // container has multiple ancestors that have nonzero scroll delta before // reaching the space where the layer is fixed. In this test, each scroll // delta occurs in a different space because of each layer's local transform. // This test checks for correct scroll compensation when the fixed-position // container is NOT the direct parent of the fixed-position layer, and the // hierarchy has various transforms that have to be processed in the correct // order. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; LayerImpl* great_grand_child = grand_child->children()[0]; gfx::Transform rotation_about_z; rotation_about_z.RotateAboutZAxis(90.0); child->SetIsContainerForFixedPositionLayers(true); child->SetTransform(rotation_about_z); grand_child->SetPosition(gfx::PointF(8.f, 6.f)); grand_child->SetTransform(rotation_about_z); // great_grand_child is positioned upside-down with respect to the render // target. great_grand_child->SetPositionConstraint(fixed_to_top_left_); // Case 1: scroll delta of 0, 0 child->SetScrollDelta(gfx::Vector2d(0, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_child_transform; expected_child_transform.PreconcatTransform(rotation_about_z); gfx::Transform expected_grand_child_transform; expected_grand_child_transform.PreconcatTransform( rotation_about_z); // child's local transform is inherited // translation because of position occurs before layer's local transform. expected_grand_child_transform.Translate(8.0, 6.0); expected_grand_child_transform.PreconcatTransform( rotation_about_z); // grand_child's local transform gfx::Transform expected_great_grand_child_transform = expected_grand_child_transform; EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 2: scroll delta of 10, 20 child->SetScrollDelta(gfx::Vector2d(10, 0)); grand_child->SetScrollDelta(gfx::Vector2d(5, 0)); ExecuteCalculateDrawProperties(root_.get()); // Here the child and grand_child are affected by scroll delta, but the fixed // position great_grand_child should not be affected. expected_child_transform.MakeIdentity(); expected_child_transform.Translate(-10.0, 0.0); // scroll delta expected_child_transform.PreconcatTransform(rotation_about_z); expected_grand_child_transform.MakeIdentity(); expected_grand_child_transform.Translate( -10.0, 0.0); // child's scroll delta is inherited expected_grand_child_transform.PreconcatTransform( rotation_about_z); // child's local transform is inherited expected_grand_child_transform.Translate(-5.0, 0.0); // grand_child's scroll delta // translation because of position occurs before layer's local transform. expected_grand_child_transform.Translate(8.0, 6.0); expected_grand_child_transform.PreconcatTransform( rotation_about_z); // grand_child's local transform EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 3: fixed-container size delta of 20, 20 SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); // Top-left fixed-position layer should not be affected by container size. EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 4: Bottom-right fixed-position layer. great_grand_child->SetPositionConstraint(fixed_to_bottom_right_); ExecuteCalculateDrawProperties(root_.get()); // Bottom-right fixed-position layer moves as container resizes. expected_great_grand_child_transform.MakeIdentity(); // Apply child layer transform. expected_great_grand_child_transform.PreconcatTransform(rotation_about_z); // Apply size delta from the child(container) layer. expected_great_grand_child_transform.Translate(20.0, 20.0); // Apply layer position from the grand child layer. expected_great_grand_child_transform.Translate(8.0, 6.0); // Apply grand child layer transform. expected_great_grand_child_transform.PreconcatTransform(rotation_about_z); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionWithIntermediateSurfaceAndTransforms) { // This test checks for correct scroll compensation when the fixed-position // container contributes to a different render surface than the fixed-position // layer. In this case, the surface draw transforms also have to be accounted // for when checking the scroll delta. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; LayerImpl* great_grand_child = grand_child->children()[0]; child->SetIsContainerForFixedPositionLayers(true); grand_child->SetPosition(gfx::PointF(8.f, 6.f)); grand_child->SetHasRenderSurface(true); great_grand_child->SetPositionConstraint(fixed_to_top_left_); great_grand_child->SetDrawsContent(true); gfx::Transform rotation_about_z; rotation_about_z.RotateAboutZAxis(90.0); grand_child->SetTransform(rotation_about_z); // Case 1: scroll delta of 0, 0 child->SetScrollDelta(gfx::Vector2d(0, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_child_transform; gfx::Transform expected_surface_draw_transform; expected_surface_draw_transform.Translate(8.0, 6.0); expected_surface_draw_transform.PreconcatTransform(rotation_about_z); gfx::Transform expected_grand_child_transform; gfx::Transform expected_great_grand_child_transform; ASSERT_TRUE(grand_child->render_surface()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_surface_draw_transform, grand_child->render_surface()->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 2: scroll delta of 10, 30 child->SetScrollDelta(gfx::Vector2d(10, 30)); ExecuteCalculateDrawProperties(root_.get()); // Here the grand_child remains unchanged, because it scrolls along with the // render surface, and the translation is actually in the render surface. But, // the fixed position great_grand_child is more awkward: its actually being // drawn with respect to the render surface, but it needs to remain fixed with // resepct to a container beyond that surface. So, the net result is that, // unlike previous tests where the fixed position layer's transform remains // unchanged, here the fixed position layer's transform explicitly contains // the translation that cancels out the scroll. expected_child_transform.MakeIdentity(); expected_child_transform.Translate(-10.0, -30.0); // scroll delta expected_surface_draw_transform.MakeIdentity(); expected_surface_draw_transform.Translate(-10.0, -30.0); // scroll delta expected_surface_draw_transform.Translate(8.0, 6.0); expected_surface_draw_transform.PreconcatTransform(rotation_about_z); // The rotation and its inverse are needed to place the scroll delta // compensation in the correct space. This test will fail if the // rotation/inverse are backwards, too, so it requires perfect order of // operations. expected_great_grand_child_transform.MakeIdentity(); expected_great_grand_child_transform.PreconcatTransform( Inverse(rotation_about_z)); // explicit canceling out the scroll delta that gets embedded in the fixed // position layer's surface. expected_great_grand_child_transform.Translate(10.0, 30.0); expected_great_grand_child_transform.PreconcatTransform(rotation_about_z); ASSERT_TRUE(grand_child->render_surface()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_surface_draw_transform, grand_child->render_surface()->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 3: fixed-container size delta of 20, 20 SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); // Top-left fixed-position layer should not be affected by container size. EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 4: Bottom-right fixed-position layer. great_grand_child->SetPositionConstraint(fixed_to_bottom_right_); ExecuteCalculateDrawProperties(root_.get()); // Bottom-right fixed-position layer moves as container resizes. expected_great_grand_child_transform.MakeIdentity(); // The rotation and its inverse are needed to place the scroll delta // compensation in the correct space. This test will fail if the // rotation/inverse are backwards, too, so it requires perfect order of // operations. expected_great_grand_child_transform.PreconcatTransform( Inverse(rotation_about_z)); // explicit canceling out the scroll delta that gets embedded in the fixed // position layer's surface. expected_great_grand_child_transform.Translate(10.0, 30.0); // Also apply size delta in the child(container) layer space. expected_great_grand_child_transform.Translate(20.0, 20.0); expected_great_grand_child_transform.PreconcatTransform(rotation_about_z); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionLayerWithMultipleIntermediateSurfaces) { // This test checks for correct scroll compensation when the fixed-position // container contributes to a different render surface than the fixed-position // layer, with additional render surfaces in-between. This checks that the // conversion to ancestor surfaces is accumulated properly in the final matrix // transform. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; LayerImpl* great_grand_child = grand_child->children()[0]; // Add one more layer to the test tree for this scenario. { gfx::Transform identity; scoped_ptr fixed_position_child = LayerImpl::Create(host_impl_.active_tree(), 5); SetLayerPropertiesForTesting(fixed_position_child.get(), identity, gfx::Point3F(), gfx::PointF(), gfx::Size(100, 100), true); great_grand_child->AddChild(fixed_position_child.Pass()); } LayerImpl* fixed_position_child = great_grand_child->children()[0]; // Actually set up the scenario here. child->SetIsContainerForFixedPositionLayers(true); grand_child->SetPosition(gfx::PointF(8.f, 6.f)); grand_child->SetHasRenderSurface(true); great_grand_child->SetPosition(gfx::PointF(40.f, 60.f)); great_grand_child->SetHasRenderSurface(true); fixed_position_child->SetPositionConstraint(fixed_to_top_left_); fixed_position_child->SetDrawsContent(true); // The additional rotations, which are non-commutative with translations, help // to verify that we have correct order-of-operations in the final scroll // compensation. Note that rotating about the center of the layer ensures we // do not accidentally clip away layers that we want to test. gfx::Transform rotation_about_z; rotation_about_z.Translate(50.0, 50.0); rotation_about_z.RotateAboutZAxis(90.0); rotation_about_z.Translate(-50.0, -50.0); grand_child->SetTransform(rotation_about_z); great_grand_child->SetTransform(rotation_about_z); // Case 1: scroll delta of 0, 0 child->SetScrollDelta(gfx::Vector2d(0, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_child_transform; gfx::Transform expected_grand_child_surface_draw_transform; expected_grand_child_surface_draw_transform.Translate(8.0, 6.0); expected_grand_child_surface_draw_transform.PreconcatTransform( rotation_about_z); gfx::Transform expected_grand_child_transform; gfx::Transform expected_great_grand_child_surface_draw_transform; expected_great_grand_child_surface_draw_transform.Translate(40.0, 60.0); expected_great_grand_child_surface_draw_transform.PreconcatTransform( rotation_about_z); gfx::Transform expected_great_grand_child_transform; gfx::Transform expected_fixed_position_child_transform; ASSERT_TRUE(grand_child->render_surface()); ASSERT_TRUE(great_grand_child->render_surface()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_grand_child_surface_draw_transform, grand_child->render_surface()->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_great_grand_child_surface_draw_transform, great_grand_child->render_surface()->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform, fixed_position_child->draw_transform()); // Case 2: scroll delta of 10, 30 child->SetScrollDelta(gfx::Vector2d(10, 30)); ExecuteCalculateDrawProperties(root_.get()); expected_child_transform.MakeIdentity(); expected_child_transform.Translate(-10.0, -30.0); // scroll delta expected_grand_child_surface_draw_transform.MakeIdentity(); expected_grand_child_surface_draw_transform.Translate(-10.0, -30.0); // scroll delta expected_grand_child_surface_draw_transform.Translate(8.0, 6.0); expected_grand_child_surface_draw_transform.PreconcatTransform( rotation_about_z); // grand_child, great_grand_child, and great_grand_child's surface are not // expected to change, since they are all not fixed, and they are all drawn // with respect to grand_child's surface that already has the scroll delta // accounted for. // But the great-great grandchild, "fixed_position_child", should have a // transform that explicitly cancels out the scroll delta. The expected // transform is: compound_draw_transform.Inverse() * translate(positive scroll // delta) * compound_origin_transform from great_grand_childSurface's origin // to the root surface. gfx::Transform compound_draw_transform; compound_draw_transform.Translate(8.0, 6.0); // origin translation of grand_child compound_draw_transform.PreconcatTransform( rotation_about_z); // rotation of grand_child compound_draw_transform.Translate( 40.0, 60.0); // origin translation of great_grand_child compound_draw_transform.PreconcatTransform( rotation_about_z); // rotation of great_grand_child expected_fixed_position_child_transform.MakeIdentity(); expected_fixed_position_child_transform.PreconcatTransform( Inverse(compound_draw_transform)); // explicit canceling out the scroll delta that gets embedded in the fixed // position layer's surface. expected_fixed_position_child_transform.Translate(10.0, 30.0); expected_fixed_position_child_transform.PreconcatTransform( compound_draw_transform); ASSERT_TRUE(grand_child->render_surface()); ASSERT_TRUE(great_grand_child->render_surface()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_grand_child_surface_draw_transform, grand_child->render_surface()->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ( expected_great_grand_child_surface_draw_transform, great_grand_child->render_surface()->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform, fixed_position_child->draw_transform()); // Case 3: fixed-container size delta of 20, 20 SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); // Top-left fixed-position layer should not be affected by container size. EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform, fixed_position_child->draw_transform()); // Case 4: Bottom-right fixed-position layer. fixed_position_child->SetPositionConstraint(fixed_to_bottom_right_); ExecuteCalculateDrawProperties(root_.get()); // Bottom-right fixed-position layer moves as container resizes. expected_fixed_position_child_transform.MakeIdentity(); expected_fixed_position_child_transform.PreconcatTransform( Inverse(compound_draw_transform)); // explicit canceling out the scroll delta that gets embedded in the fixed // position layer's surface. expected_fixed_position_child_transform.Translate(10.0, 30.0); // Also apply size delta in the child(container) layer space. expected_fixed_position_child_transform.Translate(20.0, 20.0); expected_fixed_position_child_transform.PreconcatTransform( compound_draw_transform); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform, fixed_position_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionLayerWithContainerLayerThatHasSurface) { // This test checks for correct scroll compensation when the fixed-position // container itself has a render surface. In this case, the container layer // should be treated like a layer that contributes to a render target, and // that render target is completely irrelevant; it should not affect the // scroll compensation. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; child->SetIsContainerForFixedPositionLayers(true); child->SetHasRenderSurface(true); grand_child->SetPositionConstraint(fixed_to_top_left_); grand_child->SetDrawsContent(true); // Case 1: scroll delta of 0, 0 child->SetScrollDelta(gfx::Vector2d(0, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_surface_draw_transform; expected_surface_draw_transform.Translate(0.0, 0.0); gfx::Transform expected_child_transform; gfx::Transform expected_grand_child_transform; ASSERT_TRUE(child->render_surface()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_surface_draw_transform, child->render_surface()->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 2: scroll delta of 10, 10 child->SetScrollDelta(gfx::Vector2d(10, 10)); ExecuteCalculateDrawProperties(root_.get()); // The surface is translated by scroll delta, the child transform doesn't // change because it scrolls along with the surface, but the fixed position // grand_child needs to compensate for the scroll translation. expected_surface_draw_transform.MakeIdentity(); expected_surface_draw_transform.Translate(-10.0, -10.0); expected_grand_child_transform.MakeIdentity(); expected_grand_child_transform.Translate(10.0, 10.0); ASSERT_TRUE(child->render_surface()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_surface_draw_transform, child->render_surface()->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 3: fixed-container size delta of 20, 20 SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); // Top-left fixed-position layer should not be affected by container size. EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 4: Bottom-right fixed-position layer. grand_child->SetPositionConstraint(fixed_to_bottom_right_); ExecuteCalculateDrawProperties(root_.get()); // Bottom-right fixed-position layer moves as container resizes. expected_grand_child_transform.MakeIdentity(); // The surface is translated by scroll delta, the child transform doesn't // change because it scrolls along with the surface, but the fixed position // grand_child needs to compensate for the scroll translation. expected_grand_child_transform.Translate(10.0, 10.0); // Apply size delta from the child(container) layer. expected_grand_child_transform.Translate(20.0, 20.0); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedPositionLayerThatIsAlsoFixedPositionContainer) { // This test checks the scenario where a fixed-position layer also happens to // be a container itself for a descendant fixed position layer. In particular, // the layer should not accidentally be fixed to itself. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; child->SetIsContainerForFixedPositionLayers(true); grand_child->SetPositionConstraint(fixed_to_top_left_); // This should not confuse the grand_child. If correct, the grand_child would // still be considered fixed to its container (i.e. "child"). grand_child->SetIsContainerForFixedPositionLayers(true); // Case 1: scroll delta of 0, 0 child->SetScrollDelta(gfx::Vector2d(0, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_child_transform; gfx::Transform expected_grand_child_transform; EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 2: scroll delta of 10, 10 child->SetScrollDelta(gfx::Vector2d(10, 10)); ExecuteCalculateDrawProperties(root_.get()); // Here the child is affected by scroll delta, but the fixed position // grand_child should not be affected. expected_child_transform.MakeIdentity(); expected_child_transform.Translate(-10.0, -10.0); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 3: fixed-container size delta of 20, 20 SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); // Top-left fixed-position layer should not be affected by container size. EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); // Case 4: Bottom-right fixed-position layer. grand_child->SetPositionConstraint(fixed_to_bottom_right_); ExecuteCalculateDrawProperties(root_.get()); // Bottom-right fixed-position layer moves as container resizes. expected_grand_child_transform.MakeIdentity(); // Apply size delta from the child(container) layer. expected_grand_child_transform.Translate(20.0, 20.0); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedWithinFixedWithSameContainer) { // This test checks scroll compensation for a fixed-position layer that is // inside of another fixed-position layer and both share the same container. // In this situation, the parent fixed-position layer will receive // the scroll compensation, and the child fixed-position layer does not // need to compensate further. LayerImpl* child = scroll_->children()[0]; LayerImpl* grand_child = child->children()[0]; LayerImpl* great_grand_child = grand_child->children()[0]; child->SetIsContainerForFixedPositionLayers(true); grand_child->SetPositionConstraint(fixed_to_top_left_); // Note carefully - great_grand_child is fixed to bottom right, to test // sizeDelta being applied correctly; the compensation skips the grand_child // because it is fixed to top left. great_grand_child->SetPositionConstraint(fixed_to_bottom_right_); // Case 1: scrollDelta child->SetScrollDelta(gfx::Vector2d(10, 10)); ExecuteCalculateDrawProperties(root_.get()); // Here the child is affected by scroll delta, but the fixed position // grand_child should not be affected. gfx::Transform expected_child_transform; expected_child_transform.Translate(-10.0, -10.0); gfx::Transform expected_grand_child_transform; gfx::Transform expected_great_grand_child_transform; EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); // Case 2: sizeDelta child->SetScrollDelta(gfx::Vector2d(0, 0)); SetFixedContainerSizeDelta(child, gfx::Vector2d(20, 20)); ExecuteCalculateDrawProperties(root_.get()); expected_child_transform.MakeIdentity(); expected_grand_child_transform.MakeIdentity(); // Fixed to bottom-right, size-delta compensation is applied. expected_great_grand_child_transform.MakeIdentity(); expected_great_grand_child_transform.Translate(20.0, 20.0); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform, child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform, grand_child->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform, great_grand_child->draw_transform()); } TEST_F(LayerPositionConstraintTest, ScrollCompensationForFixedWithinFixedWithInterveningContainer) { // This test checks scroll compensation for a fixed-position layer that is // inside of another fixed-position layer, but they have different fixed // position containers. In this situation, the child fixed-position element // would still have to compensate with respect to its container. LayerImpl* container1 = scroll_->children()[0]; LayerImpl* fixed_to_container1 = container1->children()[0]; LayerImpl* container2 = fixed_to_container1->children()[0]; { // Add one more layer to the hierarchy for this test. scoped_ptr fixed_to_container2_ptr = LayerImpl::Create(host_impl_.active_tree(), 5); container2->AddChild(fixed_to_container2_ptr.Pass()); } LayerImpl* fixed_to_container2 = container2->children()[0]; container1->SetIsContainerForFixedPositionLayers(true); fixed_to_container1->SetPositionConstraint(fixed_to_top_left_); container2->SetIsContainerForFixedPositionLayers(true); fixed_to_container2->SetPositionConstraint(fixed_to_top_left_); container1->SetScrollDelta(gfx::Vector2d(0, 15)); container2->SetScrollDelta(gfx::Vector2d(30, 0)); ExecuteCalculateDrawProperties(root_.get()); gfx::Transform expected_container1_transform; expected_container1_transform.Translate(0.0, -15.0); gfx::Transform expected_fixed_to_container1_transform; // Since the container is a descendant of the fixed layer above, // the expected draw transform for container2 would not // include the scrollDelta that was applied to container1. gfx::Transform expected_container2_transform; expected_container2_transform.Translate(-30.0, 0.0); gfx::Transform expected_fixed_to_container2_transform; EXPECT_TRANSFORMATION_MATRIX_EQ(expected_container1_transform, container1->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_to_container1_transform, fixed_to_container1->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_container2_transform, container2->draw_transform()); EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_to_container2_transform, fixed_to_container2->draw_transform()); } } // namespace } // namespace cc