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

#include <algorithm>
#include <set>
#include <vector>

#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#include "cc/animation/layer_animation_controller.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_impl_proxy.h"
#include "cc/test/fake_layer_tree_host.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "cc/trees/proxy.h"
#include "cc/trees/single_thread_proxy.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cc {
namespace {

class MockLayerImpl : public LayerImpl {
 public:
  static scoped_ptr<MockLayerImpl> Create(LayerTreeImpl* tree_impl,
                                          int layer_id) {
    return make_scoped_ptr(new MockLayerImpl(tree_impl, layer_id));
  }
  virtual ~MockLayerImpl() {
    if (layer_impl_destruction_list_)
      layer_impl_destruction_list_->push_back(id());
  }

  void SetLayerImplDestructionList(std::vector<int>* list) {
    layer_impl_destruction_list_ = list;
  }

 private:
  MockLayerImpl(LayerTreeImpl* tree_impl, int layer_id)
      : LayerImpl(tree_impl, layer_id),
        layer_impl_destruction_list_(NULL) {}

  std::vector<int>* layer_impl_destruction_list_;
};

class MockLayer : public Layer {
 public:
  static scoped_refptr<MockLayer> Create(
      std::vector<int>* layer_impl_destruction_list) {
    return make_scoped_refptr(new MockLayer(layer_impl_destruction_list));
  }

  virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
      OVERRIDE {
    return MockLayerImpl::Create(tree_impl, layer_id_).PassAs<LayerImpl>();
  }

  virtual void PushPropertiesTo(LayerImpl* layer_impl) OVERRIDE {
    Layer::PushPropertiesTo(layer_impl);

    MockLayerImpl* mock_layer_impl = static_cast<MockLayerImpl*>(layer_impl);
    mock_layer_impl->SetLayerImplDestructionList(layer_impl_destruction_list_);
  }

 private:
  explicit MockLayer(std::vector<int>* layer_impl_destruction_list)
      : Layer(), layer_impl_destruction_list_(layer_impl_destruction_list) {}
  virtual ~MockLayer() {}

  std::vector<int>* layer_impl_destruction_list_;
};

class FakeLayerAnimationController : public LayerAnimationController {
 public:
  static scoped_refptr<LayerAnimationController> Create() {
    return static_cast<LayerAnimationController*>(
        new FakeLayerAnimationController);
  }

  bool SynchronizedAnimations() const { return synchronized_animations_; }

 private:
  FakeLayerAnimationController()
      : LayerAnimationController(1),
        synchronized_animations_(false) {}

  virtual ~FakeLayerAnimationController() {}

  virtual void PushAnimationUpdatesTo(LayerAnimationController* controller_impl)
      OVERRIDE {
    LayerAnimationController::PushAnimationUpdatesTo(controller_impl);
    synchronized_animations_ = true;
  }

