// 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/trees/property_tree_builder.h"

#include <memory>
#include <utility>

#include "cc/animation/keyframed_animation_curve.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/picture_layer.h"
#include "cc/layers/render_surface_impl.h"
#include "cc/layers/texture_layer.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/layer_tree_impl_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
#include "ui/gfx/transform.h"

namespace cc {
namespace {

class PropertyTreeBuilderTest : public LayerTreeImplTestBase,
                                public testing::Test {
 public:
  PropertyTreeBuilderTest() : LayerTreeImplTestBase(LayerTreeSettings()) {}

  void UpdateMainDrawProperties(float device_scale_factor = 1.0f) {
    SetDeviceScaleAndUpdateViewportRect(host(), device_scale_factor);
    UpdateDrawProperties(host());
  }

  LayerImpl* ImplOf(const scoped_refptr<Layer>& layer) {
    return layer ? host_impl()->active_tree()->LayerById(layer->id()) : nullptr;
  }
  RenderSurfaceImpl* GetRenderSurfaceImpl(const scoped_refptr<Layer>& layer) {
    return GetRenderSurface(ImplOf(layer));
  }

  // Updates main thread draw properties, commits main thread tree to
  // impl-side pending tree, and updates pending tree draw properties.
  void Commit(float device_scale_factor = 1.0f) {
    UpdateMainDrawProperties(device_scale_factor);
    if (!host_impl()->pending_tree())
      host_impl()->CreatePendingTree();
    host()->CommitAndCreatePendingTree();
    // TODO(https://crbug.com/939968) This call should be handled by
    // FakeLayerTreeHost instead of manually pushing the properties from the
    // layer tree host to the pending tree.
    host()->PushLayerTreePropertiesTo(host_impl()->pending_tree());

    UpdateDrawProperties(host_impl()->pending_tree());
  }

  // Calls Commit(), then activates the pending tree, and updates active tree
  // draw properties.
  void CommitAndActivate(float device_scale_factor = 1.0f) {
    Commit(device_scale_factor);
    host_impl()->ActivateSyncTree();
    DCHECK_EQ(device_scale_factor,
              host_impl()->active_tree()->device_scale_factor());
    UpdateActiveTreeDrawProperties(device_scale_factor);
  }

  const RenderSurfaceList& GetRenderSurfaceList() {
    return host_impl()->active_tree()->GetRenderSurfaceList();
  }
};

TEST_F(PropertyTreeBuilderTest, EffectTreeTransformIdTest) {
  // Tests that effect tree node gets a valid transform id when a layer
  // has opacity but doesn't create a render surface.
  auto parent = Layer::Create();
  host()->SetRootLayer(parent);
  auto child = Layer::Create();
  parent->AddChild(child);
  child->SetIsDrawable(true);

  parent->SetBounds(gfx::Size(100, 100));
  child->SetPosition(gfx::PointF(10, 10));
  child->SetBounds(gfx::Size(100, 100));
  child->SetOpacity(0.f);
  UpdateMainDrawProperties();
  EffectNode* node = GetEffectNode(child.get());
  const int transform_tree_size =
      GetPropertyTrees(parent.get())->transform_tree.next_available_id();
  EXPECT_LT(node->transform_id, transform_tree_size);
}

TEST_F(PropertyTreeBuilderTest, RenderSurfaceForNonAxisAlignedClipping) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto rotated_and_transparent = Layer::Create();
  root->AddChild(rotated_and_transparent);
  auto clips_subtree = Layer::Create();
  rotated_and_transparent->AddChild(clips_subtree);
  auto draws_content = Layer::Create();
  clips_subtree->AddChild(draws_content);

  root->SetBounds(gfx::Size(10, 10));
  rotated_and_transparent->SetBounds(gfx::Size(10, 10));
  rotated_and_transparent->SetOpacity(0.5f);
  gfx::Transform rotate;
  rotate.Rotate(2);
  rotated_and_transparent->SetTransform(rotate);
  clips_subtree->SetBounds(gfx::Size(10, 10));
  clips_subtree->SetMasksToBounds(true);
  draws_content->SetBounds(gfx::Size(10, 10));
  draws_content->SetIsDrawable(true);

  UpdateMainDrawProperties();
  EXPECT_TRUE(GetEffectNode(clips_subtree.get())->HasRenderSurface());
}

TEST_F(PropertyTreeBuilderTest, EffectNodesForNonAxisAlignedClips) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto rotate_and_clip = Layer::Create();
  root->AddChild(rotate_and_clip);
  auto only_clip = Layer::Create();
  rotate_and_clip->AddChild(only_clip);
  auto rotate_and_clip2 = Layer::Create();
  only_clip->AddChild(rotate_and_clip2);

  gfx::Transform rotate;
  rotate.Rotate(2);
  root->SetBounds(gfx::Size(10, 10));
  rotate_and_clip->SetBounds(gfx::Size(10, 10));
  rotate_and_clip->SetTransform(rotate);
  rotate_and_clip->SetMasksToBounds(true);
  only_clip->SetBounds(gfx::Size(10, 10));
  only_clip->SetMasksToBounds(true);
  rotate_and_clip2->SetBounds(gfx::Size(10, 10));
  rotate_and_clip2->SetTransform(rotate);
  rotate_and_clip2->SetMasksToBounds(true);

  UpdateMainDrawProperties();
  // non-axis aligned clip should create an effect node
  EXPECT_NE(root->effect_tree_index(), rotate_and_clip->effect_tree_index());
  // Since only_clip's clip is in the same non-axis aligned space as
  // rotate_and_clip's clip, no new effect node should be created.
  EXPECT_EQ(rotate_and_clip->effect_tree_index(),
            only_clip->effect_tree_index());
  // rotate_and_clip2's clip and only_clip's clip are in different non-axis
  // aligned spaces. So, new effect node should be created.
  EXPECT_NE(rotate_and_clip2->effect_tree_index(),
            only_clip->effect_tree_index());
}

TEST_F(PropertyTreeBuilderTest,
       RenderSurfaceListForRenderSurfaceWithClippedLayer) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto render_surface1 = Layer::Create();
  root->AddChild(render_surface1);
  auto child = Layer::Create();
  render_surface1->AddChild(child);

  root->SetBounds(gfx::Size(10, 10));
  root->SetMasksToBounds(true);
  render_surface1->SetBounds(gfx::Size(10, 10));
  render_surface1->SetForceRenderSurfaceForTesting(true);
  child->SetIsDrawable(true);
  child->SetPosition(gfx::PointF(30.f, 30.f));
  child->SetBounds(gfx::Size(10, 10));

  CommitAndActivate();

  // The child layer's content is entirely outside the root's clip rect, so
  // the intermediate render surface should not be listed here, even if it was
  // forced to be created. Render surfaces without children or visible content
  // are unexpected at draw time (e.g. we might try to create a content texture
  // of size 0).
  ASSERT_TRUE(GetRenderSurfaceImpl(root));
  EXPECT_EQ(1U, GetRenderSurfaceList().size());
}

TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForTransparentChild) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto render_surface1 = Layer::Create();
  root->AddChild(render_surface1);
  auto child = Layer::Create();
  render_surface1->AddChild(child);

  root->SetBounds(gfx::Size(10, 10));
  render_surface1->SetBounds(gfx::Size(10, 10));
  render_surface1->SetForceRenderSurfaceForTesting(true);
  render_surface1->SetOpacity(0.f);
  child->SetBounds(gfx::Size(10, 10));
  child->SetIsDrawable(true);

  CommitAndActivate();

  // Since the layer is transparent, render_surface1_impl->GetRenderSurface()
  // should not have gotten added anywhere.  Also, the drawable content rect
  // should not have been extended by the children.
  ASSERT_TRUE(GetRenderSurfaceImpl(root));
  EXPECT_EQ(0, GetRenderSurfaceImpl(root)->num_contributors());
  EXPECT_EQ(1U, GetRenderSurfaceList().size());
  EXPECT_EQ(static_cast<viz::CompositorRenderPassId>(root->id()),
            GetRenderSurfaceList().at(0)->render_pass_id());
  EXPECT_EQ(gfx::Rect(), ImplOf(root)->visible_drawable_content_rect());
}

