[go: nahoru, domu]

Add support for (de)serializing cc::Layer hierarchy.

This CL adds a protocol buffer for serializing the Layer hierarchy,
including the mask and replica layers. This will be used whenever the
hierarchy has changed and needs to be sent to the client.

Given that the protocol still is in flux, and for good measure, all
fields in the protocol buffer are marked as optional, even the
currently required fields.

When a hierarchy is serialized, the whole hierarchy is serialized from
the root node, instead of just the changed subtree.

Most of the logic lives in the Layer class, but the possibility to
change the root of the hierarchy is extracted out to a
LayerProtoConverter. It also has functionality to build up a map of
the current tree, which will also be used during property
deserialization.

The next CL will deal with serializing the properties:
https://codereview.chromium.org/1423523002/

BUG=538710
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

Review URL: https://codereview.chromium.org/1398443008

Cr-Commit-Position: refs/heads/master@{#358424}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 2ff2593..9d3cc43 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -138,6 +138,8 @@
     "layers/layer_lists.h",
     "layers/layer_position_constraint.cc",
     "layers/layer_position_constraint.h",
+    "layers/layer_proto_converter.cc",
+    "layers/layer_proto_converter.h",
     "layers/layer_utils.cc",
     "layers/layer_utils.h",
     "layers/nine_patch_layer.cc",
@@ -753,6 +755,7 @@
     "layers/layer_impl_unittest.cc",
     "layers/layer_iterator_unittest.cc",
     "layers/layer_position_constraint_unittest.cc",
+    "layers/layer_proto_converter_unittest.cc",
     "layers/layer_unittest.cc",
     "layers/layer_utils_unittest.cc",
     "layers/nine_patch_layer_impl_unittest.cc",
diff --git a/cc/cc.gyp b/cc/cc.gyp
index 2a40437..c453330 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -200,6 +200,8 @@
         'layers/layer_lists.h',
         'layers/layer_position_constraint.cc',
         'layers/layer_position_constraint.h',
+        'layers/layer_proto_converter.cc',
+        'layers/layer_proto_converter.h',
         'layers/layer_utils.cc',
         'layers/layer_utils.h',
         'layers/nine_patch_layer.cc',
@@ -584,6 +586,7 @@
       'type': '<(component)',
       'sources': [
         'proto/display_item.proto',
+        'proto/layer.proto',
         'proto/point.proto',
         'proto/pointf.proto',
         'proto/rect.proto',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index 65107ac..222b202576 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -45,6 +45,7 @@
       'layers/layer_impl_unittest.cc',
       'layers/layer_iterator_unittest.cc',
       'layers/layer_position_constraint_unittest.cc',
+      'layers/layer_proto_converter_unittest.cc',
       'layers/layer_unittest.cc',
       'layers/layer_utils_unittest.cc',
       'layers/nine_patch_layer_impl_unittest.cc',
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index d1a95bc9..5ebe0d8c 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -21,9 +21,11 @@
 #include "cc/debug/frame_viewer_instrumentation.h"
 #include "cc/layers/layer_client.h"
 #include "cc/layers/layer_impl.h"
+#include "cc/layers/layer_proto_converter.h"
 #include "cc/layers/scrollbar_layer_interface.h"
 #include "cc/output/copy_output_request.h"
 #include "cc/output/copy_output_result.h"
+#include "cc/proto/layer.pb.h"
 #include "cc/trees/draw_property_utils.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_impl.h"
@@ -1349,6 +1351,73 @@
   num_dependents_need_push_properties_ = 0;
 }
 
+void Layer::SetTypeForProtoSerialization(proto::LayerNode* proto) const {
+  proto->set_type(proto::LayerType::Base);
+}
+
+void Layer::ToLayerNodeProto(proto::LayerNode* proto) const {
+  proto->set_id(layer_id_);
+  SetTypeForProtoSerialization(proto);
+
+  if (parent_)
+    proto->set_parent_id(parent_->id());
+
+  DCHECK_EQ(0, proto->children_size());
+  for (const auto& child : children_) {
+    child->ToLayerNodeProto(proto->add_children());
+  }
+
+  if (mask_layer_)
+    mask_layer_->ToLayerNodeProto(proto->mutable_mask_layer());
+  if (replica_layer_)
+    replica_layer_->ToLayerNodeProto(proto->mutable_replica_layer());
+}
+
+void Layer::FromLayerNodeProto(const proto::LayerNode& proto,
+                               const LayerIdMap& layer_map) {
+  DCHECK(proto.has_id());
+  layer_id_ = proto.id();
+
+  // Recursively remove all children. In the case of when the updated
+  // hierarchy has no children, or the children has changed, the old list
+  // of children must be removed. The whole hierarchy is always sent, so
+  // if there were no change in the children, they will be correctly added back
+  // below.
+  RemoveAllChildren();
+  for (int i = 0; i < proto.children_size(); ++i) {
+    const proto::LayerNode& child_proto = proto.children(i);
+    DCHECK(child_proto.has_type());
+    scoped_refptr<Layer> child =
+        LayerProtoConverter::FindOrAllocateAndConstruct(child_proto, layer_map);
+    child->FromLayerNodeProto(child_proto, layer_map);
+    AddChild(child);
+  }
+
+  if (mask_layer_)
+    mask_layer_->RemoveFromParent();
+  if (proto.has_mask_layer()) {
+    mask_layer_ = LayerProtoConverter::FindOrAllocateAndConstruct(
+        proto.mask_layer(), layer_map);
+    mask_layer_->FromLayerNodeProto(proto.mask_layer(), layer_map);
+    mask_layer_->SetParent(this);
+    // SetIsMask() is only ever called with true, so no need to reset flag.
+    mask_layer_->SetIsMask(true);
+  } else {
+    mask_layer_ = nullptr;
+  }
+
+  if (replica_layer_)
+    replica_layer_->RemoveFromParent();
+  if (proto.has_replica_layer()) {
+    replica_layer_ = LayerProtoConverter::FindOrAllocateAndConstruct(
+        proto.replica_layer(), layer_map);
+    replica_layer_->FromLayerNodeProto(proto.replica_layer(), layer_map);
+    replica_layer_->SetParent(this);
+  } else {
+    replica_layer_ = nullptr;
+  }
+}
+
 scoped_ptr<LayerImpl> Layer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
   return LayerImpl::Create(tree_impl, layer_id_,
                            new LayerImpl::SyncedScrollOffset);
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 2fec3636..5c298f5 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -67,6 +67,10 @@
 class SimpleEnclosedRegion;
 struct AnimationEvent;
 
+namespace proto {
+class LayerNode;
+}  // namespace proto
+
 // Base class for composited layers. Special layer types are derived from
 // this class.
 class CC_EXPORT Layer : public base::RefCounted<Layer>,
@@ -74,6 +78,7 @@
                         public LayerAnimationValueProvider {
  public:
   typedef LayerList LayerListType;
+  typedef base::hash_map<int, scoped_refptr<Layer>> LayerIdMap;
 
   enum LayerIdLabels {
     INVALID_ID = -1,
@@ -358,6 +363,30 @@
 
   virtual void PushPropertiesTo(LayerImpl* layer);
 
+  // Sets the type proto::LayerType that should be used for serialization
+  // of the current layer by calling LayerNode::set_type(proto::LayerType).
+  // TODO(nyquist): Start using a forward declared enum class when
+  // https://github.com/google/protobuf/issues/67 has been fixed and rolled in.
+  // This function would preferably instead return a proto::LayerType, but
+  // since that is an enum (the protobuf library does not generate enum
+  // classes), it can't be forward declared. We don't want to include
+  // //cc/proto/layer.pb.h in this header file, as it requires that all
+  // dependent targets would have to be given the config for how to include it.
+  virtual void SetTypeForProtoSerialization(proto::LayerNode* proto) const;
+
+  // Recursively iterate over this layer and all children and write the
+  // hierarchical structure to the given LayerNode proto. In addition to the
+  // structure itself, the Layer id and type is also written to facilitate
+  // construction of the correct layer on the client.
+  void ToLayerNodeProto(proto::LayerNode* proto) const;
+
+  // Recursively iterate over the given LayerNode proto and read the structure
+  // into this node and its children. The |layer_map| should be used to look
+  // for previously existing Layers, since they should be re-used between each
+  // hierarchy update.
+  void FromLayerNodeProto(const proto::LayerNode& proto,
+                          const LayerIdMap& layer_map);
+
   LayerTreeHost* layer_tree_host() { return layer_tree_host_; }
   const LayerTreeHost* layer_tree_host() const { return layer_tree_host_; }
 
diff --git a/cc/layers/layer_proto_converter.cc b/cc/layers/layer_proto_converter.cc
new file mode 100644
index 0000000..2dcc3d9
--- /dev/null
+++ b/cc/layers/layer_proto_converter.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/layer_proto_converter.h"
+
+#include "base/stl_util.h"
+#include "cc/layers/layer.h"
+#include "cc/proto/layer.pb.h"
+#include "cc/trees/layer_tree_host_common.h"
+#include "cc/trees/layer_tree_settings.h"
+
+namespace cc {
+
+LayerProtoConverter::LayerProtoConverter() {}
+
+LayerProtoConverter::~LayerProtoConverter() {}
+
+// static
+void LayerProtoConverter::SerializeLayerHierarchy(
+    const scoped_refptr<Layer> root_layer,
+    proto::LayerNode* root_node) {
+  root_layer->ToLayerNodeProto(root_node);
+}
+
+// static
+scoped_refptr<Layer> LayerProtoConverter::DeserializeLayerHierarchy(
+    scoped_refptr<Layer> existing_root,
+    const proto::LayerNode& root_node) {
+  LayerIdMap layer_id_map;
+  RecursivelyFindAllLayers(existing_root, &layer_id_map);
+
+  scoped_refptr<Layer> new_root = existing_root;
+  if (!existing_root ||
+      (root_node.has_id() && root_node.id() != existing_root->id())) {
+    // The root node has changed or there was no root node,
+    // so find or create the new root.
+    new_root = FindOrAllocateAndConstruct(root_node, layer_id_map);
+  }
+  new_root->FromLayerNodeProto(root_node, layer_id_map);
+  return new_root;
+}
+
+// static
+void LayerProtoConverter::RecursivelyFindAllLayers(
+    const scoped_refptr<Layer>& layer,
+    LayerIdMap* layer_id_map) {
+  LayerTreeHostCommon::CallFunctionForSubtree(
+      layer.get(),
+      [layer_id_map](Layer* layer) { (*layer_id_map)[layer->id()] = layer; });
+}
+
+// static
+scoped_refptr<Layer> LayerProtoConverter::FindOrAllocateAndConstruct(
+    const proto::LayerNode& proto,
+    const Layer::LayerIdMap& layer_id_map) {
+  DCHECK(proto.has_id());
+  Layer::LayerIdMap::const_iterator iter = layer_id_map.find(proto.id());
+  if (iter != layer_id_map.end())
+    return iter->second;
+  DCHECK(proto.has_type());
+  switch (proto.type()) {
+    case proto::Base:
+      return Layer::Create(LayerSettings()).get();
+  }
+  // TODO(nyquist): Add the rest of the necessary LayerTypes. This function
+  // should not return null.
+  NOTREACHED();
+  return nullptr;
+}
+
+}  // namespace cc
diff --git a/cc/layers/layer_proto_converter.h b/cc/layers/layer_proto_converter.h
new file mode 100644
index 0000000..29424a0
--- /dev/null
+++ b/cc/layers/layer_proto_converter.h
@@ -0,0 +1,54 @@
+// Copyright 2015 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.
+
+#ifndef CC_LAYERS_LAYER_PROTO_CONVERTER_H_
+#define CC_LAYERS_LAYER_PROTO_CONVERTER_H_
+
+#include "base/macros.h"
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace cc {
+
+namespace proto {
+class LayerNode;
+}
+
+// A class to faciliate (de)serialization of a Layer tree to protocol buffers.
+class CC_EXPORT LayerProtoConverter {
+ public:
+  // Starting at |root_layer|, serializes the layer hierarchy into the
+  // proto::LayerNode.
+  static void SerializeLayerHierarchy(const scoped_refptr<Layer> root_layer,
+                                      proto::LayerNode* root_node);
+
+  // Recursively iterate over the given LayerNode proto and read the structure
+  // into a local Layer structure, re-using existing Layers. returns the new
+  // root Layer after updating the hierarchy (may be the same as
+  // |existing_root|).
+  static scoped_refptr<Layer> DeserializeLayerHierarchy(
+      scoped_refptr<Layer> existing_root,
+      const proto::LayerNode& root_node);
+
+  // Returns the Layer with proto.id() as the Layer id, if it exists in
+  // |layer_id_map|. Otherwise, a new Layer is constructed of the type given
+  // from proto.type().
+  static scoped_refptr<Layer> FindOrAllocateAndConstruct(
+      const proto::LayerNode& proto,
+      const Layer::LayerIdMap& layer_id_map);
+
+ private:
+  LayerProtoConverter();
+  ~LayerProtoConverter();
+
+  using LayerIdMap = base::hash_map<int, scoped_refptr<Layer>>;
+  // Start at |layer| and recursively add |layer| and all its children and
+  // special layers to |layer_id_map|.
+  static void RecursivelyFindAllLayers(const scoped_refptr<Layer>& layer,
+                                       LayerIdMap* layer_id_map);
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_LAYER_PROTO_CONVERTER_H_
diff --git a/cc/layers/layer_proto_converter_unittest.cc b/cc/layers/layer_proto_converter_unittest.cc
new file mode 100644
index 0000000..040be97
--- /dev/null
+++ b/cc/layers/layer_proto_converter_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/layer_proto_converter.h"
+
+#include "cc/layers/layer.h"
+#include "cc/proto/layer.pb.h"
+#include "cc/trees/layer_tree_settings.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+
+TEST(LayerProtoConverterTest, TestKeepingRoot) {
+  /* Test deserialization of a tree that looks like:
+         root
+        /   \
+       a     b
+              \
+               c
+     The old root node will be reused during deserialization.
+  */
+  scoped_refptr<Layer> old_root = Layer::Create(LayerSettings());
+  proto::LayerNode root_node;
+  root_node.set_id(old_root->id());
+  root_node.set_type(proto::LayerType::Base);
+
+  proto::LayerNode* child_a_node = root_node.add_children();
+  child_a_node->set_id(442);
+  child_a_node->set_type(proto::LayerType::Base);
+  child_a_node->set_parent_id(old_root->id());  // root_node
+
+  proto::LayerNode* child_b_node = root_node.add_children();
+  child_b_node->set_id(443);
+  child_b_node->set_type(proto::LayerType::Base);
+  child_b_node->set_parent_id(old_root->id());  // root_node
+
+  proto::LayerNode* child_c_node = child_b_node->add_children();
+  child_c_node->set_id(444);
+  child_c_node->set_type(proto::LayerType::Base);
+  child_c_node->set_parent_id(child_b_node->id());
+
+  scoped_refptr<Layer> new_root =
+      LayerProtoConverter::DeserializeLayerHierarchy(old_root, root_node);
+
+  // The new root should not be the same as the old root.
+  EXPECT_EQ(old_root->id(), new_root->id());
+  ASSERT_EQ(2u, new_root->children().size());
+  scoped_refptr<Layer> child_a = new_root->children()[0];
+  scoped_refptr<Layer> child_b = new_root->children()[1];
+
+  EXPECT_EQ(child_a_node->id(), child_a->id());
+  EXPECT_EQ(child_b_node->id(), child_b->id());
+
+  EXPECT_EQ(0u, child_a->children().size());
+  ASSERT_EQ(1u, child_b->children().size());
+
+  scoped_refptr<Layer> child_c = child_b->children()[0];
+  EXPECT_EQ(child_c_node->id(), child_c->id());
+}
+
+TEST(LayerProtoConverterTest, TestSwappingRoot) {
+  /* Test deserialization of a tree that looks like:
+         root
+        /   \
+       a     b
+              \
+               c
+     The old root node will be swapped out during deserialization.
+  */
+  proto::LayerNode root_node;
+  root_node.set_id(441);
+  root_node.set_type(proto::LayerType::Base);
+
+  proto::LayerNode* child_a_node = root_node.add_children();
+  child_a_node->set_id(442);
+  child_a_node->set_type(proto::LayerType::Base);
+  child_a_node->set_parent_id(root_node.id());
+
+  proto::LayerNode* child_b_node = root_node.add_children();
+  child_b_node->set_id(443);
+  child_b_node->set_type(proto::LayerType::Base);
+  child_b_node->set_parent_id(root_node.id());
+
+  proto::LayerNode* child_c_node = child_b_node->add_children();
+  child_c_node->set_id(444);
+  child_c_node->set_type(proto::LayerType::Base);
+  child_c_node->set_parent_id(child_b_node->id());
+
+  scoped_refptr<Layer> old_root = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> new_root =
+      LayerProtoConverter::DeserializeLayerHierarchy(old_root, root_node);
+
+  // The new root should not be the same as the old root.
+  EXPECT_EQ(root_node.id(), new_root->id());
+  ASSERT_EQ(2u, new_root->children().size());
+  scoped_refptr<Layer> child_a = new_root->children()[0];
+  scoped_refptr<Layer> child_b = new_root->children()[1];
+
+  EXPECT_EQ(child_a_node->id(), child_a->id());
+  EXPECT_EQ(child_b_node->id(), child_b->id());
+
+  EXPECT_EQ(0u, child_a->children().size());
+  ASSERT_EQ(1u, child_b->children().size());
+
+  scoped_refptr<Layer> child_c = child_b->children()[0];
+  EXPECT_EQ(child_c_node->id(), child_c->id());
+}
+
+}  // namespace cc
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index d81463d3..12aa514 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -10,6 +10,7 @@
 #include "cc/layers/layer_impl.h"
 #include "cc/output/copy_output_request.h"
 #include "cc/output/copy_output_result.h"
+#include "cc/proto/layer.pb.h"
 #include "cc/test/animation_test_common.h"
 #include "cc/test/fake_impl_task_runner_provider.h"
 #include "cc/test/fake_layer_tree_host_client.h"
@@ -1349,5 +1350,222 @@
   Mock::VerifyAndClearExpectations(layer_tree_host_.get());
 }
 
+TEST_F(LayerTest, RecursiveHierarchySerialization) {
+  /* Testing serialization and deserialization of a tree that looks like this:
+          root
+          /  \
+         a    b
+               \
+                c
+     Layer c also has a mask layer and a replica layer.
+  */
+  scoped_refptr<Layer> layer_src_root = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_a = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_b = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_c = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_c_mask = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_c_replica = Layer::Create(LayerSettings());
+  layer_src_root->AddChild(layer_src_a);
+  layer_src_root->AddChild(layer_src_b);
+  layer_src_b->AddChild(layer_src_c);
+  layer_src_c->SetMaskLayer(layer_src_c_mask.get());
+  layer_src_c->SetReplicaLayer(layer_src_c_replica.get());
+
+  proto::LayerNode proto;
+  layer_src_root->ToLayerNodeProto(&proto);
+
+  Layer::LayerIdMap empty_dest_layer_map;
+  scoped_refptr<Layer> layer_dest_root = Layer::Create(LayerSettings());
+  layer_dest_root->FromLayerNodeProto(proto, empty_dest_layer_map);
+
+  EXPECT_EQ(layer_src_root->id(), layer_dest_root->id());
+  EXPECT_EQ(nullptr, layer_dest_root->parent());
+  ASSERT_EQ(2u, layer_dest_root->children().size());
+
+  scoped_refptr<Layer> layer_dest_a = layer_dest_root->children()[0];
+  EXPECT_EQ(layer_src_a->id(), layer_dest_a->id());
+  EXPECT_EQ(layer_src_root->id(), layer_dest_a->parent()->id());
+  EXPECT_EQ(0u, layer_dest_a->children().size());
+
+  scoped_refptr<Layer> layer_dest_b = layer_dest_root->children()[1];
+  EXPECT_EQ(layer_src_b->id(), layer_dest_b->id());
+  EXPECT_EQ(layer_src_root->id(), layer_dest_b->parent()->id());
+  ASSERT_EQ(1u, layer_dest_b->children().size());
+
+  scoped_refptr<Layer> layer_dest_c = layer_dest_b->children()[0];
+  EXPECT_EQ(layer_src_c->id(), layer_dest_c->id());
+  EXPECT_EQ(layer_src_b->id(), layer_dest_c->parent()->id());
+  EXPECT_EQ(0u, layer_dest_c->children().size());
+  EXPECT_EQ(layer_src_c_mask->id(), layer_dest_c->mask_layer()->id());
+  EXPECT_EQ(layer_src_c_replica->id(), layer_dest_c->replica_layer()->id());
+}
+
+TEST_F(LayerTest, RecursiveHierarchySerializationWithNodeReuse) {
+  /* Testing serialization and deserialization of a tree that initially looks
+     like this:
+          root
+          /
+         a
+     The source tree is then updated by adding layer |b|:
+          root
+          /  \
+         a    b
+     The deserialization should then re-use the Layers from last
+     deserialization.
+  */
+  scoped_refptr<Layer> layer_src_root = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_a = Layer::Create(LayerSettings());
+  layer_src_root->AddChild(layer_src_a);
+
+  proto::LayerNode root_proto_1;
+  layer_src_root->ToLayerNodeProto(&root_proto_1);
+
+  Layer::LayerIdMap dest_layer_map_1;
+  scoped_refptr<Layer> layer_dest_root = Layer::Create(LayerSettings());
+  layer_dest_root->FromLayerNodeProto(root_proto_1, dest_layer_map_1);
+
+  EXPECT_EQ(layer_src_root->id(), layer_dest_root->id());
+  ASSERT_EQ(1u, layer_dest_root->children().size());
+  scoped_refptr<Layer> layer_dest_a_1 = layer_dest_root->children()[0];
+  EXPECT_EQ(layer_src_a->id(), layer_dest_a_1->id());
+
+  // Setup new destination layer map.
+  Layer::LayerIdMap dest_layer_map_2;
+  dest_layer_map_2[layer_dest_root->id()] = layer_dest_root;
+  dest_layer_map_2[layer_dest_a_1->id()] = layer_dest_a_1;
+
+  // Add Layer |b|.
+  scoped_refptr<Layer> layer_src_b = Layer::Create(LayerSettings());
+  layer_src_root->AddChild(layer_src_b);
+
+  // Second serialization.
+  proto::LayerNode root_proto_2;
+  layer_src_root->ToLayerNodeProto(&root_proto_2);
+
+  // Second deserialization.
+  layer_dest_root->FromLayerNodeProto(root_proto_2, dest_layer_map_2);
+
+  EXPECT_EQ(layer_src_root->id(), layer_dest_root->id());
+  ASSERT_EQ(2u, layer_dest_root->children().size());
+
+  scoped_refptr<Layer> layer_dest_a_2 = layer_dest_root->children()[0];
+  EXPECT_EQ(layer_src_a->id(), layer_dest_a_2->id());
+  EXPECT_EQ(layer_src_root->id(), layer_dest_a_2->parent()->id());
+  EXPECT_EQ(0u, layer_dest_a_2->children().size());
+
+  scoped_refptr<Layer> layer_dest_b_2 = layer_dest_root->children()[1];
+  EXPECT_EQ(layer_src_b->id(), layer_dest_b_2->id());
+  EXPECT_EQ(layer_src_root->id(), layer_dest_b_2->parent()->id());
+  EXPECT_EQ(0u, layer_dest_b_2->children().size());
+
+  // Layer |a| should be the same.
+  EXPECT_EQ(layer_dest_a_1.get(), layer_dest_a_2.get());
+}
+
+TEST_F(LayerTest, DeletingSubtreeDeletesLayers) {
+  /* Testing serialization and deserialization of a tree that initially
+     looks like this:
+          root
+          /  \
+         a    b
+               \
+                c
+                 \
+                  d
+     Then the subtree rooted at node |b| is deleted in the next update.
+  */
+  scoped_refptr<Layer> layer_src_root = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_a = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_b = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_c = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_d = Layer::Create(LayerSettings());
+  layer_src_root->AddChild(layer_src_a);
+  layer_src_root->AddChild(layer_src_b);
+  layer_src_b->AddChild(layer_src_c);
+  layer_src_c->AddChild(layer_src_d);
+
+  // Serialization 1.
+  proto::LayerNode proto1;
+  layer_src_root->ToLayerNodeProto(&proto1);
+
+  // Deserialization 1.
+  Layer::LayerIdMap empty_dest_layer_map;
+  scoped_refptr<Layer> layer_dest_root = Layer::Create(LayerSettings());
+  layer_dest_root->FromLayerNodeProto(proto1, empty_dest_layer_map);
+
+  EXPECT_EQ(layer_src_root->id(), layer_dest_root->id());
+  ASSERT_EQ(2u, layer_dest_root->children().size());
+  scoped_refptr<Layer> layer_dest_a = layer_dest_root->children()[0];
+  scoped_refptr<Layer> layer_dest_b = layer_dest_root->children()[1];
+  ASSERT_EQ(1u, layer_dest_b->children().size());
+  scoped_refptr<Layer> layer_dest_c = layer_dest_b->children()[0];
+  ASSERT_EQ(1u, layer_dest_c->children().size());
+  scoped_refptr<Layer> layer_dest_d = layer_dest_c->children()[0];
+
+  // Delete the Layer |b| subtree.
+  layer_src_b->RemoveAllChildren();
+
+  // Serialization 2.
+  proto::LayerNode proto2;
+  layer_src_root->ToLayerNodeProto(&proto2);
+
+  // Deserialization 2.
+  Layer::LayerIdMap dest_layer_map_2;
+  dest_layer_map_2[layer_dest_root->id()] = layer_dest_root;
+  dest_layer_map_2[layer_dest_a->id()] = layer_dest_a;
+  dest_layer_map_2[layer_dest_b->id()] = layer_dest_b;
+  layer_dest_root->FromLayerNodeProto(proto2, dest_layer_map_2);
+
+  EXPECT_EQ(0u, layer_dest_a->children().size());
+  EXPECT_EQ(0u, layer_dest_b->children().size());
+}
+
+TEST_F(LayerTest, DeleteMaskAndReplicaLayer) {
+  scoped_refptr<Layer> layer_src_root = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_mask = Layer::Create(LayerSettings());
+  scoped_refptr<Layer> layer_src_replica = Layer::Create(LayerSettings());
+  layer_src_root->SetMaskLayer(layer_src_mask.get());
+  layer_src_root->SetReplicaLayer(layer_src_replica.get());
+
+  // Serialization 1.
+  proto::LayerNode proto1;
+  layer_src_root->ToLayerNodeProto(&proto1);
+
+  // Deserialization 1.
+  Layer::LayerIdMap dest_layer_map;
+  scoped_refptr<Layer> layer_dest_root = Layer::Create(LayerSettings());
+  layer_dest_root->FromLayerNodeProto(proto1, dest_layer_map);
+
+  EXPECT_EQ(layer_src_root->id(), layer_dest_root->id());
+  ASSERT_TRUE(layer_dest_root->mask_layer());
+  ASSERT_TRUE(layer_dest_root->replica_layer());
+  EXPECT_EQ(layer_src_root->mask_layer()->id(),
+            layer_dest_root->mask_layer()->id());
+  // TODO(nyquist): Add test for is_mask_ when PictureLayer is supported.
+  EXPECT_EQ(layer_src_root->replica_layer()->id(),
+            layer_dest_root->replica_layer()->id());
+
+  // Store the newly constructed layer structure in the id map.
+  dest_layer_map[layer_dest_root->id()] = layer_dest_root;
+  dest_layer_map[layer_dest_root->mask_layer()->id()] =
+      layer_dest_root->mask_layer();
+  dest_layer_map[layer_dest_root->replica_layer()->id()] =
+      layer_dest_root->replica_layer();
+
+  // Clear mask and replica layers.
+  layer_src_root->mask_layer()->RemoveFromParent();
+  layer_src_root->replica_layer()->RemoveFromParent();
+
+  // Serialization 2.
+  proto::LayerNode proto2;
+  layer_src_root->ToLayerNodeProto(&proto2);
+
+  // Deserialization 2.
+  layer_dest_root->FromLayerNodeProto(proto2, dest_layer_map);
+
+  EXPECT_EQ(nullptr, layer_dest_root->mask_layer());
+  EXPECT_EQ(nullptr, layer_dest_root->replica_layer());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/proto/BUILD.gn b/cc/proto/BUILD.gn
index dcd4214..31d3cd5 100644
--- a/cc/proto/BUILD.gn
+++ b/cc/proto/BUILD.gn
@@ -30,6 +30,7 @@
     # TODO(dtrainor): Move protos to their correct packages once it's possible
     # to include protos from other directories/targets (crbug.com/542423).
     "display_item.proto",
+    "layer.proto",
     "point.proto",
     "pointf.proto",
     "rect.proto",
diff --git a/cc/proto/layer.proto b/cc/proto/layer.proto
new file mode 100644
index 0000000..74a243b4f
--- /dev/null
+++ b/cc/proto/layer.proto
@@ -0,0 +1,30 @@
+// Copyright 2015 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.
+
+syntax = "proto2";
+
+package cc.proto;
+
+option optimize_for = LITE_RUNTIME;
+
+// Identifies the type of cc:Layer a LayerNode represents. It is used to
+// facilitate reconstruction of a Layer of the correct type on the client.
+enum LayerType {
+  Base = 1;
+
+  // TODO(nyquist): Add the rest of the necessary LayerTypes.
+};
+
+// Hierarchical structure for serializing the Layer tree.
+message LayerNode {
+  // required
+  optional int32 id = 1;
+  // required
+  optional LayerType type = 2;
+  optional int32 parent_id = 3;
+  // A List of all the children of the current LayerNode.
+  repeated LayerNode children = 4;
+  optional LayerNode mask_layer = 5;
+  optional LayerNode replica_layer = 6;
+}