  bool synchronized_animations_;
};

void ExpectTreesAreIdentical(Layer* layer,
                             LayerImpl* layer_impl,
                             LayerTreeImpl* tree_impl) {
  ASSERT_TRUE(layer);
  ASSERT_TRUE(layer_impl);

  EXPECT_EQ(layer->id(), layer_impl->id());
  EXPECT_EQ(layer_impl->layer_tree_impl(), tree_impl);

  EXPECT_EQ(layer->non_fast_scrollable_region(),
            layer_impl->non_fast_scrollable_region());

  ASSERT_EQ(!!layer->mask_layer(), !!layer_impl->mask_layer());
  if (layer->mask_layer()) {
    SCOPED_TRACE("mask_layer");
    ExpectTreesAreIdentical(
        layer->mask_layer(), layer_impl->mask_layer(), tree_impl);
  }

  ASSERT_EQ(!!layer->replica_layer(), !!layer_impl->replica_layer());
  if (layer->replica_layer()) {
    SCOPED_TRACE("replica_layer");
    ExpectTreesAreIdentical(
        layer->replica_layer(), layer_impl->replica_layer(), tree_impl);
  }

  const LayerList& layer_children = layer->children();
  const OwnedLayerImplList& layer_impl_children = layer_impl->children();

  ASSERT_EQ(layer_children.size(), layer_impl_children.size());

  const std::set<Layer*>* layer_scroll_children = layer->scroll_children();
  const std::set<LayerImpl*>* layer_impl_scroll_children =
      layer_impl->scroll_children();

  ASSERT_EQ(!!layer_scroll_children, !!layer_impl_scroll_children);

  if (layer_scroll_children) {
    ASSERT_EQ(
        layer_scroll_children->size(),
        layer_impl_scroll_children->size());
  }

  const Layer* layer_scroll_parent = layer->scroll_parent();
  const LayerImpl* layer_impl_scroll_parent = layer_impl->scroll_parent();

  ASSERT_EQ(!!layer_scroll_parent, !!layer_impl_scroll_parent);

  if (layer_scroll_parent) {
    ASSERT_EQ(layer_scroll_parent->id(), layer_impl_scroll_parent->id());
    ASSERT_TRUE(layer_scroll_parent->scroll_children()->find(layer) !=
        layer_scroll_parent->scroll_children()->end());
    ASSERT_TRUE(layer_impl_scroll_parent->scroll_children()->find(layer_impl) !=
        layer_impl_scroll_parent->scroll_children()->end());
  }

  const std::set<Layer*>* layer_clip_children = layer->clip_children();
  const std::set<LayerImpl*>* layer_impl_clip_children =
      layer_impl->clip_children();

  ASSERT_EQ(!!layer_clip_children, !!layer_impl_clip_children);

  if (layer_clip_children)
    ASSERT_EQ(layer_clip_children->size(), layer_impl_clip_children->size());

  const Layer* layer_clip_parent = layer->clip_parent();
  const LayerImpl* layer_impl_clip_parent = layer_impl->clip_parent();

  ASSERT_EQ(!!layer_clip_parent, !!layer_impl_clip_parent);

  if (layer_clip_parent) {
    const std::set<LayerImpl*>* clip_children_impl =
        layer_impl_clip_parent->clip_children();
    const std::set<Layer*>* clip_children =
        layer_clip_parent->clip_children();
    ASSERT_EQ(layer_clip_parent->id(), layer_impl_clip_parent->id());
    ASSERT_TRUE(clip_children->find(layer) != clip_children->end());
    ASSERT_TRUE(clip_children_impl->find(layer_impl) !=
                clip_children_impl->end());
  }

  for (size_t i = 0; i < layer_children.size(); ++i) {
    SCOPED_TRACE(base::StringPrintf("child layer %" PRIuS, i).c_str());
    ExpectTreesAreIdentical(
        layer_children[i].get(), layer_impl_children[i], tree_impl);
  }
}

class TreeSynchronizerTest : public testing::Test {
 public:
  TreeSynchronizerTest()
      : client_(FakeLayerTreeHostClient::DIRECT_3D),
        host_(FakeLayerTreeHost::Create(&client_)) {}

 protected:
  FakeLayerTreeHostClient client_;
  scoped_ptr<FakeLayerTreeHost> host_;
};

// Attempts to synchronizes a null tree. This should not crash, and should
// return a null tree.
TEST_F(TreeSynchronizerTest, SyncNullTree) {
  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(static_cast<Layer*>(NULL),
                                         scoped_ptr<LayerImpl>(),
                                         host_->active_tree());

  EXPECT_TRUE(!layer_impl_tree_root.get());
}

// Constructs a very simple tree and synchronizes it without trying to reuse any
// preexisting layers.
TEST_F(TreeSynchronizerTest, SyncSimpleTreeFromEmpty) {
  scoped_refptr<Layer> layer_tree_root = Layer::Create();
  layer_tree_root->AddChild(Layer::Create());
  layer_tree_root->AddChild(Layer::Create());

  host_->SetRootLayer(layer_tree_root);

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_->active_tree());

  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());
}