TEST_F(PropertyTreeBuilderTest,
       RenderSurfaceListForTransparentChildWithBackdropFilter) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto render_surface1 = Layer::Create();
  root->AddChild(render_surface1);
  auto child = Layer::Create();
  render_surface1->AddChild(child);

  root->SetBounds(gfx::Size(10, 10));
  render_surface1->SetBounds(gfx::Size(10, 10));
  render_surface1->SetForceRenderSurfaceForTesting(true);
  render_surface1->SetOpacity(0.f);
  render_surface1->SetIsDrawable(true);
  FilterOperations filters;
  filters.Append(FilterOperation::CreateBlurFilter(1.5f));
  render_surface1->SetBackdropFilters(filters);
  child->SetBounds(gfx::Size(10, 10));
  child->SetIsDrawable(true);
  host()->SetElementIdsForTesting();

  CommitAndActivate();
  EXPECT_EQ(2U, GetRenderSurfaceList().size());

  // The layer is fully transparent, but has a backdrop filter, so it
  // shouldn't be skipped and should be drawn.
  ASSERT_TRUE(GetRenderSurfaceImpl(root));
  EXPECT_EQ(1, GetRenderSurfaceImpl(root)->num_contributors());
  EXPECT_EQ(gfx::RectF(0, 0, 10, 10),
            GetRenderSurfaceImpl(root)->DrawableContentRect());
  EXPECT_TRUE(GetEffectNode(ImplOf(render_surface1))->is_drawn);

  // When root is transparent, the layer should not be drawn.
  host_impl()->active_tree()->SetOpacityMutated(root->element_id(), 0.f);
  host_impl()->active_tree()->SetOpacityMutated(render_surface1->element_id(),
                                                1.f);
  ImplOf(render_surface1)->set_visible_layer_rect(gfx::Rect());
  UpdateActiveTreeDrawProperties();

  EXPECT_FALSE(GetEffectNode(ImplOf(render_surface1))->is_drawn);
  EXPECT_EQ(gfx::Rect(), ImplOf(render_surface1)->visible_layer_rect());
}

TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForFilter) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto parent = Layer::Create();
  root->AddChild(parent);
  auto child1 = Layer::Create();
  parent->AddChild(child1);
  auto child2 = Layer::Create();
  parent->AddChild(child2);

  gfx::Transform scale_matrix;
  scale_matrix.Scale(2.0f, 2.0f);

  root->SetBounds(gfx::Size(100, 100));
  parent->SetTransform(scale_matrix);
  FilterOperations filters;
  filters.Append(FilterOperation::CreateBlurFilter(10.0f));
  parent->SetFilters(filters);
  parent->SetForceRenderSurfaceForTesting(true);
  child1->SetBounds(gfx::Size(25, 25));
  child1->SetIsDrawable(true);
  child1->SetForceRenderSurfaceForTesting(true);
  child2->SetPosition(gfx::PointF(25, 25));
  child2->SetBounds(gfx::Size(25, 25));
  child2->SetIsDrawable(true);
  child2->SetForceRenderSurfaceForTesting(true);

  CommitAndActivate();

  ASSERT_TRUE(GetRenderSurfaceImpl(parent));
  EXPECT_EQ(2, GetRenderSurfaceImpl(parent)->num_contributors());
  EXPECT_EQ(4U, GetRenderSurfaceList().size());

  // The rectangle enclosing child1 and child2 (0,0 50x50), expanded for the
  // blur (-30,-30 110x110), and then scaled by the scale matrix
  // (-60,-60 220x220).
  EXPECT_EQ(gfx::RectF(-60, -60, 220, 220),
            GetRenderSurfaceImpl(parent)->DrawableContentRect());
}

TEST_F(PropertyTreeBuilderTest, ForceRenderSurface) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto render_surface1 = Layer::Create();
  root->AddChild(render_surface1);
  auto child = Layer::Create();
  render_surface1->AddChild(child);

  root->SetBounds(gfx::Size(10, 10));
  render_surface1->SetBounds(gfx::Size(10, 10));
  render_surface1->SetForceRenderSurfaceForTesting(true);
  child->SetBounds(gfx::Size(10, 10));
  child->SetIsDrawable(true);

  CommitAndActivate();

  // The root layer always creates a render surface
  EXPECT_TRUE(GetRenderSurfaceImpl(root));
  EXPECT_NE(GetRenderSurfaceImpl(render_surface1), GetRenderSurfaceImpl(root));

  render_surface1->SetForceRenderSurfaceForTesting(false);
  CommitAndActivate();

  EXPECT_TRUE(GetRenderSurfaceImpl(root));
  EXPECT_EQ(GetRenderSurfaceImpl(render_surface1), GetRenderSurfaceImpl(root));
}

TEST_F(PropertyTreeBuilderTest, VisibleRectWithClippingAndFilters) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto clip = Layer::Create();
  root->AddChild(clip);
  auto filter = Layer::Create();
  clip->AddChild(filter);
  auto filter_child = Layer::Create();
  filter->AddChild(filter_child);

  root->SetBounds(gfx::Size(100, 100));
  clip->SetBounds(gfx::Size(10, 10));
  filter->SetForceRenderSurfaceForTesting(true);
  filter_child->SetBounds(gfx::Size(2000, 2000));
  filter_child->SetPosition(gfx::PointF(-50, -50));
  filter_child->SetIsDrawable(true);

  clip->SetMasksToBounds(true);

  CommitAndActivate();

  EXPECT_EQ(gfx::Rect(50, 50, 10, 10),
            ImplOf(filter_child)->visible_layer_rect());
  EXPECT_EQ(gfx::Rect(10, 10), GetRenderSurfaceImpl(filter)->content_rect());

  FilterOperations blur_filter;
  blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f));
  filter->SetFilters(blur_filter);

  CommitAndActivate();

  EXPECT_EQ(gfx::Rect(38, 38, 34, 34),
            ImplOf(filter_child)->visible_layer_rect());
  EXPECT_EQ(gfx::Rect(-12, -12, 34, 34),
            GetRenderSurfaceImpl(filter)->content_rect());

  gfx::Transform vertical_flip;
  vertical_flip.Scale(1, -1);
  sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>(
      SkMatrix(vertical_flip.matrix()), kLow_SkFilterQuality, nullptr);
  FilterOperations reflection_filter;
  reflection_filter.Append(
      FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>(
          SkBlendMode::kSrcOver, std::move(flip_filter), nullptr)));
  filter->SetFilters(reflection_filter);

  CommitAndActivate();

  EXPECT_EQ(gfx::Rect(50, 40, 10, 20),
            ImplOf(filter_child)->visible_layer_rect());
  EXPECT_EQ(gfx::Rect(0, -10, 10, 20),
            GetRenderSurfaceImpl(filter)->content_rect());
}

TEST_F(PropertyTreeBuilderTest, VisibleRectWithScalingClippingAndFilters) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto scale = Layer::Create();
  root->AddChild(scale);
  auto clip = Layer::Create();
  scale->AddChild(clip);
  auto filter = Layer::Create();
  clip->AddChild(filter);
  auto filter_child = Layer::Create();
  filter->AddChild(filter_child);

  root->SetBounds(gfx::Size(100, 100));
  clip->SetBounds(gfx::Size(10, 10));
  filter->SetForceRenderSurfaceForTesting(true);
  filter_child->SetBounds(gfx::Size(2000, 2000));
  filter_child->SetPosition(gfx::PointF(-50, -50));
  filter_child->SetIsDrawable(true);

  clip->SetMasksToBounds(true);

  gfx::Transform scale_transform;
  scale_transform.Scale(3, 3);
  scale->SetTransform(scale_transform);

  CommitAndActivate();

  EXPECT_EQ(gfx::Rect(50, 50, 10, 10),
            ImplOf(filter_child)->visible_layer_rect());
  EXPECT_EQ(gfx::Rect(30, 30), GetRenderSurfaceImpl(filter)->content_rect());

  FilterOperations blur_filter;
  blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f));
  filter->SetFilters(blur_filter);

  CommitAndActivate();

  EXPECT_EQ(gfx::Rect(38, 38, 34, 34),
            ImplOf(filter_child)->visible_layer_rect());
  EXPECT_EQ(gfx::Rect(-36, -36, 102, 102),
            GetRenderSurfaceImpl(filter)->content_rect());

  gfx::Transform vertical_flip;
  vertical_flip.Scale(1, -1);
  sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>(
      SkMatrix(vertical_flip.matrix()), kLow_SkFilterQuality, nullptr);
  FilterOperations reflection_filter;
  reflection_filter.Append(
      FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>(
          SkBlendMode::kSrcOver, std::move(flip_filter), nullptr)));
  filter->SetFilters(reflection_filter);

  CommitAndActivate();

  EXPECT_EQ(gfx::Rect(50, 40, 10, 20),
            ImplOf(filter_child)->visible_layer_rect());
  EXPECT_EQ(gfx::Rect(0, -30, 30, 60),
            GetRenderSurfaceImpl(filter)->content_rect());
}

TEST_F(PropertyTreeBuilderTest, TextureLayerSnapping) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto child = TextureLayer::CreateForMailbox(nullptr);
  root->AddChild(child);

  root->SetBounds(gfx::Size(100, 100));
  child->SetBounds(gfx::Size(100, 100));
  child->SetIsDrawable(true);
  gfx::Transform fractional_translate;
  fractional_translate.Translate(10.5f, 20.3f);
  child->SetTransform(fractional_translate);

  CommitAndActivate();

  auto child_screen_space_transform = ImplOf(child)->ScreenSpaceTransform();
  EXPECT_NE(child_screen_space_transform, fractional_translate);
  fractional_translate.RoundTranslationComponents();
  EXPECT_TRANSFORMATION_MATRIX_EQ(child_screen_space_transform,
                                  fractional_translate);
  gfx::RectF layer_bounds_in_screen_space = MathUtil::MapClippedRect(
      child_screen_space_transform, gfx::RectF(gfx::SizeF(child->bounds())));
  EXPECT_EQ(layer_bounds_in_screen_space, gfx::RectF(11.f, 20.f, 100.f, 100.f));
}

