[go: nahoru, domu]

Add cc::MirrorLayer[Impl]

This new type of layer can be used to mirror contents of another layer.
When a mirror layer is set to mirror another layer, the latter is forced
to create a render pass so that the former can add a RenderPassDrawQuad
referring to that render pass, mirroring its contents.

Note that this CL does not add damage rect handling for the mirror
layer. That would be done in a follow-up CL.

The first use case for the mirror layer would be creating docked
magnifier on CrOS. The current implementation of docked magnifier does
not work with OOP-D.

BUG=947565

Change-Id: I1609edf53eb8c1ae8faff5345ee41dc547a916a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1652202
Reviewed-by: enne <enne@chromium.org>
Commit-Queue: Mohsen Izadi <mohsen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#672744}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 25e01d1..d5d3bac 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -88,6 +88,10 @@
     "layers/layer_position_constraint.h",
     "layers/layer_sticky_position_constraint.cc",
     "layers/layer_sticky_position_constraint.h",
+    "layers/mirror_layer.cc",
+    "layers/mirror_layer.h",
+    "layers/mirror_layer_impl.cc",
+    "layers/mirror_layer_impl.h",
     "layers/nine_patch_generator.cc",
     "layers/nine_patch_generator.h",
     "layers/nine_patch_layer.cc",
@@ -605,6 +609,7 @@
     "layers/layer_list_iterator_unittest.cc",
     "layers/layer_position_constraint_unittest.cc",
     "layers/layer_unittest.cc",
+    "layers/mirror_layer_unittest.cc",
     "layers/nine_patch_generator_unittest.cc",
     "layers/nine_patch_layer_impl_unittest.cc",
     "layers/nine_patch_layer_unittest.cc",
@@ -681,6 +686,7 @@
     "trees/layer_tree_host_pixeltest_blending.cc",
     "trees/layer_tree_host_pixeltest_filters.cc",
     "trees/layer_tree_host_pixeltest_masks.cc",
+    "trees/layer_tree_host_pixeltest_mirror.cc",
     "trees/layer_tree_host_pixeltest_readback.cc",
     "trees/layer_tree_host_pixeltest_scrollbars.cc",
     "trees/layer_tree_host_pixeltest_synchronous.cc",
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 35f58477c..fc8629c 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -71,7 +71,8 @@
       has_will_change_transform_hint(false),
       trilinear_filtering(false),
       hide_layer_and_subtree(false),
-      overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto) {}
+      overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto),
+      mirror_count(0) {}
 
 Layer::Inputs::~Inputs() = default;
 
@@ -1500,6 +1501,7 @@
   layer->SetMasksToBounds(inputs_.masks_to_bounds);
   layer->SetNonFastScrollableRegion(inputs_.non_fast_scrollable_region);
   layer->SetTouchActionRegion(inputs_.touch_action_region);
+  layer->SetMirrorCount(inputs_.mirror_count);
   // TODO(sunxd): Pass the correct region for wheel event handlers, see
   // https://crbug.com/841364.
   EventListenerProperties mouse_wheel_props =
@@ -1693,6 +1695,27 @@
   SetNeedsCommit();
 }
 
+void Layer::IncrementMirrorCount() {
+  SetMirrorCount(mirror_count() + 1);
+}
+
+void Layer::DecrementMirrorCount() {
+  SetMirrorCount(mirror_count() - 1);
+}
+
+void Layer::SetMirrorCount(int mirror_count) {
+  if (inputs_.mirror_count == mirror_count)
+    return;
+
+  DCHECK_LE(0, mirror_count);
+  bool was_mirrored = inputs_.mirror_count > 0;
+  inputs_.mirror_count = mirror_count;
+  bool is_mirrored = inputs_.mirror_count > 0;
+  if (was_mirrored != is_mirrored)
+    SetPropertyTreesNeedRebuild();
+  SetNeedsPushProperties();
+}
+
 ElementListType Layer::GetElementTypeForAnimation() const {
   return ElementListType::ACTIVE;
 }
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 58631ace3..fd5c08a 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -651,6 +651,11 @@
   void SetTrilinearFiltering(bool trilinear_filtering);
   bool trilinear_filtering() const { return inputs_.trilinear_filtering; }
 
+  // Increments/decrements/gets number of layers mirroring this layer.
+  void IncrementMirrorCount();
+  void DecrementMirrorCount();
+  int mirror_count() const { return inputs_.mirror_count; }
+
   // Called on the scroll layer to trigger showing the overlay scrollbars.
   void ShowScrollbars() { needs_show_scrollbars_ = true; }
 
@@ -930,6 +935,8 @@
   // they are marked as needing to be rebuilt.
   void UpdateScrollOffset(const gfx::ScrollOffset&);
 
+  void SetMirrorCount(int mirror_count);
+
   // Encapsulates all data, callbacks or interfaces received from the embedder.
   struct Inputs {
     explicit Inputs(int layer_id);
@@ -1048,6 +1055,8 @@
     OverscrollBehavior overscroll_behavior;
 
     base::Optional<SnapContainerData> snap_container_data;
+
+    int mirror_count;
   };
 
   Layer* parent_;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 86ccf0fc..5acf62e 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -80,7 +80,8 @@
       scrollbars_hidden_(false),
       needs_show_scrollbars_(false),
       raster_even_if_not_drawn_(false),