// Constructs a very simple tree and synchronizes it attempting to reuse some
// layers
TEST_F(TreeSynchronizerTest, SyncSimpleTreeReusingLayers) {
  std::vector<int> layer_impl_destruction_list;

  scoped_refptr<Layer> layer_tree_root =
      MockLayer::Create(&layer_impl_destruction_list);
  layer_tree_root->AddChild(MockLayer::Create(&layer_impl_destruction_list));
  layer_tree_root->AddChild(MockLayer::Create(&layer_impl_destruction_list));

  host_->SetRootLayer(layer_tree_root);

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  // We have to push properties to pick up the destruction list pointer.
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());

  // Add a new layer to the Layer side
  layer_tree_root->children()[0]->
      AddChild(MockLayer::Create(&layer_impl_destruction_list));
  // Remove one.
  layer_tree_root->children()[1]->RemoveFromParent();
  int second_layer_impl_id = layer_impl_tree_root->children()[1]->id();

  // Synchronize again. After the sync the trees should be equivalent and we
  // should have created and destroyed one LayerImpl.
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  ASSERT_EQ(1u, layer_impl_destruction_list.size());
  EXPECT_EQ(second_layer_impl_id, layer_impl_destruction_list[0]);
}

// Constructs a very simple tree and checks that a stacking-order change is
// tracked properly.
TEST_F(TreeSynchronizerTest, SyncSimpleTreeAndTrackStackingOrderChange) {
  std::vector<int> layer_impl_destruction_list;

  // Set up the tree and sync once. child2 needs to be synced here, too, even
  // though we remove it to set up the intended scenario.
  scoped_refptr<Layer> layer_tree_root =
      MockLayer::Create(&layer_impl_destruction_list);
  scoped_refptr<Layer> child2 = MockLayer::Create(&layer_impl_destruction_list);
  layer_tree_root->AddChild(MockLayer::Create(&layer_impl_destruction_list));
  layer_tree_root->AddChild(child2);

  host_->SetRootLayer(layer_tree_root);

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  // We have to push properties to pick up the destruction list pointer.
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());

  layer_impl_tree_root->ResetAllChangeTrackingForSubtree();

  // re-insert the layer and sync again.
  child2->RemoveFromParent();
  layer_tree_root->AddChild(child2);
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());

  // Check that the impl thread properly tracked the change.
  EXPECT_FALSE(layer_impl_tree_root->LayerPropertyChanged());
  EXPECT_FALSE(layer_impl_tree_root->children()[0]->LayerPropertyChanged());
  EXPECT_TRUE(layer_impl_tree_root->children()[1]->LayerPropertyChanged());
}

TEST_F(TreeSynchronizerTest, SyncSimpleTreeAndProperties) {
  scoped_refptr<Layer> layer_tree_root = Layer::Create();
  layer_tree_root->AddChild(Layer::Create());
  layer_tree_root->AddChild(Layer::Create());

  host_->SetRootLayer(layer_tree_root);

  // Pick some random properties to set. The values are not important, we're
  // just testing that at least some properties are making it through.
  gfx::PointF root_position = gfx::PointF(2.3f, 7.4f);
  layer_tree_root->SetPosition(root_position);

  float first_child_opacity = 0.25f;
  layer_tree_root->children()[0]->SetOpacity(first_child_opacity);

  gfx::Size second_child_bounds = gfx::Size(25, 53);
  layer_tree_root->children()[1]->SetBounds(second_child_bounds);
  layer_tree_root->children()[1]->SavePaintProperties();

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());

  // Check that the property values we set on the Layer tree are reflected in
  // the LayerImpl tree.
  gfx::PointF root_layer_impl_position = layer_impl_tree_root->position();
  EXPECT_EQ(root_position.x(), root_layer_impl_position.x());
  EXPECT_EQ(root_position.y(), root_layer_impl_position.y());

  EXPECT_EQ(first_child_opacity,
            layer_impl_tree_root->children()[0]->opacity());

  gfx::Size second_layer_impl_child_bounds =
      layer_impl_tree_root->children()[1]->bounds();
  EXPECT_EQ(second_child_bounds.width(),
            second_layer_impl_child_bounds.width());
  EXPECT_EQ(second_child_bounds.height(),
            second_layer_impl_child_bounds.height());
}