// Verify that having animated opacity but current opacity 1 still creates
// a render surface.
TEST_F(PropertyTreeBuilderTest, AnimatedOpacityCreatesRenderSurface) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto child = Layer::Create();
  root->AddChild(child);
  auto grandchild = Layer::Create();
  child->AddChild(grandchild);

  root->SetBounds(gfx::Size(50, 50));
  child->SetBounds(gfx::Size(50, 50));
  child->SetIsDrawable(true);
  grandchild->SetBounds(gfx::Size(50, 50));
  grandchild->SetIsDrawable(true);

  host()->SetElementIdsForTesting();
  AddOpacityTransitionToElementWithAnimation(child->element_id(), timeline(),
                                             10.0, 1.f, 0.2f, false);
  CommitAndActivate();

  EXPECT_EQ(1.f, ImplOf(child)->Opacity());
  EXPECT_TRUE(GetRenderSurfaceImpl(root));
  EXPECT_NE(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root));
  EXPECT_EQ(GetRenderSurfaceImpl(grandchild), GetRenderSurfaceImpl(child));
}

static bool FilterIsAnimating(LayerImpl* layer) {
  MutatorHost* host = layer->layer_tree_impl()->mutator_host();
  return host->IsAnimatingFilterProperty(layer->element_id(),
                                         layer->GetElementTypeForAnimation());
}

// Verify that having an animated filter (but no current filter, as these
// are mutually exclusive) correctly creates a render surface.
TEST_F(PropertyTreeBuilderTest, AnimatedFilterCreatesRenderSurface) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto child = Layer::Create();
  root->AddChild(child);
  auto grandchild = Layer::Create();
  child->AddChild(grandchild);

  root->SetBounds(gfx::Size(50, 50));
  child->SetBounds(gfx::Size(50, 50));
  grandchild->SetBounds(gfx::Size(50, 50));

  host()->SetElementIdsForTesting();
  AddAnimatedFilterToElementWithAnimation(child->element_id(), timeline(), 10.0,
                                          0.1f, 0.2f);
  CommitAndActivate();

  EXPECT_TRUE(GetRenderSurfaceImpl(root));
  EXPECT_NE(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root));
  EXPECT_EQ(GetRenderSurfaceImpl(grandchild), GetRenderSurfaceImpl(child));

  EXPECT_TRUE(GetRenderSurfaceImpl(root)->Filters().IsEmpty());
  EXPECT_TRUE(GetRenderSurfaceImpl(child)->Filters().IsEmpty());

  EXPECT_FALSE(FilterIsAnimating(ImplOf(root)));
  EXPECT_TRUE(FilterIsAnimating(ImplOf(child)));
  EXPECT_FALSE(FilterIsAnimating(ImplOf(grandchild)));
}

bool HasPotentiallyRunningFilterAnimation(const LayerImpl& layer) {
  MutatorHost* host = layer.layer_tree_impl()->mutator_host();
  return host->HasPotentiallyRunningFilterAnimation(
      layer.element_id(), layer.GetElementTypeForAnimation());
}

// Verify that having a filter animation with a delayed start time creates a
// render surface.
TEST_F(PropertyTreeBuilderTest, DelayedFilterAnimationCreatesRenderSurface) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto child = Layer::Create();
  root->AddChild(child);
  auto grandchild = Layer::Create();
  child->AddChild(grandchild);

  root->SetBounds(gfx::Size(50, 50));
  child->SetBounds(gfx::Size(50, 50));
  grandchild->SetBounds(gfx::Size(50, 50));

  host()->SetElementIdsForTesting();

  std::unique_ptr<KeyframedFilterAnimationCurve> curve(
      KeyframedFilterAnimationCurve::Create());
  FilterOperations start_filters;
  start_filters.Append(FilterOperation::CreateBrightnessFilter(0.1f));
  FilterOperations end_filters;
  end_filters.Append(FilterOperation::CreateBrightnessFilter(0.3f));
  curve->AddKeyframe(
      FilterKeyframe::Create(base::TimeDelta(), start_filters, nullptr));
  curve->AddKeyframe(FilterKeyframe::Create(
      base::TimeDelta::FromMilliseconds(100), end_filters, nullptr));
  std::unique_ptr<KeyframeModel> keyframe_model =
      KeyframeModel::Create(std::move(curve), 0, 1, TargetProperty::FILTER);
  keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE);
  keyframe_model->set_time_offset(base::TimeDelta::FromMilliseconds(-1000));

  AddKeyframeModelToElementWithAnimation(child->element_id(), timeline(),
                                         std::move(keyframe_model));
  CommitAndActivate();

  EXPECT_TRUE(GetRenderSurfaceImpl(root));
  EXPECT_NE(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root));
  EXPECT_EQ(GetRenderSurfaceImpl(grandchild), GetRenderSurfaceImpl(child));

  EXPECT_TRUE(GetRenderSurfaceImpl(root)->Filters().IsEmpty());
  EXPECT_TRUE(GetRenderSurfaceImpl(child)->Filters().IsEmpty());

  EXPECT_FALSE(FilterIsAnimating(ImplOf(root)));
  EXPECT_FALSE(HasPotentiallyRunningFilterAnimation(*ImplOf(root)));
  EXPECT_FALSE(FilterIsAnimating(ImplOf(child)));
  EXPECT_TRUE(HasPotentiallyRunningFilterAnimation(*ImplOf(child)));
  EXPECT_FALSE(FilterIsAnimating(ImplOf(grandchild)));
  EXPECT_FALSE(HasPotentiallyRunningFilterAnimation(*ImplOf(grandchild)));
}

TEST_F(PropertyTreeBuilderTest, ChangingAxisAlignmentTriggersRebuild) {
  gfx::Transform translate;
  gfx::Transform rotate;

  translate.Translate(10, 10);
  rotate.Rotate(45);

  scoped_refptr<Layer> root = Layer::Create();
  root->SetBounds(gfx::Size(800, 800));

  host()->SetRootLayer(root);

  UpdateMainDrawProperties();
  EXPECT_FALSE(host()->property_trees()->needs_rebuild);

  root->SetTransform(translate);
  EXPECT_FALSE(host()->property_trees()->needs_rebuild);

  root->SetTransform(rotate);
  EXPECT_TRUE(host()->property_trees()->needs_rebuild);
}

TEST_F(PropertyTreeBuilderTest, ResetPropertyTreeIndices) {
  scoped_refptr<Layer> root = Layer::Create();
  root->SetBounds(gfx::Size(800, 800));

  gfx::Transform translate_z;
  translate_z.Translate3d(0, 0, 10);

  scoped_refptr<Layer> child = Layer::Create();
  child->SetTransform(translate_z);
  child->SetBounds(gfx::Size(100, 100));

  root->AddChild(child);

  host()->SetRootLayer(root);

  UpdateMainDrawProperties();
  EXPECT_NE(-1, child->transform_tree_index());

  child->RemoveFromParent();

  UpdateMainDrawProperties();
  EXPECT_EQ(-1, child->transform_tree_index());
}

TEST_F(PropertyTreeBuilderTest, RenderSurfaceClipsSubtree) {
  // Ensure that a Clip Node is added when a render surface applies clip.
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto significant_transform = Layer::Create();
  root->AddChild(significant_transform);
  auto layer_clips_subtree = Layer::Create();
  significant_transform->AddChild(layer_clips_subtree);
  auto render_surface = Layer::Create();
  layer_clips_subtree->AddChild(render_surface);
  auto test_layer = Layer::Create();
  render_surface->AddChild(test_layer);

  // This transform should be a significant one so that a transform node is
  // formed for it.
  gfx::Transform transform1;
  transform1.RotateAboutYAxis(45);
  transform1.RotateAboutXAxis(30);
  // This transform should be a 3d transform as we want the render surface
  // to flatten the transform
  gfx::Transform transform2;
  transform2.Translate3d(10, 10, 10);

  root->SetBounds(gfx::Size(30, 30));
  significant_transform->SetTransform(transform1);
  significant_transform->SetBounds(gfx::Size(30, 30));
  layer_clips_subtree->SetBounds(gfx::Size(30, 30));
  layer_clips_subtree->SetMasksToBounds(true);
  layer_clips_subtree->SetForceRenderSurfaceForTesting(true);
  render_surface->SetTransform(transform2);
  render_surface->SetBounds(gfx::Size(30, 30));
  render_surface->SetForceRenderSurfaceForTesting(true);
  test_layer->SetBounds(gfx::Size(30, 30));
  test_layer->SetIsDrawable(true);

  CommitAndActivate();

  EXPECT_TRUE(GetRenderSurfaceImpl(root));
  EXPECT_EQ(GetRenderSurfaceImpl(significant_transform),
            GetRenderSurfaceImpl(root));
  EXPECT_TRUE(GetRenderSurfaceImpl(layer_clips_subtree));
  EXPECT_NE(GetRenderSurfaceImpl(render_surface), GetRenderSurfaceImpl(root));
  EXPECT_EQ(GetRenderSurfaceImpl(test_layer),
            GetRenderSurfaceImpl(render_surface));

  EXPECT_EQ(gfx::Rect(30, 20), ImplOf(test_layer)->visible_layer_rect());
}

