Mac: Fix bugs in Sierra color fix

The previous fix uncondiontally replaced populated the solid color
IOSurface and updated the contents_rect for all layers on Sierra,
instead of just doing it for the ones without IOSurfaces.

Fix this bug, parameterize whether or not we are using this workaround,
and add tests for both code paths.

BUG=633805

Review-Url: https://codereview.chromium.org/2387653004
Cr-Commit-Position: refs/heads/master@{#422281}
(cherry picked from commit 496c26b5cc3983f40b65729d8fa8bbd4a9951c71)

Mac: Fix Sierra solid color layer color-mismatch

Sierra treats the color correction of IOSurface-backed layers and solid
color layers differently. To ensure that behavior is consistent, do not
use solid color layers, rather, in such cases, use an 16x16 IOSurface
that is initialized to the solid color.

BUG=633805

Review-Url: https://codereview.chromium.org/2378423002
Cr-Commit-Position: refs/heads/master@{#421899}
(cherry picked from commit 318395caaf3a43683a41d09e65050649d5fedf27)

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

Cr-Commit-Position: refs/branch-heads/2840@{#630}
Cr-Branched-From: 1ae106dbab4bddd85132d5b75c670794311f4c57-refs/heads/master@{#414607}
diff --git a/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm b/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm
index 76ce11e..05d85c5 100644
--- a/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm
+++ b/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm
@@ -6,6 +6,7 @@
 
 #include <AVFoundation/AVFoundation.h>
 
+#include "base/mac/mac_util.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/base/cocoa/animation_utils.h"
 
@@ -61,8 +62,8 @@
                    "specified, but not both.";
   }
   if (!pending_ca_renderer_layer_tree_)
-    pending_ca_renderer_layer_tree_.reset(
-        new CARendererLayerTree(allow_av_sample_buffer_display_layer_));
+    pending_ca_renderer_layer_tree_.reset(new CARendererLayerTree(
+        allow_av_sample_buffer_display_layer_, base::mac::IsAtLeastOS10_12()));
   return pending_ca_renderer_layer_tree_.get();
 }
 
diff --git a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
index 3c5dd7e..e8cabcd 100644
--- a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
+++ b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
@@ -39,6 +39,9 @@
   float scale_factor = 1.0f;
   unsigned filter = GL_LINEAR;
   scoped_refptr<gl::GLImageIOSurface> gl_image;
+
+  bool allow_av_layers = true;
+  bool allow_solid_color_layers = true;
 };
 
 scoped_refptr<gl::GLImageIOSurface> CreateGLImage(const gfx::Size& size,
@@ -74,20 +77,8 @@
                        CALayerProperties* properties,
                        CALayer* superlayer) {
   std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree(
-      new ui::CARendererLayerTree(true));
-  bool result = ScheduleCALayer(new_ca_layer_tree.get(), properties);
-  EXPECT_TRUE(result);
-  new_ca_layer_tree->CommitScheduledCALayers(
-      superlayer, std::move(ca_layer_tree), properties->scale_factor);
-  std::swap(new_ca_layer_tree, ca_layer_tree);
-}
-
-void UpdateCALayerTreeWithAVDisabled(
-    std::unique_ptr<ui::CARendererLayerTree>& ca_layer_tree,
-    CALayerProperties* properties,
-    CALayer* superlayer) {
-  std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree(
-      new ui::CARendererLayerTree(false));
+      new ui::CARendererLayerTree(properties->allow_av_layers,
+                                  properties->allow_solid_color_layers));
   bool result = ScheduleCALayer(new_ca_layer_tree.get(), properties);
   EXPECT_TRUE(result);
   new_ca_layer_tree->CommitScheduledCALayers(
@@ -110,352 +101,385 @@
 };
 
 // Test updating each layer's properties.
-TEST_F(CALayerTreeTest, PropertyUpdates) {
-  CALayerProperties properties;
-  properties.clip_rect = gfx::Rect(2, 4, 8, 16);
-  properties.transform.Translate(10, 20);
-  properties.contents_rect = gfx::RectF(0.0f, 0.25f, 0.5f, 0.75f);
-  properties.rect = gfx::Rect(16, 32, 64, 128);
-  properties.background_color = SkColorSetARGB(0xFF, 0xFF, 0, 0);
-  properties.edge_aa_mask = GL_CA_LAYER_EDGE_LEFT_CHROMIUM;
-  properties.opacity = 0.5f;
-  properties.gl_image =
-      CreateGLImage(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888, false);
-
-  std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree;
-  CALayer* root_layer = nil;
-  CALayer* clip_and_sorting_layer = nil;
-  CALayer* transform_layer = nil;
-  CALayer* content_layer = nil;
-
-  // Validate the initial values.
-  {
-    std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree(
-        new ui::CARendererLayerTree(true));
-
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    root_layer = [[superlayer_ sublayers] objectAtIndex:0];
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0];
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0];
-    EXPECT_EQ(1u, [[transform_layer sublayers] count]);
-    content_layer = [[transform_layer sublayers] objectAtIndex:0];
-
-    // Validate the clip and sorting context layer.
-    EXPECT_TRUE([clip_and_sorting_layer masksToBounds]);
-    EXPECT_EQ(gfx::Rect(properties.clip_rect.size()),
-              gfx::Rect([clip_and_sorting_layer bounds]));
-    EXPECT_EQ(properties.clip_rect.origin(),
-              gfx::Point([clip_and_sorting_layer position]));
-    EXPECT_EQ(-properties.clip_rect.origin().x(),
-              [clip_and_sorting_layer sublayerTransform].m41);
-    EXPECT_EQ(-properties.clip_rect.origin().y(),
-              [clip_and_sorting_layer sublayerTransform].m42);
-
-    // Validate the transform layer.
-    EXPECT_EQ(properties.transform.matrix().get(3, 0),
-              [transform_layer sublayerTransform].m41);
-    EXPECT_EQ(properties.transform.matrix().get(3, 1),
-              [transform_layer sublayerTransform].m42);
-
-    // Validate the content layer.
-    EXPECT_EQ(static_cast<id>(properties.gl_image->io_surface().get()),
-              [content_layer contents]);
-    EXPECT_EQ(properties.contents_rect,
-              gfx::RectF([content_layer contentsRect]));
-    EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position]));
-    EXPECT_EQ(gfx::Rect(properties.rect.size()),
-              gfx::Rect([content_layer bounds]));
-    EXPECT_EQ(kCALayerLeftEdge, [content_layer edgeAntialiasingMask]);
-    EXPECT_EQ(properties.opacity, [content_layer opacity]);
-    EXPECT_NSEQ(kCAFilterLinear, [content_layer minificationFilter]);
-    EXPECT_NSEQ(kCAFilterLinear, [content_layer magnificationFilter]);
-    if ([content_layer respondsToSelector:(@selector(contentsScale))])
-      EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]);
-  }
-
-  // Update just the clip rect and re-commit.
-  {
-    properties.clip_rect = gfx::Rect(4, 8, 16, 32);
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-
-    // Validate the clip and sorting context layer.
-    EXPECT_TRUE([clip_and_sorting_layer masksToBounds]);
-    EXPECT_EQ(gfx::Rect(properties.clip_rect.size()),
-              gfx::Rect([clip_and_sorting_layer bounds]));
-    EXPECT_EQ(properties.clip_rect.origin(),
-              gfx::Point([clip_and_sorting_layer position]));
-    EXPECT_EQ(-properties.clip_rect.origin().x(),
-              [clip_and_sorting_layer sublayerTransform].m41);
-    EXPECT_EQ(-properties.clip_rect.origin().y(),
-              [clip_and_sorting_layer sublayerTransform].m42);
-  }
-
-  // Disable clipping and re-commit.
-  {
-    properties.is_clipped = false;
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-
-    // Validate the clip and sorting context layer.
-    EXPECT_FALSE([clip_and_sorting_layer masksToBounds]);
-    EXPECT_EQ(gfx::Rect(), gfx::Rect([clip_and_sorting_layer bounds]));
-    EXPECT_EQ(gfx::Point(), gfx::Point([clip_and_sorting_layer position]));
-    EXPECT_EQ(0.0, [clip_and_sorting_layer sublayerTransform].m41);
-    EXPECT_EQ(0.0, [clip_and_sorting_layer sublayerTransform].m42);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-  }
-
-  // Change the transform and re-commit.
-  {
-    properties.transform.Translate(5, 5);
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-
-    // Validate the transform layer.
-    EXPECT_EQ(properties.transform.matrix().get(3, 0),
-              [transform_layer sublayerTransform].m41);
-    EXPECT_EQ(properties.transform.matrix().get(3, 1),
-              [transform_layer sublayerTransform].m42);
-  }
-
-  // Change the edge antialiasing mask and commit.
-  {
-    properties.edge_aa_mask = GL_CA_LAYER_EDGE_TOP_CHROMIUM;
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[transform_layer sublayers] count]);
-    EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
-
-    // Validate the content layer. Note that top and bottom edges flip.
-    EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]);
-  }
-
-  // Change the contents and commit.
-  {
-    properties.gl_image->Destroy(true);
-    properties.gl_image = nullptr;
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[transform_layer sublayers] count]);
-    EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
-
-    // Validate the content layer. Note that edge anti-aliasing no longer flips.
-    EXPECT_EQ(nil, [content_layer contents]);
-    EXPECT_EQ(kCALayerTopEdge, [content_layer edgeAntialiasingMask]);
-  }
-
-  // Change the rect size.
-  {
-    properties.rect = gfx::Rect(properties.rect.origin(), gfx::Size(32, 16));
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[transform_layer sublayers] count]);
-    EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
-
-    // Validate the content layer.
-    EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position]));
-    EXPECT_EQ(gfx::Rect(properties.rect.size()),
-              gfx::Rect([content_layer bounds]));
-  }
-
-  // Change the rect position.
-  {
-    properties.rect = gfx::Rect(gfx::Point(16, 4), properties.rect.size());
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[transform_layer sublayers] count]);
-    EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
-
-    // Validate the content layer.
-    EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position]));
-    EXPECT_EQ(gfx::Rect(properties.rect.size()),
-              gfx::Rect([content_layer bounds]));
-  }
-
-  // Change the opacity.
-  {
-    properties.opacity = 1.0f;
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[transform_layer sublayers] count]);
-    EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
-
-    // Validate the content layer.
-    EXPECT_EQ(properties.opacity, [content_layer opacity]);
-  }
-
-  // Change the filter.
-  {
-    properties.filter = GL_NEAREST;
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
-
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[transform_layer sublayers] count]);
-    EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
-
-    // Validate the content layer.
-    EXPECT_NSEQ(kCAFilterNearest, [content_layer minificationFilter]);
-    EXPECT_NSEQ(kCAFilterNearest, [content_layer magnificationFilter]);
-  }
-
-  // Add the clipping and IOSurface contents back.
-  {
-    properties.is_clipped = true;
+class CALayerTreePropertyUpdatesTest : public CALayerTreeTest {
+ public:
+  void RunTest(bool allow_solid_color_layers) {
+    CALayerProperties properties;
+    properties.allow_solid_color_layers = allow_solid_color_layers;
+    properties.clip_rect = gfx::Rect(2, 4, 8, 16);
+    properties.transform.Translate(10, 20);
+    properties.contents_rect = gfx::RectF(0.0f, 0.25f, 0.5f, 0.75f);
+    properties.rect = gfx::Rect(16, 32, 64, 128);
+    properties.background_color = SkColorSetARGB(0xFF, 0xFF, 0, 0);
+    properties.edge_aa_mask = GL_CA_LAYER_EDGE_LEFT_CHROMIUM;
+    properties.opacity = 0.5f;
     properties.gl_image =
         CreateGLImage(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888, false);
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
 
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_EQ(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_EQ(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-    EXPECT_EQ(1u, [[transform_layer sublayers] count]);
-    EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
+    std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree;
+    CALayer* root_layer = nil;
+    CALayer* clip_and_sorting_layer = nil;
+    CALayer* transform_layer = nil;
+    CALayer* content_layer = nil;
 
-    // Validate the content layer.
-    EXPECT_EQ(static_cast<id>(properties.gl_image->io_surface().get()),
-              [content_layer contents]);
-    EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]);
+    // Validate the initial values.
+    {
+      std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree(
+          new ui::CARendererLayerTree(true, allow_solid_color_layers));
+
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      root_layer = [[superlayer_ sublayers] objectAtIndex:0];
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0];
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0];
+      EXPECT_EQ(1u, [[transform_layer sublayers] count]);
+      content_layer = [[transform_layer sublayers] objectAtIndex:0];
+
+      // Validate the clip and sorting context layer.
+      EXPECT_TRUE([clip_and_sorting_layer masksToBounds]);
+      EXPECT_EQ(gfx::Rect(properties.clip_rect.size()),
+                gfx::Rect([clip_and_sorting_layer bounds]));
+      EXPECT_EQ(properties.clip_rect.origin(),
+                gfx::Point([clip_and_sorting_layer position]));
+      EXPECT_EQ(-properties.clip_rect.origin().x(),
+                [clip_and_sorting_layer sublayerTransform].m41);
+      EXPECT_EQ(-properties.clip_rect.origin().y(),
+                [clip_and_sorting_layer sublayerTransform].m42);
+
+      // Validate the transform layer.
+      EXPECT_EQ(properties.transform.matrix().get(3, 0),
+                [transform_layer sublayerTransform].m41);
+      EXPECT_EQ(properties.transform.matrix().get(3, 1),
+                [transform_layer sublayerTransform].m42);
+
+      // Validate the content layer.
+      EXPECT_EQ(static_cast<id>(properties.gl_image->io_surface().get()),
+                [content_layer contents]);
+      EXPECT_EQ(properties.contents_rect,
+                gfx::RectF([content_layer contentsRect]));
+      EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position]));
+      EXPECT_EQ(gfx::Rect(properties.rect.size()),
+                gfx::Rect([content_layer bounds]));
+      EXPECT_EQ(kCALayerLeftEdge, [content_layer edgeAntialiasingMask]);
+      EXPECT_EQ(properties.opacity, [content_layer opacity]);
+      EXPECT_NSEQ(kCAFilterLinear, [content_layer minificationFilter]);
+      EXPECT_NSEQ(kCAFilterLinear, [content_layer magnificationFilter]);
+      if ([content_layer respondsToSelector:(@selector(contentsScale))])
+        EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]);
+    }
+
+    // Update just the clip rect and re-commit.
+    {
+      properties.clip_rect = gfx::Rect(4, 8, 16, 32);
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+
+      // Validate the clip and sorting context layer.
+      EXPECT_TRUE([clip_and_sorting_layer masksToBounds]);
+      EXPECT_EQ(gfx::Rect(properties.clip_rect.size()),
+                gfx::Rect([clip_and_sorting_layer bounds]));
+      EXPECT_EQ(properties.clip_rect.origin(),
+                gfx::Point([clip_and_sorting_layer position]));
+      EXPECT_EQ(-properties.clip_rect.origin().x(),
+                [clip_and_sorting_layer sublayerTransform].m41);
+      EXPECT_EQ(-properties.clip_rect.origin().y(),
+                [clip_and_sorting_layer sublayerTransform].m42);
+    }
+
+    // Disable clipping and re-commit.
+    {
+      properties.is_clipped = false;
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+
+      // Validate the clip and sorting context layer.
+      EXPECT_FALSE([clip_and_sorting_layer masksToBounds]);
+      EXPECT_EQ(gfx::Rect(), gfx::Rect([clip_and_sorting_layer bounds]));
+      EXPECT_EQ(gfx::Point(), gfx::Point([clip_and_sorting_layer position]));
+      EXPECT_EQ(0.0, [clip_and_sorting_layer sublayerTransform].m41);
+      EXPECT_EQ(0.0, [clip_and_sorting_layer sublayerTransform].m42);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+    }
+
+    // Change the transform and re-commit.
+    {
+      properties.transform.Translate(5, 5);
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+
+      // Validate the transform layer.
+      EXPECT_EQ(properties.transform.matrix().get(3, 0),
+                [transform_layer sublayerTransform].m41);
+      EXPECT_EQ(properties.transform.matrix().get(3, 1),
+                [transform_layer sublayerTransform].m42);
+    }
+
+    // Change the edge antialiasing mask and commit.
+    {
+      properties.edge_aa_mask = GL_CA_LAYER_EDGE_TOP_CHROMIUM;
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[transform_layer sublayers] count]);
+      EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
+
+      // Validate the content layer. Note that top and bottom edges flip.
+      EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]);
+    }
+
+    // Change the contents and commit.
+    {
+      properties.gl_image->Destroy(true);
+      properties.gl_image = nullptr;
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[transform_layer sublayers] count]);
+      EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
+
+      // Validate the content layer. Note that edge anti-aliasing does not flip
+      // for solid colors.
+      if (allow_solid_color_layers) {
+        EXPECT_EQ(nil, [content_layer contents]);
+        EXPECT_EQ(kCALayerTopEdge, [content_layer edgeAntialiasingMask]);
+      } else {
+        EXPECT_EQ(ca_layer_tree->ContentsForSolidColorForTesting(
+                      properties.background_color),
+                  [content_layer contents]);
+        EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]);
+      }
+    }
+
+    // Change the rect size.
+    {
+      properties.rect = gfx::Rect(properties.rect.origin(), gfx::Size(32, 16));
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[transform_layer sublayers] count]);
+      EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
+
+      // Validate the content layer.
+      EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position]));
+      EXPECT_EQ(gfx::Rect(properties.rect.size()),
+                gfx::Rect([content_layer bounds]));
+    }
+
+    // Change the rect position.
+    {
+      properties.rect = gfx::Rect(gfx::Point(16, 4), properties.rect.size());
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[transform_layer sublayers] count]);
+      EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
+
+      // Validate the content layer.
+      EXPECT_EQ(properties.rect.origin(), gfx::Point([content_layer position]));
+      EXPECT_EQ(gfx::Rect(properties.rect.size()),
+                gfx::Rect([content_layer bounds]));
+    }
+
+    // Change the opacity.
+    {
+      properties.opacity = 1.0f;
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[transform_layer sublayers] count]);
+      EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
+
+      // Validate the content layer.
+      EXPECT_EQ(properties.opacity, [content_layer opacity]);
+    }
+
+    // Change the filter.
+    {
+      properties.filter = GL_NEAREST;
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[transform_layer sublayers] count]);
+      EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
+
+      // Validate the content layer.
+      EXPECT_NSEQ(kCAFilterNearest, [content_layer minificationFilter]);
+      EXPECT_NSEQ(kCAFilterNearest, [content_layer magnificationFilter]);
+    }
+
+    // Add the clipping and IOSurface contents back.
+    {
+      properties.is_clipped = true;
+      properties.gl_image = CreateGLImage(gfx::Size(256, 256),
+                                          gfx::BufferFormat::BGRA_8888, false);
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_EQ(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_EQ(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_EQ(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+      EXPECT_EQ(1u, [[transform_layer sublayers] count]);
+      EXPECT_EQ(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
+
+      // Validate the content layer.
+      EXPECT_EQ(static_cast<id>(properties.gl_image->io_surface().get()),
+                [content_layer contents]);
+      EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]);
+    }
+
+    // Change the scale factor. This should result in a new tree being created.
+    {
+      properties.scale_factor = 2.0f;
+      UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+
+      // Validate the tree structure.
+      EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
+      EXPECT_NE(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
+      root_layer = [[superlayer_ sublayers] objectAtIndex:0];
+      EXPECT_EQ(1u, [[root_layer sublayers] count]);
+      EXPECT_NE(clip_and_sorting_layer,
+                [[root_layer sublayers] objectAtIndex:0]);
+      clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0];
+      EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
+      EXPECT_NE(transform_layer,
+                [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
+      transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0];
+      EXPECT_EQ(1u, [[transform_layer sublayers] count]);
+      EXPECT_NE(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
+      content_layer = [[transform_layer sublayers] objectAtIndex:0];
+
+      // Validate the clip and sorting context layer.
+      EXPECT_TRUE([clip_and_sorting_layer masksToBounds]);
+      EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor,
+                                      gfx::Rect(properties.clip_rect.size())),
+                gfx::Rect([clip_and_sorting_layer bounds]));
+      EXPECT_EQ(gfx::ConvertPointToDIP(properties.scale_factor,
+                                       properties.clip_rect.origin()),
+                gfx::Point([clip_and_sorting_layer position]));
+      EXPECT_EQ(-properties.clip_rect.origin().x() / properties.scale_factor,
+                [clip_and_sorting_layer sublayerTransform].m41);
+      EXPECT_EQ(-properties.clip_rect.origin().y() / properties.scale_factor,
+                [clip_and_sorting_layer sublayerTransform].m42);
+
+      // Validate the transform layer.
+      EXPECT_EQ(
+          properties.transform.matrix().get(3, 0) / properties.scale_factor,
+          [transform_layer sublayerTransform].m41);
+      EXPECT_EQ(
+          properties.transform.matrix().get(3, 1) / properties.scale_factor,
+          [transform_layer sublayerTransform].m42);
+
+      // Validate the content layer.
+      EXPECT_EQ(static_cast<id>(properties.gl_image->io_surface().get()),
+                [content_layer contents]);
+      EXPECT_EQ(properties.contents_rect,
+                gfx::RectF([content_layer contentsRect]));
+      EXPECT_EQ(gfx::ConvertPointToDIP(properties.scale_factor,
+                                       properties.rect.origin()),
+                gfx::Point([content_layer position]));
+      EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor,
+                                      gfx::Rect(properties.rect.size())),
+                gfx::Rect([content_layer bounds]));
+      EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]);
+      EXPECT_EQ(properties.opacity, [content_layer opacity]);
+      if ([content_layer respondsToSelector:(@selector(contentsScale))])
+        EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]);
+    }
+
+    properties.gl_image->Destroy(true);
   }
