// Copyright 2011 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_impl.h"

#include "base/stl_util.h"
#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/solid_color_scrollbar_layer_impl.h"
#include "cc/paint/filter_operation.h"
#include "cc/paint/filter_operations.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_impl_task_runner_provider.h"
#include "cc/test/fake_layer_tree_frame_sink.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/test_task_graph_runner.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/tree_synchronizer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/effects/SkBlurImageFilter.h"

namespace cc {
namespace {

#define EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(code_to_test)           \
  root->layer_tree_impl()->ResetAllChangeTracking();                      \
  code_to_test;                                                           \
  EXPECT_FALSE(root->LayerPropertyChanged());                             \
  EXPECT_FALSE(child->LayerPropertyChanged());                            \
  EXPECT_FALSE(grand_child->LayerPropertyChanged());

#define EXECUTE_AND_VERIFY_SUBTREE_CHANGED(code_to_test)             \
  root->layer_tree_impl()->ResetAllChangeTracking();                 \
  code_to_test;                                                      \
  EXPECT_TRUE(root->LayerPropertyChanged());                         \
  EXPECT_TRUE(root->LayerPropertyChangedFromPropertyTrees());        \
  EXPECT_FALSE(root->LayerPropertyChangedNotFromPropertyTrees());    \
  EXPECT_TRUE(child->LayerPropertyChanged());                        \
  EXPECT_TRUE(child->LayerPropertyChangedFromPropertyTrees());       \
  EXPECT_FALSE(child->LayerPropertyChangedNotFromPropertyTrees());   \
  EXPECT_TRUE(grand_child->LayerPropertyChanged());                  \
  EXPECT_TRUE(grand_child->LayerPropertyChangedFromPropertyTrees()); \
  EXPECT_FALSE(grand_child->LayerPropertyChangedNotFromPropertyTrees());

#define EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(code_to_test)               \
  root->layer_tree_impl()->ResetAllChangeTracking();                      \
  root->layer_tree_impl()->property_trees()->full_tree_damaged = false;   \
  code_to_test;                                                           \
  EXPECT_TRUE(root->LayerPropertyChanged());                              \
  EXPECT_FALSE(root->LayerPropertyChangedFromPropertyTrees());            \
  EXPECT_TRUE(root->LayerPropertyChangedNotFromPropertyTrees());          \
  EXPECT_FALSE(child->LayerPropertyChanged());                            \
  EXPECT_FALSE(grand_child->LayerPropertyChanged());

#define VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(code_to_test)                \
  root->layer_tree_impl()->ResetAllChangeTracking();                     \
  host_impl.ForcePrepareToDraw();                                        \
  EXPECT_FALSE(host_impl.active_tree()->needs_update_draw_properties()); \
  code_to_test;                                                          \
  EXPECT_TRUE(host_impl.active_tree()->needs_update_draw_properties());

#define VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(code_to_test)             \
  root->layer_tree_impl()->ResetAllChangeTracking();                     \
  host_impl.ForcePrepareToDraw();                                        \
  EXPECT_FALSE(host_impl.active_tree()->needs_update_draw_properties()); \
  code_to_test;                                                          \
  EXPECT_FALSE(host_impl.active_tree()->needs_update_draw_properties());

static gfx::Vector2dF ScrollDelta(LayerImpl* layer_impl) {
  gfx::ScrollOffset delta = layer_impl->layer_tree_impl()
                                ->property_trees()
                                ->scroll_tree.GetScrollOffsetDeltaForTesting(
                                    layer_impl->element_id());
  return gfx::Vector2dF(delta.x(), delta.y());
}

TEST(LayerImplTest, VerifyPendingLayerChangesAreTrackedProperly) {
  //
  // This test checks that LayerPropertyChanged() has the correct behavior.
  //

  // The constructor on this will fake that we are on the correct thread.
  // Create a simple LayerImpl tree:
  FakeImplTaskRunnerProvider task_runner_provider;
  TestTaskGraphRunner task_graph_runner;
  std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink =
      FakeLayerTreeFrameSink::Create3d();
  FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner);
  host_impl.SetVisible(true);
  EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get()));
  host_impl.CreatePendingTree();
  std::unique_ptr<LayerImpl> root_ptr =
      LayerImpl::Create(host_impl.pending_tree(), 2);
  LayerImpl* root = root_ptr.get();
  host_impl.pending_tree()->SetRootLayerForTesting(std::move(root_ptr));

  root->test_properties()->force_render_surface = true;
  root->SetMasksToBounds(true);
  root->layer_tree_impl()->ResetAllChangeTracking();

  root->test_properties()->AddChild(
      LayerImpl::Create(host_impl.pending_tree(), 7));
  LayerImpl* child = root->test_properties()->children[0];
  child->test_properties()->AddChild(
      LayerImpl::Create(host_impl.pending_tree(), 8));
  LayerImpl* grand_child = child->test_properties()->children[0];
  host_impl.pending_tree()->BuildLayerListAndPropertyTreesForTesting();

  // Adding children is an internal operation and should not mark layers as
  // changed.
  EXPECT_FALSE(root->LayerPropertyChanged());
  EXPECT_FALSE(child->LayerPropertyChanged());
  EXPECT_FALSE(grand_child->LayerPropertyChanged());

  float arbitrary_number = 0.352f;
  gfx::Size arbitrary_size = gfx::Size(111, 222);
  gfx::Point arbitrary_point = gfx::Point(333, 444);

  gfx::Rect arbitrary_rect = gfx::Rect(arbitrary_point, arbitrary_size);
  SkColor arbitrary_color = SkColorSetRGB(10, 20, 30);
  gfx::Transform arbitrary_transform;
  arbitrary_transform.Scale3d(0.1f, 0.2f, 0.3f);
  FilterOperations arbitrary_filters;
  arbitrary_filters.Append(FilterOperation::CreateOpacityFilter(0.5f));

  // These properties are internal, and should not be considered "change" when
  // they are used.
  EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(
      root->SetUpdateRect(arbitrary_rect));
  EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetBounds(arbitrary_size));
  host_impl.pending_tree()->property_trees()->needs_rebuild = true;
  host_impl.pending_tree()->BuildLayerListAndPropertyTreesForTesting();

  // Changing these properties affects the entire subtree of layers.
  EXECUTE_AND_VERIFY_SUBTREE_CHANGED(host_impl.pending_tree()->SetFilterMutated(
      root->element_id(), arbitrary_filters));
  EXECUTE_AND_VERIFY_SUBTREE_CHANGED(host_impl.pending_tree()->SetFilterMutated(
      root->element_id(), FilterOperations()));
  EXECUTE_AND_VERIFY_SUBTREE_CHANGED(
      host_impl.pending_tree()->SetOpacityMutated(root->element_id(),
                                                  arbitrary_number));
  EXECUTE_AND_VERIFY_SUBTREE_CHANGED(
      host_impl.pending_tree()->SetTransformMutated(root->element_id(),
                                                    arbitrary_transform));

  // Changing these properties only affects the layer itself.
  EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->SetDrawsContent(true));
  EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(
      root->SetBackgroundColor(arbitrary_color));

  // Changing these properties does not cause the layer to be marked as changed
  // but does cause the layer to need to push properties.
  EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetElementId(ElementId(2)));

  // After setting all these properties already, setting to the exact same
  // values again should not cause any change.
  EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetMasksToBounds(true));
  EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetContentsOpaque(true));
  EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetDrawsContent(true));
  EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetBounds(root->bounds()));
}