TEST_F(PropertyTreeBuilderTest, PropertyTreesRebuildWithOpacityChanges) {
  scoped_refptr<Layer> root = Layer::Create();
  scoped_refptr<Layer> child = Layer::Create();
  child->SetIsDrawable(true);
  root->AddChild(child);
  host()->SetRootLayer(root);

  root->SetBounds(gfx::Size(100, 100));
  child->SetBounds(gfx::Size(20, 20));
  UpdateMainDrawProperties();

  // Changing the opacity from 1 to non-1 value should trigger rebuild of
  // property trees as a new effect node will be created.
  child->SetOpacity(0.5f);
  PropertyTrees* property_trees = host()->property_trees();
  EXPECT_TRUE(property_trees->needs_rebuild);

  UpdateMainDrawProperties();
  EXPECT_NE(child->effect_tree_index(), root->effect_tree_index());

  // child already has an effect node. Changing its opacity shouldn't trigger
  // a property trees rebuild.
  child->SetOpacity(0.8f);
  property_trees = host()->property_trees();
  EXPECT_FALSE(property_trees->needs_rebuild);

  UpdateMainDrawProperties();
  EXPECT_NE(child->effect_tree_index(), root->effect_tree_index());

  // Changing the opacity from non-1 value to 1 should trigger a rebuild of
  // property trees as the effect node may no longer be needed.
  child->SetOpacity(1.f);
  property_trees = host()->property_trees();
  EXPECT_TRUE(property_trees->needs_rebuild);

  UpdateMainDrawProperties();
  EXPECT_EQ(child->effect_tree_index(), root->effect_tree_index());
}

TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForTrilinearFiltering) {
  auto root = Layer::Create();
  host()->SetRootLayer(root);
  auto parent = Layer::Create();
  root->AddChild(parent);
  auto child1 = Layer::Create();
  parent->AddChild(child1);
  auto child2 = Layer::Create();
  parent->AddChild(child2);

  gfx::Transform scale_matrix;
  scale_matrix.Scale(.25f, .25f);

  root->SetBounds(gfx::Size(200, 200));
  parent->SetTransform(scale_matrix);
  parent->SetTrilinearFiltering(true);
  child1->SetBounds(gfx::Size(50, 50));
  child1->SetIsDrawable(true);
  child1->SetForceRenderSurfaceForTesting(true);
  child2->SetPosition(gfx::PointF(50, 50));
  child2->SetBounds(gfx::Size(50, 50));
  child2->SetIsDrawable(true);
  child2->SetForceRenderSurfaceForTesting(true);

  CommitAndActivate();

  ASSERT_TRUE(GetRenderSurfaceImpl(parent));
  EXPECT_EQ(2, GetRenderSurfaceImpl(parent)->num_contributors());
  EXPECT_EQ(4U, GetRenderSurfaceList().size());

  // The rectangle enclosing child1 and child2 (0,0 100x100), scaled by the
  // scale matrix to (0,0 25x25).
  EXPECT_EQ(gfx::RectF(0, 0, 25, 25),
            GetRenderSurfaceImpl(parent)->DrawableContentRect());
}

TEST_F(PropertyTreeBuilderTest, RoundedCornerBounds) {
  // Layer Tree:
  // +root
  // +--render surface
  // +----rounded corner layer 1 [should trigger render surface]
  // +----layer 1
  // +--rounded corner layer 2 [should trigger render surface]
  // +----layer 2
  // +------rounded corner layer 3 [should trigger render surface]
  // +--------rounded corner layer 4 [should trigger render surface]

  constexpr int kRoundedCorner1Radius = 2;
  constexpr int kRoundedCorner2Radius = 5;
  constexpr int kRoundedCorner3Radius = 1;
  constexpr int kRoundedCorner4Radius = 1;

  constexpr gfx::RectF kRoundedCornerLayer1Bound(15.f, 15.f, 20.f, 20.f);
  constexpr gfx::RectF kRoundedCornerLayer2Bound(40.f, 40.f, 60.f, 60.f);
  constexpr gfx::RectF kRoundedCornerLayer3Bound(0.f, 15.f, 5.f, 5.f);
  constexpr gfx::RectF kRoundedCornerLayer4Bound(1.f, 1.f, 3.f, 3.f);

  constexpr float kDeviceScale = 1.6f;

  scoped_refptr<Layer> root = Layer::Create();
  scoped_refptr<Layer> render_surface = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create();
  scoped_refptr<Layer> layer_1 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create();
  scoped_refptr<Layer> layer_2 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_4 = Layer::Create();

  // Set up layer tree
  root->AddChild(render_surface);
  root->AddChild(rounded_corner_layer_2);

  render_surface->AddChild(rounded_corner_layer_1);
  render_surface->AddChild(layer_1);

  rounded_corner_layer_2->AddChild(layer_2);

  layer_2->AddChild(rounded_corner_layer_3);

  rounded_corner_layer_3->AddChild(rounded_corner_layer_4);

  // Set the root layer on host.
  host()->SetRootLayer(root);

  // Set layer positions.
  render_surface->SetPosition(gfx::PointF(0, 0));
  rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin());
  layer_1->SetPosition(gfx::PointF(10.f, 10.f));
  rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin());
  layer_2->SetPosition(gfx::PointF(30.f, 30.f));
  rounded_corner_layer_3->SetPosition(kRoundedCornerLayer3Bound.origin());
  rounded_corner_layer_4->SetPosition(kRoundedCornerLayer4Bound.origin());

  // Set up layer bounds.
  root->SetBounds(gfx::Size(100, 100));
  render_surface->SetBounds(gfx::Size(50, 50));
  rounded_corner_layer_1->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size()));
  layer_1->SetBounds(gfx::Size(10, 10));
  rounded_corner_layer_2->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size()));
  layer_2->SetBounds(gfx::Size(25, 25));
  rounded_corner_layer_3->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size()));
  rounded_corner_layer_4->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size()));

  // Add Layer transforms.
  gfx::Transform layer_2_transform;
  constexpr gfx::Vector2dF kLayer2Translation(10.f, 10.f);
  layer_2_transform.Translate(kLayer2Translation);
  layer_2->SetTransform(layer_2_transform);

  gfx::Transform rounded_corner_layer_3_transform;
  constexpr float kRoundedCorner3Scale = 2.f;
  rounded_corner_layer_3_transform.Scale(kRoundedCorner3Scale,
                                         kRoundedCorner3Scale);
  rounded_corner_layer_3->SetTransform(rounded_corner_layer_3_transform);

  // Set the layer properties
  render_surface->SetForceRenderSurfaceForTesting(true);

  root->SetIsDrawable(true);
  render_surface->SetIsDrawable(true);
  rounded_corner_layer_1->SetIsDrawable(true);
  layer_1->SetIsDrawable(true);
  rounded_corner_layer_2->SetIsDrawable(true);
  layer_2->SetIsDrawable(true);
  rounded_corner_layer_3->SetIsDrawable(true);
  rounded_corner_layer_4->SetIsDrawable(true);

  // Set Rounded corners
  rounded_corner_layer_1->SetRoundedCorner(
      {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius,
       kRoundedCorner1Radius});
  rounded_corner_layer_2->SetRoundedCorner(
      {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius,
       kRoundedCorner2Radius});
  rounded_corner_layer_3->SetRoundedCorner(
      {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius,
       kRoundedCorner3Radius});
  rounded_corner_layer_4->SetRoundedCorner(
      {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius,
       kRoundedCorner4Radius});

  UpdateMainDrawProperties(kDeviceScale);

  // Since this effect node has no descendants that draw and no descendant that
  // has a rounded corner, it does not need a render surface.
  const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get());
  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                  kRoundedCorner1Radius);
  EXPECT_EQ(rounded_corner_bounds_1.rect(),
            gfx::RectF(kRoundedCornerLayer1Bound.size()));

  // Since this node has descendants with roudned corners, it needs a render
  // surface. It also has 2 descendants that draw.
  effect_node = GetEffectNode(rounded_corner_layer_2.get());
  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
  EXPECT_TRUE(effect_node->HasRenderSurface());
  EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                  kRoundedCorner2Radius);
  EXPECT_EQ(rounded_corner_bounds_2.rect(),
            gfx::RectF(kRoundedCornerLayer2Bound.size()));

  // Since this node has a descendant that has a rounded corner, it will trigger
  // the creation of a render surface.
  effect_node = GetEffectNode(rounded_corner_layer_3.get());
  gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds;
  EXPECT_TRUE(effect_node->HasRenderSurface());
  EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(),
                  kRoundedCorner3Radius);
  EXPECT_EQ(rounded_corner_bounds_3.rect(),
            gfx::RectF(kRoundedCornerLayer3Bound.size()));

  // Since this node has no descendants that draw nor any descendant that has a
  // rounded corner, it does not need a render surface.
  effect_node = GetEffectNode(rounded_corner_layer_4.get());
  gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(),
                  kRoundedCorner4Radius);
  EXPECT_EQ(rounded_corner_bounds_4.rect(),
            gfx::RectF(kRoundedCornerLayer4Bound.size()));

  CommitAndActivate(kDeviceScale);
  LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree();

  // Get the layer impl for each Layer.
  LayerImpl* rounded_corner_layer_1_impl =
      layer_tree_impl->LayerById(rounded_corner_layer_1->id());
  LayerImpl* rounded_corner_layer_2_impl =
      layer_tree_impl->LayerById(rounded_corner_layer_2->id());
  LayerImpl* rounded_corner_layer_3_impl =
      layer_tree_impl->LayerById(rounded_corner_layer_3->id());
  LayerImpl* rounded_corner_layer_4_impl =
      layer_tree_impl->LayerById(rounded_corner_layer_4->id());

  EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor());

  // Rounded corner layer 1
  // The render target for this layer is |render_surface|, hence its target
  // bounds are relative to |render_surface|.
  // The offset from the origin of the render target is [15, 15] and the device
  // scale factor is 1.6 thus giving the target space origin of [24, 24]. The
  // corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_rrect_1 =
      rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds;
  gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_rrect_1.GetSimpleRadius(),
                  kRoundedCorner1Radius * kDeviceScale);

  // Rounded corner layer 2
  // The render target for this layer is |root|.
  // The offset from the origin of the render target is [40, 40] and the device
  // scale factor is 1.6 thus giving the target space origin of [64, 64]. The
  // corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_self_rrect_2 =
      rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds;
  EXPECT_TRUE(actual_self_rrect_2.IsEmpty());

  bounds_in_target_space = kRoundedCornerLayer2Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  const gfx::RRectF actual_render_target_rrect_2 =
      rounded_corner_layer_2_impl->render_target()->rounded_corner_bounds();
  EXPECT_EQ(actual_render_target_rrect_2.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_render_target_rrect_2.GetSimpleRadius(),
                  kRoundedCorner2Radius * kDeviceScale);

  // Rounded corner layer 3
  // The render target for this layer is |rounded_corner_2|.
  // The net offset from the origin of the render target is [40, 55] and the
  // device scale factor is 1.6 thus giving the target space origin of [64, 88].
  // The corner radius is also scaled by a factor of 1.6 * transform scale.
  const gfx::RRectF actual_self_rrect_3 =
      rounded_corner_layer_3_impl->draw_properties().rounded_corner_bounds;
  EXPECT_TRUE(actual_self_rrect_3.IsEmpty());

  bounds_in_target_space = kRoundedCornerLayer3Bound;
  bounds_in_target_space +=
      layer_2->position().OffsetFromOrigin() + kLayer2Translation;
  bounds_in_target_space.Scale(kDeviceScale);
  gfx::SizeF transformed_size = bounds_in_target_space.size();
  transformed_size.Scale(kRoundedCorner3Scale);
  bounds_in_target_space.set_size(transformed_size);

  const gfx::RRectF actual_render_target_rrect_3 =
      rounded_corner_layer_3_impl->render_target()->rounded_corner_bounds();
  EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(),
                  kRoundedCorner3Radius * kDeviceScale * kRoundedCorner3Scale);

  // Rounded corner layer 4
  // The render target for this layer is |rounded_corner_3|.
  // The net offset from the origin of the render target is [1, 1] and the
  // net scale is 1.6 * transform scale = 3.2 thus giving the target space o
  // rigin of [3.2, 3.2].
  // The corner radius is also scaled by a factor of 3.2.
  const gfx::RRectF actual_rrect_4 =
      rounded_corner_layer_4_impl->draw_properties().rounded_corner_bounds;
  bounds_in_target_space = kRoundedCornerLayer4Bound;
  bounds_in_target_space.Scale(kDeviceScale * kRoundedCorner3Scale);
  EXPECT_EQ(actual_rrect_4.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_rrect_4.GetSimpleRadius(),
                  kRoundedCorner4Radius * kDeviceScale * kRoundedCorner3Scale);
}

