// 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 <vector>

#include "cc/animation/animation_host.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
#include "cc/test/fake_layer_tree_host.h"
#include "cc/test/fake_proxy.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/layer_test_common.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(Layer* 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);
}

void ExecuteCalculateDrawProperties(LayerImpl* root_layer) {
  RenderSurfaceList dummy_render_surface_list;
  LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
      root_layer, root_layer->bounds(), &dummy_render_surface_list);
  inputs.inner_viewport_scroll_layer =
      root_layer->layer_tree_impl()->InnerViewportScrollLayer();
  inputs.outer_viewport_scroll_layer =
      root_layer->layer_tree_impl()->OuterViewportScrollLayer();
  EXPECT_FALSE(root_layer->layer_tree_impl()->property_trees()->needs_rebuild);
  LayerTreeHostCommon::CalculateDrawProperties(&inputs);
}

class LayerPositionConstraintTest : public testing::Test {
 public:
  LayerPositionConstraintTest()
      : animation_host_(AnimationHost::CreateForTesting(ThreadInstance::MAIN)),
        layer_tree_host_(FakeLayerTreeHost::Create(&fake_client_,
                                                   &task_graph_runner_,
                                                   animation_host_.get())),
        root_impl_(nullptr),
        inner_viewport_container_layer_impl_(nullptr),
        scroll_layer_impl_(nullptr),
        outer_viewport_container_layer_impl_(nullptr),
        child_transform_layer_impl_(nullptr),
        child_impl_(nullptr),
        grand_child_impl_(nullptr),
        great_grand_child_impl_(nullptr) {
    layer_tree_host_->InitializeForTesting(
        TaskRunnerProvider::Create(nullptr, nullptr),
        std::unique_ptr<Proxy>(new FakeProxy));
    CreateTreeForTest();
    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);
  }

  void CreateTreeForTest() {
    // scroll_layer_ is the inner viewport scroll layer and child_ is the outer
    // viewport scroll layer.
    root_ = Layer::Create();
    inner_viewport_container_layer_ = Layer::Create();
    page_scale_layer_ = Layer::Create();
    scroll_layer_ = Layer::Create();
    outer_viewport_container_layer_ = Layer::Create();
    child_transform_layer_ = Layer::Create();
    child_ = Layer::Create();
    grand_child_ = Layer::Create();
    grand_child_->SetIsDrawable(true);
    great_grand_child_ = Layer::Create();
    great_grand_child_->SetIsDrawable(true);

    gfx::Transform IdentityMatrix;
    gfx::Point3F transform_origin;
    gfx::PointF position;
    gfx::Size bounds(200, 200);
    gfx::Size clip_bounds(100, 100);
    SetLayerPropertiesForTesting(inner_viewport_container_layer_.get(),
                                 IdentityMatrix, transform_origin, position,
                                 clip_bounds, true);
    SetLayerPropertiesForTesting(page_scale_layer_.get(), IdentityMatrix,
                                 transform_origin, position, clip_bounds, true);
    SetLayerPropertiesForTesting(scroll_layer_.get(), IdentityMatrix,
                                 transform_origin, position, bounds, true);
    SetLayerPropertiesForTesting(outer_viewport_container_layer_.get(),
                                 IdentityMatrix, transform_origin, position,
                                 clip_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);

    inner_viewport_container_layer_->SetMasksToBounds(true);
    scroll_layer_->SetElementId(
        LayerIdToElementIdForTesting(scroll_layer_->id()));
    scroll_layer_->SetScrollable(clip_bounds);
    scroll_layer_->SetIsContainerForFixedPositionLayers(true);

    outer_viewport_container_layer_->SetMasksToBounds(true);
    child_->SetElementId(LayerIdToElementIdForTesting(child_->id()));
    child_->SetScrollable(clip_bounds);
    grand_child_->SetElementId(
        LayerIdToElementIdForTesting(grand_child_->id()));
    grand_child_->SetScrollable(clip_bounds);
    child_->SetIsContainerForFixedPositionLayers(true);

    grand_child_->AddChild(great_grand_child_);
    child_->AddChild(grand_child_);
    child_transform_layer_->AddChild(child_);
    outer_viewport_container_layer_->AddChild(child_transform_layer_);
    scroll_layer_->AddChild(outer_viewport_container_layer_);
    page_scale_layer_->AddChild(scroll_layer_);
    inner_viewport_container_layer_->AddChild(page_scale_layer_);
    root_->AddChild(inner_viewport_container_layer_);

    child_->SetIsResizedByBrowserControls(true);

    layer_tree_host_->SetRootLayer(root_);
    LayerTreeHost::ViewportLayers viewport_layers;
    viewport_layers.page_scale = page_scale_layer_;
    viewport_layers.inner_viewport_container = inner_viewport_container_layer_;
    viewport_layers.outer_viewport_container = outer_viewport_container_layer_;
    viewport_layers.inner_viewport_scroll = scroll_layer_;
    viewport_layers.outer_viewport_scroll = child_;
    layer_tree_host_->RegisterViewportLayers(viewport_layers);
  }

  void CommitAndUpdateImplPointers() {
    LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
        root_.get(), root_->bounds());
    inputs.inner_viewport_scroll_layer =
        layer_tree_host_->inner_viewport_scroll_layer();
    inputs.outer_viewport_scroll_layer =
        layer_tree_host_->outer_viewport_scroll_layer();
    LayerTreeHostCommon::CalculateDrawPropertiesForTesting(&inputs);

    // Since scroll deltas aren't sent back to the main thread in this test
    // setup, clear them to maintain consistent state.
    if (root_impl_) {
      SetScrollOffsetDelta(scroll_layer_impl_, gfx::Vector2dF());
      SetScrollOffsetDelta(child_impl_, gfx::Vector2dF());
      SetScrollOffsetDelta(grand_child_impl_, gfx::Vector2dF());
    }
    root_impl_ = layer_tree_host_->CommitAndCreateLayerImplTree();
    layer_tree_impl_ = root_impl_->layer_tree_impl();
    inner_viewport_container_layer_impl_ =
        layer_tree_impl_->LayerById(inner_viewport_container_layer_->id());
    scroll_layer_impl_ = layer_tree_impl_->LayerById(scroll_layer_->id());
    outer_viewport_container_layer_impl_ =
        layer_tree_impl_->LayerById(outer_viewport_container_layer_->id());
    child_transform_layer_impl_ =
        layer_tree_impl_->LayerById(child_transform_layer_->id());
    child_impl_ = layer_tree_impl_->LayerById(child_->id());
    grand_child_impl_ = layer_tree_impl_->LayerById(grand_child_->id());
    great_grand_child_impl_ =
        layer_tree_impl_->LayerById(great_grand_child_->id());
  }

 protected:
  FakeLayerTreeHostClient fake_client_;
  TestTaskGraphRunner task_graph_runner_;
  std::unique_ptr<AnimationHost> animation_host_;
  std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
  scoped_refptr<Layer> root_;
  scoped_refptr<Layer> page_scale_layer_;
  scoped_refptr<Layer> inner_viewport_container_layer_;
  scoped_refptr<Layer> scroll_layer_;
  scoped_refptr<Layer> outer_viewport_container_layer_;
  scoped_refptr<Layer> child_transform_layer_;
  scoped_refptr<Layer> child_;
  scoped_refptr<Layer> grand_child_;
  scoped_refptr<Layer> great_grand_child_;
  LayerTreeImpl* layer_tree_impl_;
  LayerImpl* root_impl_;
  LayerImpl* inner_viewport_container_layer_impl_;
  LayerImpl* scroll_layer_impl_;
  LayerImpl* outer_viewport_container_layer_impl_;
  LayerImpl* child_transform_layer_impl_;
  LayerImpl* child_impl_;
  LayerImpl* grand_child_impl_;
  LayerImpl* great_grand_child_impl_;

  LayerPositionConstraint fixed_to_top_left_;
  LayerPositionConstraint fixed_to_bottom_right_;

  // LayerImpl should not be aware of synced property logics, this function is
  // a hack for the test to arbitrarily set the scroll delta for setting up.
  static void SetScrollOffsetDelta(LayerImpl* layer_impl,
                                   const gfx::Vector2dF& delta) {
    if (layer_impl->layer_tree_impl()
            ->property_trees()
            ->scroll_tree.SetScrollOffsetDeltaForTesting(
                layer_impl->element_id(), delta))
      layer_impl->layer_tree_impl()->DidUpdateScrollOffset(
          layer_impl->element_id());
  }
};

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.
  child_->SetIsContainerForFixedPositionLayers(true);
  grand_child_->SetPositionConstraint(fixed_to_top_left_);

  CommitAndUpdateImplPointers();

  // Case 1: scroll delta of 0, 0
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0));
  ExecuteCalculateDrawProperties(root_impl_);

  gfx::Transform expected_child_transform;
  gfx::Transform expected_grand_child_transform = expected_child_transform;

  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());

  // Case 2: scroll delta of 10, 10
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  child_impl_->SetDrawsContent(true);
  ExecuteCalculateDrawProperties(root_impl_);

  // 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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());

  // Case 3: fixed-container size delta of 20, 20
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // Top-left fixed-position layer should not be affected by container size.
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());

  // Case 4: Bottom-right fixed-position layer.
  grand_child_->SetPositionConstraint(fixed_to_bottom_right_);
  CommitAndUpdateImplPointers();

  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // 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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
}

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.
  child_->SetIsContainerForFixedPositionLayers(true);
  grand_child_->SetPosition(gfx::PointF(8.f, 6.f));
  great_grand_child_->SetPositionConstraint(fixed_to_top_left_);

  CommitAndUpdateImplPointers();

  // Case 1: scroll delta of 0, 0
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0));
  child_impl_->SetDrawsContent(true);
  ExecuteCalculateDrawProperties(root_impl_);

  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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());

  // Case 2: scroll delta of 10, 10
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  ExecuteCalculateDrawProperties(root_impl_);

  // 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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());

  // Case 3: fixed-container size delta of 20, 20
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // Top-left fixed-position layer should not be affected by container size.
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());

  // Case 4: Bottom-right fixed-position layer.
  great_grand_child_->SetPositionConstraint(fixed_to_bottom_right_);
  CommitAndUpdateImplPointers();
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // 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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
}

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.
  gfx::Transform rotation_about_z;
  rotation_about_z.RotateAboutZAxis(90.0);

  child_transform_layer_->SetIsContainerForFixedPositionLayers(true);
  child_transform_layer_->SetTransform(rotation_about_z);
  grand_child_->SetPosition(gfx::PointF(8.f, 6.f));
  great_grand_child_->SetPositionConstraint(fixed_to_top_left_);

  CommitAndUpdateImplPointers();

  // Case 1: scroll delta of 0, 0
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0));
  child_impl_->SetDrawsContent(true);
  ExecuteCalculateDrawProperties(root_impl_);

  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);

  gfx::Transform expected_great_grand_child_transform =
      expected_grand_child_transform;

  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());

  // Case 2: scroll delta of 10, 20
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 0));
  SetScrollOffsetDelta(grand_child_impl_, gfx::Vector2d(5, 0));
  ExecuteCalculateDrawProperties(root_impl_);

  // 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.PreconcatTransform(rotation_about_z);
  expected_child_transform.Translate(-10.0, 0.0);  // scroll delta

  expected_grand_child_transform.MakeIdentity();
  expected_grand_child_transform.PreconcatTransform(
      rotation_about_z);  // child's local transform is inherited
  expected_grand_child_transform.Translate(
      -10.0, 0.0);  // child's scroll delta is inherited
  expected_grand_child_transform.Translate(-5.0,
                                           0.0);  // grand_child's scroll delta
  // translation because of position
  expected_grand_child_transform.Translate(8.0, 6.0);

  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
}

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.
  child_->SetIsContainerForFixedPositionLayers(true);
  grand_child_->SetPosition(gfx::PointF(8.f, 6.f));
  grand_child_->SetForceRenderSurfaceForTesting(true);
  great_grand_child_->SetPositionConstraint(fixed_to_top_left_);

  gfx::Transform rotation_about_z;
  rotation_about_z.RotateAboutZAxis(90.0);
  great_grand_child_->SetTransform(rotation_about_z);

  CommitAndUpdateImplPointers();

  // Case 1: scroll delta of 0, 0
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0));
  ExecuteCalculateDrawProperties(root_impl_);

  gfx::Transform expected_child_transform;
  gfx::Transform expected_surface_draw_transform;
  expected_surface_draw_transform.Translate(8.0, 6.0);
  gfx::Transform expected_grand_child_transform;
  gfx::Transform expected_great_grand_child_transform;
  expected_great_grand_child_transform.PreconcatTransform(rotation_about_z);
  EXPECT_TRUE(GetRenderSurface(grand_child_impl_));
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_surface_draw_transform,
      GetRenderSurface(grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());

  // Case 2: scroll delta of 10, 30
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 30));
  child_impl_->SetDrawsContent(true);
  ExecuteCalculateDrawProperties(root_impl_);

  // 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_great_grand_child_transform.MakeIdentity();
  // 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);

  EXPECT_TRUE(GetRenderSurface(grand_child_impl_));
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_surface_draw_transform,
      GetRenderSurface(grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());

  // Case 3: fixed-container size delta of 20, 20
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // Top-left fixed-position layer should not be affected by container size.
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());

  // Case 4: Bottom-right fixed-position layer.
  great_grand_child_->SetPositionConstraint(fixed_to_bottom_right_);

  CommitAndUpdateImplPointers();
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 30));
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));

  ExecuteCalculateDrawProperties(root_impl_);

  // Bottom-right fixed-position layer moves as container resizes.
  expected_great_grand_child_transform.MakeIdentity();
  // 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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
}

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.

  // Add one more layer to the test tree for this scenario.
  scoped_refptr<Layer> fixed_position_child = Layer::Create();
  fixed_position_child->SetIsDrawable(true);
  SetLayerPropertiesForTesting(fixed_position_child.get(), gfx::Transform(),
                               gfx::Point3F(), gfx::PointF(),
                               gfx::Size(100, 100), true);
  great_grand_child_->AddChild(fixed_position_child);

  // Actually set up the scenario here.
  child_->SetIsContainerForFixedPositionLayers(true);
  grand_child_->SetPosition(gfx::PointF(8.f, 6.f));
  grand_child_->SetForceRenderSurfaceForTesting(true);
  great_grand_child_->SetPosition(gfx::PointF(40.f, 60.f));
  great_grand_child_->SetForceRenderSurfaceForTesting(true);
  fixed_position_child->SetPositionConstraint(fixed_to_top_left_);

  // The additional rotation, which is non-commutative with translations, helps
  // 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);
  fixed_position_child->SetTransform(rotation_about_z);

  CommitAndUpdateImplPointers();
  LayerImpl* fixed_position_child_impl =
      layer_tree_impl_->LayerById(fixed_position_child->id());

  // Case 1: scroll delta of 0, 0
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0));
  child_impl_->SetDrawsContent(true);
  ExecuteCalculateDrawProperties(root_impl_);

  gfx::Transform expected_child_transform;

  gfx::Transform expected_grand_child_surface_draw_transform;
  expected_grand_child_surface_draw_transform.Translate(8.0, 6.0);

  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);

  gfx::Transform expected_great_grand_child_transform;

  gfx::Transform expected_fixed_position_child_transform;
  expected_fixed_position_child_transform.PreconcatTransform(rotation_about_z);

  EXPECT_TRUE(GetRenderSurface(grand_child_impl_));
  EXPECT_TRUE(GetRenderSurface(great_grand_child_impl_));
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_grand_child_surface_draw_transform,
      GetRenderSurface(grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_great_grand_child_surface_draw_transform,
      GetRenderSurface(great_grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform,
                                  fixed_position_child_impl->DrawTransform());

  // Case 2: scroll delta of 10, 30
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 30));
  ExecuteCalculateDrawProperties(root_impl_);

  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);

  // 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.
  expected_fixed_position_child_transform.MakeIdentity();
  expected_fixed_position_child_transform.Translate(10.0, 30.0);
  expected_fixed_position_child_transform.PreconcatTransform(rotation_about_z);

  EXPECT_TRUE(GetRenderSurface(grand_child_impl_));
  EXPECT_TRUE(GetRenderSurface(great_grand_child_impl_));
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_grand_child_surface_draw_transform,
      GetRenderSurface(grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_great_grand_child_surface_draw_transform,
      GetRenderSurface(great_grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform,
                                  fixed_position_child_impl->DrawTransform());

  // Case 3: fixed-container size delta of 20, 20
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // Top-left fixed-position layer should not be affected by container size.
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform,
                                  fixed_position_child_impl->DrawTransform());

  // Case 4: Bottom-right fixed-position layer.
  fixed_position_child->SetPositionConstraint(fixed_to_bottom_right_);
  CommitAndUpdateImplPointers();
  fixed_position_child_impl =
      layer_tree_impl_->LayerById(fixed_position_child->id());
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 30));
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // Bottom-right fixed-position layer moves as container resizes.
  expected_fixed_position_child_transform.MakeIdentity();
  // 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(rotation_about_z);

  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform,
                                  fixed_position_child_impl->DrawTransform());
}