TEST(LayerImplTest, VerifyActiveLayerChangesAreTrackedProperly) {
  FakeImplTaskRunnerProvider task_runner_provider;
  TestTaskGraphRunner task_graph_runner;
  std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink =
      FakeLayerTreeFrameSink::Create3d();
  FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner);
  host_impl.SetVisible(true);
  EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get()));
  std::unique_ptr<LayerImpl> root_ptr =
      LayerImpl::Create(host_impl.active_tree(), 2);
  LayerImpl* root = root_ptr.get();
  host_impl.active_tree()->SetRootLayerForTesting(std::move(root_ptr));

  root->test_properties()->AddChild(
      LayerImpl::Create(host_impl.active_tree(), 7));
  LayerImpl* child = root->test_properties()->children[0];
  root->SetScrollable(gfx::Size(100, 100));
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();

  // Make root the outer viewport container layer. This ensures the later call
  // to |SetViewportBoundsDelta| will be on a viewport layer.
  LayerTreeImpl::ViewportLayerIds viewport_ids;
  viewport_ids.outer_viewport_container = root->id();
  host_impl.active_tree()->SetViewportLayersFromIds(viewport_ids);

  root->SetMasksToBounds(true);
  host_impl.active_tree()->property_trees()->needs_rebuild = true;
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
  root->layer_tree_impl()->ResetAllChangeTracking();

  // SetViewportBoundsDelta changes subtree only when masks_to_bounds is true.
  root->SetViewportBoundsDelta(gfx::Vector2d(222, 333));
  EXPECT_TRUE(root->LayerPropertyChanged());
  EXPECT_TRUE(root->LayerPropertyChangedFromPropertyTrees());
  EXPECT_FALSE(root->LayerPropertyChangedNotFromPropertyTrees());
  EXPECT_TRUE(host_impl.active_tree()->property_trees()->full_tree_damaged);

  root->SetMasksToBounds(false);
  host_impl.active_tree()->property_trees()->needs_rebuild = true;
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
  root->layer_tree_impl()->ResetAllChangeTracking();

  // SetViewportBoundsDelta does not change the subtree without masks_to_bounds.
  root->SetViewportBoundsDelta(gfx::Vector2d(333, 444));
  EXPECT_TRUE(root->LayerPropertyChanged());
  EXPECT_FALSE(root->LayerPropertyChangedFromPropertyTrees());
  EXPECT_TRUE(root->LayerPropertyChangedNotFromPropertyTrees());
  EXPECT_FALSE(host_impl.active_tree()->property_trees()->full_tree_damaged);

  host_impl.active_tree()->property_trees()->needs_rebuild = true;
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
  root->layer_tree_impl()->ResetAllChangeTracking();

  // Ensure some node is affected by the outer viewport bounds delta. This
  // ensures the later call to |SetViewportBoundsDelta| will require a
  // transform tree update.
  TransformTree& transform_tree =
      host_impl.active_tree()->property_trees()->transform_tree;
  transform_tree.AddNodeAffectedByOuterViewportBoundsDelta(
      child->transform_tree_index());
  EXPECT_FALSE(transform_tree.needs_update());
  root->SetViewportBoundsDelta(gfx::Vector2d(111, 222));
  EXPECT_TRUE(transform_tree.needs_update());

  host_impl.active_tree()->property_trees()->needs_rebuild = true;
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
  root->layer_tree_impl()->ResetAllChangeTracking();

  // Ensure scrolling changes the transform tree but does not damage all trees.
  root->ScrollBy(gfx::Vector2d(7, 9));
  EXPECT_TRUE(transform_tree.needs_update());
  EXPECT_TRUE(root->LayerPropertyChanged());
  EXPECT_TRUE(root->LayerPropertyChangedFromPropertyTrees());
  EXPECT_FALSE(root->LayerPropertyChangedNotFromPropertyTrees());
  EXPECT_FALSE(host_impl.active_tree()->property_trees()->full_tree_damaged);
}

TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties) {
  FakeImplTaskRunnerProvider task_runner_provider;
  TestTaskGraphRunner task_graph_runner;
  std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink =
      FakeLayerTreeFrameSink::Create3d();
  FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner);
  host_impl.SetVisible(true);
  EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get()));
  host_impl.active_tree()->SetRootLayerForTesting(
      LayerImpl::Create(host_impl.active_tree(), 1));
  LayerImpl* root = host_impl.active_tree()->root_layer_for_testing();
  std::unique_ptr<LayerImpl> layer_ptr =
      LayerImpl::Create(host_impl.active_tree(), 2);
  LayerImpl* layer = layer_ptr.get();
  root->test_properties()->AddChild(std::move(layer_ptr));
  layer->SetScrollable(gfx::Size(1, 1));
  std::unique_ptr<LayerImpl> layer2_ptr =
      LayerImpl::Create(host_impl.active_tree(), 3);
  LayerImpl* layer2 = layer2_ptr.get();
  root->test_properties()->AddChild(std::move(layer2_ptr));
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
  DCHECK(host_impl.CanDraw());

  float arbitrary_number = 0.352f;
  gfx::Size arbitrary_size = gfx::Size(111, 222);
  gfx::Vector2d arbitrary_vector2d = gfx::Vector2d(111, 222);
  gfx::Size large_size = gfx::Size(1000, 1000);
  SkColor arbitrary_color = SkColorSetRGB(10, 20, 30);
  gfx::Transform arbitrary_transform;
  arbitrary_transform.Scale3d(0.1f, 0.2f, 0.3f);
  FilterOperations arbitrary_filters;
  arbitrary_filters.Append(FilterOperation::CreateOpacityFilter(0.5f));

  // Set layer to draw content so that their draw property by property trees is
  // verified.
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetDrawsContent(true));
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer2->SetDrawsContent(true));

  // Create a render surface, because we must have a render surface if we have
  // filters.
  layer->test_properties()->force_render_surface = true;
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();

  // Related filter functions.
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(host_impl.active_tree()->SetFilterMutated(
      root->element_id(), arbitrary_filters));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
      host_impl.active_tree()->SetFilterMutated(root->element_id(),
                                                arbitrary_filters));
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(host_impl.active_tree()->SetFilterMutated(
      root->element_id(), FilterOperations()));
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(host_impl.active_tree()->SetFilterMutated(
      root->element_id(), arbitrary_filters));

  // Related scrolling functions.
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetBounds(large_size));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetBounds(large_size));
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
  host_impl.active_tree()->set_needs_update_draw_properties();
  host_impl.active_tree()->UpdateDrawProperties();
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->ScrollBy(arbitrary_vector2d));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->ScrollBy(gfx::Vector2d()));
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
      layer->layer_tree_impl()->DidUpdateScrollOffset(layer->element_id()));
  layer->layer_tree_impl()
      ->property_trees()
      ->scroll_tree.SetScrollOffsetDeltaForTesting(layer->element_id(),
                                                   gfx::Vector2dF());
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetCurrentScrollOffset(
      gfx::ScrollOffset(arbitrary_vector2d.x(), arbitrary_vector2d.y())));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetCurrentScrollOffset(
      gfx::ScrollOffset(arbitrary_vector2d.x(), arbitrary_vector2d.y())));

  // Unrelated functions, always set to new values, always set needs update.
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
  host_impl.active_tree()->set_needs_update_draw_properties();
  host_impl.active_tree()->UpdateDrawProperties();
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetMasksToBounds(true);
                                      layer->NoteLayerPropertyChanged());
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetContentsOpaque(true);
                                      layer->NoteLayerPropertyChanged());
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
      layer->SetBackgroundColor(arbitrary_color));
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
      host_impl.active_tree()->SetOpacityMutated(layer->element_id(),
                                                 arbitrary_number));
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(
      host_impl.active_tree()->SetTransformMutated(layer->element_id(),
                                                   arbitrary_transform));
  VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetBounds(arbitrary_size);
                                      layer->NoteLayerPropertyChanged());

  // Unrelated functions, set to the same values, no needs update.
  layer->test_properties()->filters = arbitrary_filters;
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
      host_impl.active_tree()->SetFilterMutated(layer->element_id(),
                                                arbitrary_filters));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetMasksToBounds(true));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetContentsOpaque(true));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetDrawsContent(true));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(
      layer->SetBackgroundColor(arbitrary_color));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetBounds(arbitrary_size));
  VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(layer->SetElementId(ElementId(2)));
}