-      has_transform_node_(false) {
+      has_transform_node_(false),
+      mirror_count_(0) {
   DCHECK_GT(layer_id_, 0);
 
   DCHECK(layer_tree_impl_);
@@ -345,6 +346,7 @@
   layer->clip_tree_index_ = clip_tree_index_;
   layer->scroll_tree_index_ = scroll_tree_index_;
   layer->has_will_change_transform_hint_ = has_will_change_transform_hint_;
+  layer->mirror_count_ = mirror_count_;
   layer->scrollbars_hidden_ = scrollbars_hidden_;
   if (needs_show_scrollbars_)
     layer->needs_show_scrollbars_ = needs_show_scrollbars_;
@@ -516,7 +518,6 @@
   needs_push_properties_ = false;
 
   update_rect_.SetRect(0, 0, 0, 0);
-  damage_rect_.SetRect(0, 0, 0, 0);
 }
 
 bool LayerImpl::IsActive() const {
@@ -696,12 +697,16 @@
   layer_tree_impl_->AddToElementLayerList(element_id_, this);
 }
 
+void LayerImpl::SetMirrorCount(int mirror_count) {
+  mirror_count_ = mirror_count;
+}
+
 void LayerImpl::SetUpdateRect(const gfx::Rect& update_rect) {
   update_rect_ = update_rect;
 }
 
-void LayerImpl::AddDamageRect(const gfx::Rect& damage_rect) {
-  damage_rect_.Union(damage_rect);
+gfx::Rect LayerImpl::GetDamageRect() const {
+  return gfx::Rect();
 }
 
 void LayerImpl::SetCurrentScrollOffset(const gfx::ScrollOffset& scroll_offset) {
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index d669445..423eb7b 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -194,6 +194,9 @@
   void SetElementId(ElementId element_id);
   ElementId element_id() const { return element_id_; }
 
+  void SetMirrorCount(int mirror_count);
+  int mirror_count() const { return mirror_count_; }
+
   bool IsAffectedByPageScale() const;
 
   bool Is3dSorted() const { return GetSortingContextId() != 0; }
@@ -332,8 +335,10 @@
   void SetUpdateRect(const gfx::Rect& update_rect);
   const gfx::Rect& update_rect() const { return update_rect_; }
 
-  void AddDamageRect(const gfx::Rect& damage_rect);
-  const gfx::Rect& damage_rect() const { return damage_rect_; }
+  // Denotes an area that is damaged and needs redraw. This is in the layer's
+  // space. By default returns empty rect, but can be overridden by subclasses
+  // as appropriate.
+  virtual gfx::Rect GetDamageRect() const;
 
   virtual std::unique_ptr<base::DictionaryValue> LayerAsJson() const;
   // TODO(pdr): This should be removed because there is no longer a tree
@@ -349,7 +354,7 @@
   // from property_trees changes in animaiton.
   bool LayerPropertyChangedNotFromPropertyTrees() const;
 
-  void ResetChangeTracking();
+  virtual void ResetChangeTracking();
 
   virtual SimpleEnclosedRegion VisibleOpaqueRegion() const;
 
@@ -567,10 +572,6 @@
   // This is in the layer's space.
   gfx::Rect update_rect_;
 
-  // Denotes an area that is damaged and needs redraw. This is in the layer's
-  // space.
-  gfx::Rect damage_rect_;
-
   // Group of properties that need to be computed based on the layer tree
   // hierarchy before layers can be drawn.
   DrawProperties draw_properties_;
@@ -596,6 +597,10 @@
   bool raster_even_if_not_drawn_ : 1;
 
   bool has_transform_node_ : 1;
+
+  // Number of layers mirroring this layer. If greater than zero, forces a
+  // render pass for the layer so it can be embedded by the mirroring layer.
+  int mirror_count_;
 };
 
 }  // namespace cc
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index be4d26a..7eeeffc 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -1654,6 +1654,78 @@
   test_layer->SetLayerTreeHost(nullptr);
 }
 