TEST_F(
    LayerPositionConstraintTest,
    ScrollCompensationForFixedPositionLayerWithMultipleSurfacesAndTransforms) {
  // 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, and the fixed-position
  // container is transformed. This checks that the conversion to ancestor
  // surfaces is accumulated properly in the final matrix transform.

  // Add one more layer to the test tree for this scenario.
  scoped_refptr<Layer> fixed_position_child = Layer::Create();
  fixed_position_child->SetIsDrawable(true);
  SetLayerPropertiesForTesting(fixed_position_child.get(), gfx::Transform(),
                               gfx::Point3F(), gfx::PointF(),
                               gfx::Size(100, 100), true);
  great_grand_child_->AddChild(fixed_position_child);

  // Actually set up the scenario here.
  child_transform_layer_->SetIsContainerForFixedPositionLayers(true);
  grand_child_->SetPosition(gfx::PointF(8.f, 6.f));
  grand_child_->SetForceRenderSurfaceForTesting(true);
  great_grand_child_->SetPosition(gfx::PointF(40.f, 60.f));
  great_grand_child_->SetForceRenderSurfaceForTesting(true);
  fixed_position_child->SetPositionConstraint(fixed_to_top_left_);

  // 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(30.0);
  rotation_about_z.Translate(-50.0, -50.0);
  child_transform_layer_->SetTransform(rotation_about_z);
  fixed_position_child->SetTransform(rotation_about_z);

  CommitAndUpdateImplPointers();
  LayerImpl* fixed_position_child_impl =
      layer_tree_impl_->LayerById(fixed_position_child->id());

  // Case 1: scroll delta of 0, 0
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0));
  child_impl_->SetDrawsContent(true);
  ExecuteCalculateDrawProperties(root_impl_);

  gfx::Transform expected_child_transform;
  expected_child_transform.PreconcatTransform(rotation_about_z);

  gfx::Transform expected_grand_child_surface_draw_transform;
  expected_grand_child_surface_draw_transform.PreconcatTransform(
      rotation_about_z);
  expected_grand_child_surface_draw_transform.Translate(8.0, 6.0);

  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);

  gfx::Transform expected_great_grand_child_transform;

  gfx::Transform expected_fixed_position_child_transform;
  expected_fixed_position_child_transform.PreconcatTransform(rotation_about_z);

  EXPECT_TRUE(GetRenderSurface(grand_child_impl_));
  EXPECT_TRUE(GetRenderSurface(great_grand_child_impl_));
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_grand_child_surface_draw_transform,
      GetRenderSurface(grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_great_grand_child_surface_draw_transform,
      GetRenderSurface(great_grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform,
                                  fixed_position_child_impl->DrawTransform());

  // Case 2: scroll delta of 10, 30
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 30));
  ExecuteCalculateDrawProperties(root_impl_);

  expected_child_transform.MakeIdentity();
  expected_child_transform.PreconcatTransform(rotation_about_z);
  expected_child_transform.Translate(-10.0, -30.0);  // scroll delta

  expected_grand_child_surface_draw_transform.MakeIdentity();
  expected_grand_child_surface_draw_transform.PreconcatTransform(
      rotation_about_z);
  expected_grand_child_surface_draw_transform.Translate(-10.0,
                                                        -30.0);  // scroll delta
  expected_grand_child_surface_draw_transform.Translate(8.0, 6.0);

  // 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.
  expected_fixed_position_child_transform.MakeIdentity();
  // 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(rotation_about_z);

  EXPECT_TRUE(GetRenderSurface(grand_child_impl_));
  EXPECT_TRUE(GetRenderSurface(great_grand_child_impl_));
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_grand_child_surface_draw_transform,
      GetRenderSurface(grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_great_grand_child_surface_draw_transform,
      GetRenderSurface(great_grand_child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_position_child_transform,
                                  fixed_position_child_impl->DrawTransform());
}

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.
  child_->SetIsContainerForFixedPositionLayers(true);
  child_->SetForceRenderSurfaceForTesting(true);
  grand_child_->SetPositionConstraint(fixed_to_top_left_);

  CommitAndUpdateImplPointers();

  // Case 1: scroll delta of 0, 0
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0));
  ExecuteCalculateDrawProperties(root_impl_);

  gfx::Transform expected_surface_draw_transform;
  gfx::Transform expected_child_transform;
  gfx::Transform expected_grand_child_transform;
  EXPECT_TRUE(GetRenderSurface(child_impl_));
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_surface_draw_transform,
      GetRenderSurface(child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());

  // Case 2: scroll delta of 10, 10
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  ExecuteCalculateDrawProperties(root_impl_);

  // 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);

  EXPECT_TRUE(GetRenderSurface(child_impl_));
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_surface_draw_transform,
      GetRenderSurface(child_impl_)->draw_transform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());

  // Case 3: fixed-container size delta of 20, 20
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // Top-left fixed-position layer should not be affected by container size.
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());

  // Case 4: Bottom-right fixed-position layer.
  grand_child_->SetPositionConstraint(fixed_to_bottom_right_);
  CommitAndUpdateImplPointers();
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // 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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
}

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.
  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);

  CommitAndUpdateImplPointers();

  // Case 1: scroll delta of 0, 0
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0));
  child_impl_->SetDrawsContent(true);
  ExecuteCalculateDrawProperties(root_impl_);

  gfx::Transform expected_child_transform;
  gfx::Transform expected_grand_child_transform;
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());

  // Case 2: scroll delta of 10, 10
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  ExecuteCalculateDrawProperties(root_impl_);

  // 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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());

  // Case 3: fixed-container size delta of 20, 20
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // Top-left fixed-position layer should not be affected by container size.
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());

  // Case 4: Bottom-right fixed-position layer.
  grand_child_->SetPositionConstraint(fixed_to_bottom_right_);
  CommitAndUpdateImplPointers();
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));

  ExecuteCalculateDrawProperties(root_impl_);

  // 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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
}

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.
  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_);

  CommitAndUpdateImplPointers();

  // Case 1: scrollDelta
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  child_impl_->SetDrawsContent(true);
  ExecuteCalculateDrawProperties(root_impl_);

  // 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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());

  // Case 2: sizeDelta
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(0, 0));
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  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_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_grand_child_transform,
                                  grand_child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_great_grand_child_transform,
                                  great_grand_child_impl_->DrawTransform());
}

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.

  // Add one more layer to the hierarchy for this test.
  scoped_refptr<Layer> great_great_grand_child = Layer::Create();
  great_great_grand_child->SetIsDrawable(true);
  great_grand_child_->AddChild(great_great_grand_child);

  child_->SetIsContainerForFixedPositionLayers(true);
  grand_child_->SetPositionConstraint(fixed_to_top_left_);
  great_grand_child_->SetIsContainerForFixedPositionLayers(true);
  great_grand_child_->SetElementId(
      LayerIdToElementIdForTesting(great_grand_child_->id()));
  great_grand_child_->SetScrollable(gfx::Size(100, 100));
  great_great_grand_child->SetPositionConstraint(fixed_to_top_left_);

  CommitAndUpdateImplPointers();

  LayerImpl* container1 = child_impl_;
  LayerImpl* fixed_to_container1 = grand_child_impl_;
  LayerImpl* container2 = great_grand_child_impl_;
  LayerImpl* fixed_to_container2 =
      layer_tree_impl_->LayerById(great_great_grand_child->id());

  SetScrollOffsetDelta(container1, gfx::Vector2d(0, 15));
  container1->SetDrawsContent(true);
  SetScrollOffsetDelta(container2, gfx::Vector2d(30, 0));
  container2->SetDrawsContent(true);
  ExecuteCalculateDrawProperties(root_impl_);

  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->DrawTransform());

  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_to_container1_transform,
                                  fixed_to_container1->DrawTransform());

  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_container2_transform,
                                  container2->DrawTransform());

  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_to_container2_transform,
                                  fixed_to_container2->DrawTransform());
}