+};
 
-  // Change the scale factor. This should result in a new tree being created.
-  {
-    properties.scale_factor = 2.0f;
-    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
+TEST_F(CALayerTreePropertyUpdatesTest, AllowSolidColors) {
+  RunTest(true);
+}
 
-    // Validate the tree structure.
-    EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
-    EXPECT_NE(root_layer, [[superlayer_ sublayers] objectAtIndex:0]);
-    root_layer = [[superlayer_ sublayers] objectAtIndex:0];
-    EXPECT_EQ(1u, [[root_layer sublayers] count]);
-    EXPECT_NE(clip_and_sorting_layer, [[root_layer sublayers] objectAtIndex:0]);
-    clip_and_sorting_layer = [[root_layer sublayers] objectAtIndex:0];
-    EXPECT_EQ(1u, [[clip_and_sorting_layer sublayers] count]);
-    EXPECT_NE(transform_layer,
-              [[clip_and_sorting_layer sublayers] objectAtIndex:0]);
-    transform_layer = [[clip_and_sorting_layer sublayers] objectAtIndex:0];
-    EXPECT_EQ(1u, [[transform_layer sublayers] count]);
-    EXPECT_NE(content_layer, [[transform_layer sublayers] objectAtIndex:0]);
-    content_layer = [[transform_layer sublayers] objectAtIndex:0];
-
-    // Validate the clip and sorting context layer.
-    EXPECT_TRUE([clip_and_sorting_layer masksToBounds]);
-    EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor,
-                                    gfx::Rect(properties.clip_rect.size())),
-              gfx::Rect([clip_and_sorting_layer bounds]));
-    EXPECT_EQ(gfx::ConvertPointToDIP(properties.scale_factor,
-                                     properties.clip_rect.origin()),
-              gfx::Point([clip_and_sorting_layer position]));
-    EXPECT_EQ(-properties.clip_rect.origin().x() / properties.scale_factor,
-              [clip_and_sorting_layer sublayerTransform].m41);
-    EXPECT_EQ(-properties.clip_rect.origin().y() / properties.scale_factor,
-              [clip_and_sorting_layer sublayerTransform].m42);
-
-    // Validate the transform layer.
-    EXPECT_EQ(properties.transform.matrix().get(3, 0) / properties.scale_factor,
-              [transform_layer sublayerTransform].m41);
-    EXPECT_EQ(properties.transform.matrix().get(3, 1) / properties.scale_factor,
-              [transform_layer sublayerTransform].m42);
-
-    // Validate the content layer.
-    EXPECT_EQ(static_cast<id>(properties.gl_image->io_surface().get()),
-              [content_layer contents]);
-    EXPECT_EQ(properties.contents_rect,
-              gfx::RectF([content_layer contentsRect]));
-    EXPECT_EQ(gfx::ConvertPointToDIP(properties.scale_factor,
-                                     properties.rect.origin()),
-              gfx::Point([content_layer position]));
-    EXPECT_EQ(gfx::ConvertRectToDIP(properties.scale_factor,
-                                    gfx::Rect(properties.rect.size())),
-              gfx::Rect([content_layer bounds]));
-    EXPECT_EQ(kCALayerBottomEdge, [content_layer edgeAntialiasingMask]);
-    EXPECT_EQ(properties.opacity, [content_layer opacity]);
-    if ([content_layer respondsToSelector:(@selector(contentsScale))])
-      EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]);
-  }
-
-  properties.gl_image->Destroy(true);
+TEST_F(CALayerTreePropertyUpdatesTest, DisallowSolidColors) {
+  RunTest(false);
 }
 
 // Verify that sorting context zero is split at non-flat transforms.