+// Verifies that mirror count is pushed to the LayerImpl.
+TEST_F(LayerTest, MirrorCountIsPushed) {
+  scoped_refptr<Layer> test_layer = Layer::Create();
+  std::unique_ptr<LayerImpl> impl_layer =
+      LayerImpl::Create(host_impl_.active_tree(), 1);
+  test_layer->SetLayerTreeHost(layer_tree_host_.get());
+  EXPECT_EQ(0, test_layer->mirror_count());
+  EXPECT_EQ(0, impl_layer->mirror_count());
+
+  test_layer->IncrementMirrorCount();
+  EXPECT_EQ(1, test_layer->mirror_count());
+  EXPECT_EQ(0, impl_layer->mirror_count());
+
+  test_layer->PushPropertiesTo(impl_layer.get());
+  EXPECT_EQ(1, test_layer->mirror_count());
+  EXPECT_EQ(1, impl_layer->mirror_count());
+
+  test_layer->SetLayerTreeHost(nullptr);
+}
+
+// Verifies that when mirror count of the layer is incremented or decremented,
+// SetPropertyTreesNeedRebuild() and SetNeedsPushProperties() are called
+// appropriately.
+TEST_F(LayerTest, UpdateMirrorCount) {
+  scoped_refptr<Layer> test_layer = Layer::Create();
+  test_layer->SetLayerTreeHost(layer_tree_host_.get());
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+  EXPECT_EQ(0, test_layer->mirror_count());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_EQ(0u, layer_tree_host_->LayersThatShouldPushProperties().size());
+
+  // Incrementing mirror count from zero should trigger property trees rebuild.
+  test_layer->IncrementMirrorCount();
+  EXPECT_EQ(1, test_layer->mirror_count());
+  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             test_layer.get()));
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Incrementing mirror count from non-zero should not trigger property trees
+  // rebuild.
+  test_layer->IncrementMirrorCount();
+  EXPECT_EQ(2, test_layer->mirror_count());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             test_layer.get()));
+
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Decrementing mirror count to non-zero should not trigger property trees
+  // rebuild.
+  test_layer->DecrementMirrorCount();
+  EXPECT_EQ(1, test_layer->mirror_count());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             test_layer.get()));
+
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Decrementing mirror count to zero should trigger property trees rebuild.
+  test_layer->DecrementMirrorCount();
+  EXPECT_EQ(0, test_layer->mirror_count());
+  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             test_layer.get()));
+
+  test_layer->SetLayerTreeHost(nullptr);
+}
+
 class LayerTestWithLayerLists : public LayerTest {
  protected:
   void SetUp() override {
diff --git a/cc/layers/mirror_layer.cc b/cc/layers/mirror_layer.cc
new file mode 100644
index 0000000..b89e300d
--- /dev/null
+++ b/cc/layers/mirror_layer.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 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/mirror_layer.h"
+
+#include "cc/layers/mirror_layer_impl.h"
+
+namespace cc {
+
+std::unique_ptr<LayerImpl> MirrorLayer::CreateLayerImpl(
+    LayerTreeImpl* tree_impl) {
+  return MirrorLayerImpl::Create(tree_impl, id());
+}
+
+void MirrorLayer::PushPropertiesTo(LayerImpl* layer) {
+  Layer::PushPropertiesTo(layer);
+
+  auto* mirror_layer = static_cast<MirrorLayerImpl*>(layer);
+  mirror_layer->SetMirroredLayerId(mirrored_layer_->id());
+}
+
+void MirrorLayer::SetLayerTreeHost(LayerTreeHost* host) {
+#if DCHECK_IS_ON()
+  if (host && host != layer_tree_host()) {
+    for (auto* p = parent(); p; p = p->parent())
+      DCHECK_NE(p, mirrored_layer_.get());
+  }
+#endif
+
+  Layer::SetLayerTreeHost(host);
+}
+
+scoped_refptr<MirrorLayer> MirrorLayer::Create(
+    scoped_refptr<Layer> mirrored_layer) {
+  return base::WrapRefCounted(new MirrorLayer(std::move(mirrored_layer)));
+}
+
+MirrorLayer::MirrorLayer(scoped_refptr<Layer> mirrored_layer)
+    : mirrored_layer_(std::move(mirrored_layer)) {
+  DCHECK(mirrored_layer_);
+  mirrored_layer_->IncrementMirrorCount();
+}
+
+MirrorLayer::~MirrorLayer() {
+  mirrored_layer_->DecrementMirrorCount();
+}
+
+}  // namespace cc
diff --git a/cc/layers/mirror_layer.h b/cc/layers/mirror_layer.h
new file mode 100644
index 0000000..9915dcf
--- /dev/null
+++ b/cc/layers/mirror_layer.h
@@ -0,0 +1,42 @@
+// Copyright 2019 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_MIRROR_LAYER_H_
+#define CC_LAYERS_MIRROR_LAYER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "cc/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace cc {
+
+// A layer that can mirror contents of another Layer.
+class CC_EXPORT MirrorLayer : public Layer {
+ public:
+  static scoped_refptr<MirrorLayer> Create(scoped_refptr<Layer> mirrored_layer);
+
+  MirrorLayer(const MirrorLayer&) = delete;
+  MirrorLayer& operator=(const MirrorLayer&) = delete;
+
+  Layer* mirrored_layer() const { return mirrored_layer_.get(); }
+
+  // Layer overrides.
+  std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
+  void PushPropertiesTo(LayerImpl* layer) override;
+  void SetLayerTreeHost(LayerTreeHost* host) override;
+
+ protected:
+  explicit MirrorLayer(scoped_refptr<Layer> mirrored_layer);
+
+ private:
+  ~MirrorLayer() override;
+
+  // A reference to a layer that is mirrored by this layer. |mirrored_layer_|
+  // cannot be an ancestor of |this|.
+  scoped_refptr<Layer> mirrored_layer_;
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_MIRROR_LAYER_H_
diff --git a/cc/layers/mirror_layer_impl.cc b/cc/layers/mirror_layer_impl.cc
new file mode 100644
index 0000000..00de04dc
--- /dev/null
+++ b/cc/layers/mirror_layer_impl.cc
@@ -0,0 +1,88 @@
+// Copyright 2019 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/mirror_layer_impl.h"
+
+#include "cc/trees/effect_node.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/occlusion.h"
+#include "components/viz/common/quads/render_pass_draw_quad.h"
+
+namespace cc {
+
+MirrorLayerImpl::MirrorLayerImpl(LayerTreeImpl* tree_impl, int id)
+    : LayerImpl(tree_impl, id) {}
+
+MirrorLayerImpl::~MirrorLayerImpl() = default;
+
+std::unique_ptr<LayerImpl> MirrorLayerImpl::CreateLayerImpl(
+    LayerTreeImpl* tree_impl) {
+  return MirrorLayerImpl::Create(tree_impl, id());
+}
+
+void MirrorLayerImpl::AppendQuads(viz::RenderPass* render_pass,
+                                  AppendQuadsData* append_quads_data) {
+  // TODO(mohsen): Currently, effects on the mirrored layer (e.g mask and
+  // opacity) are ignored. Consider applying them here.
+
+  auto* mirrored_layer = layer_tree_impl()->LayerById(mirrored_layer_id_);
+  auto* mirrored_render_surface =
+      GetEffectTree().GetRenderSurface(mirrored_layer->effect_tree_index());
+  gfx::Rect content_rect = mirrored_render_surface->content_rect();
+
+  gfx::Rect unoccluded_content_rect =
+      draw_properties().occlusion_in_content_space.GetUnoccludedContentRect(
+          content_rect);
+  if (unoccluded_content_rect.IsEmpty())
+    return;
+
+  viz::SharedQuadState* shared_quad_state =
+      render_pass->CreateAndAppendSharedQuadState();
+  bool contents_opaque = false;
+  auto* effect_node = GetEffectTree().Node(effect_tree_index());
+  shared_quad_state->SetAll(
+      draw_properties().target_space_transform, content_rect, content_rect,
+      draw_properties().rounded_corner_bounds, draw_properties().clip_rect,
+      draw_properties().is_clipped, contents_opaque, draw_properties().opacity,
+      effect_node->HasRenderSurface() ? SkBlendMode::kSrcOver
+                                      : effect_node->blend_mode,
+      GetSortingContextId());
+  shared_quad_state->is_fast_rounded_corner =
+      draw_properties().is_fast_rounded_corner;
+
+  AppendDebugBorderQuad(render_pass, content_rect, shared_quad_state,
+                        append_quads_data);
+
+  viz::ResourceId mask_resource_id = 0;
+  gfx::RectF mask_uv_rect;
+  gfx::Size mask_texture_size;
+
+  auto* mirrored_effect_node = mirrored_render_surface->OwningEffectNode();
+  auto* quad = render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
+  quad->SetNew(shared_quad_state, content_rect, unoccluded_content_rect,
+               mirrored_layer_id_, mask_resource_id, mask_uv_rect,
+               mask_texture_size, mirrored_effect_node->surface_contents_scale,
+               mirrored_effect_node->filters_origin,
+               gfx::RectF(gfx::Rect(content_rect.size())),
+               !layer_tree_impl()->settings().enable_edge_anti_aliasing, 0.f);
+}
+
+void MirrorLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+  LayerImpl::PushPropertiesTo(layer);
+
+  auto* mirror_layer = static_cast<MirrorLayerImpl*>(layer);
+  mirror_layer->SetMirroredLayerId(mirrored_layer_id_);
+}
+
+gfx::Rect MirrorLayerImpl::GetDamageRect() const {
+  // TOOD(mohsen): Currently, the whole layer is marked as damaged. We should
+  // only consider the damage from the mirrored layer.
+  return gfx::Rect(bounds());
+}
+
+const char* MirrorLayerImpl::LayerTypeAsString() const {
+  return "cc::MirrorLayerImpl";
+}
+
+}  // namespace cc
diff --git a/cc/layers/mirror_layer_impl.h b/cc/layers/mirror_layer_impl.h
new file mode 100644
index 0000000..0ab1a270
--- /dev/null
+++ b/cc/layers/mirror_layer_impl.h
@@ -0,0 +1,62 @@
+// Copyright 2019 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_MIRROR_LAYER_IMPL_H_
+#define CC_LAYERS_MIRROR_LAYER_IMPL_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "cc/cc_export.h"
+#include "cc/layers/layer_impl.h"
+
+namespace cc {
+
+// This type of layer is used to mirror contents of another layer (specified by
+// |mirrored_layer_id_|) by forcing a render pass for the mirrored layer and
+// adding a RenderPassDrawQuad in the compositor frame for this layer referring
+// to that render pass. The mirroring layer should not be a descendant of the
+// mirrored layer (in terms of the effect tree). Due to ordering requirements
+// for render passes in the compositor frame, the render pass containing
+// mirroring layer should appear after the render pass created for the mirrored
+// layer. Currently, render passes are in reverse-draw order of the effect tree,
+// so we should be careful that this reverse-draw order does not conflict with
+// render pass ordering requirement mentioned above.
+// TODO(mohsen): If necessary, reorder render passes in compositor frame such
+// that the render pass containing mirroring layer appears after the render pass
+// created for the mirrored layer.
+class CC_EXPORT MirrorLayerImpl : public LayerImpl {
+ public:
+  static std::unique_ptr<MirrorLayerImpl> Create(LayerTreeImpl* tree_impl,
+                                                 int id) {
+    return base::WrapUnique(new MirrorLayerImpl(tree_impl, id));
+  }
+
+  MirrorLayerImpl(const MirrorLayerImpl&) = delete;
+  MirrorLayerImpl& operator=(const MirrorLayerImpl&) = delete;
+
+  ~MirrorLayerImpl() override;
+
+  void SetMirroredLayerId(int id) { mirrored_layer_id_ = id; }
+  int mirrored_layer_id() const { return mirrored_layer_id_; }
+
+  // LayerImpl overrides.
+  std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
+  void AppendQuads(viz::RenderPass* render_pass,
+                   AppendQuadsData* append_quads_data) override;
+  void PushPropertiesTo(LayerImpl* layer) override;
+  gfx::Rect GetDamageRect() const override;
+
+ protected:
+  MirrorLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ private:
+  const char* LayerTypeAsString() const override;
+
+  int mirrored_layer_id_ = 0;
+};
+
+}  // namespace cc
+
+#endif  // CC_LAYERS_MIRROR_LAYER_IMPL_H_
diff --git a/cc/layers/mirror_layer_unittest.cc b/cc/layers/mirror_layer_unittest.cc
new file mode 100644
index 0000000..9630d80
--- /dev/null
+++ b/cc/layers/mirror_layer_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright 2019 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 <memory>
+
+#include "cc/animation/animation_host.h"
+#include "cc/layers/mirror_layer.h"
+#include "cc/layers/mirror_layer_impl.h"
+#include "cc/test/fake_impl_task_runner_provider.h"
+#include "cc/test/fake_layer_tree_host.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/test_task_graph_runner.h"
+#include "cc/trees/tree_synchronizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class MirrorLayerTest : public testing::Test {
+ public:
+  MirrorLayerTest() : host_impl_(&task_runner_provider_, &task_graph_runner_) {}
+
+  // Synchronizes |layer_tree_host_| and |host_impl_| and pushes surface ids.
+  void SynchronizeTrees() {
+    TreeSynchronizer::PushLayerProperties(layer_tree_host_.get(),
+                                          host_impl_.pending_tree());
+  }
+
+ protected:
+  void SetUp() override {
+    animation_host_ = AnimationHost::CreateForTesting(ThreadInstance::MAIN);
+    layer_tree_host_ = FakeLayerTreeHost::Create(
+        &fake_client_, &task_graph_runner_, animation_host_.get());
+    layer_tree_host_->SetViewportSizeAndScale(gfx::Size(10, 10), 1.f,
+                                              viz::LocalSurfaceIdAllocation());
+    host_impl_.CreatePendingTree();
+  }
+
+  void TearDown() override {
+    layer_tree_host_->SetRootLayer(nullptr);
+    layer_tree_host_ = nullptr;
+  }
+
+  FakeLayerTreeHostClient fake_client_;
+  FakeImplTaskRunnerProvider task_runner_provider_;
+  TestTaskGraphRunner task_graph_runner_;
+  std::unique_ptr<AnimationHost> animation_host_;
+  std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
+  FakeLayerTreeHostImpl host_impl_;
+};
+
+// This test verifies that MirrorLayer properties are pushed across to
+// MirrorLayerImpl.
+TEST_F(MirrorLayerTest, PushProperties) {
+  auto root = Layer::Create();
+  layer_tree_host_->SetRootLayer(root);
+
+  auto mirrored = Layer::Create();
+  root->AddChild(mirrored);
+  auto mirror = MirrorLayer::Create(mirrored);
+  root->AddChild(mirror);
+
+  EXPECT_EQ(1, mirrored->mirror_count());
+  EXPECT_EQ(mirrored.get(), mirror->mirrored_layer());
+
+  auto root_impl = LayerImpl::Create(host_impl_.pending_tree(), root->id());
+  auto mirrored_impl =
+      LayerImpl::Create(host_impl_.pending_tree(), mirrored->id());
+  auto mirror_impl =
+      MirrorLayerImpl::Create(host_impl_.pending_tree(), mirror->id());
+
+  // Verify that impl layers have default property values.
+  EXPECT_EQ(0, mirrored_impl->mirror_count());
+  EXPECT_EQ(0, mirror_impl->mirrored_layer_id());
+
+  SynchronizeTrees();
+
+  // Verify that property values are pushed to impl layers.
+  EXPECT_EQ(1, mirrored_impl->mirror_count());
+  EXPECT_EQ(mirrored_impl->id(), mirror_impl->mirrored_layer_id());
+}
+
+// This test verifies adding/removing mirror layers updates mirror count
+// properly and sets appropriate bits on the layer tree host.
+TEST_F(MirrorLayerTest, MirrorCount) {
+  auto mirrored = Layer::Create();
+  mirrored->SetLayerTreeHost(layer_tree_host_.get());
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+  EXPECT_EQ(0, mirrored->mirror_count());
+
+  // Creating the first mirror layer should trigger property trees rebuild.
+  auto mirror1 = MirrorLayer::Create(mirrored);
+  EXPECT_EQ(1, mirrored->mirror_count());
+  EXPECT_EQ(mirrored.get(), mirror1->mirrored_layer());
+  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             mirrored.get()));
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Creating a second mirror layer should not trigger property trees rebuild.
+  auto mirror2 = MirrorLayer::Create(mirrored);
+  EXPECT_EQ(2, mirrored->mirror_count());
+  EXPECT_EQ(mirrored.get(), mirror2->mirrored_layer());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             mirrored.get()));
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Destroying one of the mirror layers should not trigger property trees
+  // rebuild.
+  mirror1->RemoveFromParent();
+  mirror1 = nullptr;
+  EXPECT_EQ(1, mirrored->mirror_count());
+  EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_EQ(1u, layer_tree_host_->LayersThatShouldPushProperties().size());
+
+  layer_tree_host_->property_trees()->needs_rebuild = false;
+  layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+  // Destroying the only remaining mirror layer should trigger property trees
+  // rebuild.
+  mirror2->RemoveFromParent();
+  mirror2 = nullptr;
+  EXPECT_EQ(0, mirrored->mirror_count());
+  EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+  EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+                             mirrored.get()));
+
+  mirrored->SetLayerTreeHost(nullptr);
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 644a34b..fa04946c 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -764,7 +764,7 @@
 
 void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) {
   if (layer_tree_impl()->IsActiveTree())
-    AddDamageRect(tile->enclosing_layer_rect());
+    damage_rect_.Union(tile->enclosing_layer_rect());
   if (tile->draw_info().NeedsRaster()) {
     PictureLayerTiling* tiling =
         tilings_->FindTilingWithScaleKey(tile->contents_scale_key());
@@ -773,6 +773,15 @@
   }
 }
 