TEST_F(LayerPositionConstraintTest,
       ScrollCompensationForOuterViewportBoundsDelta) {
  // This test checks for correct scroll compensation when the fixed-position
  // container is the outer viewport scroll layer and has non-zero bounds delta.
  scoped_refptr<Layer> fixed_child = Layer::Create();
  fixed_child->SetIsDrawable(true);
  fixed_child->SetBounds(gfx::Size(300, 300));
  child_->AddChild(fixed_child);
  fixed_child->SetPositionConstraint(fixed_to_top_left_);

  CommitAndUpdateImplPointers();

  LayerImpl* fixed_child_impl =
      root_impl_->layer_tree_impl()->FindActiveTreeLayerById(fixed_child->id());

  // Case 1: fixed-container size delta of 20, 20
  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  child_impl_->SetDrawsContent(true);
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  gfx::Transform expected_scroll_layer_transform;
  expected_scroll_layer_transform.Translate(-10.0, -10.0);
  gfx::Transform expected_fixed_child_transform;

  ExecuteCalculateDrawProperties(root_impl_);

  // Top-left fixed-position layer should not be affected by container size.
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_scroll_layer_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_child_transform,
                                  fixed_child_impl->DrawTransform());

  // Case 2: Bottom-right fixed-position layer.
  fixed_child->SetPositionConstraint(fixed_to_bottom_right_);
  CommitAndUpdateImplPointers();
  fixed_child_impl =
      root_impl_->layer_tree_impl()->FindActiveTreeLayerById(fixed_child->id());

  SetScrollOffsetDelta(child_impl_, gfx::Vector2d(10, 10));
  outer_viewport_container_layer_impl_->SetViewportBoundsDelta(
      gfx::Vector2d(20, 20));
  ExecuteCalculateDrawProperties(root_impl_);

  // Bottom-right fixed-position layer moves as container resizes.
  expected_fixed_child_transform.MakeIdentity();
  // Apply size delta.
  expected_fixed_child_transform.Translate(20.0, 20.0);

  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_scroll_layer_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_child_transform,
                                  fixed_child_impl->DrawTransform());
}

}  // namespace
}  // namespace cc