TEST(LayerImplTest, SafeOpaqueBackgroundColor) {
  FakeImplTaskRunnerProvider task_runner_provider;
  TestTaskGraphRunner task_graph_runner;
  std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink =
      FakeLayerTreeFrameSink::Create3d();
  FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner);
  host_impl.SetVisible(true);
  EXPECT_TRUE(host_impl.InitializeFrameSink(layer_tree_frame_sink.get()));
  host_impl.active_tree()->SetRootLayerForTesting(
      LayerImpl::Create(host_impl.active_tree(), 1));
  LayerImpl* layer = host_impl.active_tree()->root_layer_for_testing();

  for (int contents_opaque = 0; contents_opaque < 2; ++contents_opaque) {
    for (int layer_opaque = 0; layer_opaque < 2; ++layer_opaque) {
      for (int host_opaque = 0; host_opaque < 2; ++host_opaque) {
        layer->SetContentsOpaque(!!contents_opaque);
        layer->SetBackgroundColor(layer_opaque ? SK_ColorRED
                                               : SK_ColorTRANSPARENT);
        host_impl.active_tree()->set_background_color(
            host_opaque ? SK_ColorRED : SK_ColorTRANSPARENT);
        host_impl.active_tree()->property_trees()->needs_rebuild = true;
        host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();

        SkColor safe_color = layer->SafeOpaqueBackgroundColor();
        if (contents_opaque) {
          EXPECT_EQ(SkColorGetA(safe_color), 255u)
              << "Flags: " << contents_opaque << ", " << layer_opaque << ", "
              << host_opaque << "\n";
        } else {
          EXPECT_NE(SkColorGetA(safe_color), 255u)
              << "Flags: " << contents_opaque << ", " << layer_opaque << ", "
              << host_opaque << "\n";
        }
      }
    }
  }
}