+gfx::Rect PictureLayerImpl::GetDamageRect() const {
+  return damage_rect_;
+}
+
+void PictureLayerImpl::ResetChangeTracking() {
+  LayerImpl::ResetChangeTracking();
+  damage_rect_.SetRect(0, 0, 0, 0);
+}
+
 void PictureLayerImpl::DidBeginTracing() {
   raster_source_->DidBeginTracing();
 }
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 6d564e1..f122754 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -52,6 +52,8 @@
   void AppendQuads(viz::RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
   void NotifyTileStateChanged(const Tile* tile) override;
+  gfx::Rect GetDamageRect() const override;
+  void ResetChangeTracking() override;
   void ResetRasterScale();
   void DidBeginTracing() override;
   void ReleaseResources() override;
@@ -242,6 +244,10 @@
 
   gfx::Size content_bounds_;
   TileSizeCalculator tile_size_calculator_;
+
+  // Denotes an area that is damaged and needs redraw. This is in the layer's
+  // space.
+  gfx::Rect damage_rect_;
 };
 
 }  // namespace cc
diff --git a/cc/trees/damage_tracker.cc b/cc/trees/damage_tracker.cc
index f3ddee2..743b681 100644
--- a/cc/trees/damage_tracker.cc
+++ b/cc/trees/damage_tracker.cc
@@ -386,7 +386,7 @@
     // If the layer properties haven't changed, then the the target surface is
     // only affected by the layer's damaged area, which could be empty.
     gfx::Rect damage_rect =
