// 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/proto/layer_position_constraint.pb.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/test_task_graph_runner.h"
#include "cc/trees/layer_tree_host_common.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cc {
namespace {

class LayerWithForcedDrawsContent : public Layer {
 public:
  LayerWithForcedDrawsContent() {}

  bool DrawsContent() const override;

 private:
  ~LayerWithForcedDrawsContent() override {}
};

bool LayerWithForcedDrawsContent::DrawsContent() const {
  return true;
}

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) {
  std::vector<LayerImpl*> dummy_render_surface_layer_list;
  LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
      root_layer, root_layer->bounds(), &dummy_render_surface_layer_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();
    scroll_layer_ = Layer::Create();
    outer_viewport_container_layer_ = Layer::Create();
    child_transform_layer_ = Layer::Create();
    child_ = Layer::Create();
    grand_child_ = make_scoped_refptr(new LayerWithForcedDrawsContent());
    great_grand_child_ = make_scoped_refptr(new LayerWithForcedDrawsContent());

    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(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_->SetScrollClipLayerId(inner_viewport_container_layer_->id());
    scroll_layer_->SetIsContainerForFixedPositionLayers(true);

    outer_viewport_container_layer_->SetMasksToBounds(true);
    child_->SetScrollClipLayerId(outer_viewport_container_layer_->id());
    grand_child_->SetScrollClipLayerId(outer_viewport_container_layer_->id());

    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_);
    inner_viewport_container_layer_->AddChild(scroll_layer_);
    root_->AddChild(inner_viewport_container_layer_);

    layer_tree_host_->SetRootLayer(root_);
    layer_tree_host_->GetLayerTree()->RegisterViewportLayers(
        nullptr, root_, scroll_layer_, child_);
  }

  void CommitAndUpdateImplPointers() {
    LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
        root_.get(), root_->bounds());
    inputs.inner_viewport_scroll_layer =
        layer_tree_host_->GetLayerTree()->inner_viewport_scroll_layer();
    inputs.outer_viewport_scroll_layer =
        layer_tree_host_->GetLayerTree()->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> 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->id(),
                                                         delta))
      layer_impl->layer_tree_impl()->DidUpdateScrollOffset(layer_impl->id());
  }
};

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.
  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
  SetFixedContainerSizeDelta(child_impl_, 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));
  SetFixedContainerSizeDelta(child_impl_, 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
  SetFixedContainerSizeDelta(child_impl_, 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));
  SetFixedContainerSizeDelta(child_impl_, 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(grand_child_impl_->render_surface());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_surface_draw_transform,
      grand_child_impl_->render_surface()->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(grand_child_impl_->render_surface());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_surface_draw_transform,
      grand_child_impl_->render_surface()->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
  SetFixedContainerSizeDelta(child_impl_, 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));
  SetFixedContainerSizeDelta(child_impl_, 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 =
      make_scoped_refptr(new LayerWithForcedDrawsContent());
  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(grand_child_impl_->render_surface());
  EXPECT_TRUE(great_grand_child_impl_->render_surface());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_grand_child_surface_draw_transform,
      grand_child_impl_->render_surface()->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,
      great_grand_child_impl_->render_surface()->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(grand_child_impl_->render_surface());
  EXPECT_TRUE(great_grand_child_impl_->render_surface());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_grand_child_surface_draw_transform,
      grand_child_impl_->render_surface()->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,
      great_grand_child_impl_->render_surface()->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
  SetFixedContainerSizeDelta(child_impl_, 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));
  SetFixedContainerSizeDelta(child_impl_, 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 =
      make_scoped_refptr(new LayerWithForcedDrawsContent());
  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(grand_child_impl_->render_surface());
  EXPECT_TRUE(great_grand_child_impl_->render_surface());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_grand_child_surface_draw_transform,
      grand_child_impl_->render_surface()->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,
      great_grand_child_impl_->render_surface()->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(grand_child_impl_->render_surface());
  EXPECT_TRUE(great_grand_child_impl_->render_surface());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_child_transform,
                                  child_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_grand_child_surface_draw_transform,
      grand_child_impl_->render_surface()->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,
      great_grand_child_impl_->render_surface()->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(child_impl_->render_surface());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_surface_draw_transform,
      child_impl_->render_surface()->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(child_impl_->render_surface());
  EXPECT_TRANSFORMATION_MATRIX_EQ(
      expected_surface_draw_transform,
      child_impl_->render_surface()->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
  SetFixedContainerSizeDelta(child_impl_, 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));
  SetFixedContainerSizeDelta(child_impl_, 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
  SetFixedContainerSizeDelta(child_impl_, 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));
  SetFixedContainerSizeDelta(child_impl_, 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));
  SetFixedContainerSizeDelta(child_impl_, 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 =
      make_scoped_refptr(new LayerWithForcedDrawsContent());
  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_->SetScrollClipLayerId(root_->id());
  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,
       ScrollCompensationForInnerViewportBoundsDelta) {
  // This test checks for correct scroll compensation when the fixed-position
  // container is the inner viewport scroll layer and has non-zero bounds delta.
  scoped_refptr<Layer> fixed_child =
      make_scoped_refptr(new LayerWithForcedDrawsContent());
  fixed_child->SetBounds(gfx::Size(300, 300));
  scroll_layer_->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(scroll_layer_impl_, gfx::Vector2d(10, 10));
  scroll_layer_impl_->SetDrawsContent(true);
  SetFixedContainerSizeDelta(scroll_layer_impl_, 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,
                                  scroll_layer_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(scroll_layer_impl_, gfx::Vector2d(10, 10));
  SetFixedContainerSizeDelta(scroll_layer_impl_, 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,
                                  scroll_layer_impl_->DrawTransform());
  EXPECT_TRANSFORMATION_MATRIX_EQ(expected_fixed_child_transform,
                                  fixed_child_impl->DrawTransform());
}

void VerifySerializeAndDeserializeProto(bool is_fixed_position,
                                        bool is_fixed_to_right_edge,
                                        bool is_fixed_to_bottom_edge) {
  LayerPositionConstraint constraint;
  constraint.set_is_fixed_position(is_fixed_position);
  constraint.set_is_fixed_to_right_edge(is_fixed_to_right_edge);
  constraint.set_is_fixed_to_bottom_edge(is_fixed_to_bottom_edge);
  proto::LayerPositionConstraint proto;
  constraint.ToProtobuf(&proto);

  LayerPositionConstraint constraint2;
  constraint2.FromProtobuf(proto);
  EXPECT_EQ(constraint, constraint2);
}

TEST(LayerPositionConstraintSerializationTest, SerializeAndDeserializeProto) {
  VerifySerializeAndDeserializeProto(true, true, true);
  VerifySerializeAndDeserializeProto(true, true, false);
  VerifySerializeAndDeserializeProto(true, false, true);
  VerifySerializeAndDeserializeProto(true, false, false);
  VerifySerializeAndDeserializeProto(false, true, true);
  VerifySerializeAndDeserializeProto(false, true, false);
  VerifySerializeAndDeserializeProto(false, false, true);
  VerifySerializeAndDeserializeProto(false, false, false);
}

}  // namespace
}  // namespace cc