TEST(LayerImplTest, PerspectiveTransformHasReasonableScale) {
  FakeImplTaskRunnerProvider task_runner_provider;
  TestTaskGraphRunner task_graph_runner;
  std::unique_ptr<LayerTreeFrameSink> layer_tree_frame_sink =
      FakeLayerTreeFrameSink::Create3d();
  LayerTreeSettings settings;
  FakeLayerTreeHostImpl host_impl(settings, &task_runner_provider,
                                  &task_graph_runner);
  auto owned_layer = LayerImpl::Create(host_impl.active_tree(), 1);
  LayerImpl* layer = owned_layer.get();
  layer->SetBounds(gfx::Size(10, 10));
  layer->set_contributes_to_drawn_render_surface(true);
  host_impl.active_tree()->SetRootLayerForTesting(std::move(owned_layer));
  host_impl.active_tree()->BuildLayerListAndPropertyTreesForTesting();

  // Ensure that we are close to the maximum scale for the matrix.
  {
    gfx::Transform transform;
    transform.Scale(10.2f, 15.1f);
    transform.ApplyPerspectiveDepth(10);
    layer->draw_properties().screen_space_transform = transform;

    ASSERT_TRUE(layer->ScreenSpaceTransform().HasPerspective());
    EXPECT_FLOAT_EQ(15.f, layer->GetIdealContentsScale());
  }
  // Ensure that we don't fall below the device scale factor.
  {
    gfx::Transform transform;
    transform.Scale(0.1f, 0.2f);
    transform.ApplyPerspectiveDepth(10);
    layer->draw_properties().screen_space_transform = transform;

    ASSERT_TRUE(layer->ScreenSpaceTransform().HasPerspective());
    EXPECT_FLOAT_EQ(1.f, layer->GetIdealContentsScale());
  }
  // Ensure that large scales don't end up extremely large.
  {
    gfx::Transform transform;
    transform.Scale(10000.1f, 10000.2f);
    transform.ApplyPerspectiveDepth(10);
    layer->draw_properties().screen_space_transform = transform;

    ASSERT_TRUE(layer->ScreenSpaceTransform().HasPerspective());
    EXPECT_FLOAT_EQ(127.f, layer->GetIdealContentsScale());
  }
  // Test case from crbug.com/766021.
  {
    gfx::Transform transform(-0.9397f, -0.7019f, 0.2796f, 2383.4521f,   // row 1
                             -0.0038f, 0.0785f, 1.0613f, 1876.4553f,    // row 2
                             -0.0835f, 0.9081f, -0.4105f, -2208.3035f,  // row 3
                             0.0001f, -0.0008f, 0.0003f, 2.8435f);      // row 4
    layer->draw_properties().screen_space_transform = transform;

    ASSERT_TRUE(layer->ScreenSpaceTransform().HasPerspective());
    EXPECT_FLOAT_EQ(1.f, layer->GetIdealContentsScale());
  }
}