-        gfx::UnionRects(layer->update_rect(), layer->damage_rect());
+        gfx::UnionRects(layer->update_rect(), layer->GetDamageRect());
     damage_rect.Intersect(gfx::Rect(layer->bounds()));
 
     if (!damage_rect.IsEmpty()) {
@@ -416,7 +416,7 @@
 
   if (layer_is_new || !layer->update_rect().IsEmpty() ||
       layer->LayerPropertyChangedNotFromPropertyTrees() ||
-      !layer->damage_rect().IsEmpty() || property_change_on_non_target_node) {
+      !layer->GetDamageRect().IsEmpty() || property_change_on_non_target_node) {
     has_damage_from_contributing_content_ |= !damage_for_this_update_.IsEmpty();
   }
 }
diff --git a/cc/trees/damage_tracker_unittest.cc b/cc/trees/damage_tracker_unittest.cc
index fcd75ca..b9046c66 100644
--- a/cc/trees/damage_tracker_unittest.cc
+++ b/cc/trees/damage_tracker_unittest.cc
@@ -26,6 +26,36 @@
 namespace cc {
 namespace {
 
+class TestLayerImpl : public LayerImpl {
+ public:
+  TestLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+  void AddDamageRect(const gfx::Rect& damage_rect);
+
+  // LayerImpl overrides.
+  gfx::Rect GetDamageRect() const override;
+  void ResetChangeTracking() override;
+
+ private:
+  gfx::Rect damage_rect_;
+};
+
+TestLayerImpl::TestLayerImpl(LayerTreeImpl* tree_impl, int id)
+    : LayerImpl(tree_impl, id) {}
+
+void TestLayerImpl::AddDamageRect(const gfx::Rect& damage_rect) {
+  damage_rect_.Union(damage_rect);
+}
+
+gfx::Rect TestLayerImpl::GetDamageRect() const {
+  return damage_rect_;
+}
+
+void TestLayerImpl::ResetChangeTracking() {
+  LayerImpl::ResetChangeTracking();
+  damage_rect_.SetRect(0, 0, 0, 0);
+}
+
 void ExecuteCalculateDrawProperties(LayerImpl* root,
                                     float device_scale_factor,
                                     RenderSurfaceList* render_surface_list) {
@@ -71,16 +101,15 @@
 
   LayerImpl* CreateTestTreeWithOneSurface(int number_of_children) {
     host_impl_.active_tree()->DetachLayers();
-    std::unique_ptr<LayerImpl> root =
-        LayerImpl::Create(host_impl_.active_tree(), 1);
+    auto root = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 1);
 
     root->SetBounds(gfx::Size(500, 500));
     root->SetDrawsContent(true);
     root->test_properties()->force_render_surface = true;
 
     for (int i = 0; i < number_of_children; ++i) {
-      std::unique_ptr<LayerImpl> child =
-          LayerImpl::Create(host_impl_.active_tree(), 2 + i);
+      auto child =
+          std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 2 + i);
       child->test_properties()->position = gfx::PointF(100.f, 100.f);
       child->SetBounds(gfx::Size(30, 30));
       child->SetDrawsContent(true);
@@ -98,16 +127,13 @@
     // two children of its own.
 
     host_impl_.active_tree()->DetachLayers();
-    std::unique_ptr<LayerImpl> root =
-        LayerImpl::Create(host_impl_.active_tree(), 1);
-    std::unique_ptr<LayerImpl> child1 =
-        LayerImpl::Create(host_impl_.active_tree(), 2);
-    std::unique_ptr<LayerImpl> child2 =
-        LayerImpl::Create(host_impl_.active_tree(), 3);
-    std::unique_ptr<LayerImpl> grand_child1 =
-        LayerImpl::Create(host_impl_.active_tree(), 4);
-    std::unique_ptr<LayerImpl> grand_child2 =
-        LayerImpl::Create(host_impl_.active_tree(), 5);
+    auto root = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 1);
+    auto child1 = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 2);
+    auto child2 = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 3);
+    auto grand_child1 =
+        std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 4);
+    auto grand_child2 =
+        std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 5);
 
     root->SetBounds(gfx::Size(500, 500));
     root->SetDrawsContent(true);