TEST_F(TreeSynchronizerTest, ReuseLayerImplsAfterStructuralChange) {
  std::vector<int> layer_impl_destruction_list;

  // Set up a tree with this sort of structure:
  // root --- A --- B ---+--- C
  //                     |
  //                     +--- D
  scoped_refptr<Layer> layer_tree_root =
      MockLayer::Create(&layer_impl_destruction_list);
  layer_tree_root->AddChild(MockLayer::Create(&layer_impl_destruction_list));

  scoped_refptr<Layer> layer_a = layer_tree_root->children()[0].get();
  layer_a->AddChild(MockLayer::Create(&layer_impl_destruction_list));

  scoped_refptr<Layer> layer_b = layer_a->children()[0].get();
  layer_b->AddChild(MockLayer::Create(&layer_impl_destruction_list));

  scoped_refptr<Layer> layer_c = layer_b->children()[0].get();
  layer_b->AddChild(MockLayer::Create(&layer_impl_destruction_list));
  scoped_refptr<Layer> layer_d = layer_b->children()[1].get();

  host_->SetRootLayer(layer_tree_root);

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  // We have to push properties to pick up the destruction list pointer.
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());

  // Now restructure the tree to look like this:
  // root --- D ---+--- A
  //               |
  //               +--- C --- B
  layer_tree_root->RemoveAllChildren();
  layer_d->RemoveAllChildren();
  layer_tree_root->AddChild(layer_d);
  layer_a->RemoveAllChildren();
  layer_d->AddChild(layer_a);
  layer_c->RemoveAllChildren();
  layer_d->AddChild(layer_c);
  layer_b->RemoveAllChildren();
  layer_c->AddChild(layer_b);

  // After another synchronize our trees should match and we should not have
  // destroyed any LayerImpls
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  EXPECT_EQ(0u, layer_impl_destruction_list.size());
}