TEST_F(PropertyTreeBuilderTest, RoundedCornerBoundsInterveningRenderTarget) {
  // Layer Tree:
  // +root
  // +--rounded corner layer 1 [should not trigger render surface]
  // +----render surface [Does not draw]
  // +------rounded corner layer 2 [should not trigger render surface]

  constexpr int kRoundedCorner1Radius = 2;
  constexpr int kRoundedCorner2Radius = 5;

  constexpr gfx::RectF kRoundedCornerLayer1Bound(60.f, 0.f, 40.f, 30.f);
  constexpr gfx::RectF kRoundedCornerLayer2Bound(0.f, 0.f, 30.f, 20.f);

  constexpr float kDeviceScale = 1.6f;

  scoped_refptr<Layer> root = Layer::Create();
  scoped_refptr<Layer> render_surface = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create();

  // Set up layer tree
  root->AddChild(rounded_corner_layer_1);
  rounded_corner_layer_1->AddChild(render_surface);
  render_surface->AddChild(rounded_corner_layer_2);

  // Set the root layer on host.
  host()->SetRootLayer(root);

  // Set layer positions.
  rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin());
  render_surface->SetPosition(gfx::PointF(0, 0));
  rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin());

  // Set up layer bounds.
  root->SetBounds(gfx::Size(100, 100));
  rounded_corner_layer_1->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size()));
  render_surface->SetBounds(gfx::Size(30, 30));
  rounded_corner_layer_2->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size()));

  // Set the layer properties
  render_surface->SetForceRenderSurfaceForTesting(true);

  root->SetIsDrawable(true);
  rounded_corner_layer_1->SetIsDrawable(true);
  rounded_corner_layer_2->SetIsDrawable(true);

  // Set Rounded corners
  rounded_corner_layer_1->SetRoundedCorner(
      {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius,
       kRoundedCorner1Radius});
  rounded_corner_layer_2->SetRoundedCorner(
      {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius,
       kRoundedCorner2Radius});

  UpdateMainDrawProperties(kDeviceScale);

  // Since this effect node has only 1 descendant that draws and no descendant
  // that has a rounded corner before the render surface, it does not need a
  // render surface.
  const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get());
  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                  kRoundedCorner1Radius);
  EXPECT_EQ(rounded_corner_bounds_1.rect(),
            gfx::RectF(kRoundedCornerLayer1Bound.size()));

  // Since this effect node has no descendants that draw and no descendant that
  // has a rounded corner, it does not need a render surface.
  effect_node = GetEffectNode(rounded_corner_layer_2.get());
  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                  kRoundedCorner2Radius);
  EXPECT_EQ(rounded_corner_bounds_2.rect(),
            gfx::RectF(kRoundedCornerLayer2Bound.size()));

  CommitAndActivate(kDeviceScale);
  LayerTreeImpl* layer_tree_impl = host_impl()->active_tree();

  // Get the layer impl for each Layer.
  LayerImpl* rounded_corner_layer_1_impl =
      layer_tree_impl->LayerById(rounded_corner_layer_1->id());
  LayerImpl* rounded_corner_layer_2_impl =
      layer_tree_impl->LayerById(rounded_corner_layer_2->id());

  EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor());

  // Rounded corner layer 1
  // The render target for this layer is |root|, hence its target
  // bounds are relative to |root|.
  // The offset from the origin of the render target is [60, 0] and the device
  // scale factor is 1.6 thus giving the target space origin of [96, 0]. The
  // corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_rrect_1 =
      rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds;
  gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_rrect_1.GetSimpleRadius(),
                  kRoundedCorner1Radius * kDeviceScale);

  // Rounded corner layer 2
  // The render target for this layer is |render_surface|.
  // The offset from the origin of the render target is [0, 0].
  const gfx::RRectF actual_rrect_2 =
      rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds;
  bounds_in_target_space = kRoundedCornerLayer2Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  EXPECT_EQ(actual_rrect_2.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_rrect_2.GetSimpleRadius(),
                  kRoundedCorner2Radius * kDeviceScale);
}