@@ -276,7 +302,8 @@
 
 TEST_F(DamageTrackerTest, VerifyDamageForLayerDamageRects) {
   LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface();
-  LayerImpl* child = root->test_properties()->children[0];
+  auto* child =
+      static_cast<TestLayerImpl*>(root->test_properties()->children[0]);
 
   // CASE 1: Adding the layer damage rect should cause the corresponding damage
   // to the surface.
@@ -336,7 +363,8 @@
 
 TEST_F(DamageTrackerTest, VerifyDamageForLayerUpdateAndDamageRects) {
   LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface();
-  LayerImpl* child = root->test_properties()->children[0];
+  auto* child =
+      static_cast<TestLayerImpl*>(root->test_properties()->children[0]);
 
   // CASE 1: Adding the layer damage rect and update rect should cause the
   // corresponding damage to the surface.
@@ -547,9 +575,12 @@
 TEST_F(DamageTrackerTest,
        VerifyDamageForUpdateAndDamageRectsFromContributingContents) {
   LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
-  LayerImpl* child1 = root->test_properties()->children[0];
-  LayerImpl* child2 = root->test_properties()->children[1];
-  LayerImpl* grandchild1 = child1->test_properties()->children[0];
+  auto* child1 =
+      static_cast<TestLayerImpl*>(root->test_properties()->children[0]);
+  auto* child2 =
+      static_cast<TestLayerImpl*>(root->test_properties()->children[1]);
+  auto* grandchild1 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[0]);
 
   // CASE 1: Adding the layer1's damage rect and update rect should cause the
   // corresponding damage to the surface.
@@ -1912,8 +1943,10 @@
 TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) {
   LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
   LayerImpl* child1 = root->test_properties()->children[0];
-  LayerImpl* grandchild1 = child1->test_properties()->children[0];
-  LayerImpl* grandchild2 = child1->test_properties()->children[1];
+  auto* grandchild1 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[0]);
+  auto* grandchild2 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[1]);
 
   // Really far left.
   grandchild1->test_properties()->position =
@@ -2002,8 +2035,10 @@
 TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) {
   LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
   LayerImpl* child1 = root->test_properties()->children[0];
-  LayerImpl* grandchild1 = child1->test_properties()->children[0];
-  LayerImpl* grandchild2 = child1->test_properties()->children[1];
+  auto* grandchild1 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[0]);
+  auto* grandchild2 =
+      static_cast<TestLayerImpl*>(child1->test_properties()->children[1]);
 
   // Set up a moving pixels filter on the child.
   FilterOperations filters;
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h
index 340305e..dd77eab 100644
--- a/cc/trees/effect_node.h
+++ b/cc/trees/effect_node.h
@@ -40,6 +40,7 @@
   kTrilinearFiltering,
   kCache,
   kCopyRequest,
+  kMirrored,
   // This must be the last value because it's used in tracing code to know the
   // number of reasons.
   kTest,