@@ -485,7 +509,7 @@
 
   // Schedule and commit the layers.
   std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree(
-      new ui::CARendererLayerTree(true));
+      new ui::CARendererLayerTree(true, true));
   for (size_t i = 0; i < 5; ++i) {
     properties.gl_image = gl_images[i];
     properties.transform = transforms[i];
@@ -568,7 +592,7 @@
 
   // Schedule and commit the layers.
   std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree(
-      new ui::CARendererLayerTree(true));
+      new ui::CARendererLayerTree(true, true));
   for (size_t i = 0; i < 3; ++i) {
     properties.sorting_context_id = sorting_context_ids[i];
     properties.gl_image = gl_images[i];
@@ -632,7 +656,7 @@
   };
 
   std::unique_ptr<ui::CARendererLayerTree> ca_layer_tree(
-      new ui::CARendererLayerTree(true));
+      new ui::CARendererLayerTree(true, true));
   // First send the various clip parameters to sorting context zero. This is
   // legitimate.
   for (size_t i = 0; i < 3; ++i) {
@@ -807,7 +831,8 @@
   }
 
   {
-    UpdateCALayerTreeWithAVDisabled(ca_layer_tree, &properties, superlayer_);
+    properties.allow_av_layers = false;
+    UpdateCALayerTree(ca_layer_tree, &properties, superlayer_);
 
     // Validate the tree structure.
     EXPECT_EQ(1u, [[superlayer_ sublayers] count]);
@@ -845,7 +870,7 @@
   // Test a configuration with no background.
   {
     std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree(
-        new ui::CARendererLayerTree(true));
+        new ui::CARendererLayerTree(true, true));
     bool result = ScheduleCALayer(new_ca_layer_tree.get(), &properties);
     EXPECT_TRUE(result);
     new_ca_layer_tree->CommitScheduledCALayers(
@@ -875,7 +900,7 @@
   // Test a configuration with a black background.
   {
     std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree(
-        new ui::CARendererLayerTree(true));
+        new ui::CARendererLayerTree(true, true));
     bool result = ScheduleCALayer(new_ca_layer_tree.get(), &properties_black);
     EXPECT_TRUE(result);
     result = ScheduleCALayer(new_ca_layer_tree.get(), &properties);
@@ -907,7 +932,7 @@
   // Test a configuration with a white background. It will fail.
   {
     std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree(
-        new ui::CARendererLayerTree(true));
+        new ui::CARendererLayerTree(true, true));
     bool result = ScheduleCALayer(new_ca_layer_tree.get(), &properties_white);
     EXPECT_TRUE(result);
     result = ScheduleCALayer(new_ca_layer_tree.get(), &properties);
@@ -939,7 +964,7 @@
   // Test a configuration with a black foreground. It too will fail.
   {
     std::unique_ptr<ui::CARendererLayerTree> new_ca_layer_tree(
-        new ui::CARendererLayerTree(true));
+        new ui::CARendererLayerTree(true, true));
     bool result = ScheduleCALayer(new_ca_layer_tree.get(), &properties);
     EXPECT_TRUE(result);
     result = ScheduleCALayer(new_ca_layer_tree.get(), &properties_black);
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
index cf651ad..e4724a8f 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
@@ -14,6 +14,7 @@
 
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
+#include "base/memory/ref_counted.h"
 #include "ui/accelerated_widget_mac/accelerated_widget_mac_export.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -32,7 +33,8 @@
 // https://docs.google.com/document/d/1DtSN9zzvCF44_FQPM7ie01UxGHagQ66zfF5L9HnigQY/edit?usp=sharing
 class ACCELERATED_WIDGET_MAC_EXPORT CARendererLayerTree {
  public:
-  CARendererLayerTree(bool allow_av_sample_buffer_display_layer);
+  CARendererLayerTree(bool allow_av_sample_buffer_display_layer,
+                      bool allow_solid_color_layers);
 
   // This will remove all CALayers from this tree from their superlayer.
   ~CARendererLayerTree();
@@ -58,7 +60,11 @@
   bool CommitFullscreenLowPowerLayer(
       AVSampleBufferDisplayLayer* fullscreen_low_power_layer);
 
+  // Returns the contents used for a given solid color.
+  id ContentsForSolidColorForTesting(unsigned int color);
+
  private:
+  class SolidColorContents;
   struct RootLayer;
   struct ClipAndSortingLayer;
   struct TransformLayer;
@@ -161,6 +167,7 @@
     // their use count.
     const gfx::ScopedInUseIOSurface io_surface;
     const base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer;
+    scoped_refptr<SolidColorContents> solid_color_contents;
     gfx::RectF contents_rect;
     gfx::Rect rect;
     unsigned background_color = 0;
@@ -184,6 +191,7 @@
   float scale_factor_ = 1;
   bool has_committed_ = false;
   const bool allow_av_sample_buffer_display_layer_ = true;
+  const bool allow_solid_color_layers_ = true;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(CARendererLayerTree);
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
index 985b06e..a139dc7 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -10,6 +10,7 @@
 #include <GLES2/gl2extchromium.h>
 
 #include "base/command_line.h"
+#include "base/lazy_instance.h"
 #include "base/mac/sdk_forward_declarations.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -129,10 +130,81 @@
 
 }  // namespace
 
+class CARendererLayerTree::SolidColorContents
+    : public base::RefCounted<CARendererLayerTree::SolidColorContents> {
+ public:
+  static scoped_refptr<SolidColorContents> Get(SkColor color);
+  id GetContents() const;
+
+ private:
+  friend class base::RefCounted<SolidColorContents>;
+
+  SolidColorContents(SkColor color, IOSurfaceRef io_surface);
+  ~SolidColorContents();
+
+  SkColor color_ = 0;
+  base::ScopedCFTypeRef<IOSurfaceRef> io_surface_;
+  static base::LazyInstance<std::map<SkColor, SolidColorContents*>> map_;
+};
+
+base::LazyInstance<std::map<SkColor, CARendererLayerTree::SolidColorContents*>>
+    CARendererLayerTree::SolidColorContents::map_;
+
+// static
+scoped_refptr<CARendererLayerTree::SolidColorContents>
+CARendererLayerTree::SolidColorContents::Get(SkColor color) {
+  const int kSolidColorContentsSize = 16;
+
+  auto found = map_.Get().find(color);
+  if (found != map_.Get().end())
+    return found->second;
+
+  IOSurfaceRef io_surface = CreateIOSurface(
+      gfx::Size(kSolidColorContentsSize, kSolidColorContentsSize),
+      gfx::BufferFormat::BGRA_8888);
+  if (!io_surface)
+    return nullptr;
+
+  size_t bytes_per_row = IOSurfaceGetBytesPerRowOfPlane(io_surface, 0);
+  IOSurfaceLock(io_surface, 0, NULL);
+  char* row_base_address =
+      reinterpret_cast<char*>(IOSurfaceGetBaseAddress(io_surface));
+  for (int i = 0; i < kSolidColorContentsSize; ++i) {
+    unsigned int* pixel = reinterpret_cast<unsigned int*>(row_base_address);
+    for (int j = 0; j < kSolidColorContentsSize; ++j)
+      *(pixel++) = color;
+    row_base_address += bytes_per_row;
+  }
+  IOSurfaceUnlock(io_surface, 0, NULL);
+
+  return new SolidColorContents(color, io_surface);
+}
+
+id CARendererLayerTree::SolidColorContents::GetContents() const {
+  return static_cast<id>(io_surface_.get());
+}
+
+CARendererLayerTree::SolidColorContents::SolidColorContents(
+    SkColor color,
+    IOSurfaceRef io_surface)
+    : color_(color), io_surface_(io_surface) {
+  DCHECK(map_.Get().find(color_) == map_.Get().end());
+  map_.Get()[color_] = this;
+}
+
+CARendererLayerTree::SolidColorContents::~SolidColorContents() {
+  auto found = map_.Get().find(color_);
+  DCHECK(found != map_.Get().end());
+  DCHECK(found->second == this);
+  map_.Get().erase(color_);
+}
+
 CARendererLayerTree::CARendererLayerTree(
-    bool allow_av_sample_buffer_display_layer)
+    bool allow_av_sample_buffer_display_layer,
+    bool allow_solid_color_layers)
     : allow_av_sample_buffer_display_layer_(
-          allow_av_sample_buffer_display_layer) {}
+          allow_av_sample_buffer_display_layer),
+      allow_solid_color_layers_(allow_solid_color_layers) {}
 CARendererLayerTree::~CARendererLayerTree() {}
 
 bool CARendererLayerTree::ScheduleCALayer(const CARendererLayerParams& params) {
@@ -216,6 +288,9 @@
   return true;
 }
 