class LayerImplScrollTest : public testing::Test {
 public:
  LayerImplScrollTest() : LayerImplScrollTest(LayerTreeSettings()) {}

  explicit LayerImplScrollTest(const LayerTreeSettings& settings)
      : host_impl_(settings, &task_runner_provider_, &task_graph_runner_),
        root_id_(7) {
    host_impl_.active_tree()->SetRootLayerForTesting(
        LayerImpl::Create(host_impl_.active_tree(), root_id_));
    host_impl_.active_tree()
        ->root_layer_for_testing()
        ->test_properties()
        ->AddChild(LayerImpl::Create(host_impl_.active_tree(), root_id_ + 1));
    // Set the max scroll offset by noting that the root layer has bounds (1,1),
    // thus whatever bounds are set for the layer will be the max scroll
    // offset plus 1 in each direction.
    host_impl_.active_tree()->root_layer_for_testing()->SetBounds(
        gfx::Size(1, 1));
    layer()->SetScrollable(gfx::Size(1, 1));
    gfx::Vector2d max_scroll_offset(51, 81);
    layer()->SetBounds(gfx::Size(max_scroll_offset.x(), max_scroll_offset.y()));
    host_impl_.active_tree()->BuildLayerListAndPropertyTreesForTesting();
  }

  LayerImpl* layer() {
    return host_impl_.active_tree()
        ->root_layer_for_testing()
        ->test_properties()
        ->children[0];
  }

  ScrollTree* scroll_tree(LayerImpl* layer_impl) {
    return &layer_impl->layer_tree_impl()->property_trees()->scroll_tree;
  }

  LayerTreeHostImpl& host_impl() { return host_impl_; }

  LayerTreeImpl* tree() { return host_impl_.active_tree(); }

 private:
  FakeImplTaskRunnerProvider task_runner_provider_;
  TestTaskGraphRunner task_graph_runner_;
  FakeLayerTreeHostImpl host_impl_;
  int root_id_;
};

class CommitToPendingTreeLayerImplScrollTest : public LayerImplScrollTest {
 public:
  CommitToPendingTreeLayerImplScrollTest() : LayerImplScrollTest(settings()) {}

  LayerTreeSettings settings() {
    LayerTreeSettings tree_settings;
    tree_settings.commit_to_active_tree = false;
    return tree_settings;
  }
};