TEST_F(PropertyTreeBuilderTest, RoundedCornerBoundsSiblingRenderTarget) {
  // Layer Tree:
  // +root
  // +--rounded corner layer 1 [should trigger render surface]
  // +----render surface [Does not draw]
  // +----rounded corner layer 2 [should not trigger render surface]

  constexpr int kRoundedCorner1Radius = 2;
  constexpr int kRoundedCorner2Radius = 5;

  constexpr gfx::RectF kRoundedCornerLayer1Bound(0.f, 60.f, 30.f, 40.f);
  constexpr gfx::RectF kRoundedCornerLayer2Bound(0.f, 0.f, 20.f, 30.f);

  constexpr float kDeviceScale = 1.6f;

  scoped_refptr<Layer> root = Layer::Create();
  scoped_refptr<Layer> render_surface = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create();

  // Set up layer tree
  root->AddChild(rounded_corner_layer_1);
  rounded_corner_layer_1->AddChild(render_surface);
  rounded_corner_layer_1->AddChild(rounded_corner_layer_2);

  // Set the root layer on host.
  host()->SetRootLayer(root);

  // Set layer positions.
  rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin());
  render_surface->SetPosition(gfx::PointF(0, 0));
  rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin());

  // Set up layer bounds.
  root->SetBounds(gfx::Size(100, 100));
  rounded_corner_layer_1->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size()));
  render_surface->SetBounds(gfx::Size(30, 30));
  rounded_corner_layer_2->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size()));

  // Set the layer properties
  render_surface->SetForceRenderSurfaceForTesting(true);

  root->SetIsDrawable(true);
  rounded_corner_layer_1->SetIsDrawable(true);
  rounded_corner_layer_2->SetIsDrawable(true);

  // Set Rounded corners
  rounded_corner_layer_1->SetRoundedCorner(
      {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius,
       kRoundedCorner1Radius});
  rounded_corner_layer_2->SetRoundedCorner(
      {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius,
       kRoundedCorner2Radius});

  UpdateMainDrawProperties(kDeviceScale);

  // Since this effect node has 1 descendant with a rounded corner without a
  // render surface along the chain, it need a render surface.
  const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get());
  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
  EXPECT_TRUE(effect_node->HasRenderSurface());
  EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                  kRoundedCorner1Radius);
  EXPECT_EQ(rounded_corner_bounds_1.rect(),
            gfx::RectF(kRoundedCornerLayer1Bound.size()));

  // Since this effect node has no descendants that draw and no descendant that
  // has a rounded corner, it does not need a render surface.
  effect_node = GetEffectNode(rounded_corner_layer_2.get());
  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                  kRoundedCorner2Radius);
  EXPECT_EQ(rounded_corner_bounds_2.rect(),
            gfx::RectF(kRoundedCornerLayer2Bound.size()));

  CommitAndActivate(kDeviceScale);
  LayerTreeImpl* layer_tree_impl = host_impl()->active_tree();

  // Get the layer impl for each Layer.
  LayerImpl* rounded_corner_layer_1_impl =
      layer_tree_impl->LayerById(rounded_corner_layer_1->id());
  LayerImpl* rounded_corner_layer_2_impl =
      layer_tree_impl->LayerById(rounded_corner_layer_2->id());

  EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor());

  // Rounded corner layer 1
  // The render target for this layer is |root|, hence its target
  // bounds are relative to |root|.
  // The offset from the origin of the render target is [0, 60] and the device
  // scale factor is 1.6 thus giving the target space origin of [0, 96]. The
  // corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_self_rrect_1 =
      rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds;
  EXPECT_TRUE(actual_self_rrect_1.IsEmpty());

  gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  const gfx::RRectF actual_render_target_rrect_1 =
      rounded_corner_layer_1_impl->render_target()->rounded_corner_bounds();
  EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(),
                  kRoundedCorner1Radius * kDeviceScale);

  // Rounded corner layer 2
  // The render target for this layer is |render_surface|.
  // The offset from the origin of the render target is [0, 0].
  const gfx::RRectF actual_rrect_2 =
      rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds;
  bounds_in_target_space = kRoundedCornerLayer2Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  EXPECT_EQ(actual_rrect_2.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_rrect_2.GetSimpleRadius(),
                  kRoundedCorner2Radius * kDeviceScale);
}

TEST_F(PropertyTreeBuilderTest, FastRoundedCornerDoesNotTriggerRenderSurface) {
  // Layer Tree:
  // +root
  // +--fast rounded corner layer [should not trigger render surface]
  // +----layer 1
  // +----layer 2
  // +--rounded corner layer [should trigger render surface]
  // +----layer 3
  // +----layer 4

  constexpr int kRoundedCorner1Radius = 2;
  constexpr int kRoundedCorner2Radius = 5;

  constexpr gfx::RectF kRoundedCornerLayer1Bound(0.f, 0.f, 50.f, 50.f);
  constexpr gfx::RectF kRoundedCornerLayer2Bound(40.f, 40.f, 60.f, 60.f);

  constexpr float kDeviceScale = 1.6f;

  scoped_refptr<Layer> root = Layer::Create();
  scoped_refptr<Layer> fast_rounded_corner_layer = Layer::Create();
  scoped_refptr<Layer> layer_1 = Layer::Create();
  scoped_refptr<Layer> layer_2 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer = Layer::Create();
  scoped_refptr<Layer> layer_3 = Layer::Create();
  scoped_refptr<Layer> layer_4 = Layer::Create();

  // Set up layer tree
  root->AddChild(fast_rounded_corner_layer);
  root->AddChild(rounded_corner_layer);

  fast_rounded_corner_layer->AddChild(layer_1);
  fast_rounded_corner_layer->AddChild(layer_2);

  rounded_corner_layer->AddChild(layer_3);
  rounded_corner_layer->AddChild(layer_4);

  // Set the root layer on host.
  host()->SetRootLayer(root);

  // Set layer positions.
  fast_rounded_corner_layer->SetPosition(kRoundedCornerLayer1Bound.origin());
  layer_1->SetPosition(gfx::PointF(0.f, 0.f));
  layer_2->SetPosition(gfx::PointF(25.f, 0.f));
  rounded_corner_layer->SetPosition(kRoundedCornerLayer2Bound.origin());
  layer_3->SetPosition(gfx::PointF(0.f, 0.f));
  layer_4->SetPosition(gfx::PointF(30.f, 0.f));

  // Set up layer bounds.
  root->SetBounds(gfx::Size(100, 100));
  fast_rounded_corner_layer->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size()));
  layer_1->SetBounds(gfx::Size(25, 25));
  layer_2->SetBounds(gfx::Size(25, 25));
  rounded_corner_layer->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size()));
  layer_3->SetBounds(gfx::Size(30, 60));
  layer_4->SetBounds(gfx::Size(30, 60));

  root->SetIsDrawable(true);
  fast_rounded_corner_layer->SetIsDrawable(true);
  layer_1->SetIsDrawable(true);
  layer_2->SetIsDrawable(true);
  rounded_corner_layer->SetIsDrawable(true);
  layer_3->SetIsDrawable(true);
  layer_4->SetIsDrawable(true);

  // Set Rounded corners
  fast_rounded_corner_layer->SetRoundedCorner(
      {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius,
       kRoundedCorner1Radius});
  rounded_corner_layer->SetRoundedCorner(
      {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius,
       kRoundedCorner2Radius});

  fast_rounded_corner_layer->SetIsFastRoundedCorner(true);

  UpdateMainDrawProperties(kDeviceScale);

  // Since this layer has a fast rounded corner, it should not have a render
  // surface even though it has 2 layers in the subtree that draws content.
  const EffectNode* effect_node =
      GetEffectNode(fast_rounded_corner_layer.get());
  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_TRUE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                  kRoundedCorner1Radius);
  EXPECT_EQ(rounded_corner_bounds_1.rect(),
            gfx::RectF(kRoundedCornerLayer1Bound.size()));

  // Since this node has 2 descendants that draw, it will have a rounded corner.
  effect_node = GetEffectNode(rounded_corner_layer.get());
  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
  EXPECT_TRUE(effect_node->HasRenderSurface());
  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                  kRoundedCorner2Radius);
  EXPECT_EQ(rounded_corner_bounds_2.rect(),
            gfx::RectF(kRoundedCornerLayer2Bound.size()));

  CommitAndActivate(kDeviceScale);
  LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree();

  // Get the layer impl for each Layer.
  LayerImpl* fast_rounded_corner_layer_impl =
      layer_tree_impl->LayerById(fast_rounded_corner_layer->id());
  LayerImpl* layer_1_impl = layer_tree_impl->LayerById(layer_1->id());
  LayerImpl* layer_2_impl = layer_tree_impl->LayerById(layer_2->id());
  LayerImpl* rounded_corner_layer_impl =
      layer_tree_impl->LayerById(rounded_corner_layer->id());
  LayerImpl* layer_3_impl = layer_tree_impl->LayerById(layer_3->id());
  LayerImpl* layer_4_impl = layer_tree_impl->LayerById(layer_4->id());

  EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor());

  // Fast rounded corner layer.
  // The render target for this layer is |root|, hence its target bounds are
  // relative to |root|.
  // The offset from the origin of the render target is [0, 0] and the device
  // scale factor is 1.6.
  const gfx::RRectF actual_rrect_1 =
      fast_rounded_corner_layer_impl->draw_properties().rounded_corner_bounds;
  gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_rrect_1.GetSimpleRadius(),
                  kRoundedCorner1Radius * kDeviceScale);

  // Layer 1 and layer 2 rounded corner bounds
  // This should have the same rounded corner boudns as fast rounded corner
  // layer.
  const gfx::RRectF layer_1_rrect =
      layer_1_impl->draw_properties().rounded_corner_bounds;
  const gfx::RRectF layer_2_rrect =
      layer_2_impl->draw_properties().rounded_corner_bounds;
  EXPECT_EQ(actual_rrect_1, layer_1_rrect);
  EXPECT_EQ(actual_rrect_1, layer_2_rrect);

  // Rounded corner layer
  // The render target for this layer is |root|.
  // The offset from the origin of the render target is [40, 40] and the device
  // scale factor is 1.6 thus giving the target space origin of [64, 64]. The
  // corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_self_rrect_2 =
      rounded_corner_layer_impl->draw_properties().rounded_corner_bounds;
  EXPECT_TRUE(actual_self_rrect_2.IsEmpty());

  bounds_in_target_space = kRoundedCornerLayer2Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  const gfx::RRectF actual_render_target_rrect_2 =
      rounded_corner_layer_impl->render_target()->rounded_corner_bounds();
  EXPECT_EQ(actual_render_target_rrect_2.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_render_target_rrect_2.GetSimpleRadius(),
                  kRoundedCorner2Radius * kDeviceScale);

  // Layer 3 and layer 4 should have no rounded corner bounds set as their
  // parent is a render surface.
  const gfx::RRectF layer_3_rrect =
      layer_3_impl->draw_properties().rounded_corner_bounds;
  const gfx::RRectF layer_4_rrect =
      layer_4_impl->draw_properties().rounded_corner_bounds;
  EXPECT_TRUE(layer_3_rrect.IsEmpty());
  EXPECT_TRUE(layer_4_rrect.IsEmpty());
}