+id CARendererLayerTree::ContentsForSolidColorForTesting(SkColor color) {
+  return SolidColorContents::Get(color)->GetContents();
+}
 
 CARendererLayerTree::RootLayer::RootLayer() {}
 
@@ -289,6 +364,17 @@
       ca_filter(filter == GL_LINEAR ? kCAFilterLinear : kCAFilterNearest) {
   DCHECK(filter == GL_LINEAR || filter == GL_NEAREST);
 
+  // On Mac OS Sierra, solid color layers are not color color corrected to the
+  // output monitor color space, but IOSurface-backed layers are color
+  // corrected. Note that this is only the case when the CALayers are shared
+  // across processes. To make colors consistent across both solid color and
+  // IOSurface-backed layers, use a cache of solid-color IOSurfaces as contents.
+  // https://crbug.com/633805
+  if (!io_surface && !tree->allow_solid_color_layers_) {
+    solid_color_contents = SolidColorContents::Get(background_color);
+    ContentLayer::contents_rect = gfx::RectF(0, 0, 1, 1);
+  }
+
   // Because the root layer has setGeometryFlipped:YES, there is some ambiguity
   // about what exactly top and bottom mean. This ambiguity is resolved in
   // different ways for solid color CALayers and for CALayers that have content
@@ -301,7 +387,7 @@
     ca_edge_aa_mask |= kCALayerLeftEdge;
   if (edge_aa_mask & GL_CA_LAYER_EDGE_RIGHT_CHROMIUM)
     ca_edge_aa_mask |= kCALayerRightEdge;
-  if (io_surface) {
+  if (io_surface || solid_color_contents) {
     if (edge_aa_mask & GL_CA_LAYER_EDGE_TOP_CHROMIUM)
       ca_edge_aa_mask |= kCALayerBottomEdge;
     if (edge_aa_mask & GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM)
@@ -326,6 +412,7 @@
 CARendererLayerTree::ContentLayer::ContentLayer(ContentLayer&& layer)
     : io_surface(layer.io_surface),
       cv_pixel_buffer(layer.cv_pixel_buffer),
+      solid_color_contents(layer.solid_color_contents),
       contents_rect(layer.contents_rect),
       rect(layer.rect),
       background_color(layer.background_color),
@@ -548,7 +635,8 @@
     std::swap(ca_layer, old_layer->ca_layer);
     std::swap(av_layer, old_layer->av_layer);
     update_contents = old_layer->io_surface != io_surface ||
-                      old_layer->cv_pixel_buffer != cv_pixel_buffer;
+                      old_layer->cv_pixel_buffer != cv_pixel_buffer ||
+                      old_layer->solid_color_contents != solid_color_contents;
     update_contents_rect = old_layer->contents_rect != contents_rect;
     update_rect = old_layer->rect != rect;
     update_background_color = old_layer->background_color != background_color;
@@ -585,7 +673,13 @@
     }
   } else {
     if (update_contents) {
-      [ca_layer setContents:static_cast<id>(io_surface.get())];
+      if (io_surface) {
+        [ca_layer setContents:static_cast<id>(io_surface.get())];
+      } else if (solid_color_contents) {
+        [ca_layer setContents:solid_color_contents->GetContents()];
+      } else {
+        [ca_layer setContents:nil];
+      }
       if ([ca_layer respondsToSelector:(@selector(setContentsScale:))])
         [ca_layer setContentsScale:scale_factor];
     }
@@ -626,9 +720,15 @@
       if (use_av_layer) {
         // Yellow represents an AV layer that changed this frame.
         color.reset(CGColorCreateGenericRGB(1, 1, 0, 1));
-      } else {
-        // Pink represents a CALayer that changed this frame.
+      } else if (io_surface) {
+        // Magenta represents a CALayer that changed this frame.
         color.reset(CGColorCreateGenericRGB(1, 0, 1, 1));
+      } else if (solid_color_contents) {
+        // Cyan represents a solid color IOSurface-backed layer.
+        color.reset(CGColorCreateGenericRGB(0, 1, 1, 1));
+      } else {
+        // Red represents a solid color layer.
+        color.reset(CGColorCreateGenericRGB(1, 0, 0, 1));
       }
     } else {
       // Grey represents a CALayer that has not changed.