diff --git a/cc/trees/layer_tree_host_pixeltest_mirror.cc b/cc/trees/layer_tree_host_pixeltest_mirror.cc
new file mode 100644
index 0000000..ed011f43
--- /dev/null
+++ b/cc/trees/layer_tree_host_pixeltest_mirror.cc
@@ -0,0 +1,83 @@
+// Copyright 2019 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 "build/build_config.h"
+#include "cc/layers/mirror_layer.h"
+#include "cc/layers/solid_color_layer.h"
+#include "cc/test/layer_tree_pixel_test.h"
+#include "cc/test/pixel_comparator.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform_util.h"
+
+#if !defined(OS_ANDROID)
+
+namespace cc {
+namespace {
+
+class LayerTreeHostMirrorPixelTest
+    : public LayerTreePixelTest,
+      public ::testing::WithParamInterface<LayerTreeTest::RendererType> {
+ protected:
+  RendererType renderer_type() { return GetParam(); }
+};
+
+const LayerTreeTest::RendererType kRendererTypes[] = {
+    LayerTreeTest::RENDERER_GL,
+    LayerTreeTest::RENDERER_SKIA_GL,
+    LayerTreeTest::RENDERER_SOFTWARE,
+#if defined(ENABLE_CC_VULKAN_TESTS)
+    LayerTreeTest::RENDERER_SKIA_VK,
+#endif
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         LayerTreeHostMirrorPixelTest,
+                         ::testing::ValuesIn(kRendererTypes));
+
+// Verifies that a mirror layer with a scale mirrors another layer correctly.
+TEST_P(LayerTreeHostMirrorPixelTest, MirrorLayer) {
+  const float scale = 2.f;
+  gfx::Rect background_bounds(120, 180);
+  gfx::Rect mirrored_bounds(10, 10, 50, 50);
+  gfx::Rect mirror_bounds(10, 70, 100, 100);
+
+  auto background = CreateSolidColorLayer(background_bounds, SK_ColorWHITE);
+
+  auto mirrored_layer = CreateSolidColorLayerWithBorder(
+      mirrored_bounds, SK_ColorGREEN, 5, SK_ColorBLUE);
+
+  auto mirror_layer = MirrorLayer::Create(mirrored_layer);
+  mirror_layer->SetIsDrawable(true);
+  mirror_layer->SetBounds(mirror_bounds.size());
+  mirror_layer->SetPosition(gfx::PointF(mirror_bounds.origin()));
+  mirror_layer->SetTransform(gfx::GetScaleTransform(gfx::Point(), scale));
+  background->AddChild(mirrored_layer);
+  background->AddChild(mirror_layer);
+
+  if (renderer_type() == RENDERER_SOFTWARE) {
+    const bool discard_alpha = true;
+    const float error_pixels_percentage_limit = 3.f;
+    const float small_error_pixels_percentage_limit = 0.f;
+    const float avg_abs_error_limit = 65.f;
+    const int max_abs_error_limit = 120;
+    const int small_error_threshold = 0;
+    pixel_comparator_ = std::make_unique<FuzzyPixelComparator>(
+        discard_alpha, error_pixels_percentage_limit,
+        small_error_pixels_percentage_limit, avg_abs_error_limit,
+        max_abs_error_limit, small_error_threshold);
+  }
+
+#if defined(ENABLE_CC_VULKAN_TESTS) && defined(OS_LINUX)
+  if (renderer_type() == RENDERER_SKIA_VK)
+    pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true);
+#endif
+
+  RunPixelTest(renderer_type(), background,
+               base::FilePath(FILE_PATH_LITERAL("mirror_layer.png")));
+}
+
+}  // namespace
+}  // namespace cc
+
+#endif  // !defined(OS_ANDROID)
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 7d20ffa..cf90575 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -867,6 +867,14 @@
   return !layer->test_properties()->copy_requests.empty();
 }
 
+static inline int MirrorCount(Layer* layer) {
+  return layer->mirror_count();
+}
+
+static inline int MirrorCount(LayerImpl* layer) {
+  return 0;
+}
+
 static inline bool PropertyChanged(Layer* layer) {
   return layer->subtree_property_changed();
 }
@@ -979,6 +987,10 @@
   if (HasCopyRequest(layer))
     return RenderSurfaceReason::kCopyRequest;
 
+  // If the layer is mirrored.
+  if (MirrorCount(layer))
+    return RenderSurfaceReason::kMirrored;
+
   return RenderSurfaceReason::kNone;
 }
 
diff --git a/components/viz/test/data/mirror_layer.png b/components/viz/test/data/mirror_layer.png
new file mode 100644
index 0000000..4dc0edc9
--- /dev/null
+++ b/components/viz/test/data/mirror_layer.png
Binary files differ
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 3b95dd1a..d4dd1215 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/numerics/ranges.h"
 #include "base/trace_event/trace_event.h"
+#include "cc/layers/mirror_layer.h"
 #include "cc/layers/nine_patch_layer.h"
 #include "cc/layers/picture_layer.h"
 #include "cc/layers/solid_color_layer.h"
@@ -240,6 +241,24 @@
   return mirror;
 }
 
+void Layer::MirrorLayer(Layer* mirrored_layer) {
+  DCHECK_EQ(type_, LAYER_SOLID_COLOR);
+
+  if (!mirrored_layer) {
+    SetShowSolidColorContent();
+    return;
+  }
+
+  auto new_layer = cc::MirrorLayer::Create(mirrored_layer->cc_layer_);
+  SwitchToLayer(new_layer);
+  mirror_layer_ = new_layer;
+  gfx::Rect bounds = bounds_;
+  bounds.set_size(mirrored_layer->bounds().size());
+  SetBounds(bounds);
+
+  RecomputeDrawsContentAndUVRect();
+}
+
 const Compositor* Layer::GetCompositor() const {
   return GetRoot(this)->compositor_;
 }
@@ -684,6 +703,7 @@
   solid_color_layer_ = nullptr;
   texture_layer_ = nullptr;
   surface_layer_ = nullptr;