TEST_F(LayerImplScrollTest, ScrollByWithZeroOffset) {
  // Test that LayerImpl::ScrollBy only affects ScrollDelta and total scroll
  // offset is bounded by the range [0, max scroll offset].

  EXPECT_VECTOR_EQ(gfx::Vector2dF(), layer()->CurrentScrollOffset());
  EXPECT_VECTOR_EQ(gfx::Vector2dF(),
                   scroll_tree(layer())->GetScrollOffsetBaseForTesting(
                       layer()->element_id()));
  EXPECT_VECTOR_EQ(gfx::Vector2dF(), ScrollDelta(layer()));

  layer()->ScrollBy(gfx::Vector2dF(-100, 100));
  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 80), layer()->CurrentScrollOffset());

  EXPECT_VECTOR_EQ(ScrollDelta(layer()), layer()->CurrentScrollOffset());
  EXPECT_VECTOR_EQ(gfx::Vector2dF(),
                   scroll_tree(layer())->GetScrollOffsetBaseForTesting(
                       layer()->element_id()));

  layer()->ScrollBy(gfx::Vector2dF(100, -100));
  EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 0), layer()->CurrentScrollOffset());

  EXPECT_VECTOR_EQ(ScrollDelta(layer()), layer()->CurrentScrollOffset());
  EXPECT_VECTOR_EQ(gfx::Vector2dF(),
                   scroll_tree(layer())->GetScrollOffsetBaseForTesting(
                       layer()->element_id()));
}

TEST_F(LayerImplScrollTest, ScrollByWithNonZeroOffset) {
  gfx::ScrollOffset scroll_offset(10, 5);
  scroll_tree(layer())->UpdateScrollOffsetBaseForTesting(layer()->element_id(),
                                                         scroll_offset);

  EXPECT_VECTOR_EQ(scroll_offset, layer()->CurrentScrollOffset());
  EXPECT_VECTOR_EQ(scroll_offset,
                   scroll_tree(layer())->GetScrollOffsetBaseForTesting(
                       layer()->element_id()));
  EXPECT_VECTOR_EQ(gfx::Vector2dF(), ScrollDelta(layer()));

  layer()->ScrollBy(gfx::Vector2dF(-100, 100));
  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 80), layer()->CurrentScrollOffset());

  EXPECT_VECTOR_EQ(
      gfx::ScrollOffsetWithDelta(scroll_offset, ScrollDelta(layer())),
      layer()->CurrentScrollOffset());
  EXPECT_VECTOR_EQ(scroll_offset,
                   scroll_tree(layer())->GetScrollOffsetBaseForTesting(
                       layer()->element_id()));

  layer()->ScrollBy(gfx::Vector2dF(100, -100));
  EXPECT_VECTOR_EQ(gfx::Vector2dF(50, 0), layer()->CurrentScrollOffset());

  EXPECT_VECTOR_EQ(
      gfx::ScrollOffsetWithDelta(scroll_offset, ScrollDelta(layer())),
      layer()->CurrentScrollOffset());
  EXPECT_VECTOR_EQ(scroll_offset,
                   scroll_tree(layer())->GetScrollOffsetBaseForTesting(
                       layer()->element_id()));
}

TEST_F(LayerImplScrollTest, ApplySentScrollsNoListener) {
  gfx::ScrollOffset scroll_offset(10, 5);
  gfx::Vector2dF scroll_delta(20.5f, 8.5f);
  gfx::Vector2d sent_scroll_delta(12, -3);

  scroll_tree(layer())->UpdateScrollOffsetBaseForTesting(layer()->element_id(),
                                                         scroll_offset);
  layer()->ScrollBy(sent_scroll_delta);
  scroll_tree(layer())->CollectScrollDeltasForTesting();
  layer()->SetCurrentScrollOffset(scroll_offset +
                                  gfx::ScrollOffset(scroll_delta));

  EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(scroll_offset, scroll_delta),
                   layer()->CurrentScrollOffset());
  EXPECT_VECTOR_EQ(scroll_delta, ScrollDelta(layer()));
  EXPECT_VECTOR_EQ(scroll_offset,
                   scroll_tree(layer())->GetScrollOffsetBaseForTesting(
                       layer()->element_id()));

  scroll_tree(layer())->ApplySentScrollDeltasFromAbortedCommit();

  EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(scroll_offset, scroll_delta),
                   layer()->CurrentScrollOffset());
  EXPECT_VECTOR_EQ(scroll_delta - sent_scroll_delta, ScrollDelta(layer()));
  EXPECT_VECTOR_EQ(gfx::ScrollOffsetWithDelta(scroll_offset, sent_scroll_delta),
                   scroll_tree(layer())->GetScrollOffsetBaseForTesting(
                       layer()->element_id()));
}