TEST_F(PropertyTreeBuilderTest,
       FastRoundedCornerTriggersRenderSurfaceInAncestor) {
  // Layer Tree:
  // +root
  // +--rounded corner layer [1] [should trigger render surface]
  // +----fast rounded corner layer [2] [should not trigger render surface]
  // +--rounded corner layer [3] [should trigger render surface]
  // +----rounded corner layer [4] [should not trigger render surface]

  constexpr int kRoundedCorner1Radius = 2;
  constexpr int kRoundedCorner2Radius = 5;
  constexpr int kRoundedCorner3Radius = 1;
  constexpr int kRoundedCorner4Radius = 3;

  constexpr gfx::RectF kRoundedCornerLayer1Bound(5.f, 5.f, 50.f, 50.f);
  constexpr gfx::RectF kRoundedCornerLayer2Bound(0.f, 0.f, 25.f, 25.f);
  constexpr gfx::RectF kRoundedCornerLayer3Bound(40.f, 40.f, 60.f, 60.f);
  constexpr gfx::RectF kRoundedCornerLayer4Bound(30.f, 0.f, 30.f, 60.f);

  constexpr float kDeviceScale = 1.6f;

  scoped_refptr<Layer> root = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create();
  scoped_refptr<Layer> fast_rounded_corner_layer_2 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_4 = Layer::Create();

  // Set up layer tree
  root->AddChild(rounded_corner_layer_1);
  root->AddChild(rounded_corner_layer_3);

  rounded_corner_layer_1->AddChild(fast_rounded_corner_layer_2);

  rounded_corner_layer_3->AddChild(rounded_corner_layer_4);

  // Set the root layer on host.
  host()->SetRootLayer(root);

  // Set layer positions.
  rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin());
  fast_rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin());
  rounded_corner_layer_3->SetPosition(kRoundedCornerLayer3Bound.origin());
  rounded_corner_layer_4->SetPosition(kRoundedCornerLayer4Bound.origin());

  // Set up layer bounds.
  root->SetBounds(gfx::Size(100, 100));
  rounded_corner_layer_1->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size()));
  fast_rounded_corner_layer_2->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size()));
  rounded_corner_layer_3->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size()));
  rounded_corner_layer_4->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size()));

  root->SetIsDrawable(true);
  rounded_corner_layer_1->SetIsDrawable(true);
  fast_rounded_corner_layer_2->SetIsDrawable(true);
  rounded_corner_layer_3->SetIsDrawable(true);
  rounded_corner_layer_4->SetIsDrawable(true);

  // Set Rounded corners
  rounded_corner_layer_1->SetRoundedCorner(
      {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius,
       kRoundedCorner1Radius});
  fast_rounded_corner_layer_2->SetRoundedCorner(
      {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius,
       kRoundedCorner2Radius});
  rounded_corner_layer_3->SetRoundedCorner(
      {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius,
       kRoundedCorner3Radius});
  rounded_corner_layer_4->SetRoundedCorner(
      {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius,
       kRoundedCorner4Radius});

  fast_rounded_corner_layer_2->SetIsFastRoundedCorner(true);

  UpdateMainDrawProperties(kDeviceScale);

  // Since this layer has a descendant that has rounded corner, this node will
  // require a render surface.
  const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get());
  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
  EXPECT_TRUE(effect_node->HasRenderSurface());
  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                  kRoundedCorner1Radius);
  EXPECT_EQ(rounded_corner_bounds_1.rect(),
            gfx::RectF(kRoundedCornerLayer1Bound.size()));

  // Since this layer has no descendant with rounded corner or drawable, it will
  // not have a render surface.
  effect_node = GetEffectNode(fast_rounded_corner_layer_2.get());
  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_TRUE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                  kRoundedCorner2Radius);
  EXPECT_EQ(rounded_corner_bounds_2.rect(),
            gfx::RectF(kRoundedCornerLayer2Bound.size()));

  // Since this layer has 1 descendant with a rounded corner, it should have a
  // render surface.
  effect_node = GetEffectNode(rounded_corner_layer_3.get());
  gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds;
  EXPECT_TRUE(effect_node->HasRenderSurface());
  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(),
                  kRoundedCorner3Radius);
  EXPECT_EQ(rounded_corner_bounds_3.rect(),
            gfx::RectF(kRoundedCornerLayer3Bound.size()));

  // Since this layer no descendants, it would no thave a render pass.
  effect_node = GetEffectNode(rounded_corner_layer_4.get());
  gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(),
                  kRoundedCorner4Radius);
  EXPECT_EQ(rounded_corner_bounds_4.rect(),
            gfx::RectF(kRoundedCornerLayer4Bound.size()));

  CommitAndActivate(kDeviceScale);
  LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree();

  // Get the layer impl for each Layer.
  LayerImpl* rounded_corner_layer_impl_1 =
      layer_tree_impl->LayerById(rounded_corner_layer_1->id());
  LayerImpl* fast_rounded_corner_layer_impl_2 =
      layer_tree_impl->LayerById(fast_rounded_corner_layer_2->id());
  LayerImpl* rounded_corner_layer_impl_3 =
      layer_tree_impl->LayerById(rounded_corner_layer_3->id());
  LayerImpl* rounded_corner_layer_impl_4 =
      layer_tree_impl->LayerById(rounded_corner_layer_4->id());

  EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor());

  // Rounded corner layer 1.
  // The render target for this layer is |root|, hence its target bounds are
  // relative to |root|.
  // The offset from the origin of the render target is [5, 5] and the device
  // scale factor is 1.6 giving a total offset of [8, 8].
  const gfx::RRectF actual_self_rrect_1 =
      rounded_corner_layer_impl_1->draw_properties().rounded_corner_bounds;
  EXPECT_TRUE(actual_self_rrect_1.IsEmpty());

  gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  const gfx::RRectF actual_render_target_rrect_1 =
      rounded_corner_layer_impl_1->render_target()->rounded_corner_bounds();
  EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(),
                  kRoundedCorner1Radius * kDeviceScale);

  // Fast rounded corner layer 2
  // The render target for this layer is |rounded_corner_layer_1|.
  // The offset from the origin of the render target is [0, 0] and the device
  // scale factor is 1.6. The corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_self_rrect_2 =
      fast_rounded_corner_layer_impl_2->draw_properties().rounded_corner_bounds;
  bounds_in_target_space = kRoundedCornerLayer2Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  EXPECT_EQ(actual_self_rrect_2.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_self_rrect_2.GetSimpleRadius(),
                  kRoundedCorner2Radius * kDeviceScale);

  // Rounded corner layer 3
  // The render target for this layer is |root|.
  // The offset from the origin of the render target is [40, 40] and the device
  // scale factor is 1.6 thus giving the target space origin of [64, 64]. The
  // corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_self_rrect_3 =
      rounded_corner_layer_impl_3->draw_properties().rounded_corner_bounds;
  EXPECT_TRUE(actual_self_rrect_3.IsEmpty());

  bounds_in_target_space = kRoundedCornerLayer3Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  const gfx::RRectF actual_render_target_rrect_3 =
      rounded_corner_layer_impl_3->render_target()->rounded_corner_bounds();
  EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(),
                  kRoundedCorner3Radius * kDeviceScale);

  // Rounded corner layer 4
  // The render target for this layer is |rounded_corner_layer_3|.
  // The offset from the origin of the render target is [30, 0] and the device
  // scale factor is 1.6 thus giving the target space origin of [48, 0]. The
  // corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_self_rrect_4 =
      rounded_corner_layer_impl_4->draw_properties().rounded_corner_bounds;
  bounds_in_target_space = kRoundedCornerLayer4Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  EXPECT_EQ(actual_self_rrect_4.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_self_rrect_4.GetSimpleRadius(),
                  kRoundedCorner4Radius * kDeviceScale);
}