// Constructs a very simple tree, synchronizes it, then synchronizes to a
// totally new tree. All layers from the old tree should be deleted.
TEST_F(TreeSynchronizerTest, SyncSimpleTreeThenDestroy) {
  std::vector<int> layer_impl_destruction_list;

  scoped_refptr<Layer> old_layer_tree_root =
      MockLayer::Create(&layer_impl_destruction_list);
  old_layer_tree_root->AddChild(
      MockLayer::Create(&layer_impl_destruction_list));
  old_layer_tree_root->AddChild(
      MockLayer::Create(&layer_impl_destruction_list));

  host_->SetRootLayer(old_layer_tree_root);

  int old_tree_root_layer_id = old_layer_tree_root->id();
  int old_tree_first_child_layer_id = old_layer_tree_root->children()[0]->id();
  int old_tree_second_child_layer_id = old_layer_tree_root->children()[1]->id();

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(old_layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(old_layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  // We have to push properties to pick up the destruction list pointer.
  TreeSynchronizer::PushProperties(old_layer_tree_root.get(),
                                   layer_impl_tree_root.get());

  // Remove all children on the Layer side.
  old_layer_tree_root->RemoveAllChildren();

  // Synchronize again. After the sync all LayerImpls from the old tree should
  // be deleted.
  scoped_refptr<Layer> new_layer_tree_root = Layer::Create();
  host_->SetRootLayer(new_layer_tree_root);
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(new_layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(new_layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  ASSERT_EQ(3u, layer_impl_destruction_list.size());

  EXPECT_TRUE(std::find(layer_impl_destruction_list.begin(),
                        layer_impl_destruction_list.end(),
                        old_tree_root_layer_id) !=
              layer_impl_destruction_list.end());
  EXPECT_TRUE(std::find(layer_impl_destruction_list.begin(),
                        layer_impl_destruction_list.end(),
                        old_tree_first_child_layer_id) !=
              layer_impl_destruction_list.end());
  EXPECT_TRUE(std::find(layer_impl_destruction_list.begin(),
                        layer_impl_destruction_list.end(),
                        old_tree_second_child_layer_id) !=
              layer_impl_destruction_list.end());
}

// Constructs+syncs a tree with mask, replica, and replica mask layers.
TEST_F(TreeSynchronizerTest, SyncMaskReplicaAndReplicaMaskLayers) {
  scoped_refptr<Layer> layer_tree_root = Layer::Create();
  layer_tree_root->AddChild(Layer::Create());
  layer_tree_root->AddChild(Layer::Create());
  layer_tree_root->AddChild(Layer::Create());

  // First child gets a mask layer.
  scoped_refptr<Layer> mask_layer = Layer::Create();
  layer_tree_root->children()[0]->SetMaskLayer(mask_layer.get());

  // Second child gets a replica layer.
  scoped_refptr<Layer> replica_layer = Layer::Create();
  layer_tree_root->children()[1]->SetReplicaLayer(replica_layer.get());

  // Third child gets a replica layer with a mask layer.
  scoped_refptr<Layer> replica_layer_with_mask = Layer::Create();
  scoped_refptr<Layer> replica_mask_layer = Layer::Create();
  replica_layer_with_mask->SetMaskLayer(replica_mask_layer.get());
  layer_tree_root->children()[2]->
      SetReplicaLayer(replica_layer_with_mask.get());

  host_->SetRootLayer(layer_tree_root);

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_->active_tree());

  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  // Remove the mask layer.
  layer_tree_root->children()[0]->SetMaskLayer(NULL);
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  // Remove the replica layer.
  layer_tree_root->children()[1]->SetReplicaLayer(NULL);
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());

  // Remove the replica mask.
  replica_layer_with_mask->SetMaskLayer(NULL);
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_->active_tree());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_->active_tree());
}

TEST_F(TreeSynchronizerTest, SynchronizeAnimations) {
  LayerTreeSettings settings;
  FakeProxy proxy;
  DebugScopedSetImplThread impl(&proxy);
  FakeRenderingStatsInstrumentation stats_instrumentation;
  scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
      new TestSharedBitmapManager());
  scoped_ptr<LayerTreeHostImpl> host_impl =
      LayerTreeHostImpl::Create(settings,
                                NULL,
                                &proxy,
                                &stats_instrumentation,
                                shared_bitmap_manager.get(),
                                0);

  scoped_refptr<Layer> layer_tree_root = Layer::Create();
  host_->SetRootLayer(layer_tree_root);

  layer_tree_root->SetLayerAnimationControllerForTest(
      FakeLayerAnimationController::Create());

  EXPECT_FALSE(static_cast<FakeLayerAnimationController*>(
      layer_tree_root->layer_animation_controller())->SynchronizedAnimations());

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_->active_tree());
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_->active_tree());

  EXPECT_TRUE(static_cast<FakeLayerAnimationController*>(
      layer_tree_root->layer_animation_controller())->SynchronizedAnimations());
}