TEST_F(LayerImplScrollTest, ScrollUserUnscrollableLayer) {
  gfx::ScrollOffset scroll_offset(10, 5);
  gfx::Vector2dF scroll_delta(20.5f, 8.5f);

  layer()->test_properties()->user_scrollable_vertical = false;
  layer()->layer_tree_impl()->property_trees()->needs_rebuild = true;
  layer()->layer_tree_impl()->BuildLayerListAndPropertyTreesForTesting();
  scroll_tree(layer())->UpdateScrollOffsetBaseForTesting(layer()->element_id(),
                                                         scroll_offset);
  gfx::Vector2dF unscrolled = layer()->ScrollBy(scroll_delta);

  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 8.5f), unscrolled);
  EXPECT_VECTOR_EQ(gfx::Vector2dF(30.5f, 5), layer()->CurrentScrollOffset());
}

// |LayerImpl::all_touch_action_regions_| is a cache of all regions on
// |LayerImpl::touch_action_region_| and must be invalidated on changes.
TEST_F(LayerImplScrollTest, TouchActionRegionCacheInvalidation) {
  host_impl().CreatePendingTree();
  std::unique_ptr<LayerImpl> pending_layer =
      LayerImpl::Create(host_impl().pending_tree(), 2);

  TouchActionRegion region;
  region.Union(kTouchActionNone, gfx::Rect(0, 0, 50, 50));
  pending_layer->SetTouchActionRegion(region);

  // The values for GetAllTouchActionRegions should be correct on both layers.
  // Note that querying GetAllTouchActionRegions will update the cached value
  // in |LayerImpl::all_touch_action_regions_|.
  EXPECT_EQ(pending_layer->GetAllTouchActionRegions(), region.GetAllRegions());
  EXPECT_EQ(layer()->GetAllTouchActionRegions(), Region());

  pending_layer->PushPropertiesTo(layer());

  // After pushing properties, the value for GetAllTouchActionRegions should
  // not be stale.
  EXPECT_EQ(pending_layer->GetAllTouchActionRegions(), region.GetAllRegions());
  EXPECT_EQ(layer()->GetAllTouchActionRegions(), region.GetAllRegions());
}

TEST_F(CommitToPendingTreeLayerImplScrollTest,
       PushPropertiesToMirrorsCurrentScrollOffset) {
  gfx::ScrollOffset scroll_offset(10, 5);
  gfx::Vector2dF scroll_delta(12, 18);

  host_impl().CreatePendingTree();

  scroll_tree(layer())->UpdateScrollOffsetBaseForTesting(layer()->element_id(),
                                                         scroll_offset);
  gfx::Vector2dF unscrolled = layer()->ScrollBy(scroll_delta);

  EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), unscrolled);
  EXPECT_VECTOR_EQ(gfx::Vector2dF(22, 23), layer()->CurrentScrollOffset());

  scroll_tree(layer())->CollectScrollDeltasForTesting();

  std::unique_ptr<LayerImpl> pending_layer =
      LayerImpl::Create(host_impl().sync_tree(), layer()->id());
  pending_layer->SetElementId(
      LayerIdToElementIdForTesting(pending_layer->id()));
  scroll_tree(pending_layer.get())
      ->UpdateScrollOffsetBaseForTesting(pending_layer->element_id(),
                                         layer()->CurrentScrollOffset());

  pending_layer->PushPropertiesTo(layer());

  EXPECT_VECTOR_EQ(gfx::Vector2dF(22, 23), layer()->CurrentScrollOffset());
  EXPECT_VECTOR_EQ(layer()->CurrentScrollOffset(),
                   pending_layer->CurrentScrollOffset());
}

}  // namespace
}  // namespace cc