+  mirror_layer_ = nullptr;
 
   for (auto* child : children_) {
     DCHECK(child->cc_layer_);
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 9ffad84..98471b7 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -35,6 +35,7 @@
 
 namespace cc {
 class Layer;
+class MirrorLayer;
 class NinePatchLayer;
 class SolidColorLayer;
 class SurfaceLayer;
@@ -82,6 +83,11 @@
   // content is only mirrored if painted by a delegate or backed by a surface.
   std::unique_ptr<Layer> Mirror();
 
+  // Sets up this layer to mirror contents of |mirrored_layer|. This layer
+  // should be of type |LAYER_SOLID_COLOR| and should not be a descendant of the
+  // |mirrored_layer|.
+  void MirrorLayer(Layer* mirrored_layer);
+
   // This method is relevant only if this layer is a mirror destination layer.
   // Sets whether this mirror layer's bounds are synchronized with the source
   // layer's bounds.
@@ -437,6 +443,7 @@
   bool FillsBoundsCompletely() const override;
   size_t GetApproximateUnsharedMemoryUsage() const override;
 
+  cc::MirrorLayer* mirror_layer_for_testing() { return mirror_layer_.get(); }
   cc::Layer* cc_layer_for_testing() { return cc_layer_; }
   const cc::Layer* cc_layer_for_testing() const { return cc_layer_; }
 
@@ -652,6 +659,7 @@
   // Ownership of the layer is held through one of the strongly typed layer
   // pointers, depending on which sort of layer this is.
   scoped_refptr<cc::PictureLayer> content_layer_;
+  scoped_refptr<cc::MirrorLayer> mirror_layer_;
   scoped_refptr<cc::NinePatchLayer> nine_patch_layer_;
   scoped_refptr<cc::TextureLayer> texture_layer_;
   scoped_refptr<cc::SolidColorLayer> solid_color_layer_;
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index 252f92a..c05d461 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -30,6 +30,7 @@
 #include "cc/animation/keyframe_effect.h"
 #include "cc/animation/single_keyframe_effect_animation.h"
 #include "cc/layers/layer.h"
+#include "cc/layers/mirror_layer.h"
 #include "cc/test/pixel_test_utils.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
@@ -1511,6 +1512,69 @@
   EXPECT_EQ(mask->damaged_region_for_testing().bounds(), gfx::Rect());
 }
 
+// Verifies that when a layer is mirroring other layers, mirror count
+// of mirrored layers is updated properly.
+TEST_F(LayerWithNullDelegateTest, MirrorLayer) {
+  std::unique_ptr<Layer> mirrored_layer_1(CreateLayer(LAYER_SOLID_COLOR));
+  auto* mirrored_layer_1_cc = mirrored_layer_1->cc_layer_for_testing();
+
+  std::unique_ptr<Layer> mirrored_layer_2(CreateLayer(LAYER_SOLID_COLOR));
+  auto* mirrored_layer_2_cc = mirrored_layer_2->cc_layer_for_testing();
+
+  std::unique_ptr<Layer> mirror_layer(CreateLayer(LAYER_SOLID_COLOR));
+
+  // Originally, mirror counts should be zero.
+  auto* mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+  EXPECT_EQ(nullptr, mirror_layer_cc);
+  EXPECT_EQ(0, mirrored_layer_1_cc->mirror_count());
+  EXPECT_EQ(0, mirrored_layer_2_cc->mirror_count());
+
+  // Mirror the first layer. Its mirror count should be increased.
+  mirror_layer->MirrorLayer(mirrored_layer_1.get());
+  mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+  ASSERT_NE(nullptr, mirror_layer_cc);
+  EXPECT_EQ(mirror_layer->cc_layer_for_testing(), mirror_layer_cc);
+  EXPECT_EQ(mirrored_layer_1_cc, mirror_layer_cc->mirrored_layer());
+  EXPECT_EQ(1, mirrored_layer_1_cc->mirror_count());
+  EXPECT_EQ(0, mirrored_layer_2_cc->mirror_count());
+
+  // Mirror the second layer. Its mirror count should be increased, but mirror
+  // count for the first mirrored layer should be set back to zero.
+  mirror_layer->MirrorLayer(mirrored_layer_2.get());
+  mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+  ASSERT_NE(nullptr, mirror_layer_cc);
+  EXPECT_EQ(mirror_layer->cc_layer_for_testing(), mirror_layer_cc);
+  EXPECT_EQ(mirrored_layer_2_cc, mirror_layer_cc->mirrored_layer());
+  EXPECT_EQ(0, mirrored_layer_1_cc->mirror_count());
+  EXPECT_EQ(1, mirrored_layer_2_cc->mirror_count());
+
+  // Un-mirror the layer. All mirror counts should be set to zero.
+  mirror_layer->MirrorLayer(nullptr);
+  mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+  EXPECT_EQ(nullptr, mirror_layer_cc);
+  EXPECT_EQ(0, mirrored_layer_1_cc->mirror_count());
+  EXPECT_EQ(0, mirrored_layer_2_cc->mirror_count());
+}
+
+// Verifies that when a layer is mirroring another layer, its size matches the
+// size of the mirrored layer.
+TEST_F(LayerWithNullDelegateTest, MirrorLayerBounds) {
+  const gfx::Rect mirrored_bounds(0, 0, 50, 50);
+  const gfx::Rect mirror_bounds(0, 50, 10, 10);
+
+  std::unique_ptr<Layer> mirrored_layer(CreateLayer(LAYER_SOLID_COLOR));
+  mirrored_layer->SetBounds(mirrored_bounds);
+
+  std::unique_ptr<Layer> mirror_layer(CreateLayer(LAYER_SOLID_COLOR));
+  mirror_layer->SetBounds(mirror_bounds);
+
+  EXPECT_EQ(mirror_bounds, mirror_layer->bounds());
+
+  mirror_layer->MirrorLayer(mirrored_layer.get());
+  EXPECT_EQ(mirror_bounds.origin(), mirror_layer->bounds().origin());
+  EXPECT_EQ(mirrored_bounds.size(), mirror_layer->bounds().size());
+}
+
 void ExpectRgba(int x, int y, SkColor expected_color, SkColor actual_color) {
   EXPECT_EQ(expected_color, actual_color)
       << "Pixel error at x=" << x << " y=" << y << "; "