TEST_F(TreeSynchronizerTest, SynchronizeScrollParent) {
  LayerTreeSettings settings;
  FakeProxy proxy;
  DebugScopedSetImplThread impl(&proxy);
  FakeRenderingStatsInstrumentation stats_instrumentation;
  scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
      new TestSharedBitmapManager());
  scoped_ptr<LayerTreeHostImpl> host_impl =
      LayerTreeHostImpl::Create(settings,
                                NULL,
                                &proxy,
                                &stats_instrumentation,
                                shared_bitmap_manager.get(),
                                0);

  scoped_refptr<Layer> layer_tree_root = Layer::Create();
  scoped_refptr<Layer> scroll_parent = Layer::Create();
  layer_tree_root->AddChild(scroll_parent);
  layer_tree_root->AddChild(Layer::Create());
  layer_tree_root->AddChild(Layer::Create());

  host_->SetRootLayer(layer_tree_root);

  // First child is the second and third child's scroll parent.
  layer_tree_root->children()[1]->SetScrollParent(scroll_parent.get());
  layer_tree_root->children()[2]->SetScrollParent(scroll_parent.get());

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_impl->active_tree());
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());
  {
    SCOPED_TRACE("case one");
    ExpectTreesAreIdentical(layer_tree_root.get(),
                            layer_impl_tree_root.get(),
                            host_impl->active_tree());
  }

  // Remove the first scroll child.
  layer_tree_root->children()[1]->RemoveFromParent();
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_impl->active_tree());
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());
  {
    SCOPED_TRACE("case two");
    ExpectTreesAreIdentical(layer_tree_root.get(),
                            layer_impl_tree_root.get(),
                            host_impl->active_tree());
  }

  // Add an additional scroll layer.
  scoped_refptr<Layer> additional_scroll_child = Layer::Create();
  layer_tree_root->AddChild(additional_scroll_child);
  additional_scroll_child->SetScrollParent(scroll_parent.get());
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_impl->active_tree());
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());
  {
    SCOPED_TRACE("case three");
    ExpectTreesAreIdentical(layer_tree_root.get(),
                            layer_impl_tree_root.get(),
                            host_impl->active_tree());
  }
}

TEST_F(TreeSynchronizerTest, SynchronizeClipParent) {
  LayerTreeSettings settings;
  FakeProxy proxy;
  DebugScopedSetImplThread impl(&proxy);
  FakeRenderingStatsInstrumentation stats_instrumentation;
  scoped_ptr<SharedBitmapManager> shared_bitmap_manager(
      new TestSharedBitmapManager());
  scoped_ptr<LayerTreeHostImpl> host_impl =
      LayerTreeHostImpl::Create(settings,
                                NULL,
                                &proxy,
                                &stats_instrumentation,
                                shared_bitmap_manager.get(),
                                0);

  scoped_refptr<Layer> layer_tree_root = Layer::Create();
  scoped_refptr<Layer> clip_parent = Layer::Create();
  scoped_refptr<Layer> intervening = Layer::Create();
  scoped_refptr<Layer> clip_child1 = Layer::Create();
  scoped_refptr<Layer> clip_child2 = Layer::Create();
  layer_tree_root->AddChild(clip_parent);
  clip_parent->AddChild(intervening);
  intervening->AddChild(clip_child1);
  intervening->AddChild(clip_child2);

  host_->SetRootLayer(layer_tree_root);

  // First child is the second and third child's scroll parent.
  clip_child1->SetClipParent(clip_parent.get());
  clip_child2->SetClipParent(clip_parent.get());

  scoped_ptr<LayerImpl> layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         scoped_ptr<LayerImpl>(),
                                         host_impl->active_tree());
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_impl->active_tree());

  // Remove the first clip child.
  clip_child1->RemoveFromParent();
  clip_child1 = NULL;

  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_impl->active_tree());
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_impl->active_tree());

  // Add an additional clip child.
  scoped_refptr<Layer> additional_clip_child = Layer::Create();
  intervening->AddChild(additional_clip_child);
  additional_clip_child->SetClipParent(clip_parent.get());
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_impl->active_tree());
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_impl->active_tree());

  // Remove the nearest clipping ancestor.
  clip_parent->RemoveFromParent();
  clip_parent = NULL;
  layer_impl_tree_root =
      TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
                                         layer_impl_tree_root.Pass(),
                                         host_impl->active_tree());
  TreeSynchronizer::PushProperties(layer_tree_root.get(),
                                   layer_impl_tree_root.get());
  ExpectTreesAreIdentical(layer_tree_root.get(),
                          layer_impl_tree_root.get(),
                          host_impl->active_tree());

  // The clip children should have been unhooked.
  EXPECT_EQ(2u, intervening->children().size());
  EXPECT_FALSE(clip_child2->clip_parent());
  EXPECT_FALSE(additional_clip_child->clip_parent());
}

}  // namespace
}  // namespace cc