TEST_F(PropertyTreeBuilderTest,
       FastRoundedCornerDoesNotTriggerRenderSurfaceFromSubtree) {
  // Layer Tree:
  // +root
  // +--fast rounded corner layer 1 [should trigger render surface]
  // +----rounded corner layer 1 [should not trigger render surface]
  // +--rounded corner layer 2 [should trigger render surface]
  // +----rounded corner layer 3 [should not trigger render surface]
  constexpr int kRoundedCorner1Radius = 2;
  constexpr int kRoundedCorner2Radius = 5;
  constexpr int kRoundedCorner3Radius = 4;
  constexpr int kRoundedCorner4Radius = 5;

  constexpr gfx::RectF kRoundedCornerLayer1Bound(10.f, 5.f, 45.f, 50.f);
  constexpr gfx::RectF kRoundedCornerLayer2Bound(5.f, 5.f, 20.f, 20.f);
  constexpr gfx::RectF kRoundedCornerLayer3Bound(60.f, 5.f, 40.f, 25.f);
  constexpr gfx::RectF kRoundedCornerLayer4Bound(0.f, 10.f, 10.f, 20.f);

  constexpr float kDeviceScale = 1.6f;

  scoped_refptr<Layer> root = Layer::Create();
  scoped_refptr<Layer> fast_rounded_corner_layer_1 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create();
  scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create();

  // Set up layer tree
  root->AddChild(fast_rounded_corner_layer_1);
  root->AddChild(rounded_corner_layer_2);

  fast_rounded_corner_layer_1->AddChild(rounded_corner_layer_1);
  rounded_corner_layer_2->AddChild(rounded_corner_layer_3);

  // Set the root layer on host.
  host()->SetRootLayer(root);

  // Set layer positions.
  fast_rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin());
  rounded_corner_layer_1->SetPosition(kRoundedCornerLayer2Bound.origin());
  rounded_corner_layer_2->SetPosition(kRoundedCornerLayer3Bound.origin());
  rounded_corner_layer_3->SetPosition(kRoundedCornerLayer4Bound.origin());

  // Set up layer bounds.
  root->SetBounds(gfx::Size(100, 100));
  fast_rounded_corner_layer_1->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size()));
  rounded_corner_layer_1->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size()));
  rounded_corner_layer_2->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size()));
  rounded_corner_layer_3->SetBounds(
      gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size()));

  root->SetIsDrawable(true);
  fast_rounded_corner_layer_1->SetIsDrawable(true);
  rounded_corner_layer_1->SetIsDrawable(true);
  rounded_corner_layer_2->SetIsDrawable(true);
  rounded_corner_layer_3->SetIsDrawable(true);

  // Set Rounded corners
  fast_rounded_corner_layer_1->SetRoundedCorner(
      {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius,
       kRoundedCorner1Radius});
  rounded_corner_layer_1->SetRoundedCorner(
      {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius,
       kRoundedCorner2Radius});
  rounded_corner_layer_2->SetRoundedCorner(
      {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius,
       kRoundedCorner3Radius});
  rounded_corner_layer_3->SetRoundedCorner(
      {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius,
       kRoundedCorner4Radius});

  fast_rounded_corner_layer_1->SetIsFastRoundedCorner(true);

  UpdateMainDrawProperties(kDeviceScale);

  // Since this layer has a descendant with rounded corner, it needs a render
  // surface.
  const EffectNode* effect_node =
      GetEffectNode(fast_rounded_corner_layer_1.get());
  gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds;
  EXPECT_TRUE(effect_node->HasRenderSurface());
  EXPECT_TRUE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(),
                  kRoundedCorner1Radius);
  EXPECT_EQ(rounded_corner_bounds_1.rect(),
            gfx::RectF(kRoundedCornerLayer1Bound.size()));

  // Since this layer has no descendant with rounded corner or drawable, it will
  // not have a render surface.
  effect_node = GetEffectNode(rounded_corner_layer_1.get());
  gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(),
                  kRoundedCorner2Radius);
  EXPECT_EQ(rounded_corner_bounds_2.rect(),
            gfx::RectF(kRoundedCornerLayer2Bound.size()));

  // Since this layer has a descendant with rounded corner, it should have a
  // render surface.
  effect_node = GetEffectNode(rounded_corner_layer_2.get());
  gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds;
  EXPECT_TRUE(effect_node->HasRenderSurface());
  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(),
                  kRoundedCorner3Radius);
  EXPECT_EQ(rounded_corner_bounds_3.rect(),
            gfx::RectF(kRoundedCornerLayer3Bound.size()));

  // Since this layer has no descendant, it does not need a render surface.
  effect_node = GetEffectNode(rounded_corner_layer_3.get());
  gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds;
  EXPECT_FALSE(effect_node->HasRenderSurface());
  EXPECT_FALSE(effect_node->is_fast_rounded_corner);
  EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(),
                  kRoundedCorner4Radius);
  EXPECT_EQ(rounded_corner_bounds_4.rect(),
            gfx::RectF(kRoundedCornerLayer4Bound.size()));

  CommitAndActivate(kDeviceScale);
  LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree();

  // Get the layer impl for each Layer.
  LayerImpl* fast_rounded_corner_layer_impl_1 =
      layer_tree_impl->LayerById(fast_rounded_corner_layer_1->id());
  LayerImpl* rounded_corner_layer_impl_1 =
      layer_tree_impl->LayerById(rounded_corner_layer_1->id());
  LayerImpl* rounded_corner_layer_impl_2 =
      layer_tree_impl->LayerById(rounded_corner_layer_2->id());
  LayerImpl* rounded_corner_layer_impl_3 =
      layer_tree_impl->LayerById(rounded_corner_layer_3->id());

  EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor());

  // Fast rounded corner layer 1.
  // The render target for this layer is |root|, hence its target bounds are
  // relative to |root|.
  // The offset from the origin of the render target is [5, 5] and the device
  // scale factor is 1.6.
  const gfx::RRectF actual_self_rrect_1 =
      fast_rounded_corner_layer_impl_1->draw_properties().rounded_corner_bounds;
  EXPECT_TRUE(actual_self_rrect_1.IsEmpty());

  gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  const gfx::RRectF actual_render_target_rrect_1 =
      fast_rounded_corner_layer_impl_1->render_target()
          ->rounded_corner_bounds();
  EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(),
                  kRoundedCorner1Radius * kDeviceScale);

  // Rounded corner layer 1
  // The render target for this layer is |fast_rounded_corner_layer_1|.
  // The offset from the origin of the render target is [0, 0] and the device
  // scale factor is 1.6. The corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_self_rrect_2 =
      rounded_corner_layer_impl_1->draw_properties().rounded_corner_bounds;
  bounds_in_target_space = kRoundedCornerLayer2Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  EXPECT_EQ(actual_self_rrect_2.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_self_rrect_2.GetSimpleRadius(),
                  kRoundedCorner2Radius * kDeviceScale);

  // Rounded corner layer 3
  // The render target for this layer is |root|.
  // The offset from the origin of the render target is [5, 5] and the device
  // scale factor is 1.6 thus giving the target space origin of [8, 8]. The
  // corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_self_rrect_3 =
      rounded_corner_layer_impl_2->draw_properties().rounded_corner_bounds;
  EXPECT_TRUE(actual_self_rrect_3.IsEmpty());

  bounds_in_target_space = kRoundedCornerLayer3Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  const gfx::RRectF actual_render_target_rrect_3 =
      rounded_corner_layer_impl_2->render_target()->rounded_corner_bounds();
  EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(),
                  kRoundedCorner3Radius * kDeviceScale);

  // Rounded corner layer 4
  // The render target for this layer is |rounded_corner_layer_2|.
  // The offset from the origin of the render target is [0, 5] and the device
  // scale factor is 1.6 thus giving the target space origin of [0, 8]. The
  // corner radius is also scaled by a factor of 1.6.
  const gfx::RRectF actual_self_rrect_4 =
      rounded_corner_layer_impl_3->draw_properties().rounded_corner_bounds;
  bounds_in_target_space = kRoundedCornerLayer4Bound;
  bounds_in_target_space.Scale(kDeviceScale);
  EXPECT_EQ(actual_self_rrect_4.rect(), bounds_in_target_space);
  EXPECT_FLOAT_EQ(actual_self_rrect_4.GetSimpleRadius(),
                  kRoundedCorner4Radius * kDeviceScale);
}

}  // namespace
}  // namespace cc
