diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
index e417cbf..f9cc3d7 100644
--- a/cc/output/gl_renderer.cc
+++ b/cc/output/gl_renderer.cc
@@ -182,7 +182,6 @@
 
   // Blending refers to modifications to the backdrop.
   bool use_shaders_for_blending = false;
-  const Program* program = nullptr;
 
   bool use_aa = false;
 
@@ -603,8 +602,7 @@
 void GLRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
   SetBlendEnabled(quad->ShouldDrawWithBlending());
 
-  const Program* program = GetProgram(ProgramKey::DebugBorder());
-  SetUseProgram(program);
+  SetUseProgram(ProgramKey::DebugBorder(), gfx::ColorSpace::CreateSRGB());
 
   // Use the full quad_rect for debug quads to not move the edges based on
   // partial swaps.
@@ -1307,16 +1305,14 @@
         SamplerTypeFromTextureTarget(params->mask_resource_lock->target());
   }
 
-  params->program = GetProgram(ProgramKey::RenderPass(
-      tex_coord_precision, sampler_type, shader_blend_mode,
-      params->use_aa ? USE_AA : NO_AA, mask_mode, mask_for_background,
-      params->use_color_matrix));
-  SetUseProgram(params->program);
+  SetUseProgram(ProgramKey::RenderPass(
+                    tex_coord_precision, sampler_type, shader_blend_mode,
+                    params->use_aa ? USE_AA : NO_AA, mask_mode,
+                    mask_for_background, params->use_color_matrix),
+                current_frame()->device_color_space);
 }
 
 void GLRenderer::UpdateRPDQUniforms(DrawRenderPassDrawQuadParams* params) {
-  const Program* program = params->program;
-
   gfx::RectF tex_rect(params->src_offset.x(), params->src_offset.y(),
                       params->dst_rect.width(), params->dst_rect.height());
   gfx::Size texture_size;
@@ -1328,26 +1324,29 @@
   }
   tex_rect.Scale(1.0f / texture_size.width(), 1.0f / texture_size.height());
 
-  DCHECK(program->vertex_tex_transform_location() != -1 || IsContextLost());
+  DCHECK(current_program_->vertex_tex_transform_location() != -1 ||
+         IsContextLost());
   if (params->source_needs_flip) {
     // Flip the content vertically in the shader, as the RenderPass input
     // texture is already oriented the same way as the framebuffer, but the
     // projection transform does a flip.
-    gl_->Uniform4f(program->vertex_tex_transform_location(), tex_rect.x(),
-                   1.0f - tex_rect.y(), tex_rect.width(), -tex_rect.height());
+    gl_->Uniform4f(current_program_->vertex_tex_transform_location(),
+                   tex_rect.x(), 1.0f - tex_rect.y(), tex_rect.width(),
+                   -tex_rect.height());
   } else {
     // Tile textures are oriented opposite the framebuffer, so can use
     // the projection transform to do the flip.
-    gl_->Uniform4f(program->vertex_tex_transform_location(), tex_rect.x(),
-                   tex_rect.y(), tex_rect.width(), tex_rect.height());
+    gl_->Uniform4f(current_program_->vertex_tex_transform_location(),
+                   tex_rect.x(), tex_rect.y(), tex_rect.width(),
+                   tex_rect.height());
   }
 
   GLint last_texture_unit = 0;
-  if (program->mask_sampler_location() != -1) {
+  if (current_program_->mask_sampler_location() != -1) {
     DCHECK(params->mask_resource_lock);
-    DCHECK_NE(program->mask_tex_coord_scale_location(), 1);
-    DCHECK_NE(program->mask_tex_coord_offset_location(), 1);
-    gl_->Uniform1i(program->mask_sampler_location(), 1);
+    DCHECK_NE(current_program_->mask_tex_coord_scale_location(), 1);
+    DCHECK_NE(current_program_->mask_tex_coord_offset_location(), 1);
+    gl_->Uniform1i(current_program_->mask_sampler_location(), 1);
 
     gfx::RectF mask_uv_rect = params->quad->MaskUVRect();
     if (SamplerTypeFromTextureTarget(params->mask_resource_lock->target()) !=
@@ -1360,16 +1359,16 @@
       // framebuffer and the RenderPass contents texture, so we flip the tex
       // coords from the RenderPass texture to find the mask texture coords.
       gl_->Uniform2f(
-          program->mask_tex_coord_offset_location(), mask_uv_rect.x(),
+          current_program_->mask_tex_coord_offset_location(), mask_uv_rect.x(),
           mask_uv_rect.height() / tex_rect.height() + mask_uv_rect.y());
-      gl_->Uniform2f(program->mask_tex_coord_scale_location(),
+      gl_->Uniform2f(current_program_->mask_tex_coord_scale_location(),
                      mask_uv_rect.width() / tex_rect.width(),
                      -mask_uv_rect.height() / tex_rect.height());
     } else {
       // Tile textures are oriented the same way as mask textures.
-      gl_->Uniform2f(program->mask_tex_coord_offset_location(),
+      gl_->Uniform2f(current_program_->mask_tex_coord_offset_location(),
                      mask_uv_rect.x(), mask_uv_rect.y());
-      gl_->Uniform2f(program->mask_tex_coord_scale_location(),
+      gl_->Uniform2f(current_program_->mask_tex_coord_scale_location(),
                      mask_uv_rect.width() / tex_rect.width(),
                      mask_uv_rect.height() / tex_rect.height());
     }
@@ -1377,34 +1376,35 @@
     last_texture_unit = 1;
   }
 
-  if (program->edge_location() != -1)
-    gl_->Uniform3fv(program->edge_location(), 8, params->edge);
+  if (current_program_->edge_location() != -1)
+    gl_->Uniform3fv(current_program_->edge_location(), 8, params->edge);
 
-  if (program->color_matrix_location() != -1) {
+  if (current_program_->color_matrix_location() != -1) {
     float matrix[16];
     for (int i = 0; i < 4; ++i) {
       for (int j = 0; j < 4; ++j)
         matrix[i * 4 + j] = SkScalarToFloat(params->color_matrix[j * 5 + i]);
     }
-    gl_->UniformMatrix4fv(program->color_matrix_location(), 1, false, matrix);
+    gl_->UniformMatrix4fv(current_program_->color_matrix_location(), 1, false,
+                          matrix);
   }
   static const float kScale = 1.0f / 255.0f;
-  if (program->color_offset_location() != -1) {
+  if (current_program_->color_offset_location() != -1) {
     float offset[4];
     for (int i = 0; i < 4; ++i)
       offset[i] = SkScalarToFloat(params->color_matrix[i * 5 + 4]) * kScale;
 
-    gl_->Uniform4fv(program->color_offset_location(), 1, offset);
+    gl_->Uniform4fv(current_program_->color_offset_location(), 1, offset);
   }
 
-  if (program->backdrop_location() != -1) {
+  if (current_program_->backdrop_location() != -1) {
     DCHECK(params->background_texture || params->background_image_id);
-    DCHECK_NE(program->backdrop_location(), 0);
-    DCHECK_NE(program->backdrop_rect_location(), 0);
+    DCHECK_NE(current_program_->backdrop_location(), 0);
+    DCHECK_NE(current_program_->backdrop_rect_location(), 0);
 
-    gl_->Uniform1i(program->backdrop_location(), ++last_texture_unit);
+    gl_->Uniform1i(current_program_->backdrop_location(), ++last_texture_unit);
 
-    gl_->Uniform4f(program->backdrop_rect_location(),
+    gl_->Uniform4f(current_program_->backdrop_rect_location(),
                    params->background_rect.x(), params->background_rect.y(),
                    params->background_rect.width(),
                    params->background_rect.height());
@@ -1414,7 +1414,7 @@
       gl_->BindTexture(GL_TEXTURE_2D, params->background_image_id);
       gl_->ActiveTexture(GL_TEXTURE0);
       if (params->mask_for_background)
-        gl_->Uniform1i(program->original_backdrop_location(),
+        gl_->Uniform1i(current_program_->original_backdrop_location(),
                        ++last_texture_unit);
     }
     if (params->background_texture) {
@@ -1748,13 +1748,15 @@
   SetupQuadForClippingAndAntialiasing(device_transform, quad, aa_quad,
                                       clip_region, &local_quad, edge);
 
-  const Program* program =
-      GetProgram(ProgramKey::SolidColor(use_aa ? USE_AA : NO_AA));
-  SetUseProgram(program);
+  // TODO(ccameron): Solid color draw quads need to specify their implied
+  // color space. Assume SRGB (which is wrong) for now.
+  gfx::ColorSpace quad_color_space = gfx::ColorSpace::CreateSRGB();
+  SetUseProgram(ProgramKey::SolidColor(use_aa ? USE_AA : NO_AA),
+                quad_color_space);
   SetShaderColor(color, opacity);
 
   if (use_aa) {
-    gl_->Uniform3fv(program->edge_location(), 8, edge);
+    gl_->Uniform3fv(current_program_->edge_location(), 8, edge);
   }
 
   // Enable blending when the quad properties require it or if we decided
@@ -1898,18 +1900,17 @@
     fragment_tex_scale_y /= texture_size.height();
   }
 
-  const Program* program = GetProgram(ProgramKey::Tile(
-      tex_coord_precision, sampler, USE_AA,
-      quad->swizzle_contents ? DO_SWIZZLE : NO_SWIZZLE, false));
+  SetUseProgram(
+      ProgramKey::Tile(tex_coord_precision, sampler, USE_AA,
+                       quad->swizzle_contents ? DO_SWIZZLE : NO_SWIZZLE, false),
+      quad_resource_lock.color_space());
 
-  SetUseProgram(program);
+  gl_->Uniform3fv(current_program_->edge_location(), 8, edge);
 
-  gl_->Uniform3fv(program->edge_location(), 8, edge);
-
-  gl_->Uniform4f(program->vertex_tex_transform_location(),
+  gl_->Uniform4f(current_program_->vertex_tex_transform_location(),
                  vertex_tex_translate_x, vertex_tex_translate_y,
                  vertex_tex_scale_x, vertex_tex_scale_y);
-  gl_->Uniform4f(program->fragment_tex_transform_location(),
+  gl_->Uniform4f(current_program_->fragment_tex_transform_location(),
                  fragment_tex_translate_x, fragment_tex_translate_y,
                  fragment_tex_scale_x, fragment_tex_scale_y);
 
@@ -1975,14 +1976,13 @@
   TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
       gl_, &highp_threshold_cache_, highp_threshold_min_, quad->texture_size);
 
-  const Program* program = GetProgram(
+  SetUseProgram(
       ProgramKey::Tile(tex_coord_precision, sampler, NO_AA,
                        quad->swizzle_contents ? DO_SWIZZLE : NO_SWIZZLE,
-                       !quad->ShouldDrawWithBlending()));
+                       !quad->ShouldDrawWithBlending()),
+      quad_resource_lock.color_space());
 
-  SetUseProgram(program);
-
-  gl_->Uniform4f(program->vertex_tex_transform_location(),
+  gl_->Uniform4f(current_program_->vertex_tex_transform_location(),
                  vertex_tex_translate_x, vertex_tex_translate_y,
                  vertex_tex_scale_x, vertex_tex_scale_y);
 
@@ -2021,7 +2021,7 @@
       tile_quad.p1().y(), tile_quad.p2().x(), tile_quad.p2().y(),
       tile_quad.p3().x(), tile_quad.p3().y(),
   };
-  gl_->Uniform2fv(program->quad_location(), 4, gl_quad);
+  gl_->Uniform2fv(current_program_->quad_location(), 4, gl_quad);
 
   SetShaderMatrix(current_frame()->projection_matrix *
                   quad->shared_quad_state->quad_to_target_transform);
@@ -2031,25 +2031,12 @@
 
 // TODO(ccameron): This has been replicated in ui/gfx/color_transform.cc. Delete
 // one of the instances.
-void ComputeYUVToRGBMatrices(YUVVideoDrawQuad::ColorSpace color_space,
+void ComputeYUVToRGBMatrices(const gfx::ColorSpace& src_color_space,
+                             const gfx::ColorSpace& dst_color_space,
                              uint32_t bits_per_channel,
                              float resource_multiplier,
                              float resource_offset,
-                             ColorConversionMode color_conversion_mode,
                              float* yuv_to_rgb_matrix) {
-  gfx::ColorSpace gfx_color_space;
-  switch (color_space) {
-    case YUVVideoDrawQuad::REC_601:
-      gfx_color_space = gfx::ColorSpace::CreateREC601();
-      break;
-    case YUVVideoDrawQuad::REC_709:
-      gfx_color_space = gfx::ColorSpace::CreateREC709();
-      break;
-    case YUVVideoDrawQuad::JPEG:
-      gfx_color_space = gfx::ColorSpace::CreateJpeg();
-      break;
-  }
-
   // Compute the matrix |full_transform| which converts input YUV values to RGB
   // values.
   SkMatrix44 full_transform;
@@ -2062,7 +2049,7 @@
 
   // If we're using a LUT for conversion, we only need the resource adjust,
   // so just return this matrix.
-  if (color_conversion_mode == COLOR_CONVERSION_MODE_LUT) {
+  if (dst_color_space.IsValid()) {
     full_transform.asColMajorf(yuv_to_rgb_matrix);
     return;
   }
@@ -2070,14 +2057,14 @@
   // Then apply the range adjust.
   {
     SkMatrix44 range_adjust;
-    gfx_color_space.GetRangeAdjustMatrix(&range_adjust);
+    src_color_space.GetRangeAdjustMatrix(&range_adjust);
     full_transform.postConcat(range_adjust);
   }
 
   // Then apply the YUV to RGB full_transform.
   {
     SkMatrix44 rgb_to_yuv;
-    gfx_color_space.GetTransferMatrix(&rgb_to_yuv);
+    src_color_space.GetTransferMatrix(&rgb_to_yuv);
     SkMatrix44 yuv_to_rgb;
     rgb_to_yuv.invert(&yuv_to_rgb);
     full_transform.postConcat(yuv_to_rgb);
@@ -2100,10 +2087,28 @@
       quad->v_plane_resource_id() == quad->u_plane_resource_id()
           ? UV_TEXTURE_MODE_UV
           : UV_TEXTURE_MODE_U_V;
-  ColorConversionMode color_conversion_mode =
-      base::FeatureList::IsEnabled(media::kVideoColorManagement)
-          ? COLOR_CONVERSION_MODE_LUT
-          : COLOR_CONVERSION_MODE_NONE;
+
+  // TODO(ccameron): There are currently three sources of the color space: the
+  // resource, quad->color_space, and quad->video_color_space. Remove two of
+  // them.
+  gfx::ColorSpace src_color_space = quad->video_color_space;
+  gfx::ColorSpace dst_color_space = current_frame()->device_color_space;
+  if (!base::FeatureList::IsEnabled(media::kVideoColorManagement)) {
+    if (!settings_->enable_color_correct_rendering)
+      dst_color_space = gfx::ColorSpace();
+    switch (quad->color_space) {
+      case YUVVideoDrawQuad::REC_601:
+        src_color_space = gfx::ColorSpace::CreateREC601();
+        break;
+      case YUVVideoDrawQuad::REC_709:
+        src_color_space = gfx::ColorSpace::CreateREC709();
+        break;
+      case YUVVideoDrawQuad::JPEG:
+        src_color_space = gfx::ColorSpace::CreateJpeg();
+        break;
+    }
+  }
+
   ResourceProvider::ScopedSamplerGL y_plane_lock(
       resource_provider_, quad->y_plane_resource_id(), GL_TEXTURE1, GL_LINEAR);
   ResourceProvider::ScopedSamplerGL u_plane_lock(
@@ -2129,10 +2134,9 @@
   // All planes must have the same sampler type.
   SamplerType sampler = SamplerTypeFromTextureTarget(y_plane_lock.target());
 
-  const Program* program = GetProgram(
-      ProgramKey::YUVVideo(tex_coord_precision, sampler, alpha_texture_mode,
-                           uv_texture_mode, color_conversion_mode));
-  SetUseProgram(program);
+  SetUseProgram(ProgramKey::YUVVideo(tex_coord_precision, sampler,
+                                     alpha_texture_mode, uv_texture_mode),
+                src_color_space, dst_color_space);
 
   gfx::SizeF ya_tex_scale(1.0f, 1.0f);
   gfx::SizeF uv_tex_scale(1.0f, 1.0f);
@@ -2163,14 +2167,14 @@
   float uv_vertex_tex_scale_y =
       quad->uv_tex_coord_rect.height() * uv_tex_scale.height();
 
-  gl_->Uniform2f(program->ya_tex_scale_location(), ya_vertex_tex_scale_x,
-                 ya_vertex_tex_scale_y);
-  gl_->Uniform2f(program->ya_tex_offset_location(), ya_vertex_tex_translate_x,
-                 ya_vertex_tex_translate_y);
-  gl_->Uniform2f(program->uv_tex_scale_location(), uv_vertex_tex_scale_x,
-                 uv_vertex_tex_scale_y);
-  gl_->Uniform2f(program->uv_tex_offset_location(), uv_vertex_tex_translate_x,
-                 uv_vertex_tex_translate_y);
+  gl_->Uniform2f(current_program_->ya_tex_scale_location(),
+                 ya_vertex_tex_scale_x, ya_vertex_tex_scale_y);
+  gl_->Uniform2f(current_program_->ya_tex_offset_location(),
+                 ya_vertex_tex_translate_x, ya_vertex_tex_translate_y);
+  gl_->Uniform2f(current_program_->uv_tex_scale_location(),
+                 uv_vertex_tex_scale_x, uv_vertex_tex_scale_y);
+  gl_->Uniform2f(current_program_->uv_tex_offset_location(),
+                 uv_vertex_tex_translate_x, uv_vertex_tex_translate_y);
 
   gfx::RectF ya_clamp_rect(ya_vertex_tex_translate_x, ya_vertex_tex_translate_y,
                            ya_vertex_tex_scale_x, ya_vertex_tex_scale_y);
@@ -2180,38 +2184,29 @@
                            uv_vertex_tex_scale_x, uv_vertex_tex_scale_y);
   uv_clamp_rect.Inset(0.5f * uv_tex_scale.width(),
                       0.5f * uv_tex_scale.height());
-  gl_->Uniform4f(program->ya_clamp_rect_location(), ya_clamp_rect.x(),
+  gl_->Uniform4f(current_program_->ya_clamp_rect_location(), ya_clamp_rect.x(),
                  ya_clamp_rect.y(), ya_clamp_rect.right(),
                  ya_clamp_rect.bottom());
-  gl_->Uniform4f(program->uv_clamp_rect_location(), uv_clamp_rect.x(),
+  gl_->Uniform4f(current_program_->uv_clamp_rect_location(), uv_clamp_rect.x(),
                  uv_clamp_rect.y(), uv_clamp_rect.right(),
                  uv_clamp_rect.bottom());
 
-  gl_->Uniform1i(program->y_texture_location(), 1);
+  gl_->Uniform1i(current_program_->y_texture_location(), 1);
   if (uv_texture_mode == UV_TEXTURE_MODE_UV) {
-    gl_->Uniform1i(program->uv_texture_location(), 2);
+    gl_->Uniform1i(current_program_->uv_texture_location(), 2);
   } else {
-    gl_->Uniform1i(program->u_texture_location(), 2);
-    gl_->Uniform1i(program->v_texture_location(), 3);
+    gl_->Uniform1i(current_program_->u_texture_location(), 2);
+    gl_->Uniform1i(current_program_->v_texture_location(), 3);
   }
   if (alpha_texture_mode == YUV_HAS_ALPHA_TEXTURE)
-    gl_->Uniform1i(program->a_texture_location(), 4);
+    gl_->Uniform1i(current_program_->a_texture_location(), 4);
 
-  if (color_conversion_mode == COLOR_CONVERSION_MODE_LUT) {
-    ColorLUTCache::LUT lut = color_lut_cache_.GetLUT(
-        quad->video_color_space, current_frame()->device_color_space);
-    gl_->ActiveTexture(GL_TEXTURE5);
-    gl_->BindTexture(GL_TEXTURE_2D, lut.texture);
-    gl_->Uniform1i(program->lut_texture_location(), 5);
-    gl_->Uniform1f(program->lut_size_location(), lut.size);
-    gl_->ActiveTexture(GL_TEXTURE0);
-  }
   float yuv_to_rgb_matrix[16] = {0};
-  ComputeYUVToRGBMatrices(quad->color_space, quad->bits_per_channel,
-                          quad->resource_multiplier, quad->resource_offset,
-                          color_conversion_mode, yuv_to_rgb_matrix);
-  gl_->UniformMatrix4fv(program->yuv_and_resource_matrix_location(), 1, 0,
-                        yuv_to_rgb_matrix);
+  ComputeYUVToRGBMatrices(src_color_space, dst_color_space,
+                          quad->bits_per_channel, quad->resource_multiplier,
+                          quad->resource_offset, yuv_to_rgb_matrix);
+  gl_->UniformMatrix4fv(current_program_->yuv_and_resource_matrix_location(), 1,
+                        0, yuv_to_rgb_matrix);
 
   // The transform and vertex data are used to figure out the extents that the
   // un-antialiased quad should have and which vertex this is and the float
@@ -2248,20 +2243,19 @@
       gl_, &highp_threshold_cache_, highp_threshold_min_,
       quad->shared_quad_state->visible_quad_layer_rect.bottom_right());
 
-  const Program* program =
-      GetProgram(ProgramKey::VideoStream(tex_coord_precision));
-  SetUseProgram(program);
-
   ResourceProvider::ScopedReadLockGL lock(resource_provider_,
                                           quad->resource_id());
 
+  SetUseProgram(ProgramKey::VideoStream(tex_coord_precision),
+                lock.color_space());
+
   DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_));
   gl_->BindTexture(GL_TEXTURE_EXTERNAL_OES, lock.texture_id());
 
   static float gl_matrix[16];
   ToGLMatrix(&gl_matrix[0], quad->matrix);
   gl_->UniformMatrix4fvStreamTextureMatrixCHROMIUM(
-      program->tex_matrix_location(), false, gl_matrix);
+      current_program_->tex_matrix_location(), false, gl_matrix);
 
   SetShaderOpacity(quad);
   if (!clip_region) {
@@ -2282,7 +2276,7 @@
 
 void GLRenderer::FlushTextureQuadCache(BoundGeometry flush_binding) {
   // Check to see if we have anything to draw.
-  if (!draw_cache_.program)
+  if (draw_cache_.is_empty)
     return;
 
   PrepareGeometry(flush_binding);
@@ -2290,14 +2284,15 @@
   // Set the correct blending mode.
   SetBlendEnabled(draw_cache_.needs_blending);
 
-  // Bind the program to the GL state.
-  SetUseProgram(draw_cache_.program);
-
   // Assume the current active textures is 0.
   ResourceProvider::ScopedSamplerGL locked_quad(
       resource_provider_,
       draw_cache_.resource_id,
       draw_cache_.nearest_neighbor ? GL_NEAREST : GL_LINEAR);
+
+  // Bind the program to the GL state.
+  SetUseProgram(draw_cache_.program_key, locked_quad.color_space());
+
   DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(gl_));
   gl_->BindTexture(locked_quad.target(), locked_quad.texture_id());
 
@@ -2308,22 +2303,22 @@
 
   // Upload the tranforms for both points and uvs.
   gl_->UniformMatrix4fv(
-      draw_cache_.program->matrix_location(),
+      current_program_->matrix_location(),
       static_cast<int>(draw_cache_.matrix_data.size()), false,
       reinterpret_cast<float*>(&draw_cache_.matrix_data.front()));
-  gl_->Uniform4fv(draw_cache_.program->vertex_tex_transform_location(),
+  gl_->Uniform4fv(current_program_->vertex_tex_transform_location(),
                   static_cast<int>(draw_cache_.uv_xform_data.size()),
                   reinterpret_cast<float*>(&draw_cache_.uv_xform_data.front()));
 
   if (draw_cache_.background_color != SK_ColorTRANSPARENT) {
     Float4 background_color =
         PremultipliedColor(draw_cache_.background_color, 1.f);
-    gl_->Uniform4fv(draw_cache_.program->background_color_location(), 1,
+    gl_->Uniform4fv(current_program_->background_color_location(), 1,
                     background_color.data);
   }
 
   gl_->Uniform1fv(
-      draw_cache_.program->vertex_opacity_location(),
+      current_program_->vertex_opacity_location(),
       static_cast<int>(draw_cache_.vertex_opacity_data.size()),
       static_cast<float*>(&draw_cache_.vertex_opacity_data.front()));
 
@@ -2339,11 +2334,10 @@
     // When we draw the composited borders we have one flush per quad.
     DCHECK_EQ(1u, draw_cache_.matrix_data.size());
     SetBlendEnabled(false);
-    const Program* program = GetProgram(ProgramKey::DebugBorder());
-    SetUseProgram(program);
+    SetUseProgram(ProgramKey::DebugBorder(), gfx::ColorSpace::CreateSRGB());
 
     gl_->UniformMatrix4fv(
-        program->matrix_location(), 1, false,
+        current_program_->matrix_location(), 1, false,
         reinterpret_cast<float*>(&draw_cache_.matrix_data.front()));
 
     // Pick a random color based on the scale on X and Y.
@@ -2359,7 +2353,7 @@
   }
 
   // Clear the cache.
-  draw_cache_.program = nullptr;
+  draw_cache_.is_empty = true;
   draw_cache_.resource_id = -1;
   draw_cache_.uv_xform_data.resize(0);
   draw_cache_.vertex_opacity_data.resize(0);
@@ -2391,21 +2385,22 @@
                                           quad->resource_id());
   const SamplerType sampler = SamplerTypeFromTextureTarget(lock.target());
 
-  const Program* program = GetProgram(ProgramKey::Texture(
+  ProgramKey program_key = ProgramKey::Texture(
       tex_coord_precision, sampler,
       quad->premultiplied_alpha ? PREMULTIPLIED_ALPHA : NON_PREMULTIPLIED_ALPHA,
-      quad->background_color != SK_ColorTRANSPARENT));
+      quad->background_color != SK_ColorTRANSPARENT);
   int resource_id = quad->resource_id();
 
   size_t max_quads = StaticGeometryBinding::NUM_QUADS;
-  if (draw_cache_.program != program ||
+  if (draw_cache_.is_empty || draw_cache_.program_key != program_key ||
       draw_cache_.resource_id != resource_id ||
       draw_cache_.needs_blending != quad->ShouldDrawWithBlending() ||
       draw_cache_.nearest_neighbor != quad->nearest_neighbor ||
       draw_cache_.background_color != quad->background_color ||
       draw_cache_.matrix_data.size() >= max_quads) {
     FlushTextureQuadCache(SHARED_BINDING);
-    draw_cache_.program = program;
+    draw_cache_.is_empty = false;
+    draw_cache_.program_key = program_key;
     draw_cache_.resource_id = resource_id;
     draw_cache_.needs_blending = quad->ShouldDrawWithBlending();
     draw_cache_.nearest_neighbor = quad->nearest_neighbor;
@@ -2537,7 +2532,7 @@
 }
 
 void GLRenderer::SetShaderQuadF(const gfx::QuadF& quad) {
-  if (!program_shadow_ || program_shadow_->quad_location() == -1)
+  if (!current_program_ || current_program_->quad_location() == -1)
     return;
   float gl_quad[8];
   gl_quad[0] = quad.p1().x();
@@ -2548,30 +2543,30 @@
   gl_quad[5] = quad.p3().y();
   gl_quad[6] = quad.p4().x();
   gl_quad[7] = quad.p4().y();
-  gl_->Uniform2fv(program_shadow_->quad_location(), 4, gl_quad);
+  gl_->Uniform2fv(current_program_->quad_location(), 4, gl_quad);
 }
 
 void GLRenderer::SetShaderOpacity(const DrawQuad* quad) {
-  if (!program_shadow_ || program_shadow_->alpha_location() == -1)
+  if (!current_program_ || current_program_->alpha_location() == -1)
     return;
-  gl_->Uniform1f(program_shadow_->alpha_location(),
+  gl_->Uniform1f(current_program_->alpha_location(),
                  quad->shared_quad_state->opacity);
 }
 
 void GLRenderer::SetShaderMatrix(const gfx::Transform& transform) {
-  if (!program_shadow_ || program_shadow_->matrix_location() == -1)
+  if (!current_program_ || current_program_->matrix_location() == -1)
     return;
   float gl_matrix[16];
   ToGLMatrix(gl_matrix, transform);
-  gl_->UniformMatrix4fv(program_shadow_->matrix_location(), 1, false,
+  gl_->UniformMatrix4fv(current_program_->matrix_location(), 1, false,
                         gl_matrix);
 }
 
 void GLRenderer::SetShaderColor(SkColor color, float opacity) {
-  if (!program_shadow_ || program_shadow_->color_location() == -1)
+  if (!current_program_ || current_program_->color_location() == -1)
     return;
   Float4 float_color = PremultipliedColor(color, opacity);
-  gl_->Uniform4fv(program_shadow_->color_location(), 1, float_color.data);
+  gl_->Uniform4fv(current_program_->color_location(), 1, float_color.data);
 }
 
 void GLRenderer::SetStencilEnabled(bool enabled) {
@@ -2596,28 +2591,6 @@
   blend_shadow_ = enabled;
 }
 
-void GLRenderer::SetUseProgram(const Program* program) {
-  DCHECK(program);
-  DCHECK(program->initialized() || IsContextLost());
-  if (program == program_shadow_)
-    return;
-  gl_->UseProgram(program->program());
-
-  // Set uniforms that are common to all programs.
-  if (program->sampler_location() != -1)
-    gl_->Uniform1i(program->sampler_location(), 0);
-  if (program->viewport_location() != -1) {
-    float viewport[4] = {
-        static_cast<float>(current_window_space_viewport_.x()),
-        static_cast<float>(current_window_space_viewport_.y()),
-        static_cast<float>(current_window_space_viewport_.width()),
-        static_cast<float>(current_window_space_viewport_.height()),
-    };
-    gl_->Uniform4fv(program->viewport_location(), 1, viewport);
-  }
-  program_shadow_ = program;
-}
-
 void GLRenderer::DrawQuadGeometryClippedByQuadF(
     const gfx::Transform& draw_transform,
     const gfx::RectF& quad_rect,
@@ -3043,13 +3016,58 @@
   bound_geometry_ = binding;
 }
 
-const Program* GLRenderer::GetProgram(const ProgramKey& desc) {
-  std::unique_ptr<Program>& program = program_cache_[desc];
+void GLRenderer::SetUseProgram(const ProgramKey& program_key,
+                               const gfx::ColorSpace& src_color_space) {
+  gfx::ColorSpace dst_color_space;
+  if (settings_->enable_color_correct_rendering)
+    dst_color_space = current_frame()->device_color_space;
+  SetUseProgram(program_key, src_color_space, dst_color_space);
+}
+
+void GLRenderer::SetUseProgram(const ProgramKey& program_key_no_color,
+                               const gfx::ColorSpace& src_color_space,
+                               const gfx::ColorSpace& dst_color_space) {
+  ProgramKey program_key = program_key_no_color;
+  if (src_color_space.IsValid() && dst_color_space.IsValid())
+    program_key.SetColorConversionMode(COLOR_CONVERSION_MODE_LUT);
+
+  // Create and set the program if needed.
+  std::unique_ptr<Program>& program = program_cache_[program_key];
   if (!program) {
     program.reset(new Program);
-    program->Initialize(output_surface_->context_provider(), desc);
+    program->Initialize(output_surface_->context_provider(), program_key);
   }
-  return program.get();
+  DCHECK(program);
+  if (current_program_ != program.get()) {
+    current_program_ = program.get();
+    gl_->UseProgram(current_program_->program());
+  }
+  if (!current_program_->initialized()) {
+    DCHECK(IsContextLost());
+    return;
+  }
+
+  // Set uniforms that are common to all programs.
+  if (current_program_->sampler_location() != -1)
+    gl_->Uniform1i(current_program_->sampler_location(), 0);
+  if (current_program_->viewport_location() != -1) {
+    float viewport[4] = {
+        static_cast<float>(current_window_space_viewport_.x()),
+        static_cast<float>(current_window_space_viewport_.y()),
+        static_cast<float>(current_window_space_viewport_.width()),
+        static_cast<float>(current_window_space_viewport_.height()),
+    };
+    gl_->Uniform4fv(current_program_->viewport_location(), 1, viewport);
+  }
+  if (current_program_->lut_texture_location() != -1) {
+    ColorLUTCache::LUT lut =
+        color_lut_cache_.GetLUT(src_color_space, dst_color_space);
+    gl_->ActiveTexture(GL_TEXTURE5);
+    gl_->BindTexture(GL_TEXTURE_2D, lut.texture);
+    gl_->Uniform1i(current_program_->lut_texture_location(), 5);
+    gl_->Uniform1f(current_program_->lut_size_location(), lut.size);
+    gl_->ActiveTexture(GL_TEXTURE0);
+  }
 }
 
 const Program* GLRenderer::GetProgramIfInitialized(
@@ -3081,7 +3099,7 @@
   scissor_rect_ = gfx::Rect();
   stencil_shadow_ = false;
   blend_shadow_ = true;
-  program_shadow_ = nullptr;
+  current_program_ = nullptr;
 
   RestoreGLState();
 }
@@ -3097,8 +3115,8 @@
   gl_->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
   gl_->ActiveTexture(GL_TEXTURE0);
 
-  if (program_shadow_)
-    gl_->UseProgram(program_shadow_->program());
+  if (current_program_)
+    gl_->UseProgram(current_program_->program());
 
   if (stencil_shadow_)
     gl_->Enable(GL_STENCIL_TEST);
@@ -3422,8 +3440,7 @@
 
   PrepareGeometry(SHARED_BINDING);
 
-  const Program* program = GetProgram(ProgramKey::DebugBorder());
-  SetUseProgram(program);
+  SetUseProgram(ProgramKey::DebugBorder(), gfx::ColorSpace::CreateSRGB());
 
   gfx::Transform render_matrix;
   render_matrix.Translate(0.5 * output_rect.width() + output_rect.x(),
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
index 529d257..ea73b6f8 100644
--- a/cc/output/gl_renderer.h
+++ b/cc/output/gl_renderer.h
@@ -215,7 +215,18 @@
   void DrawQuadGeometry(const gfx::Transform& projection_matrix,
                         const gfx::Transform& draw_transform,
                         const gfx::RectF& quad_rect);
-  void SetUseProgram(const Program* program);
+
+  // If |dst_color_space| is invalid, then no color conversion (apart from
+  // YUV to RGB conversion) is performed. This explicit argument is available
+  // so that video color conversion can be enabled separately from general color
+  // conversion.
+  // TODO(ccameron): Remove the version with an explicit |dst_color_space|,
+  // since that will always be the device color space.
+  void SetUseProgram(const ProgramKey& program_key,
+                     const gfx::ColorSpace& src_color_space,
+                     const gfx::ColorSpace& dst_color_space);
+  void SetUseProgram(const ProgramKey& program_key,
+                     const gfx::ColorSpace& src_color_space);
 
   bool MakeContextCurrent();
 
@@ -280,10 +291,6 @@
   std::unique_ptr<DynamicGeometryBinding> clipped_geometry_;
   gfx::QuadF shared_geometry_quad_;
 
-  // If the requested program has not yet been initialized, this will initialize
-  // the program before returning it.
-  const Program* GetProgram(const ProgramKey& key);
-
   // This will return nullptr if the requested program has not yet been
   // initialized.
   const Program* GetProgramIfInitialized(const ProgramKey& key) const;
@@ -302,7 +309,7 @@
   bool is_scissor_enabled_ = false;
   bool stencil_shadow_ = false;
   bool blend_shadow_ = false;
-  const Program* program_shadow_ = nullptr;
+  const Program* current_program_ = nullptr;
   TexturedQuadDrawCache draw_cache_;
   int highp_threshold_min_ = 0;
   int highp_threshold_cache_ = 0;
diff --git a/cc/output/gl_renderer_draw_cache.h b/cc/output/gl_renderer_draw_cache.h
index 493ae4ca2..a2e43f2 100644
--- a/cc/output/gl_renderer_draw_cache.h
+++ b/cc/output/gl_renderer_draw_cache.h
@@ -8,12 +8,11 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "cc/output/program_binding.h"
 #include "third_party/skia/include/core/SkColor.h"
 
 namespace cc {
 
-class Program;
-
 // Collects 4 floats at a time for easy upload to GL.
 struct Float4 {
   float data[4];
@@ -31,8 +30,10 @@
   TexturedQuadDrawCache();
   ~TexturedQuadDrawCache();
 
+  bool is_empty = true;
+
   // Values tracked to determine if textured quads may be coalesced.
-  const Program* program = nullptr;
+  ProgramKey program_key;
   int resource_id = -1;
   bool needs_blending = false;
   bool nearest_neighbor = false;
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
index 42cdb4f..c4683999 100644
--- a/cc/output/gl_renderer_unittest.cc
+++ b/cc/output/gl_renderer_unittest.cc
@@ -133,28 +133,47 @@
     ASSERT_FALSE(renderer()->IsContextLost());
   }
 
+  void TestShader(const ProgramKey& program_key) {
+    renderer()->SetCurrentFrameForTesting(GLRenderer::DrawingFrame());
+    const size_t kNumSrcColorSpaces = 3;
+    gfx::ColorSpace src_color_spaces[kNumSrcColorSpaces] = {
+        gfx::ColorSpace(), gfx::ColorSpace::CreateSRGB(),
+        gfx::ColorSpace::CreateREC709(),
+    };
+    const size_t kNumDstColorSpaces = 3;
+    gfx::ColorSpace dst_color_spaces[kNumDstColorSpaces] = {
+        gfx::ColorSpace(), gfx::ColorSpace::CreateSRGB(),
+        gfx::ColorSpace::CreateSCRGBLinear(),
+    };
+    for (size_t i = 0; i < kNumDstColorSpaces; ++i) {
+      for (size_t j = 0; j < kNumSrcColorSpaces; ++j) {
+        renderer()->SetUseProgram(program_key, src_color_spaces[j],
+                                  dst_color_spaces[i]);
+        EXPECT_TRUE(renderer()->current_program_->initialized());
+      }
+    }
+  }
+
   void TestBasicShaders() {
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(ProgramKey::DebugBorder()));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(ProgramKey::SolidColor(NO_AA)));
-    EXPECT_PROGRAM_VALID(
-        renderer()->GetProgram(ProgramKey::SolidColor(USE_AA)));
+    TestShader(ProgramKey::DebugBorder());
+    TestShader(ProgramKey::SolidColor(NO_AA));
+    TestShader(ProgramKey::SolidColor(USE_AA));
   }
 
   void TestShadersWithPrecision(TexCoordPrecision precision) {
     // This program uses external textures and sampler, so it won't compile
     // everywhere.
-    if (context_provider()->ContextCapabilities().egl_image_external)
-      EXPECT_PROGRAM_VALID(
-          renderer()->GetProgram(ProgramKey::VideoStream(precision)));
+    if (context_provider()->ContextCapabilities().egl_image_external) {
+      TestShader(ProgramKey::VideoStream(precision));
+    }
   }
 
   void TestShadersWithPrecisionAndBlend(TexCoordPrecision precision,
                                         BlendMode blend_mode) {
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(ProgramKey::RenderPass(
-        precision, SAMPLER_TYPE_2D, blend_mode, NO_AA, NO_MASK, false, false)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, USE_AA,
-                               NO_MASK, false, false)));
+    TestShader(ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode,
+                                      NO_AA, NO_MASK, false, false));
+    TestShader(ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode,
+                                      USE_AA, NO_MASK, false, false));
   }
 
   void TestShadersWithPrecisionAndSampler(TexCoordPrecision precision,
@@ -165,41 +184,29 @@
       return;
     }
 
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::Texture(precision, sampler, PREMULTIPLIED_ALPHA, false)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::Texture(precision, sampler, PREMULTIPLIED_ALPHA, true)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(ProgramKey::Texture(
-        precision, sampler, NON_PREMULTIPLIED_ALPHA, false)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(ProgramKey::Texture(
-        precision, sampler, NON_PREMULTIPLIED_ALPHA, true)));
-
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE, false)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE, false)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::Tile(precision, sampler, USE_AA, NO_SWIZZLE, false)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::Tile(precision, sampler, USE_AA, DO_SWIZZLE, false)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE, true)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE, true)));
+    TestShader(
+        ProgramKey::Texture(precision, sampler, PREMULTIPLIED_ALPHA, false));
+    TestShader(
+        ProgramKey::Texture(precision, sampler, PREMULTIPLIED_ALPHA, true));
+    TestShader(ProgramKey::Texture(precision, sampler, NON_PREMULTIPLIED_ALPHA,
+                                   false));
+    TestShader(
+        ProgramKey::Texture(precision, sampler, NON_PREMULTIPLIED_ALPHA, true));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE, false));
+    TestShader(ProgramKey::Tile(precision, sampler, USE_AA, NO_SWIZZLE, false));
+    TestShader(ProgramKey::Tile(precision, sampler, USE_AA, DO_SWIZZLE, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE, true));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE, true));
 
     // Iterate over alpha plane, nv12, and color_lut parameters.
     UVTextureMode uv_modes[2] = {UV_TEXTURE_MODE_UV, UV_TEXTURE_MODE_U_V};
     YUVAlphaTextureMode a_modes[2] = {YUV_NO_ALPHA_TEXTURE,
                                       YUV_HAS_ALPHA_TEXTURE};
-    ColorConversionMode c_modes[2] = {COLOR_CONVERSION_MODE_NONE,
-                                      COLOR_CONVERSION_MODE_LUT};
     for (int j = 0; j < 2; j++) {
       for (int k = 0; k < 2; k++) {
-        for (int l = 0; l < 2; l++) {
-          const Program* program = renderer()->GetProgram(ProgramKey::YUVVideo(
-              precision, sampler, a_modes[j], uv_modes[k], c_modes[l]));
-          EXPECT_PROGRAM_VALID(program);
-        }
+        TestShader(
+            ProgramKey::YUVVideo(precision, sampler, a_modes[j], uv_modes[k]));
       }
     }
   }
@@ -214,18 +221,14 @@
       return;
     }
 
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, HAS_MASK,
-                               mask_for_background, false)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA, HAS_MASK,
-                               mask_for_background, true)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, HAS_MASK,
-                               mask_for_background, false)));
-    EXPECT_PROGRAM_VALID(renderer()->GetProgram(
-        ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA, HAS_MASK,
-                               mask_for_background, true)));
+    TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA,
+                                      HAS_MASK, mask_for_background, false));
+    TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, NO_AA,
+                                      HAS_MASK, mask_for_background, true));
+    TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA,
+                                      HAS_MASK, mask_for_background, false));
+    TestShader(ProgramKey::RenderPass(precision, sampler, blend_mode, USE_AA,
+                                      HAS_MASK, mask_for_background, true));
   }
 };
 
@@ -410,7 +413,7 @@
         ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, NO_AA,
                                NO_MASK, false, false));
     EXPECT_PROGRAM_VALID(program);
-    EXPECT_EQ(program, renderer_->program_shadow_);
+    EXPECT_EQ(program, renderer_->current_program_);
   }
 
   void TestRenderPassColorMatrixProgram(TexCoordPrecision precision,
@@ -419,7 +422,7 @@
         ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, NO_AA,
                                NO_MASK, false, true));
     EXPECT_PROGRAM_VALID(program);
-    EXPECT_EQ(program, renderer_->program_shadow_);
+    EXPECT_EQ(program, renderer_->current_program_);
   }
 
   void TestRenderPassMaskProgram(TexCoordPrecision precision,
@@ -429,7 +432,7 @@
         renderer_->GetProgramIfInitialized(ProgramKey::RenderPass(
             precision, sampler, blend_mode, NO_AA, HAS_MASK, false, false));
     EXPECT_PROGRAM_VALID(program);
-    EXPECT_EQ(program, renderer_->program_shadow_);
+    EXPECT_EQ(program, renderer_->current_program_);
   }
 
   void TestRenderPassMaskColorMatrixProgram(TexCoordPrecision precision,
@@ -439,7 +442,7 @@
         renderer_->GetProgramIfInitialized(ProgramKey::RenderPass(
             precision, sampler, blend_mode, NO_AA, HAS_MASK, false, true));
     EXPECT_PROGRAM_VALID(program);
-    EXPECT_EQ(program, renderer_->program_shadow_);
+    EXPECT_EQ(program, renderer_->current_program_);
   }
 
   void TestRenderPassProgramAA(TexCoordPrecision precision,
@@ -448,7 +451,7 @@
         ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, USE_AA,
                                NO_MASK, false, false));
     EXPECT_PROGRAM_VALID(program);
-    EXPECT_EQ(program, renderer_->program_shadow_);
+    EXPECT_EQ(program, renderer_->current_program_);
   }
 
   void TestRenderPassColorMatrixProgramAA(TexCoordPrecision precision,
@@ -457,7 +460,7 @@
         ProgramKey::RenderPass(precision, SAMPLER_TYPE_2D, blend_mode, USE_AA,
                                NO_MASK, false, true));
     EXPECT_PROGRAM_VALID(program);
-    EXPECT_EQ(program, renderer_->program_shadow_);
+    EXPECT_EQ(program, renderer_->current_program_);
   }
 
   void TestRenderPassMaskProgramAA(TexCoordPrecision precision,
@@ -467,7 +470,7 @@
         renderer_->GetProgramIfInitialized(ProgramKey::RenderPass(
             precision, sampler, blend_mode, USE_AA, HAS_MASK, false, false));
     EXPECT_PROGRAM_VALID(program);
-    EXPECT_EQ(program, renderer_->program_shadow_);
+    EXPECT_EQ(program, renderer_->current_program_);
   }
 
   void TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision,
@@ -477,14 +480,14 @@
         renderer_->GetProgramIfInitialized(ProgramKey::RenderPass(
             precision, sampler, blend_mode, USE_AA, HAS_MASK, false, true));
     EXPECT_PROGRAM_VALID(program);
-    EXPECT_EQ(program, renderer_->program_shadow_);
+    EXPECT_EQ(program, renderer_->current_program_);
   }
 
   void TestSolidColorProgramAA() {
     const Program* program =
         renderer_->GetProgramIfInitialized(ProgramKey::SolidColor(USE_AA));
     EXPECT_PROGRAM_VALID(program);
-    EXPECT_EQ(program, renderer_->program_shadow_);
+    EXPECT_EQ(program, renderer_->current_program_);
   }
 
   RendererSettings settings_;
diff --git a/cc/output/program_binding.cc b/cc/output/program_binding.cc
index d80ef68..dc8fe84 100644
--- a/cc/output/program_binding.cc
+++ b/cc/output/program_binding.cc
@@ -32,6 +32,10 @@
          color_conversion_mode_ == other.color_conversion_mode_;
 }
 
+bool ProgramKey::operator!=(const ProgramKey& other) const {
+  return !(*this == other);
+}
+
 // static
 ProgramKey ProgramKey::DebugBorder() {
   ProgramKey result;
@@ -110,8 +114,7 @@
 ProgramKey ProgramKey::YUVVideo(TexCoordPrecision precision,
                                 SamplerType sampler,
                                 YUVAlphaTextureMode yuv_alpha_texture_mode,
-                                UVTextureMode uv_texture_mode,
-                                ColorConversionMode color_conversion_mode) {
+                                UVTextureMode uv_texture_mode) {
   ProgramKey result;
   result.type_ = PROGRAM_TYPE_YUV_VIDEO;
   result.precision_ = precision;
@@ -122,7 +125,6 @@
   result.uv_texture_mode_ = uv_texture_mode;
   DCHECK(uv_texture_mode == UV_TEXTURE_MODE_UV ||
          uv_texture_mode == UV_TEXTURE_MODE_U_V);
-  result.color_conversion_mode_ = color_conversion_mode;
   return result;
 }
 
diff --git a/cc/output/program_binding.h b/cc/output/program_binding.h
index b79a0c29..3b9a382b 100644
--- a/cc/output/program_binding.h
+++ b/cc/output/program_binding.h
@@ -66,6 +66,7 @@
 
 class CC_EXPORT ProgramKey {
  public:
+  ProgramKey();
   ProgramKey(const ProgramKey& other);
   ~ProgramKey();
 
@@ -93,13 +94,16 @@
   static ProgramKey YUVVideo(TexCoordPrecision precision,
                              SamplerType sampler,
                              YUVAlphaTextureMode yuv_alpha_texture_mode,
-                             UVTextureMode uv_texture_mode,
-                             ColorConversionMode color_conversion_mode);
+                             UVTextureMode uv_texture_mode);
 
   bool operator==(const ProgramKey& other) const;
+  bool operator!=(const ProgramKey& other) const;
+
+  void SetColorConversionMode(ColorConversionMode color_conversion_mode) {
+    color_conversion_mode_ = color_conversion_mode;
+  }
 
  private:
-  ProgramKey();
   friend struct ProgramKeyHash;
   friend class Program;
 
@@ -159,6 +163,7 @@
     fragment_shader_.premultiply_alpha_mode_ = key.premultiplied_alpha_;
     fragment_shader_.mask_mode_ = key.mask_mode_;
     fragment_shader_.mask_for_background_ = key.mask_for_background_;
+    fragment_shader_.color_conversion_mode_ = key.color_conversion_mode_;
 
     switch (key.type_) {
       case PROGRAM_TYPE_DEBUG_BORDER:
@@ -382,7 +387,6 @@
     fragment_shader_.has_uniform_alpha_ = true;
     fragment_shader_.yuv_alpha_texture_mode_ = key.yuv_alpha_texture_mode_;
     fragment_shader_.uv_texture_mode_ = key.uv_texture_mode_;
-    fragment_shader_.color_conversion_mode_ = key.color_conversion_mode_;
   }
 
   void InitializeInternal(ContextProvider* context_provider) {
diff --git a/cc/output/renderer_settings.cc b/cc/output/renderer_settings.cc
index 6ba4b48..85b032c 100644
--- a/cc/output/renderer_settings.cc
+++ b/cc/output/renderer_settings.cc
@@ -12,21 +12,7 @@
 namespace cc {
 
 RendererSettings::RendererSettings()
-    : allow_antialiasing(true),
-      force_antialiasing(false),
-      force_blending_with_shaders(false),
-      partial_swap_enabled(false),
-      finish_rendering_on_resize(false),
-      should_clear_root_render_pass(true),
-      disable_display_vsync(false),
-      release_overlay_resources_after_gpu_query(false),
-      gl_composited_texture_quad_border(false),
-      show_overdraw_feedback(false),
-      refresh_rate(60.0),
-      highp_threshold_min(0),
-      texture_id_allocation_chunk_size(64),
-      use_gpu_memory_buffer_resources(false),
-      preferred_tile_format(PlatformColor::BestTextureFormat()) {}
+    : preferred_tile_format(PlatformColor::BestTextureFormat()) {}
 
 RendererSettings::RendererSettings(const RendererSettings& other) = default;
 
@@ -46,6 +32,8 @@
          gl_composited_texture_quad_border ==
              other.gl_composited_texture_quad_border &&
          show_overdraw_feedback == other.show_overdraw_feedback &&
+         enable_color_correct_rendering ==
+             other.enable_color_correct_rendering &&
          refresh_rate == other.refresh_rate &&
          highp_threshold_min == other.highp_threshold_min &&
          texture_id_allocation_chunk_size ==
diff --git a/cc/output/renderer_settings.h b/cc/output/renderer_settings.h
index c3e0d2d..ced1ec0c 100644
--- a/cc/output/renderer_settings.h
+++ b/cc/output/renderer_settings.h
@@ -19,21 +19,22 @@
   RendererSettings(const RendererSettings& other);
   ~RendererSettings();
 
-  bool allow_antialiasing;
-  bool force_antialiasing;
-  bool force_blending_with_shaders;
-  bool partial_swap_enabled;
-  bool finish_rendering_on_resize;
-  bool should_clear_root_render_pass;
-  bool disable_display_vsync;
-  bool release_overlay_resources_after_gpu_query;
-  bool gl_composited_texture_quad_border;
-  bool show_overdraw_feedback;
+  bool allow_antialiasing = true;
+  bool force_antialiasing = false;
+  bool force_blending_with_shaders = false;
+  bool partial_swap_enabled = false;
+  bool finish_rendering_on_resize = false;
+  bool should_clear_root_render_pass = true;
+  bool disable_display_vsync = false;
+  bool release_overlay_resources_after_gpu_query = false;
+  bool gl_composited_texture_quad_border = false;
+  bool show_overdraw_feedback = false;
+  bool enable_color_correct_rendering = false;
 
-  double refresh_rate;
-  int highp_threshold_min;
-  size_t texture_id_allocation_chunk_size;
-  bool use_gpu_memory_buffer_resources;
+  double refresh_rate = 60.0;
+  int highp_threshold_min = 0;
+  size_t texture_id_allocation_chunk_size = 64;
+  bool use_gpu_memory_buffer_resources = false;
   ResourceFormat preferred_tile_format;
   BufferToTextureTargetMap buffer_to_texture_target_map;
 
diff --git a/cc/output/shader.cc b/cc/output/shader.cc
index e64dd05..2972183 100644
--- a/cc/output/shader.cc
+++ b/cc/output/shader.cc
@@ -60,28 +60,32 @@
 static std::string SetFragmentTexCoordPrecision(
     TexCoordPrecision requested_precision,
     std::string shader_string) {
+  std::string prefix;
   switch (requested_precision) {
     case TEX_COORD_PRECISION_HIGH:
       DCHECK_NE(shader_string.find("TexCoordPrecision"), std::string::npos);
-      return "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
-             "  #define TexCoordPrecision highp\n"
-             "#else\n"
-             "  #define TexCoordPrecision mediump\n"
-             "#endif\n" +
-             shader_string;
+      prefix =
+          "#ifdef GL_FRAGMENT_PRECISION_HIGH\n"
+          "  #define TexCoordPrecision highp\n"
+          "#else\n"
+          "  #define TexCoordPrecision mediump\n"
+          "#endif\n";
+      break;
     case TEX_COORD_PRECISION_MEDIUM:
       DCHECK_NE(shader_string.find("TexCoordPrecision"), std::string::npos);
-      return "#define TexCoordPrecision mediump\n" + shader_string;
+      prefix = "#define TexCoordPrecision mediump\n";
+      break;
     case TEX_COORD_PRECISION_NA:
       DCHECK_EQ(shader_string.find("TexCoordPrecision"), std::string::npos);
       DCHECK_EQ(shader_string.find("texture2D"), std::string::npos);
       DCHECK_EQ(shader_string.find("texture2DRect"), std::string::npos);
-      return shader_string;
+      break;
     default:
       NOTREACHED();
       break;
   }
-  return shader_string;
+  std::string lut_prefix = "#define LutLookup texture2D\n";
+  return prefix + lut_prefix + shader_string;
 }
 
 TexCoordPrecision TexCoordPrecisionRequired(GLES2Interface* context,
@@ -884,8 +888,8 @@
     HDR("  // [0.5 .. 31.5] / N (assuming a LUT size of 17^3)");
     HDR("  pos.xy = (pos.xy + vec2(0.5)) / size;");
     HDR("  pos.y = (pos.y + layer) / size;");
-    HDR("  return mix(texture2D(sampler, pos.xy),");
-    HDR("             texture2D(sampler, pos.xy + vec2(0, 1.0 / size)),");
+    HDR("  return mix(LutLookup(sampler, pos.xy),");
+    HDR("             LutLookup(sampler, pos.xy + vec2(0, 1.0 / size)),");
     HDR("             pos.z - layer);");
     HDR("}");
     SRC("texColor.xyz = LUT(lut_texture, texColor.xyz, lut_size).xyz;");
diff --git a/chrome/VERSION b/chrome/VERSION
index c39ca2e7..4e03bd3 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=58
 MINOR=0
-BUILD=3009
+BUILD=3010
 PATCH=0
diff --git a/chrome/browser/extensions/native_bindings_apitest.cc b/chrome/browser/extensions/native_bindings_apitest.cc
index c1d5ed9..6585656 100644
--- a/chrome/browser/extensions/native_bindings_apitest.cc
+++ b/chrome/browser/extensions/native_bindings_apitest.cc
@@ -5,6 +5,7 @@
 #include "base/command_line.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "extensions/common/switches.h"
+#include "net/dns/mock_host_resolver.h"
 
 namespace extensions {
 
@@ -32,6 +33,7 @@
 };
 
 IN_PROC_BROWSER_TEST_F(NativeBindingsApiTest, SimpleEndToEndTest) {
+  host_resolver()->AddRule("*", "127.0.0.1");
   embedded_test_server()->ServeFilesFromDirectory(test_data_dir_);
   ASSERT_TRUE(StartEmbeddedTestServer());
   ASSERT_TRUE(RunExtensionTest("native_bindings/extension")) << message_;
diff --git a/chrome/test/data/extensions/api_test/native_bindings/extension/background.js b/chrome/test/data/extensions/api_test/native_bindings/extension/background.js
index 7300d5c..7cfaa0fc 100644
--- a/chrome/test/data/extensions/api_test/native_bindings/extension/background.js
+++ b/chrome/test/data/extensions/api_test/native_bindings/extension/background.js
@@ -93,7 +93,28 @@
     chrome.test.assertTrue(!!chrome.cast.streaming.udpTransport);
     chrome.test.assertTrue(!!chrome.cast.streaming.udpTransport.setOptions);
     chrome.test.succeed();
-  }
+  },
+  function injectScript() {
+    var url =
+        'http://example.com:' + portNumber + '/native_bindings/simple.html';
+    // Create a tab, and inject code in it to change its title.
+    // chrome.tabs.executeScript relies on external type references
+    // (extensionTypes.InjectDetails), so this exercises that flow as well.
+    chrome.tabs.create({url: url}, function(tab) {
+      chrome.test.assertTrue(!!tab, 'tab');
+      chrome.test.assertEq(new URL(url).host, new URL(tab.url).host);
+      var code = 'document.title = "new title";';
+      chrome.tabs.executeScript(tab.id, {code: code}, function(results) {
+        chrome.test.assertTrue(!!results, 'results');
+        chrome.test.assertEq(1, results.length);
+        chrome.test.assertEq('new title', results[0]);
+        chrome.tabs.get(tab.id, tab => {
+          chrome.test.assertEq('new title', tab.title);
+          chrome.test.succeed();
+        });
+      });
+    });
+  },
 ];
 
 chrome.test.getConfig(config => {
diff --git a/chrome/test/data/extensions/api_test/native_bindings/extension/manifest.json b/chrome/test/data/extensions/api_test/native_bindings/extension/manifest.json
index d189c9ad..da21db2 100644
--- a/chrome/test/data/extensions/api_test/native_bindings/extension/manifest.json
+++ b/chrome/test/data/extensions/api_test/native_bindings/extension/manifest.json
@@ -4,7 +4,7 @@
   "description": "A simple e2e test for native bindings, id is ddchlicdkolnonkihahngkmmmjnjlkkf",
   "manifest_version": 2,
   "version": "0.1",
-  "permissions": ["idle", "tabs", "cast.streaming"],
+  "permissions": ["idle", "tabs", "cast.streaming", "*://example.com:*/*"],
   "background": {
     "persistent": false,
     "page": "background.html"
diff --git a/chrome/test/data/extensions/api_test/native_bindings/simple.html b/chrome/test/data/extensions/api_test/native_bindings/simple.html
new file mode 100644
index 0000000..8fb2f0f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/native_bindings/simple.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+<head>
+  <title>Original Title</title>
+</head>
+<body>
+Content
+</body>
+</html>
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 7b818ab..ae598c0 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -1105,10 +1105,7 @@
 
 void RenderWidgetCompositor::DidSubmitCompositorFrame() {}
 
-void RenderWidgetCompositor::DidLoseCompositorFrameSink() {
-  // The CompositorFrameSink is not lost in layout tests (single thread mode).
-  NOTREACHED();
-}
+void RenderWidgetCompositor::DidLoseCompositorFrameSink() {}
 
 void RenderWidgetCompositor::SetFrameSinkId(
     const cc::FrameSinkId& frame_sink_id) {
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index a7df614..34cfe50 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -34,6 +34,8 @@
     "api_request_handler.h",
     "api_signature.cc",
     "api_signature.h",
+    "api_type_reference_map.cc",
+    "api_type_reference_map.h",
     "app_window_custom_bindings.cc",
     "app_window_custom_bindings.h",
     "argument_spec.cc",
diff --git a/extensions/renderer/api_binding.cc b/extensions/renderer/api_binding.cc
index 6056346..1ffa3ba 100644
--- a/extensions/renderer/api_binding.cc
+++ b/extensions/renderer/api_binding.cc
@@ -17,6 +17,7 @@
 #include "extensions/renderer/api_event_handler.h"
 #include "extensions/renderer/api_request_handler.h"
 #include "extensions/renderer/api_signature.h"
+#include "extensions/renderer/api_type_reference_map.h"
 #include "extensions/renderer/v8_helpers.h"
 #include "gin/arguments.h"
 #include "gin/per_context_data.h"
@@ -99,7 +100,7 @@
                        const base::ListValue* event_definitions,
                        const SendRequestMethod& callback,
                        std::unique_ptr<APIBindingHooks> binding_hooks,
-                       ArgumentSpec::RefMap* type_refs,
+                       APITypeReferenceMap* type_refs,
                        APIRequestHandler* request_handler)
     : api_name_(api_name),
       method_callback_(callback),
@@ -129,9 +130,6 @@
       CHECK(type->GetAsDictionary(&type_dict));
       std::string id;
       CHECK(type_dict->GetString("id", &id));
-      DCHECK(type_refs->find(id) == type_refs->end());
-      // TODO(devlin): refs are sometimes preceeded by the API namespace; we
-      // might need to take that into account.
       auto argument_spec = base::MakeUnique<ArgumentSpec>(*type_dict);
       const std::set<std::string>& enum_values = argument_spec->enum_values();
       if (!enum_values.empty()) {
@@ -147,7 +145,7 @@
               std::make_pair(enum_value, GetJSEnumEntryName(enum_value)));
         }
       }
-      (*type_refs)[id] = std::move(argument_spec);
+      type_refs->AddSpec(id, std::move(argument_spec));
     }
   }
 
diff --git a/extensions/renderer/api_binding.h b/extensions/renderer/api_binding.h
index 343c59de..8d9468b 100644
--- a/extensions/renderer/api_binding.h
+++ b/extensions/renderer/api_binding.h
@@ -29,6 +29,7 @@
 class APIEventHandler;
 class APIRequestHandler;
 class APISignature;
+class APITypeReferenceMap;
 
 // A class that vends v8::Objects for extension APIs. These APIs have function
 // interceptors for all exposed methods, which call back into the APIBinding.
@@ -66,7 +67,7 @@
   // The callback type for handling an API call.
   using HandlerCallback = base::Callback<void(gin::Arguments*)>;
 
-  // The ArgumentSpec::RefMap is required to outlive this object.
+  // The APITypeReferenceMap is required to outlive this object.
   // |function_definitions|, |type_definitions| and |event_definitions|
   // may be null if the API does not specify any of that category.
   APIBinding(const std::string& name,
@@ -75,7 +76,7 @@
              const base::ListValue* event_definitions,
              const SendRequestMethod& callback,
              std::unique_ptr<APIBindingHooks> binding_hooks,
-             ArgumentSpec::RefMap* type_refs,
+             APITypeReferenceMap* type_refs,
              APIRequestHandler* request_handler);
   ~APIBinding();
 
@@ -120,7 +121,7 @@
   std::unique_ptr<APIBindingHooks> binding_hooks_;
 
   // The reference map for all known types; required to outlive this object.
-  const ArgumentSpec::RefMap* type_refs_;
+  const APITypeReferenceMap* type_refs_;
 
   // The associated request handler, shared between this and other bindings.
   // Required to outlive this object.
diff --git a/extensions/renderer/api_binding_hooks.cc b/extensions/renderer/api_binding_hooks.cc
index 78feb4b..2d07276 100644
--- a/extensions/renderer/api_binding_hooks.cc
+++ b/extensions/renderer/api_binding_hooks.cc
@@ -223,7 +223,7 @@
     v8::Local<v8::Context> context,
     const APISignature* signature,
     std::vector<v8::Local<v8::Value>>* arguments,
-    const ArgumentSpec::RefMap& type_refs) {
+    const APITypeReferenceMap& type_refs) {
   // Easy case: a native custom hook.
   auto request_hooks_iter = request_hooks_.find(method_name);
   if (request_hooks_iter != request_hooks_.end()) {
diff --git a/extensions/renderer/api_binding_hooks.h b/extensions/renderer/api_binding_hooks.h
index d4d939b..4896771a 100644
--- a/extensions/renderer/api_binding_hooks.h
+++ b/extensions/renderer/api_binding_hooks.h
@@ -61,7 +61,7 @@
       base::Callback<RequestResult(const APISignature*,
                                    v8::Local<v8::Context> context,
                                    std::vector<v8::Local<v8::Value>>*,
-                                   const ArgumentSpec::RefMap&)>;
+                                   const APITypeReferenceMap&)>;
 
   explicit APIBindingHooks(const binding::RunJSFunctionSync& run_js);
   ~APIBindingHooks();
@@ -86,7 +86,7 @@
                          v8::Local<v8::Context> context,
                          const APISignature* signature,
                          std::vector<v8::Local<v8::Value>>* arguments,
-                         const ArgumentSpec::RefMap& type_refs);
+                         const APITypeReferenceMap& type_refs);
 
   // Returns a JS interface that can be used to register hooks.
   v8::Local<v8::Object> GetJSHookInterface(const std::string& api_name,
diff --git a/extensions/renderer/api_binding_unittest.cc b/extensions/renderer/api_binding_unittest.cc
index 389d6ec..e56e93d9 100644
--- a/extensions/renderer/api_binding_unittest.cc
+++ b/extensions/renderer/api_binding_unittest.cc
@@ -13,6 +13,7 @@
 #include "extensions/renderer/api_binding_test_util.h"
 #include "extensions/renderer/api_event_handler.h"
 #include "extensions/renderer/api_request_handler.h"
+#include "extensions/renderer/api_type_reference_map.h"
 #include "gin/arguments.h"
 #include "gin/converter.h"
 #include "gin/public/context_holder.h"
@@ -131,7 +132,8 @@
   }
 
  protected:
-  APIBindingUnittest() {}
+  APIBindingUnittest()
+      : type_refs_(APITypeReferenceMap::InitializeTypeCallback()) {}
   void SetUp() override {
     APIBindingTest::SetUp();
     request_handler_ = base::MakeUnique<APIRequestHandler>(
@@ -214,7 +216,7 @@
   APIBinding* binding() { return binding_.get(); }
   APIEventHandler* event_handler() { return event_handler_.get(); }
   APIRequestHandler* request_handler() { return request_handler_.get(); }
-  const ArgumentSpec::RefMap& type_refs() const { return type_refs_; }
+  const APITypeReferenceMap& type_refs() const { return type_refs_; }
 
  private:
   void RunTest(v8::Local<v8::Context> context,
@@ -229,7 +231,7 @@
   std::unique_ptr<APIBinding> binding_;
   std::unique_ptr<APIEventHandler> event_handler_;
   std::unique_ptr<APIRequestHandler> request_handler_;
-  ArgumentSpec::RefMap type_refs_;
+  APITypeReferenceMap type_refs_;
 
   std::unique_ptr<base::ListValue> binding_functions_;
   std::unique_ptr<base::ListValue> binding_events_;
@@ -431,8 +433,8 @@
   SetTypes(kTypes);
   InitializeBinding();
   EXPECT_EQ(2u, type_refs().size());
-  EXPECT_TRUE(base::ContainsKey(type_refs(), "refObj"));
-  EXPECT_TRUE(base::ContainsKey(type_refs(), "refEnum"));
+  EXPECT_TRUE(type_refs().GetSpec("refObj"));
+  EXPECT_TRUE(type_refs().GetSpec("refEnum"));
 
   v8::HandleScope handle_scope(isolate());
   v8::Local<v8::Context> context = ContextLocal();
@@ -585,7 +587,7 @@
   auto hook = [](bool* did_call, const APISignature* signature,
                  v8::Local<v8::Context> context,
                  std::vector<v8::Local<v8::Value>>* arguments,
-                 const ArgumentSpec::RefMap& ref_map) {
+                 const APITypeReferenceMap& ref_map) {
     *did_call = true;
     APIBindingHooks::RequestResult result(
         APIBindingHooks::RequestResult::HANDLED);
@@ -887,7 +889,7 @@
   auto hook = [](bool* did_call, const APISignature* signature,
                  v8::Local<v8::Context> context,
                  std::vector<v8::Local<v8::Value>>* arguments,
-                 const ArgumentSpec::RefMap& ref_map) {
+                 const APITypeReferenceMap& ref_map) {
     APIBindingHooks::RequestResult result(
         APIBindingHooks::RequestResult::HANDLED);
     if (arguments->size() != 1u) {  // ASSERT* messes with the return type.
diff --git a/extensions/renderer/api_bindings_system.cc b/extensions/renderer/api_bindings_system.cc
index 040d620..868c465d 100644
--- a/extensions/renderer/api_bindings_system.cc
+++ b/extensions/renderer/api_bindings_system.cc
@@ -17,7 +17,9 @@
     const GetAPISchemaMethod& get_api_schema,
     const APIBinding::SendRequestMethod& send_request,
     const APIEventHandler::EventListenersChangedMethod& event_listeners_changed)
-    : request_handler_(call_js),
+    : type_reference_map_(base::Bind(&APIBindingsSystem::InitializeType,
+                                     base::Unretained(this))),
+      request_handler_(call_js),
       event_handler_(call_js, event_listeners_changed),
       call_js_(call_js),
       call_js_sync_(call_js_sync),
@@ -70,6 +72,25 @@
       send_request_, std::move(hooks), &type_reference_map_, &request_handler_);
 }
 
+void APIBindingsSystem::InitializeType(const std::string& type_name) {
+  // In order to initialize the type, we just initialize the full binding. This
+  // seems like a lot of work, but in practice, trying to extract out only the
+  // types from the schema, and then update the reference map based on that, is
+  // close enough to the same cost. Additionally, this happens lazily on API
+  // use, and relatively few APIs specify types from another API. Finally, this
+  // will also go away if/when we generate all these specifications.
+  std::string::size_type dot = type_name.rfind('.');
+  // The type name should be fully qualified (include the API name).
+  DCHECK_NE(std::string::npos, dot);
+  DCHECK_LT(dot, type_name.size() - 1);
+  std::string api_name = type_name.substr(0, dot);
+  // If we've already instantiated the binding, the type should have been in
+  // there.
+  DCHECK(api_bindings_.find(api_name) == api_bindings_.end());
+
+  api_bindings_[api_name] = CreateNewAPIBinding(api_name);
+}
+
 void APIBindingsSystem::CompleteRequest(int request_id,
                                         const base::ListValue& response) {
   request_handler_.CompleteRequest(request_id, response);
diff --git a/extensions/renderer/api_bindings_system.h b/extensions/renderer/api_bindings_system.h
index 52cfdf89..ae7442d5 100644
--- a/extensions/renderer/api_bindings_system.h
+++ b/extensions/renderer/api_bindings_system.h
@@ -15,7 +15,7 @@
 #include "extensions/renderer/api_binding_types.h"
 #include "extensions/renderer/api_event_handler.h"
 #include "extensions/renderer/api_request_handler.h"
-#include "extensions/renderer/argument_spec.h"
+#include "extensions/renderer/api_type_reference_map.h"
 
 namespace base {
 class DictionaryValue;
@@ -72,8 +72,12 @@
   // Creates a new APIBinding for the given |api_name|.
   std::unique_ptr<APIBinding> CreateNewAPIBinding(const std::string& api_name);
 
+  // Callback for the APITypeReferenceMap in order to initialize an unknown
+  // type.
+  void InitializeType(const std::string& name);
+
   // The map of cached API reference types.
-  ArgumentSpec::RefMap type_reference_map_;
+  APITypeReferenceMap type_reference_map_;
 
   // The request handler associated with the system.
   APIRequestHandler request_handler_;
diff --git a/extensions/renderer/api_bindings_system_unittest.cc b/extensions/renderer/api_bindings_system_unittest.cc
index 9c4fa7b..e5df38d 100644
--- a/extensions/renderer/api_bindings_system_unittest.cc
+++ b/extensions/renderer/api_bindings_system_unittest.cc
@@ -29,7 +29,7 @@
 const char kAlphaAPISpec[] =
     "{"
     "  'types': [{"
-    "    'id': 'objRef',"
+    "    'id': 'alpha.objRef',"
     "    'type': 'object',"
     "    'properties': {"
     "      'prop1': {'type': 'string'},"
@@ -49,7 +49,7 @@
     "    'name': 'functionWithRefAndCallback',"
     "    'parameters': [{"
     "      'name': 'ref',"
-    "      '$ref': 'objRef'"
+    "      '$ref': 'alpha.objRef'"
     "    }, {"
     "      'name': 'callback',"
     "      'type': 'function'"
@@ -70,6 +70,15 @@
     "  }]"
     "}";
 
+const char kGammaAPIName[] = "gamma";
+const char kGammaAPISpec[] =
+    "{"
+    "  'functions': [{"
+    "    'name': 'functionWithExternalRef',"
+    "    'parameters': [{ 'name': 'someRef', '$ref': 'alpha.objRef' }]"
+    "  }]"
+    "}";
+
 bool AllowAllAPIs(const std::string& name) {
   return true;
 }
@@ -197,7 +206,9 @@
       const char* name;
       const char* spec;
     } api_data[] = {
-        {kAlphaAPIName, kAlphaAPISpec}, {kBetaAPIName, kBetaAPISpec},
+        {kAlphaAPIName, kAlphaAPISpec},
+        {kBetaAPIName, kBetaAPISpec},
+        {kGammaAPIName, kGammaAPISpec},
     };
     for (const auto& api : api_data) {
       std::unique_ptr<base::DictionaryValue> api_schema =
@@ -301,10 +312,10 @@
   v8::Local<v8::Context> context = ContextLocal();
 
   bool did_call = false;
-  auto hook = [](bool* did_call,
-                 const APISignature* signature, v8::Local<v8::Context> context,
+  auto hook = [](bool* did_call, const APISignature* signature,
+                 v8::Local<v8::Context> context,
                  std::vector<v8::Local<v8::Value>>* arguments,
-                 const ArgumentSpec::RefMap& type_refs) {
+                 const APITypeReferenceMap& type_refs) {
     *did_call = true;
     APIBindingHooks::RequestResult result(
         APIBindingHooks::RequestResult::HANDLED);
@@ -405,6 +416,27 @@
   }
 }
 
+// Test that references to other API's types works.
+TEST_F(APIBindingsSystemTest, CrossAPIReferences) {
+  v8::HandleScope handle_scope(isolate());
+  v8::Local<v8::Context> context = ContextLocal();
+
+  // Instantiate gamma API. Note: It's important that we haven't instantiated
+  // alpha API yet, since this tests that we can lazily populate the type
+  // information.
+  v8::Local<v8::Object> gamma_api = bindings_system()->CreateAPIInstance(
+      kGammaAPIName, context, isolate(), base::Bind(&AllowAllAPIs), nullptr);
+  ASSERT_FALSE(gamma_api.IsEmpty());
+
+  {
+    // Test a simple call -> response.
+    const char kTestCall[] = "obj.functionWithExternalRef({prop1: 'foo'});";
+    CallFunctionOnObject(context, gamma_api, kTestCall);
+    ValidateLastRequest("gamma.functionWithExternalRef", "[{'prop1':'foo'}]");
+    reset_last_request();
+  }
+}
+
 // An implementation using real API schemas.
 class APIBindingsSystemTestWithRealAPI : public APIBindingsSystemTestBase {
  protected:
diff --git a/extensions/renderer/api_signature.cc b/extensions/renderer/api_signature.cc
index 283f407..261775e5 100644
--- a/extensions/renderer/api_signature.cc
+++ b/extensions/renderer/api_signature.cc
@@ -8,6 +8,7 @@
 
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
+#include "extensions/renderer/argument_spec.h"
 #include "gin/arguments.h"
 
 namespace extensions {
@@ -22,13 +23,13 @@
   ArgumentParser(v8::Local<v8::Context> context,
                  const std::vector<std::unique_ptr<ArgumentSpec>>& signature,
                  const std::vector<v8::Local<v8::Value>>& arguments,
-                 const ArgumentSpec::RefMap& type_refs,
+                 const APITypeReferenceMap& type_refs,
                  std::string* error)
-    : context_(context),
-      signature_(signature),
-      arguments_(arguments),
-      type_refs_(type_refs),
-      error_(error) {}
+      : context_(context),
+        signature_(signature),
+        arguments_(arguments),
+        type_refs_(type_refs),
+        error_(error) {}
 
   // Tries to parse the arguments against the expected signature.
   bool ParseArguments();
@@ -68,7 +69,7 @@
   v8::Local<v8::Context> context_;
   const std::vector<std::unique_ptr<ArgumentSpec>>& signature_;
   const std::vector<v8::Local<v8::Value>>& arguments_;
-  const ArgumentSpec::RefMap& type_refs_;
+  const APITypeReferenceMap& type_refs_;
   std::string* error_;
   size_t current_index_ = 0;
 
@@ -80,7 +81,7 @@
   V8ArgumentParser(v8::Local<v8::Context> context,
                    const std::vector<std::unique_ptr<ArgumentSpec>>& signature,
                    const std::vector<v8::Local<v8::Value>>& arguments,
-                   const ArgumentSpec::RefMap& type_refs,
+                   const APITypeReferenceMap& type_refs,
                    std::string* error,
                    std::vector<v8::Local<v8::Value>>* values)
       : ArgumentParser(context, signature, arguments, type_refs, error),
@@ -107,7 +108,7 @@
       v8::Local<v8::Context> context,
       const std::vector<std::unique_ptr<ArgumentSpec>>& signature,
       const std::vector<v8::Local<v8::Value>>& arguments,
-      const ArgumentSpec::RefMap& type_refs,
+      const APITypeReferenceMap& type_refs,
       std::string* error,
       base::ListValue* list_value)
       : ArgumentParser(context, signature, arguments, type_refs, error),
@@ -230,7 +231,7 @@
 bool APISignature::ParseArgumentsToV8(
     v8::Local<v8::Context> context,
     const std::vector<v8::Local<v8::Value>>& arguments,
-    const ArgumentSpec::RefMap& type_refs,
+    const APITypeReferenceMap& type_refs,
     std::vector<v8::Local<v8::Value>>* v8_out,
     std::string* error) const {
   DCHECK(v8_out);
@@ -246,7 +247,7 @@
 bool APISignature::ParseArgumentsToJSON(
     v8::Local<v8::Context> context,
     const std::vector<v8::Local<v8::Value>>& arguments,
-    const ArgumentSpec::RefMap& type_refs,
+    const APITypeReferenceMap& type_refs,
     std::unique_ptr<base::ListValue>* json_out,
     v8::Local<v8::Function>* callback_out,
     std::string* error) const {
diff --git a/extensions/renderer/api_signature.h b/extensions/renderer/api_signature.h
index 42d25df7..abc91e02 100644
--- a/extensions/renderer/api_signature.h
+++ b/extensions/renderer/api_signature.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "extensions/renderer/argument_spec.h"
 #include "v8/include/v8.h"
 
 namespace base {
@@ -18,6 +17,8 @@
 }
 
 namespace extensions {
+class APITypeReferenceMap;
+class ArgumentSpec;
 
 // A representation of the expected signature for an API method, along with the
 // ability to match provided arguments and convert them to base::Values.
@@ -33,7 +34,7 @@
   // Returns true if the arguments were successfully parsed and converted.
   bool ParseArgumentsToV8(v8::Local<v8::Context> context,
                           const std::vector<v8::Local<v8::Value>>& arguments,
-                          const ArgumentSpec::RefMap& type_refs,
+                          const APITypeReferenceMap& type_refs,
                           std::vector<v8::Local<v8::Value>>* args_out,
                           std::string* error) const;
 
@@ -43,7 +44,7 @@
   // callback values, respectively. On failure, returns false populates |error|.
   bool ParseArgumentsToJSON(v8::Local<v8::Context> context,
                             const std::vector<v8::Local<v8::Value>>& arguments,
-                            const ArgumentSpec::RefMap& type_refs,
+                            const APITypeReferenceMap& type_refs,
                             std::unique_ptr<base::ListValue>* args_out,
                             v8::Local<v8::Function>* callback_out,
                             std::string* error) const;
diff --git a/extensions/renderer/api_type_reference_map.cc b/extensions/renderer/api_type_reference_map.cc
new file mode 100644
index 0000000..d700e34bc
--- /dev/null
+++ b/extensions/renderer/api_type_reference_map.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 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 "extensions/renderer/api_type_reference_map.h"
+
+#include "extensions/renderer/argument_spec.h"
+
+namespace extensions {
+
+APITypeReferenceMap::APITypeReferenceMap(
+    const InitializeTypeCallback& initialize_type)
+    : initialize_type_(initialize_type) {}
+APITypeReferenceMap::~APITypeReferenceMap() = default;
+
+void APITypeReferenceMap::AddSpec(const std::string& name,
+                                  std::unique_ptr<ArgumentSpec> spec) {
+  DCHECK(type_refs_.find(name) == type_refs_.end());
+  type_refs_[name] = std::move(spec);
+}
+
+const ArgumentSpec* APITypeReferenceMap::GetSpec(
+    const std::string& name) const {
+  auto iter = type_refs_.find(name);
+  if (iter == type_refs_.end()) {
+    initialize_type_.Run(name);
+    iter = type_refs_.find(name);
+  }
+  return iter == type_refs_.end() ? nullptr : iter->second.get();
+}
+
+}  // namespace extensions
diff --git a/extensions/renderer/api_type_reference_map.h b/extensions/renderer/api_type_reference_map.h
new file mode 100644
index 0000000..cf572bd1
--- /dev/null
+++ b/extensions/renderer/api_type_reference_map.h
@@ -0,0 +1,48 @@
+// Copyright 2017 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 EXTENSIONS_RENDERER_API_TYPE_REFERENCE_MAP_H_
+#define EXTENSIONS_RENDERER_API_TYPE_REFERENCE_MAP_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+
+namespace extensions {
+class ArgumentSpec;
+
+// A map from type name -> ArgumentSpec for API type definitions. This is used
+// when an argument is declared to be a reference to a type defined elsewhere.
+class APITypeReferenceMap {
+ public:
+  // A callback used to initialize an unknown type, so that these can be
+  // created lazily.
+  using InitializeTypeCallback = base::Callback<void(const std::string& name)>;
+
+  explicit APITypeReferenceMap(const InitializeTypeCallback& initialize_type);
+  ~APITypeReferenceMap();
+
+  // Adds the |spec| to the map under the given |name|.
+  void AddSpec(const std::string& name, std::unique_ptr<ArgumentSpec> spec);
+
+  // Returns the spec for the given |name|.
+  const ArgumentSpec* GetSpec(const std::string& name) const;
+
+  bool empty() const { return type_refs_.empty(); }
+  size_t size() const { return type_refs_.size(); }
+
+ private:
+  InitializeTypeCallback initialize_type_;
+
+  std::map<std::string, std::unique_ptr<ArgumentSpec>> type_refs_;
+
+  DISALLOW_COPY_AND_ASSIGN(APITypeReferenceMap);
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_RENDERER_API_TYPE_REFERENCE_MAP_H_
diff --git a/extensions/renderer/argument_spec.cc b/extensions/renderer/argument_spec.cc
index b72e3c0..cd8da0f 100644
--- a/extensions/renderer/argument_spec.cc
+++ b/extensions/renderer/argument_spec.cc
@@ -7,6 +7,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "content/public/child/v8_value_converter.h"
+#include "extensions/renderer/api_type_reference_map.h"
 #include "gin/converter.h"
 #include "gin/dictionary.h"
 
@@ -126,7 +127,7 @@
 
 bool ArgumentSpec::ParseArgument(v8::Local<v8::Context> context,
                                  v8::Local<v8::Value> value,
-                                 const RefMap& refs,
+                                 const APITypeReferenceMap& refs,
                                  std::unique_ptr<base::Value>* out_value,
                                  std::string* error) const {
   if (type_ == ArgumentType::FUNCTION) {
@@ -137,9 +138,9 @@
 
   if (type_ == ArgumentType::REF) {
     DCHECK(ref_);
-    auto iter = refs.find(ref_.value());
-    DCHECK(iter != refs.end()) << ref_.value();
-    return iter->second->ParseArgument(context, value, refs, out_value, error);
+    const ArgumentSpec* reference = refs.GetSpec(ref_.value());
+    DCHECK(reference) << ref_.value();
+    return reference->ParseArgument(context, value, refs, out_value, error);
   }
 
   if (type_ == ArgumentType::CHOICES) {
@@ -244,7 +245,7 @@
 bool ArgumentSpec::ParseArgumentToObject(
     v8::Local<v8::Context> context,
     v8::Local<v8::Object> object,
-    const RefMap& refs,
+    const APITypeReferenceMap& refs,
     std::unique_ptr<base::Value>* out_value,
     std::string* error) const {
   DCHECK_EQ(ArgumentType::OBJECT, type_);
@@ -288,7 +289,7 @@
 
 bool ArgumentSpec::ParseArgumentToArray(v8::Local<v8::Context> context,
                                         v8::Local<v8::Array> value,
-                                        const RefMap& refs,
+                                        const APITypeReferenceMap& refs,
                                         std::unique_ptr<base::Value>* out_value,
                                         std::string* error) const {
   DCHECK_EQ(ArgumentType::LIST, type_);
diff --git a/extensions/renderer/argument_spec.h b/extensions/renderer/argument_spec.h
index 8145d01..8c30d384 100644
--- a/extensions/renderer/argument_spec.h
+++ b/extensions/renderer/argument_spec.h
@@ -21,6 +21,7 @@
 }
 
 namespace extensions {
+class APITypeReferenceMap;
 
 enum class ArgumentType {
   INTEGER,
@@ -39,10 +40,6 @@
 // A description of a given Argument to an Extension.
 class ArgumentSpec {
  public:
-  // A map from name -> definition for type definitions. This is used when an
-  // argument is declared to be a reference to a type defined elsewhere.
-  using RefMap = std::map<std::string, std::unique_ptr<ArgumentSpec>>;
-
   // Reads the description from |value| and sets associated fields.
   // TODO(devlin): We should strongly think about generating these instead of
   // populating them at runtime.
@@ -54,7 +51,7 @@
   // |out_value|. Otherwise, no conversion is performed.
   bool ParseArgument(v8::Local<v8::Context> context,
                      v8::Local<v8::Value> value,
-                     const RefMap& refs,
+                     const APITypeReferenceMap& refs,
                      std::unique_ptr<base::Value>* out_value,
                      std::string* error) const;
 
@@ -78,12 +75,12 @@
                                   std::string* error) const;
   bool ParseArgumentToObject(v8::Local<v8::Context> context,
                              v8::Local<v8::Object> object,
-                             const RefMap& refs,
+                             const APITypeReferenceMap& refs,
                              std::unique_ptr<base::Value>* out_value,
                              std::string* error) const;
   bool ParseArgumentToArray(v8::Local<v8::Context> context,
                             v8::Local<v8::Array> value,
-                            const RefMap& refs,
+                            const APITypeReferenceMap& refs,
                             std::unique_ptr<base::Value>* out_value,
                             std::string* error) const;
   bool ParseArgumentToAny(v8::Local<v8::Context> context,
diff --git a/extensions/renderer/argument_spec_unittest.cc b/extensions/renderer/argument_spec_unittest.cc
index da2ac4e4..c6afd26 100644
--- a/extensions/renderer/argument_spec_unittest.cc
+++ b/extensions/renderer/argument_spec_unittest.cc
@@ -5,6 +5,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
 #include "extensions/renderer/api_binding_test_util.h"
+#include "extensions/renderer/api_type_reference_map.h"
 #include "extensions/renderer/argument_spec.h"
 #include "gin/converter.h"
 #include "gin/public/isolate_holder.h"
@@ -16,7 +17,8 @@
 
 class ArgumentSpecUnitTest : public gin::V8Test {
  protected:
-  ArgumentSpecUnitTest() {}
+  ArgumentSpecUnitTest()
+      : type_refs_(APITypeReferenceMap::InitializeTypeCallback()) {}
   ~ArgumentSpecUnitTest() override {}
   void ExpectSuccess(const ArgumentSpec& spec,
                      const std::string& script_source,
@@ -59,7 +61,7 @@
   }
 
   void AddTypeRef(const std::string& id, std::unique_ptr<ArgumentSpec> spec) {
-    type_refs_[id] = std::move(spec);
+    type_refs_.AddSpec(id, std::move(spec));
   }
 
  private:
@@ -73,7 +75,7 @@
                const base::Value* expected_value,
                const std::string& expected_thrown_message);
 
-  ArgumentSpec::RefMap type_refs_;
+  APITypeReferenceMap type_refs_;
 
   DISALLOW_COPY_AND_ASSIGN(ArgumentSpecUnitTest);
 };
diff --git a/ios/chrome/browser/crash_report/crash_restore_helper.mm b/ios/chrome/browser/crash_report/crash_restore_helper.mm
index 6a66ab7..29a14209 100644
--- a/ios/chrome/browser/crash_report/crash_restore_helper.mm
+++ b/ios/chrome/browser/crash_report/crash_restore_helper.mm
@@ -296,7 +296,7 @@
   tabRestoreService->LoadTabsFromLastSession();
 
   web::WebState::CreateParams params(_browserState);
-  for (CRWNavigationManagerStorage* session in sessions) {
+  for (CRWSessionStorage* session in sessions) {
     std::unique_ptr<web::WebState> webState =
         web::WebState::Create(params, session);
     // Add all tabs at the 0 position as the position is relative to an old
diff --git a/ios/chrome/browser/sessions/session_service.mm b/ios/chrome/browser/sessions/session_service.mm
index 4112e5ac..f7a4e8a 100644
--- a/ios/chrome/browser/sessions/session_service.mm
+++ b/ios/chrome/browser/sessions/session_service.mm
@@ -21,7 +21,7 @@
 #import "ios/chrome/browser/sessions/session_window.h"
 #import "ios/web/navigation/crw_session_certificate_policy_manager.h"
 #import "ios/web/public/crw_navigation_item_storage.h"
-#import "ios/web/public/crw_navigation_manager_storage.h"
+#import "ios/web/public/crw_session_storage.h"
 #include "ios/web/public/web_thread.h"
 
 // When C++ exceptions are disabled, the C++ library defines |try| and
@@ -211,9 +211,9 @@
   // class on disk.
   [SessionWindowUnarchiver setClass:[CRWSessionCertificatePolicyManager class]
                        forClassName:@"SessionCertificatePolicyManager"];
-  [SessionWindowUnarchiver setClass:[CRWNavigationManagerStorage class]
+  [SessionWindowUnarchiver setClass:[CRWSessionStorage class]
                        forClassName:@"SessionController"];
-  [SessionWindowUnarchiver setClass:[CRWNavigationManagerStorage class]
+  [SessionWindowUnarchiver setClass:[CRWSessionStorage class]
                        forClassName:@"CRWSessionController"];
   [SessionWindowUnarchiver setClass:[CRWNavigationItemStorage class]
                        forClassName:@"SessionEntry"];
diff --git a/ios/chrome/browser/sessions/session_window.h b/ios/chrome/browser/sessions/session_window.h
index 0e4615d..bfeddf1 100644
--- a/ios/chrome/browser/sessions/session_window.h
+++ b/ios/chrome/browser/sessions/session_window.h
@@ -11,7 +11,7 @@
 
 #import "ios/web/web_state/web_state_impl.h"
 
-@class CRWNavigationManagerStorage;
+@class CRWSessionStorage;
 
 // Encapsulates everything required to save a session "window". For iOS, there
 // will only be one window at a time.
@@ -21,10 +21,10 @@
 // initialized "empty" (without any sessions) and sessions are added one at a
 // time.  For example:
 //  SessionWindowIOS* window = [[SessionWindow alloc] init];
-//  [window addSerializedSession:session_storage];
+//  [window addSerializedSessionStorage:session_storage];
 //  ...
 //  [window setSelectedIndex:mySelectedIndex];
-- (void)addSerializedSession:(CRWNavigationManagerStorage*)session;
+- (void)addSerializedSessionStorage:(CRWSessionStorage*)session;
 
 // Clears all added sessions.
 - (void)clearSessions;
diff --git a/ios/chrome/browser/sessions/session_window.mm b/ios/chrome/browser/sessions/session_window.mm
index 7334719..001b301 100644
--- a/ios/chrome/browser/sessions/session_window.mm
+++ b/ios/chrome/browser/sessions/session_window.mm
@@ -61,7 +61,7 @@
 
 #pragma mark - Public
 
-- (void)addSerializedSession:(CRWNavigationManagerStorage*)session {
+- (void)addSerializedSessionStorage:(CRWSessionStorage*)session {
   [_sessions addObject:session];
   // Set the selected index to 0 (this session) if this is the first session
   // added.
diff --git a/ios/chrome/browser/sessions/session_window_unittest.mm b/ios/chrome/browser/sessions/session_window_unittest.mm
index eb00a9d..8f9d4fd 100644
--- a/ios/chrome/browser/sessions/session_window_unittest.mm
+++ b/ios/chrome/browser/sessions/session_window_unittest.mm
@@ -14,7 +14,7 @@
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/sessions/session_service.h"
 #import "ios/web/navigation/crw_session_controller.h"
-#import "ios/web/public/crw_navigation_manager_storage.h"
+#import "ios/web/public/crw_session_storage.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -66,10 +66,8 @@
   std::unique_ptr<WebStateImpl> webState2(CreateWebState(@"window2", nil, NO));
   base::scoped_nsobject<SessionWindowIOS> sessionWindow(
       [[SessionWindowIOS alloc] init]);
-  [sessionWindow
-      addSerializedSession:webState1->BuildSerializedNavigationManager()];
-  [sessionWindow
-      addSerializedSession:webState2->BuildSerializedNavigationManager()];
+  [sessionWindow addSerializedSessionStorage:webState1->BuildSessionStorage()];
+  [sessionWindow addSerializedSessionStorage:webState2->BuildSessionStorage()];
   [sessionWindow setSelectedIndex:1];
 
   EXPECT_TRUE(sessionWindow.get() != nil);
@@ -91,10 +89,8 @@
   std::unique_ptr<WebStateImpl> webState2(CreateWebState(windowName2, nil, NO));
   NSString* openerId2 =
       webState2->GetNavigationManagerImpl().GetSessionController().openerId;
-  [sessionWindow
-      addSerializedSession:webState1->BuildSerializedNavigationManager()];
-  [sessionWindow
-      addSerializedSession:webState2->BuildSerializedNavigationManager()];
+  [sessionWindow addSerializedSessionStorage:webState1->BuildSessionStorage()];
+  [sessionWindow addSerializedSessionStorage:webState2->BuildSessionStorage()];
 
   [sessionWindow setSelectedIndex:1];
 
@@ -109,12 +105,12 @@
   EXPECT_EQ(unarchivedObj.selectedIndex, sessionWindow.get().selectedIndex);
   NSArray* sessions = unarchivedObj.sessions;
   ASSERT_EQ(2U, sessions.count);
-  CRWNavigationManagerStorage* unarchivedSession1 = sessions[0];
+  CRWSessionStorage* unarchivedSession1 = sessions[0];
   EXPECT_NSEQ(windowName1, unarchivedSession1.windowName);
   EXPECT_NSEQ(openerId1, unarchivedSession1.openerID);
   EXPECT_TRUE(unarchivedSession1.openedByDOM);
 
-  CRWNavigationManagerStorage* unarchivedSession2 = sessions[1];
+  CRWSessionStorage* unarchivedSession2 = sessions[1];
   EXPECT_NSEQ(windowName2, unarchivedSession2.windowName);
   EXPECT_NSEQ(openerId2, unarchivedSession2.openerID);
   EXPECT_FALSE(unarchivedSession2.openedByDOM);
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 6b405c2..a49cd809 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -809,7 +809,7 @@
   for (Tab* tab in self) {
     web::WebState* webState = tab.webState;
     DCHECK(webState);
-    [window addSerializedSession:webState->BuildSerializedNavigationManager()];
+    [window addSerializedSessionStorage:webState->BuildSessionStorage()];
   }
   window.selectedIndex = [self indexOfTab:_currentTab];
   return window;
@@ -963,7 +963,7 @@
   scoped_refptr<web::CertificatePolicyCache> policyCache =
       web::BrowserState::GetCertificatePolicyCache(_browserState);
 
-  for (CRWNavigationManagerStorage* session in sessions) {
+  for (CRWSessionStorage* session in sessions) {
     std::unique_ptr<web::WebState> webState =
         web::WebState::Create(params, session);
     DCHECK_EQ(webState->GetBrowserState(), _browserState);
diff --git a/ios/chrome/browser/tabs/tab_model_unittest.mm b/ios/chrome/browser/tabs/tab_model_unittest.mm
index 56507f5..1213402f 100644
--- a/ios/chrome/browser/tabs/tab_model_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_model_unittest.mm
@@ -183,8 +183,8 @@
     SessionWindowIOS* window = [[SessionWindowIOS alloc] init];
     for (int i = 0; i < entries; i++) {
       NSString* windowName = [NSString stringWithFormat:@"window %d", i + 1];
-      [window addSerializedSession:CreateWebState(windowName)
-                                       ->BuildSerializedNavigationManager()];
+      [window addSerializedSessionStorage:CreateWebState(windowName)
+                                              ->BuildSessionStorage()];
     }
     if (entries)
       [window setSelectedIndex:1];
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index d084d0f..1723dec7 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -68,10 +68,12 @@
     "navigation/navigation_manager_facade_delegate.h",
     "navigation/navigation_manager_impl.h",
     "navigation/navigation_manager_impl.mm",
-    "navigation/navigation_manager_storage_builder.h",
-    "navigation/navigation_manager_storage_builder.mm",
     "navigation/nscoder_util.h",
     "navigation/nscoder_util.mm",
+    "navigation/serializable_user_data_manager_impl.h",
+    "navigation/serializable_user_data_manager_impl.mm",
+    "navigation/session_storage_builder.h",
+    "navigation/session_storage_builder.mm",
     "navigation/time_smoother.cc",
     "navigation/time_smoother.h",
     "net/cert_host_pair.cc",
@@ -103,8 +105,8 @@
     "public/certificate_policy_cache.h",
     "public/crw_navigation_item_storage.h",
     "public/crw_navigation_item_storage.mm",
-    "public/crw_navigation_manager_storage.h",
-    "public/crw_navigation_manager_storage.mm",
+    "public/crw_session_storage.h",
+    "public/crw_session_storage.mm",
     "public/favicon_status.cc",
     "public/favicon_status.h",
     "public/favicon_url.cc",
@@ -125,6 +127,7 @@
     "public/referrer_util.cc",
     "public/referrer_util.h",
     "public/security_style.h",
+    "public/serializable_user_data_manager.h",
     "public/ssl_status.cc",
     "public/ssl_status.h",
     "public/url_data_source_ios.h",
@@ -539,6 +542,7 @@
     "payments/payment_request_unittest.cc",
     "public/origin_util_unittest.mm",
     "public/referrer_util_unittest.cc",
+    "public/serializable_user_data_manager_unittest.mm",
     "public/test/crw_mock_web_state_delegate.h",
     "public/test/crw_mock_web_state_delegate.mm",
     "public/web_state/page_viewport_state_unittest.mm",
diff --git a/ios/web/navigation/crw_session_controller.mm b/ios/web/navigation/crw_session_controller.mm
index c0e48532..2374036 100644
--- a/ios/web/navigation/crw_session_controller.mm
+++ b/ios/web/navigation/crw_session_controller.mm
@@ -91,7 +91,7 @@
     CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager;
 
 // Expose setters for serialization properties.  These are exposed in a category
-// in NavigationManagerStorageBuilder, and will be removed as ownership of
+// in SessionStorageBuilder, and will be removed as ownership of
 // their backing ivars moves to NavigationManagerImpl.
 @property(nonatomic, readwrite, copy) NSString* openerId;
 @property(nonatomic, readwrite, getter=isOpenedByDOM) BOOL openedByDOM;
diff --git a/ios/web/navigation/navigation_manager_impl.h b/ios/web/navigation/navigation_manager_impl.h
index 33d4fbe6..cf1b295 100644
--- a/ios/web/navigation/navigation_manager_impl.h
+++ b/ios/web/navigation/navigation_manager_impl.h
@@ -24,7 +24,7 @@
 struct Referrer;
 class NavigationManagerDelegate;
 class NavigationManagerFacadeDelegate;
-class NavigationManagerStorageBuilder;
+class SessionStorageBuilder;
 
 // Implementation of NavigationManager.
 // Generally mirrors upstream's NavigationController.
@@ -142,9 +142,9 @@
   int GetIndexForOffset(int offset) const;
 
  private:
-  // The NavigationManagerStorageBuilder functions require access to
-  // private variables of NavigationManagerImpl.
-  friend NavigationManagerStorageBuilder;
+  // The SessionStorageBuilder functions require access to private variables of
+  // NavigationManagerImpl.
+  friend SessionStorageBuilder;
 
   // Returns true if the PageTransition for the underlying navigation item at
   // |index| has ui::PAGE_TRANSITION_IS_REDIRECT_MASK.
diff --git a/ios/web/navigation/navigation_manager_storage_builder.h b/ios/web/navigation/navigation_manager_storage_builder.h
deleted file mode 100644
index f979075..0000000
--- a/ios/web/navigation/navigation_manager_storage_builder.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_WEB_NAVIGATION_SERIALIZED_NAVIGATION_MANAGER_BUILDER_H_
-#define IOS_WEB_NAVIGATION_SERIALIZED_NAVIGATION_MANAGER_BUILDER_H_
-
-#include <memory>
-
-@class CRWNavigationManagerStorage;
-
-namespace web {
-
-class NavigationManagerImpl;
-
-// Class that can serialize and deserialize NavigationManagers.
-class NavigationManagerStorageBuilder {
- public:
-  // Creates a serialized NavigationManager from |navigation_manager|.
-  CRWNavigationManagerStorage* BuildStorage(
-      NavigationManagerImpl* navigation_manager) const;
-  // Creates a NavigationManager from |navigation_manager_storage|.
-  std::unique_ptr<NavigationManagerImpl> BuildNavigationManagerImpl(
-      CRWNavigationManagerStorage* navigation_manager_storage) const;
-};
-
-}  // namespace web
-
-#endif  // IOS_WEB_NAVIGATION_SERIALIZED_NAVIGATION_MANAGER_BUILDER_H_
diff --git a/ios/web/navigation/serializable_user_data_manager_impl.h b/ios/web/navigation/serializable_user_data_manager_impl.h
new file mode 100644
index 0000000..38fa1fb
--- /dev/null
+++ b/ios/web/navigation/serializable_user_data_manager_impl.h
@@ -0,0 +1,53 @@
+// Copyright 2017 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 IOS_WEB_NAVIGATION_SERIALIZABLE_USER_DATA_MANAGER_IMPL_H_
+#define IOS_WEB_NAVIGATION_SERIALIZABLE_USER_DATA_MANAGER_IMPL_H_
+
+#import "base/mac/scoped_nsobject.h"
+#import "ios/web/public/serializable_user_data_manager.h"
+
+namespace web {
+
+class SerializableUserDataImpl : public SerializableUserData {
+ public:
+  SerializableUserDataImpl();
+  ~SerializableUserDataImpl();
+
+  // Constructor taking the NSDictionary holding the serializable data.
+  explicit SerializableUserDataImpl(NSDictionary* data);
+
+  // SerializableUserData:
+  void Encode(NSCoder* coder) override;
+  void Decode(NSCoder* coder) override;
+
+  // Returns the serializable data.
+  NSDictionary* data() { return data_; };
+
+ private:
+  // The dictionary passed on initialization.  After calling Decode(), this will
+  // contain the data that is decoded from the NSCoder.
+  base::scoped_nsobject<NSDictionary> data_;
+};
+
+class SerializableUserDataManagerImpl : public SerializableUserDataManager {
+ public:
+  SerializableUserDataManagerImpl();
+  ~SerializableUserDataManagerImpl();
+
+  // SerializableUserDataManager:
+  void AddSerializableData(id<NSCoding> data, NSString* key) override;
+  id<NSCoding> GetValueForSerializationKey(NSString* key) override;
+  std::unique_ptr<SerializableUserData> CreateSerializableUserData()
+      const override;
+  void AddSerializableUserData(SerializableUserData* data) override;
+
+ private:
+  // The dictionary that stores serializable user data.
+  base::scoped_nsobject<NSMutableDictionary> data_;
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_NAVIGATION_SERIALIZABLE_USER_DATA_MANAGER_IMPL_H_
diff --git a/ios/web/navigation/serializable_user_data_manager_impl.mm b/ios/web/navigation/serializable_user_data_manager_impl.mm
new file mode 100644
index 0000000..e3dd7177
--- /dev/null
+++ b/ios/web/navigation/serializable_user_data_manager_impl.mm
@@ -0,0 +1,117 @@
+// Copyright 2017 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.
+
+#import "ios/web/navigation/serializable_user_data_manager_impl.h"
+
+#import "base/mac/foundation_util.h"
+#import "ios/web/public/web_state/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace web {
+namespace {
+// The key under which SerializableUserDataMangerWrapper are stored in the
+// WebState's user data.
+const void* const kSerializableUserDataManagerKey =
+    &kSerializableUserDataManagerKey;
+// The key under which SerializableUserDataImpl's data is encoded.
+NSString* const kSerializedUserDataKey = @"serializedUserData";
+
+// Wrapper class used to associate SerializableUserDataManagerImpls with its
+// associated WebState.
+class SerializableUserDataManagerWrapper : base::SupportsUserData::Data {
+ public:
+  // Returns the SerializableUserDataMangerWrapper associated with |web_state|,
+  // creating one if necessary.
+  static SerializableUserDataManagerWrapper* FromWebState(
+      web::WebState* web_state) {
+    DCHECK(web_state);
+    SerializableUserDataManagerWrapper* wrapper =
+        static_cast<SerializableUserDataManagerWrapper*>(
+            web_state->GetUserData(kSerializableUserDataManagerKey));
+    if (!wrapper)
+      wrapper = new SerializableUserDataManagerWrapper(web_state);
+    return wrapper;
+  }
+
+  // Returns the manager owned by this wrapper.
+  SerializableUserDataManagerImpl* manager() { return &manager_; }
+
+ private:
+  // The SerializableUserDataMangerWrapper owned by this object.
+  SerializableUserDataManagerImpl manager_;
+
+  // Private constructor.  The created object will be added to |web_state|'s
+  // user data.
+  SerializableUserDataManagerWrapper(web::WebState* web_state) {
+    DCHECK(web_state);
+    web_state->SetUserData(kSerializableUserDataManagerKey, this);
+  }
+};
+}  // namespace
+
+// static
+std::unique_ptr<SerializableUserData> SerializableUserData::Create() {
+  return std::unique_ptr<SerializableUserData>(new SerializableUserDataImpl());
+}
+
+SerializableUserDataImpl::SerializableUserDataImpl()
+    : data_([[NSDictionary alloc] init]) {}
+
+SerializableUserDataImpl::~SerializableUserDataImpl() {}
+
+SerializableUserDataImpl::SerializableUserDataImpl(NSDictionary* data)
+    : data_([data copy]) {}
+
+void SerializableUserDataImpl::Encode(NSCoder* coder) {
+  [coder encodeObject:data_ forKey:kSerializedUserDataKey];
+}
+
+void SerializableUserDataImpl::Decode(NSCoder* coder) {
+  NSDictionary* data = base::mac::ObjCCastStrict<NSDictionary>(
+      [coder decodeObjectForKey:kSerializedUserDataKey]);
+  data_.reset([data mutableCopy]);
+}
+
+// static
+SerializableUserDataManager* SerializableUserDataManager::FromWebState(
+    web::WebState* web_state) {
+  DCHECK(web_state);
+  return SerializableUserDataManagerWrapper::FromWebState(web_state)->manager();
+}
+
+SerializableUserDataManagerImpl::SerializableUserDataManagerImpl()
+    : data_([[NSMutableDictionary alloc] init]) {}
+
+SerializableUserDataManagerImpl::~SerializableUserDataManagerImpl() {}
+
+void SerializableUserDataManagerImpl::AddSerializableData(id<NSCoding> data,
+                                                          NSString* key) {
+  DCHECK(data);
+  DCHECK(key.length);
+  [data_ setObject:data forKey:key];
+}
+
+id<NSCoding> SerializableUserDataManagerImpl::GetValueForSerializationKey(
+    NSString* key) {
+  return [data_ objectForKey:key];
+}
+
+std::unique_ptr<SerializableUserData>
+SerializableUserDataManagerImpl::CreateSerializableUserData() const {
+  return std::unique_ptr<SerializableUserData>(
+      new SerializableUserDataImpl(data_));
+}
+
+void SerializableUserDataManagerImpl::AddSerializableUserData(
+    SerializableUserData* data) {
+  DCHECK(data);
+  SerializableUserDataImpl* data_impl =
+      static_cast<SerializableUserDataImpl*>(data);
+  data_.reset([data_impl->data() mutableCopy]);
+}
+
+}  // namespace web
diff --git a/ios/web/navigation/session_storage_builder.h b/ios/web/navigation/session_storage_builder.h
new file mode 100644
index 0000000..4f4c2b9
--- /dev/null
+++ b/ios/web/navigation/session_storage_builder.h
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_NAVIGATION_SERIALIZED_NAVIGATION_MANAGER_BUILDER_H_
+#define IOS_WEB_NAVIGATION_SERIALIZED_NAVIGATION_MANAGER_BUILDER_H_
+
+#include <memory>
+
+@class CRWSessionStorage;
+
+namespace web {
+
+class WebStateImpl;
+
+// Class that can serialize and deserialize session information.
+class SessionStorageBuilder {
+ public:
+  // Creates a serializable session storage from |web_state|.
+  CRWSessionStorage* BuildStorage(WebStateImpl* web_state) const;
+  // Populates |web_state| with |storage|'s session information.
+  void ExtractSessionState(WebStateImpl* web_state,
+                           CRWSessionStorage* storage) const;
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_NAVIGATION_SERIALIZED_NAVIGATION_MANAGER_BUILDER_H_
diff --git a/ios/web/navigation/navigation_manager_storage_builder.mm b/ios/web/navigation/session_storage_builder.mm
similarity index 66%
rename from ios/web/navigation/navigation_manager_storage_builder.mm
rename to ios/web/navigation/session_storage_builder.mm
index a039ec87..ba44f37 100644
--- a/ios/web/navigation/navigation_manager_storage_builder.mm
+++ b/ios/web/navigation/session_storage_builder.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/navigation/navigation_manager_storage_builder.h"
+#import "ios/web/navigation/session_storage_builder.h"
 
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
@@ -12,7 +12,9 @@
 #import "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/navigation_item_storage_builder.h"
 #include "ios/web/navigation/navigation_manager_impl.h"
-#import "ios/web/public/crw_navigation_manager_storage.h"
+#import "ios/web/public/crw_session_storage.h"
+#import "ios/web/public/serializable_user_data_manager.h"
+#import "ios/web/web_state/web_state_impl.h"
 
 // CRWSessionController's readonly properties redefined as readwrite.  These
 // will be removed and NavigationManagerImpl's ivars will be written directly
@@ -30,11 +32,14 @@
 
 namespace web {
 
-CRWNavigationManagerStorage* NavigationManagerStorageBuilder::BuildStorage(
-    NavigationManagerImpl* navigation_manager) const {
+CRWSessionStorage* SessionStorageBuilder::BuildStorage(
+    WebStateImpl* web_state) const {
+  DCHECK(web_state);
+  web::NavigationManagerImpl* navigation_manager =
+      web_state->navigation_manager_.get();
   DCHECK(navigation_manager);
-  CRWNavigationManagerStorage* serialized_navigation_manager =
-      [[CRWNavigationManagerStorage alloc] init];
+  CRWSessionStorage* serialized_navigation_manager =
+      [[CRWSessionStorage alloc] init];
   CRWSessionController* session_controller =
       navigation_manager->GetSessionController();
   serialized_navigation_manager.tabID = session_controller.tabId;
@@ -58,14 +63,19 @@
     [item_storages addObject:item_storage_builder.BuildStorage(item)];
   }
   serialized_navigation_manager.itemStorages = item_storages;
+  web::SerializableUserDataManager* user_data_manager =
+      web::SerializableUserDataManager::FromWebState(web_state);
+  [serialized_navigation_manager
+      setSerializableUserData:user_data_manager->CreateSerializableUserData()];
   return serialized_navigation_manager;
 }
 
-std::unique_ptr<NavigationManagerImpl>
-NavigationManagerStorageBuilder::BuildNavigationManagerImpl(
-    CRWNavigationManagerStorage* navigation_manager_serialization) const {
-  DCHECK(navigation_manager_serialization);
-  NSArray* item_storages = navigation_manager_serialization.itemStorages;
+void SessionStorageBuilder::ExtractSessionState(
+    WebStateImpl* web_state,
+    CRWSessionStorage* storage) const {
+  DCHECK(web_state);
+  DCHECK(storage);
+  NSArray* item_storages = storage.itemStorages;
   web::ScopedNavigationItemList items(item_storages.count);
   NavigationItemStorageBuilder item_storage_builder;
   for (size_t index = 0; index < item_storages.count; ++index) {
@@ -73,32 +83,26 @@
         item_storage_builder.BuildNavigationItemImpl(item_storages[index]);
     items[index] = std::move(item_impl);
   }
-  NSUInteger current_index =
-      navigation_manager_serialization.currentNavigationIndex;
+  NSUInteger current_index = storage.currentNavigationIndex;
   base::scoped_nsobject<CRWSessionController> session_controller(
       [[CRWSessionController alloc] initWithNavigationItems:std::move(items)
                                                currentIndex:current_index
                                                browserState:nullptr]);
-  [session_controller setTabId:navigation_manager_serialization.tabID];
-  [session_controller setOpenerId:navigation_manager_serialization.openerID];
+  [session_controller setTabId:storage.tabID];
+  [session_controller setOpenerId:storage.openerID];
+  [session_controller setOpenedByDOM:storage.openedByDOM];
+  [session_controller setOpenerNavigationIndex:storage.openerNavigationIndex];
+  [session_controller setWindowName:storage.windowName];
   [session_controller
-      setOpenedByDOM:navigation_manager_serialization.openedByDOM];
-  [session_controller setOpenerNavigationIndex:navigation_manager_serialization
-                                                   .openerNavigationIndex];
+      setPreviousNavigationIndex:storage.previousNavigationIndex];
+  [session_controller setLastVisitedTimestamp:storage.lastVisitedTimestamp];
   [session_controller
-      setWindowName:navigation_manager_serialization.windowName];
-  [session_controller
-      setPreviousNavigationIndex:navigation_manager_serialization
-                                     .previousNavigationIndex];
-  [session_controller setLastVisitedTimestamp:navigation_manager_serialization
-                                                  .lastVisitedTimestamp];
-  [session_controller
-      setSessionCertificatePolicyManager:navigation_manager_serialization
+      setSessionCertificatePolicyManager:storage
                                              .sessionCertificatePolicyManager];
-  std::unique_ptr<NavigationManagerImpl> navigation_manager(
-      new NavigationManagerImpl());
-  navigation_manager->SetSessionController(session_controller);
-  return navigation_manager;
+  web_state->navigation_manager_.reset(new NavigationManagerImpl());
+  web_state->navigation_manager_->SetSessionController(session_controller);
+  web::SerializableUserDataManager::FromWebState(web_state)
+      ->AddSerializableUserData(storage.userData);
 }
 
 }  // namespace web
diff --git a/ios/web/public/crw_navigation_manager_storage.h b/ios/web/public/crw_session_storage.h
similarity index 63%
rename from ios/web/public/crw_navigation_manager_storage.h
rename to ios/web/public/crw_session_storage.h
index 7360c7d9..fa74465 100644
--- a/ios/web/public/crw_navigation_manager_storage.h
+++ b/ios/web/public/crw_session_storage.h
@@ -2,17 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_WEB_PUBLIC_CRW_NAVIGATION_MANAGER_STORAGE_H_
-#define IOS_WEB_PUBLIC_CRW_NAVIGATION_MANAGER_STORAGE_H_
+#ifndef IOS_WEB_PUBLIC_CRW_SESSION_STORAGE_H_
+#define IOS_WEB_PUBLIC_CRW_SESSION_STORAGE_H_
 
 #import <Foundation/Foundation.h>
+#include <memory>
 
 @class CRWSessionCertificatePolicyManager;
 
-// NSCoding-compliant class used to serialize NavigationManager's persisted
-// properties.
+namespace web {
+class SerializableUserData;
+}
+
+// NSCoding-compliant class used to serialize session state.
 // TODO(crbug.com/685388): Investigate using code from the sessions component.
-@interface CRWNavigationManagerStorage : NSObject<NSCoding>
+@interface CRWSessionStorage : NSObject<NSCoding>
 
 @property(nonatomic, copy) NSString* tabID;
 @property(nonatomic, copy) NSString* openerID;
@@ -25,7 +29,12 @@
 @property(nonatomic, copy) NSArray* itemStorages;
 @property(nonatomic, retain)
     CRWSessionCertificatePolicyManager* sessionCertificatePolicyManager;
+@property(nonatomic, readonly) web::SerializableUserData* userData;
+
+// Setter for |userData|.  The receiver takes ownership of |userData|.
+- (void)setSerializableUserData:
+    (std::unique_ptr<web::SerializableUserData>)userData;
 
 @end
 
-#endif  // IOS_WEB_PUBLIC_CRW_NAVIGATION_MANAGER_STORAGE_H_
+#endif  // IOS_WEB_PUBLIC_CRW_SESSION_STORAGE_H_
diff --git a/ios/web/public/crw_navigation_manager_storage.mm b/ios/web/public/crw_session_storage.mm
similarity index 84%
rename from ios/web/public/crw_navigation_manager_storage.mm
rename to ios/web/public/crw_session_storage.mm
index 8f77b09..c10d885c 100644
--- a/ios/web/public/crw_navigation_manager_storage.mm
+++ b/ios/web/public/crw_session_storage.mm
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/public/crw_navigation_manager_storage.h"
+#import "ios/web/public/crw_session_storage.h"
 
 #import "ios/web/navigation/crw_session_certificate_policy_manager.h"
+#import "ios/web/public/serializable_user_data_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -24,7 +25,14 @@
 NSString* const kWindowNameKey = @"windowName";
 }
 
-@implementation CRWNavigationManagerStorage
+@interface CRWSessionStorage () {
+  // Backing object for property of same name.
+  std::unique_ptr<web::SerializableUserData> _userData;
+}
+
+@end
+
+@implementation CRWSessionStorage
 
 @synthesize tabID = _tabID;
 @synthesize openerID = _openerID;
@@ -37,6 +45,19 @@
 @synthesize itemStorages = _itemStorages;
 @synthesize sessionCertificatePolicyManager = _sessionCertificatePolicyManager;
 
+#pragma mark - Accessors
+
+- (web::SerializableUserData*)userData {
+  return _userData.get();
+}
+
+- (void)setSerializableUserData:
+    (std::unique_ptr<web::SerializableUserData>)userData {
+  _userData = std::move(userData);
+}
+
+#pragma mark - NSCoding
+
 - (instancetype)initWithCoder:(nonnull NSCoder*)decoder {
   self = [super init];
   if (self) {
@@ -63,6 +84,8 @@
       _sessionCertificatePolicyManager =
           [[CRWSessionCertificatePolicyManager alloc] init];
     }
+    _userData = web::SerializableUserData::Create();
+    _userData->Decode(decoder);
   }
   return self;
 }
@@ -82,6 +105,8 @@
   [coder encodeObject:self.itemStorages forKey:kItemStoragesKey];
   [coder encodeObject:self.sessionCertificatePolicyManager
                forKey:kCertificatePolicyManagerKey];
+  if (_userData)
+    _userData->Encode(coder);
   // rendererInitiated is deliberately not preserved, as upstream.
 }
 
diff --git a/ios/web/public/serializable_user_data_manager.h b/ios/web/public/serializable_user_data_manager.h
new file mode 100644
index 0000000..d8a8596
--- /dev/null
+++ b/ios/web/public/serializable_user_data_manager.h
@@ -0,0 +1,54 @@
+// Copyright 2017 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 IOS_WEB_PUBLIC_SERIALIZABLE_USER_DATA_MANAGER_H_
+#define IOS_WEB_PUBLIC_SERIALIZABLE_USER_DATA_MANAGER_H_
+
+#import <Foundation/Foundation.h>
+#include <memory>
+
+namespace web {
+
+class WebState;
+
+// Class used to serialize values added to SerializableUserDataManager.
+class SerializableUserData {
+ public:
+  // Factory method.
+  static std::unique_ptr<SerializableUserData> Create();
+
+  // Encodes the data with |coder|.
+  virtual void Encode(NSCoder* coder) = 0;
+
+  // Decodes the data from |coder|.
+  virtual void Decode(NSCoder* coder) = 0;
+};
+
+// Class that can be used to add serializable user data to a WebState.
+class SerializableUserDataManager {
+ public:
+  // Returns the SerializableUserDataManager instance associated with
+  // |web_state|, instantiating one if necessary.
+  static SerializableUserDataManager* FromWebState(web::WebState* web_state);
+
+  // Adds |data| to the user data, allowing it to be encoded under |key|.
+  // |data| is expected to be non-nil.  If |key| has already been used, its
+  // associated value will be overwritten.
+  virtual void AddSerializableData(id<NSCoding> data, NSString* key) = 0;
+
+  // Returns the value that has been stored under |key|.
+  virtual id<NSCoding> GetValueForSerializationKey(NSString* key) = 0;
+
+  // Creates a SerializableUserData that can be used to encode the values added
+  // to the manager.
+  virtual std::unique_ptr<SerializableUserData> CreateSerializableUserData()
+      const = 0;
+
+  // Adds the values decoded from |data| to the manager.
+  virtual void AddSerializableUserData(SerializableUserData* data) = 0;
+};
+
+}  // namespace web
+
+#endif  // IOS_WEB_PUBLIC_SERIALIZABLE_USER_DATA_MANAGER_H_
diff --git a/ios/web/public/serializable_user_data_manager_unittest.mm b/ios/web/public/serializable_user_data_manager_unittest.mm
new file mode 100644
index 0000000..90d4019f
--- /dev/null
+++ b/ios/web/public/serializable_user_data_manager_unittest.mm
@@ -0,0 +1,64 @@
+// Copyright 2017 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.
+
+#import "ios/web/public/serializable_user_data_manager.h"
+
+#import "base/mac/scoped_nsobject.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+namespace {
+// User Data and Key to use for tests.
+NSString* const kTestUserData = @"TestUserData";
+NSString* const kTestUserDataKey = @"TestUserDataKey";
+}  // namespace
+
+class SerializableUserDataManagerTest : public PlatformTest {
+ protected:
+  // Convenience getter for the user data manager.
+  web::SerializableUserDataManager* manager() {
+    return web::SerializableUserDataManager::FromWebState(&web_state_);
+  }
+
+  web::TestWebState web_state_;
+};
+
+// Tests that serializable data can be successfully added and read.
+TEST_F(SerializableUserDataManagerTest, SetAndReadData) {
+  manager()->AddSerializableData(kTestUserData, kTestUserDataKey);
+  id value = manager()->GetValueForSerializationKey(kTestUserDataKey);
+  EXPECT_NSEQ(value, kTestUserData);
+}
+
+// Tests that SerializableUserData can successfully encode and decode.
+TEST_F(SerializableUserDataManagerTest, EncodeDecode) {
+  // Create a SerializableUserData instance for the test data.
+  manager()->AddSerializableData(kTestUserData, kTestUserDataKey);
+  std::unique_ptr<web::SerializableUserData> user_data =
+      manager()->CreateSerializableUserData();
+
+  // Archive the serializable user data.
+  base::scoped_nsobject<NSMutableData> data([[NSMutableData alloc] init]);
+  base::scoped_nsobject<NSKeyedArchiver> archiver(
+      [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]);
+  user_data->Encode(archiver);
+  [archiver finishEncoding];
+
+  // Create a new SerializableUserData by unarchiving.
+  base::scoped_nsobject<NSKeyedUnarchiver> unarchiver(
+      [[NSKeyedUnarchiver alloc] initForReadingWithData:data]);
+  std::unique_ptr<web::SerializableUserData> decoded_data =
+      web::SerializableUserData::Create();
+  decoded_data->Decode(unarchiver);
+
+  // Add the decoded user data to a new WebState and verify its contents.
+  web::TestWebState decoded_web_state;
+  web::SerializableUserDataManager* decoded_manager =
+      web::SerializableUserDataManager::FromWebState(&decoded_web_state);
+  decoded_manager->AddSerializableUserData(decoded_data.get());
+  id decoded_value =
+      decoded_manager->GetValueForSerializationKey(kTestUserDataKey);
+  EXPECT_NSEQ(decoded_value, kTestUserData);
+}
diff --git a/ios/web/public/test/fakes/test_web_state.h b/ios/web/public/test/fakes/test_web_state.h
index 15a8354..7b27256 100644
--- a/ios/web/public/test/fakes/test_web_state.h
+++ b/ios/web/public/test/fakes/test_web_state.h
@@ -38,7 +38,7 @@
   void Stop() override {}
   const NavigationManager* GetNavigationManager() const override;
   NavigationManager* GetNavigationManager() override;
-  CRWNavigationManagerStorage* BuildSerializedNavigationManager() override;
+  CRWSessionStorage* BuildSessionStorage() override;
   CRWJSInjectionReceiver* GetJSInjectionReceiver() const override;
   void ExecuteJavaScript(const base::string16& javascript) override;
   void ExecuteJavaScript(const base::string16& javascript,
diff --git a/ios/web/public/test/fakes/test_web_state.mm b/ios/web/public/test/fakes/test_web_state.mm
index 9b821db7..3ef1a84 100644
--- a/ios/web/public/test/fakes/test_web_state.mm
+++ b/ios/web/public/test/fakes/test_web_state.mm
@@ -68,7 +68,7 @@
   return navigation_manager_.get();
 }
 
-CRWNavigationManagerStorage* TestWebState::BuildSerializedNavigationManager() {
+CRWSessionStorage* TestWebState::BuildSessionStorage() {
   return nil;
 }
 
diff --git a/ios/web/public/web_state/web_state.h b/ios/web/public/web_state/web_state.h
index 9ee59870..ec3f464 100644
--- a/ios/web/public/web_state/web_state.h
+++ b/ios/web/public/web_state/web_state.h
@@ -24,7 +24,7 @@
 class SkBitmap;
 
 @class CRWJSInjectionReceiver;
-@class CRWNavigationManagerStorage;
+@class CRWSessionStorage;
 @protocol CRWScrollableContent;
 @protocol CRWWebViewProxy;
 typedef id<CRWWebViewProxy> CRWWebViewProxyType;
@@ -100,9 +100,8 @@
   // Creates a new WebState.
   static std::unique_ptr<WebState> Create(const CreateParams& params);
   // Creates a new WebState from a serialized NavigationManager.
-  static std::unique_ptr<WebState> Create(
-      const CreateParams& params,
-      CRWNavigationManagerStorage* session_storage);
+  static std::unique_ptr<WebState> Create(const CreateParams& params,
+                                          CRWSessionStorage* session_storage);
 
   ~WebState() override {}
 
@@ -140,9 +139,9 @@
   virtual const NavigationManager* GetNavigationManager() const = 0;
   virtual NavigationManager* GetNavigationManager() = 0;
 
-  // Creates a serialized version of the NavigationManager. The returned value
+  // Creates a serializable representation of the session. The returned value
   // is autoreleased.
-  virtual CRWNavigationManagerStorage* BuildSerializedNavigationManager() = 0;
+  virtual CRWSessionStorage* BuildSessionStorage() = 0;
 
   // Gets the CRWJSInjectionReceiver associated with this WebState.
   virtual CRWJSInjectionReceiver* GetJSInjectionReceiver() const = 0;
diff --git a/ios/web/web_state/web_state_impl.h b/ios/web/web_state/web_state_impl.h
index 28fa5c79..905e516 100644
--- a/ios/web/web_state/web_state_impl.h
+++ b/ios/web/web_state/web_state_impl.h
@@ -25,7 +25,7 @@
 #import "ios/web/public/web_state/web_state_delegate.h"
 #include "url/gurl.h"
 
-@class CRWNavigationManagerStorage;
+@class CRWSessionStorage;
 @class CRWWebController;
 @protocol CRWWebViewProxy;
 @class NSURLRequest;
@@ -66,8 +66,7 @@
   // Constructor for WebStateImpls created for new sessions.
   WebStateImpl(BrowserState* browser_state);
   // Constructor for WebStatesImpls created for deserialized sessions
-  WebStateImpl(BrowserState* browser_state,
-               CRWNavigationManagerStorage* session_storage);
+  WebStateImpl(BrowserState* browser_state, CRWSessionStorage* session_storage);
   ~WebStateImpl() override;
 
   // Gets/Sets the CRWWebController that backs this object.
@@ -204,7 +203,7 @@
   void Stop() override;
   const NavigationManager* GetNavigationManager() const override;
   NavigationManager* GetNavigationManager() override;
-  CRWNavigationManagerStorage* BuildSerializedNavigationManager() override;
+  CRWSessionStorage* BuildSessionStorage() override;
   CRWJSInjectionReceiver* GetJSInjectionReceiver() const override;
   void ExecuteJavaScript(const base::string16& javascript) override;
   void ExecuteJavaScript(const base::string16& javascript,
@@ -282,6 +281,10 @@
   void RemovePolicyDecider(WebStatePolicyDecider* decider) override;
 
  private:
+  // The SessionStorageBuilder functions require access to private variables of
+  // WebStateImpl.
+  friend SessionStorageBuilder;
+
   // Creates a WebUIIOS object for |url| that is owned by the caller. Returns
   // nullptr if |url| does not correspond to a WebUI page.
   std::unique_ptr<web::WebUIIOS> CreateWebUIIOS(const GURL& url);
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm
index 864fee3..99a3dfb 100644
--- a/ios/web/web_state/web_state_impl.mm
+++ b/ios/web/web_state/web_state_impl.mm
@@ -15,9 +15,9 @@
 #import "ios/web/navigation/crw_session_controller.h"
 #import "ios/web/navigation/crw_session_entry.h"
 #import "ios/web/navigation/navigation_item_impl.h"
-#import "ios/web/navigation/navigation_manager_storage_builder.h"
+#import "ios/web/navigation/session_storage_builder.h"
 #include "ios/web/public/browser_state.h"
-#import "ios/web/public/crw_navigation_manager_storage.h"
+#import "ios/web/public/crw_session_storage.h"
 #import "ios/web/public/image_fetcher/image_data_fetcher.h"
 #import "ios/web/public/java_script_dialog_presenter.h"
 #import "ios/web/public/navigation_item.h"
@@ -66,9 +66,8 @@
 }
 
 /* static */
-std::unique_ptr<WebState> WebState::Create(
-    const CreateParams& params,
-    CRWNavigationManagerStorage* session_storage) {
+std::unique_ptr<WebState> WebState::Create(const CreateParams& params,
+                                           CRWSessionStorage* session_storage) {
   std::unique_ptr<WebStateImpl> web_state(
       new WebStateImpl(params.browser_state, session_storage));
 
@@ -82,7 +81,7 @@
     : WebStateImpl(browser_state, nullptr) {}
 
 WebStateImpl::WebStateImpl(BrowserState* browser_state,
-                           CRWNavigationManagerStorage* session_storage)
+                           CRWSessionStorage* session_storage)
     : delegate_(nullptr),
       is_loading_(false),
       is_being_destroyed_(false),
@@ -92,9 +91,8 @@
       weak_factory_(this) {
   // Create or deserialize the NavigationManager.
   if (session_storage) {
-    NavigationManagerStorageBuilder session_storage_builder;
-    navigation_manager_ =
-        session_storage_builder.BuildNavigationManagerImpl(session_storage);
+    SessionStorageBuilder session_storage_builder;
+    session_storage_builder.ExtractSessionState(this, session_storage);
   } else {
     navigation_manager_.reset(new NavigationManagerImpl());
   }
@@ -656,9 +654,9 @@
   return &GetNavigationManagerImpl();
 }
 
-CRWNavigationManagerStorage* WebStateImpl::BuildSerializedNavigationManager() {
-  NavigationManagerStorageBuilder session_storage_builder;
-  return session_storage_builder.BuildStorage(navigation_manager_.get());
+CRWSessionStorage* WebStateImpl::BuildSessionStorage() {
+  SessionStorageBuilder session_storage_builder;
+  return session_storage_builder.BuildStorage(this);
 }
 
 CRWJSInjectionReceiver* WebStateImpl::GetJSInjectionReceiver() const {
diff --git a/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/CanvasCaptureMediaStream-capture-out-of-DOM-element.html b/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/CanvasCaptureMediaStream-capture-out-of-DOM-element.html
new file mode 100644
index 0000000..bed039a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/mediacapturefromelement/CanvasCaptureMediaStream-capture-out-of-DOM-element.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<script src =../../resources/testharness.js></script>
+<script src =../../resources/testharnessreport.js></script>
+<script>
+
+async_test(t => {
+  var canvas = document.createElement('canvas');
+
+  var recorder = new MediaRecorder(canvas.captureStream());
+  recorder.ondataavailable = function() {
+    t.step_func_done(function() {
+      assert_true(event.data.size > 0, 'Recorded data size should be > 0');
+    })();
+    recorder.stop();
+  }
+  recorder.start(0);
+
+  var ctx = canvas.getContext('2d');
+  ctx.fillStyle = 'green';
+  ctx.fillRect(0, 0, canvas.width, canvas.height);
+}, "Verify that drawing to a 2D canvas that is not attached to the DOM dispatches a frame to an attached MediaRecorder." );
+
+async_test(t => {
+  var canvas = document.createElement('canvas');
+
+  var recorder = new MediaRecorder(canvas.captureStream());
+  recorder.ondataavailable = function() {
+    t.step_func_done(function() {
+      assert_true(event.data.size > 0, 'Recorded data size should be > 0');
+    })();
+    recorder.stop();
+  }
+  recorder.start(0);
+
+  var gl = canvas.getContext('webgl');
+  gl.clearColor(0, 1, 0, 1);
+  gl.clear(gl.COLOR_BUFFER_BIT);
+}, "Verify that drawing to a webgl canvas that is not attached to the DOM dispatches a frame to an attached MediaRecorder." );
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
index eba91ca..5e84495 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -325,8 +325,47 @@
     buffer()->didDraw(rect);
 }
 
-void HTMLCanvasElement::didFinalizeFrame() {
+void HTMLCanvasElement::finalizeFrame() {
+  if (hasImageBuffer())
+    m_imageBuffer->finalizeFrame();
+  m_context->incrementFrameCount();
   notifyListenersCanvasChanged();
+}
+
+void HTMLCanvasElement::didDisableAcceleration() {
+  // We must force a paint invalidation on the canvas even if it's
+  // content did not change because it layer was destroyed.
+  didDraw(FloatRect(0, 0, size().width(), size().height()));
+}
+
+void HTMLCanvasElement::restoreCanvasMatrixClipStack(SkCanvas* canvas) const {
+  if (m_context)
+    m_context->restoreCanvasMatrixClipStack(canvas);
+}
+
+void HTMLCanvasElement::doDeferredPaintInvalidation() {
+  DCHECK(!m_dirtyRect.isEmpty());
+  if (m_context->is2d()) {
+    FloatRect srcRect(0, 0, size().width(), size().height());
+    m_dirtyRect.intersect(srcRect);
+    LayoutBox* lb = layoutBox();
+    FloatRect invalidationRect;
+    if (lb) {
+      FloatRect mappedDirtyRect =
+          mapRect(m_dirtyRect, srcRect, FloatRect(lb->contentBoxRect()));
+      if (m_context->isAccelerated()) {
+        // Accelerated 2D canvases need the dirty rect to be expressed relative
+        // to the content box, as opposed to the layout box.
+        mappedDirtyRect.move(-lb->contentBoxOffset());
+      }
+      invalidationRect = mappedDirtyRect;
+    } else {
+      invalidationRect = m_dirtyRect;
+    }
+    if (hasImageBuffer()) {
+      m_imageBuffer->doPaintInvalidation(invalidationRect);
+    }
+  }
 
   if (m_dirtyRect.isEmpty())
     return;
@@ -388,48 +427,6 @@
     m_pendingRenderingModeSwitch = false;
   }
 
-  m_context->incrementFrameCount();
-}
-
-void HTMLCanvasElement::didDisableAcceleration() {
-  // We must force a paint invalidation on the canvas even if it's
-  // content did not change because it layer was destroyed.
-  didDraw(FloatRect(0, 0, size().width(), size().height()));
-}
-
-void HTMLCanvasElement::restoreCanvasMatrixClipStack(
-    PaintCanvas* canvas) const {
-  if (m_context)
-    m_context->restoreCanvasMatrixClipStack(canvas);
-}
-
-void HTMLCanvasElement::doDeferredPaintInvalidation() {
-  DCHECK(!m_dirtyRect.isEmpty());
-  if (!m_context->is2d()) {
-    didFinalizeFrame();
-  } else {
-    FloatRect srcRect(0, 0, size().width(), size().height());
-    m_dirtyRect.intersect(srcRect);
-    LayoutBox* lb = layoutBox();
-    FloatRect invalidationRect;
-    if (lb) {
-      FloatRect mappedDirtyRect =
-          mapRect(m_dirtyRect, srcRect, FloatRect(lb->contentBoxRect()));
-      if (m_context->isAccelerated()) {
-        // Accelerated 2D canvases need the dirty rect to be expressed relative
-        // to the content box, as opposed to the layout box.
-        mappedDirtyRect.move(-lb->contentBoxOffset());
-      }
-      invalidationRect = mappedDirtyRect;
-    } else {
-      invalidationRect = m_dirtyRect;
-    }
-    if (hasImageBuffer()) {
-      m_imageBuffer->finalizeFrame(invalidationRect);
-    } else {
-      didFinalizeFrame();
-    }
-  }
   DCHECK(m_dirtyRect.isEmpty());
 }
 
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.h b/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
index 70ee8cb..4c9dfeb6 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
@@ -170,6 +170,12 @@
 
   InsertionNotificationRequest insertedInto(ContainerNode*) override;
 
+  bool isDirty() { return !m_dirtyRect.isEmpty(); }
+
+  void doDeferredPaintInvalidation();
+
+  void finalizeFrame();
+
   // ContextLifecycleObserver and PageVisibilityObserver implementation
   void contextDestroyed(ExecutionContext*) override;
 
@@ -194,13 +200,9 @@
 
   // ImageBufferClient implementation
   void notifySurfaceInvalid() override;
-  bool isDirty() override { return !m_dirtyRect.isEmpty(); }
   void didDisableAcceleration() override;
-  void didFinalizeFrame() override;
   void restoreCanvasMatrixClipStack(PaintCanvas*) const override;
 
-  void doDeferredPaintInvalidation();
-
   // ImageBitmapSource implementation
   IntSize bitmapSourceSize() const override;
   ScriptPromise createImageBitmap(ScriptState*,
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.cpp
index 7fdd8f3d..ce4019c1 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.cpp
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.cpp
@@ -29,6 +29,7 @@
 #include "core/html/canvas/CanvasImageSource.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/weborigin/SecurityOrigin.h"
+#include "public/platform/Platform.h"
 
 constexpr const char* kLegacyCanvasColorSpaceName = "legacy-srgb";
 constexpr const char* kSRGBCanvasColorSpaceName = "srgb";
@@ -127,11 +128,15 @@
 }
 
 void CanvasRenderingContext::dispose() {
+  if (m_finalizeFrameScheduled) {
+    Platform::current()->currentThread()->removeTaskObserver(this);
+  }
+
   // HTMLCanvasElement and CanvasRenderingContext have a circular reference.
   // When the pair is no longer reachable, their destruction order is non-
   // deterministic, so the first of the two to be destroyed needs to notify
   // the other in order to break the circular reference.  This is to avoid
-  // an error when CanvasRenderingContext2D::didProcessTask() is invoked
+  // an error when CanvasRenderingContext::didProcessTask() is invoked
   // after the HTMLCanvasElement is destroyed.
   if (canvas()) {
     canvas()->detachContext();
@@ -143,6 +148,26 @@
   }
 }
 
+void CanvasRenderingContext::didDraw(const SkIRect& dirtyRect) {
+  canvas()->didDraw(SkRect::Make(dirtyRect));
+  if (!m_finalizeFrameScheduled) {
+    m_finalizeFrameScheduled = true;
+    Platform::current()->currentThread()->addTaskObserver(this);
+  }
+}
+
+void CanvasRenderingContext::didProcessTask() {
+  Platform::current()->currentThread()->removeTaskObserver(this);
+  m_finalizeFrameScheduled = false;
+
+  if (!canvas())
+    return;
+
+  // The end of a script task that drew content to the canvas is the point
+  // at which the current frame may be considered complete.
+  canvas()->finalizeFrame();
+}
+
 CanvasRenderingContext::ContextType CanvasRenderingContext::contextTypeFromId(
     const String& id) {
   if (id == "2d")
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.h b/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.h
index 406457d..b420788 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.h
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasRenderingContext.h
@@ -32,6 +32,7 @@
 #include "core/layout/HitTestCanvasResult.h"
 #include "core/offscreencanvas/OffscreenCanvas.h"
 #include "platform/graphics/ColorBehavior.h"
+#include "public/platform/WebThread.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "wtf/HashSet.h"
@@ -56,7 +57,8 @@
 
 class CORE_EXPORT CanvasRenderingContext
     : public GarbageCollectedFinalized<CanvasRenderingContext>,
-      public ScriptWrappable {
+      public ScriptWrappable,
+      public WebThread::TaskObserver {
   WTF_MAKE_NONCOPYABLE(CanvasRenderingContext);
   USING_PRE_FINALIZER(CanvasRenderingContext, dispose);
 
@@ -109,6 +111,7 @@
     NOTREACHED();
   }
   virtual bool isPaintable() const = 0;
+  virtual void didDraw(const SkIRect& dirtyRect);
 
   // Return true if the content is updated.
   virtual bool paintRenderingResultsToCanvas(SourceDrawingBuffer) {
@@ -131,6 +134,10 @@
   };
   virtual void loseContext(LostContextMode) {}
 
+  // WebThread::TaskObserver implementation
+  void didProcessTask() override;
+  void willProcessTask() final {}
+
   // Canvas2D-specific interface
   virtual bool is2d() const { return false; }
   virtual void restoreCanvasMatrixClipStack(PaintCanvas*) const {}
@@ -199,6 +206,7 @@
   HashSet<String> m_dirtyURLs;
   CanvasColorSpace m_colorSpace;
   CanvasContextCreationAttributes m_creationAttributes;
+  bool m_finalizeFrameScheduled = false;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/ChartViewport.js b/third_party/WebKit/Source/devtools/front_end/perf_ui/ChartViewport.js
index ede93ece8..0a1353e 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/ChartViewport.js
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/ChartViewport.js
@@ -86,6 +86,7 @@
     this._totalHeight = 0;
     this._pendingAnimationTimeLeft = 0;
     this._pendingAnimationTimeRight = 0;
+    this._updateContentElementSize();
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js b/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js
index 2636587..2d334da8 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js
@@ -1201,12 +1201,14 @@
    */
   reset() {
     super.reset();
+    this._rawTimelineData = null;
+    this._rawTimelineDataLength = 0;
     this._highlightedMarkerIndex = -1;
     this._highlightedEntryIndex = -1;
     this._selectedEntryIndex = -1;
     /** @type {!Map<string,!Map<string,number>>} */
     this._textWidth = new Map();
-    this.update();
+    this.scheduleUpdate();
   }
 
   _enabled() {
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/TimelineOverviewPane.js b/third_party/WebKit/Source/devtools/front_end/perf_ui/TimelineOverviewPane.js
index b61661f..bfc5cd0 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/TimelineOverviewPane.js
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/TimelineOverviewPane.js
@@ -260,7 +260,7 @@
     for (var i = 0; i < this._overviewControls.length; ++i)
       this._overviewControls[i].reset();
     this._popoverHelper.hidePopover();
-    this._update();
+    this.scheduleUpdate();
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineDetailsView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineDetailsView.js
index 74c2ebb..3242212c 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineDetailsView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineDetailsView.js
@@ -56,6 +56,8 @@
     this._model = model;
     this._tabbedPane.closeTabs(
         [Timeline.TimelineDetailsView.Tab.PaintProfiler, Timeline.TimelineDetailsView.Tab.LayerViewer], false);
+    for (var view of this._rangeDetailViews.values())
+      view.setModel(model);
     this._lazyPaintProfilerView = null;
     this._lazyLayersView = null;
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
index e4729621..5e9b0bb 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartView.js
@@ -119,12 +119,11 @@
     this._mainView.alwaysShowVerticalScroll();
     this._mainView.enableRuler(false);
 
-    var networkViewGroupExpansionSetting =
+    this._networkViewGroupExpansionSetting =
         Common.settings.createSetting('timelineFlamechartNetworkViewGroupExpansion', {});
     this._networkDataProvider = new Timeline.TimelineFlameChartNetworkDataProvider();
-    this._networkView = new PerfUI.FlameChart(this._networkDataProvider, this, networkViewGroupExpansionSetting);
+    this._networkView = new PerfUI.FlameChart(this._networkDataProvider, this, this._networkViewGroupExpansionSetting);
     this._networkView.alwaysShowVerticalScroll();
-    networkViewGroupExpansionSetting.addChangeListener(this.resizeToPreferredHeights.bind(this));
 
     this._networkPane = new UI.VBox();
     this._networkPane.setMinimumSize(23, 23);
@@ -201,18 +200,13 @@
 
   _refresh() {
     this._dataProvider.setModel(this._model);
-    this._mainView.reset();
-
     this._networkDataProvider.setModel(this._model);
-    this._networkView.reset();
-
-    this._detailsView.setModel(this._model);
+    if (this._detailsView)
+      this._detailsView.setModel(this._model);
 
     this._nextExtensionIndex = 0;
     this._appendExtensionData();
-    this._mainView.scheduleUpdate();
 
-    this._networkDataProvider.reset();
     if (this._networkDataProvider.isEmpty()) {
       this._mainView.enableRuler(true);
       this._splitWidget.hideSidebar();
@@ -221,7 +215,8 @@
       this._splitWidget.showBoth();
       this.resizeToPreferredHeights();
     }
-    this._networkView.scheduleUpdate();
+    this._mainView.reset();
+    this._networkView.reset();
   }
 
   _appendExtensionData() {
@@ -250,6 +245,7 @@
    * @override
    */
   willHide() {
+    this._networkViewGroupExpansionSetting.removeChangeListener(this.resizeToPreferredHeights, this);
     Bindings.blackboxManager.removeChangeListener(this._boundRefresh);
   }
 
@@ -257,7 +253,10 @@
    * @override
    */
   wasShown() {
+    this._networkViewGroupExpansionSetting.addChangeListener(this.resizeToPreferredHeights, this);
     Bindings.blackboxManager.addChangeListener(this._boundRefresh);
+    if (this._needsResizeToPreferredHeights)
+      this.resizeToPreferredHeights();
     this._mainView.scheduleUpdate();
     this._networkView.scheduleUpdate();
   }
@@ -320,6 +319,11 @@
   }
 
   resizeToPreferredHeights() {
+    if (!this.isShowing()) {
+      this._needsResizeToPreferredHeights = true;
+      return;
+    }
+    this._needsResizeToPreferredHeights = false;
     this._networkPane.element.classList.toggle(
         'timeline-network-resizer-disabled', !this._networkDataProvider.isExpanded());
     this._splitWidget.setSidebarSize(
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineNetworkFlameChart.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineNetworkFlameChart.js
index 9250b3f..d066c15 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineNetworkFlameChart.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineNetworkFlameChart.js
@@ -9,9 +9,7 @@
 Timeline.TimelineFlameChartNetworkDataProvider = class {
   constructor() {
     this._font = '11px ' + Host.fontFamily();
-    /** @type {?TimelineModel.TimelineModel} */
-    this._model = null;
-    this.reset();
+    this.setModel(null);
     this._style = {
       padding: 4,
       height: 17,
@@ -32,6 +30,10 @@
    */
   setModel(performanceModel) {
     this._model = performanceModel && performanceModel.timelineModel();
+    this._maxLevel = 0;
+    this._timelineData = null;
+    /** @type {!Array<!TimelineModel.TimelineModel.NetworkRequest>} */
+    this._requests = [];
   }
 
   /**
@@ -81,13 +83,6 @@
     return this._timeSpan;
   }
 
-  reset() {
-    this._maxLevel = 0;
-    this._timelineData = null;
-    /** @type {!Array<!TimelineModel.TimelineModel.NetworkRequest>} */
-    this._requests = [];
-  }
-
   /**
    * @param {number} startTime
    * @param {number} endTime
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
index 57e5e602..acdbcf2 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelinePanel.js
@@ -615,6 +615,7 @@
       this._performanceModel.dispose();
     this._performanceModel = model;
     this._currentViews.forEach(view => view.setModel(this._performanceModel));
+
     this._overviewPane.reset();
     if (model) {
       this._overviewPane.setBounds(
@@ -622,6 +623,7 @@
     }
     for (var control of this._overviewControls)
       control.setModel(model);
+
     if (model) {
       var cpuProfiles = model.timelineModel().cpuProfiles();
       cpuProfiles.forEach(profile => PerfUI.LineLevelProfile.instance().appendCPUProfile(profile));
@@ -632,8 +634,13 @@
       this.requestWindowTimes(0, Infinity);
     }
     this._overviewPane.scheduleUpdate();
+    if (this._detailsView)
+      this._detailsView.setModel(model);
+
     this.select(null);
     this._updateSearchHighlight(false, true);
+    if (this._flameChart)
+      this._flameChart.resizeToPreferredHeights();
   }
 
   /**
@@ -769,8 +776,6 @@
     this._backingStorage = backingStorage;
     this._setModel(performanceModel);
 
-    if (this._flameChart)
-      this._flameChart.resizeToPreferredHeights();
     if (this._detailsSplitWidget)
       this._detailsSplitWidget.showBoth();
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
index 8f038cc1..c065640 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineTreeView.js
@@ -30,6 +30,7 @@
    */
   setModel(model) {
     this._model = model;
+    this.refreshTree();
   }
 
   /**
diff --git a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
index 493afde..882496e8 100644
--- a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
@@ -134,7 +134,7 @@
                                 canvas->document().frame()),
           this,
           &CanvasRenderingContext2D::tryRestoreContextEvent),
-      m_pruneLocalFontCacheScheduled(false) {
+      m_shouldPruneLocalFontCache(false) {
   if (document.settings() &&
       document.settings()->getAntialiasedClips2dCanvasEnabled())
     m_clipAntialiasing = AntiAliased;
@@ -149,11 +149,6 @@
 
 CanvasRenderingContext2D::~CanvasRenderingContext2D() {}
 
-void CanvasRenderingContext2D::dispose() {
-  if (m_pruneLocalFontCacheScheduled)
-    Platform::current()->currentThread()->removeTaskObserver(this);
-}
-
 void CanvasRenderingContext2D::validateStateStack() const {
 #if DCHECK_IS_ON()
   if (PaintCanvas* skCanvas = canvas()->existingDrawingCanvas()) {
@@ -362,7 +357,7 @@
       buffer->setHasExpensiveOp();
   }
 
-  canvas()->didDraw(SkRect::Make(dirtyRect));
+  CanvasRenderingContext::didDraw(dirtyRect);
 }
 
 bool CanvasRenderingContext2D::stateHasFilter() {
@@ -490,7 +485,7 @@
       DCHECK(!m_fontLRUList.contains(newFont));
       m_fontLRUList.add(newFont);
       pruneLocalFontCache(canvasFontCache->hardMaxFonts());  // hard limit
-      schedulePruneLocalFontCacheIfNeeded();                 // soft limit
+      m_shouldPruneLocalFontCache = true;                    // apply soft limit
       modifiableState().setFont(
           fontStyle->font(), canvas()->document().styleEngine().fontSelector());
     }
@@ -508,31 +503,19 @@
   modifiableState().setUnparsedFont(newFontSafeCopy);
 }
 
-void CanvasRenderingContext2D::schedulePruneLocalFontCacheIfNeeded() {
-  if (m_pruneLocalFontCacheScheduled)
-    return;
-  m_pruneLocalFontCacheScheduled = true;
-  Platform::current()->currentThread()->addTaskObserver(this);
-}
-
 void CanvasRenderingContext2D::didProcessTask() {
-  Platform::current()->currentThread()->removeTaskObserver(this);
-
+  CanvasRenderingContext::didProcessTask();
   // This should be the only place where canvas() needs to be checked for
-  // nullness because the circular refence with HTMLCanvasElement mean the
-  // canvas and the context keep each other alive as long as the pair is
-  // referenced the task observer is the only persisten refernce to this object
-  // that is not traced, so didProcessTask() may be call at a time when the
+  // nullness because the circular refence with HTMLCanvasElement means the
+  // canvas and the context keep each other alive. As long as the pair is
+  // referenced, the task observer is the only persistent refernce to this
+  // object
+  // that is not traced, so didProcessTask() may be called at a time when the
   // canvas has been garbage collected but not the context.
-  if (!canvas())
-    return;
-
-  // The rendering surface needs to be prepared now because it will be too late
-  // to create a layer once we are in the paint invalidation phase.
-  canvas()->prepareSurfaceForPaintingIfNeeded();
-
-  pruneLocalFontCache(canvas()->document().canvasFontCache()->maxFonts());
-  m_pruneLocalFontCacheScheduled = false;
+  if (m_shouldPruneLocalFontCache && canvas()) {
+    m_shouldPruneLocalFontCache = false;
+    pruneLocalFontCache(canvas()->document().canvasFontCache()->maxFonts());
+  }
 }
 
 void CanvasRenderingContext2D::pruneLocalFontCache(size_t targetSize) {
diff --git a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.h b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.h
index 357c848..919bc97 100644
--- a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.h
+++ b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.h
@@ -67,11 +67,9 @@
 class MODULES_EXPORT CanvasRenderingContext2D final
     : public CanvasRenderingContext,
       public BaseRenderingContext2D,
-      public WebThread::TaskObserver,
       public SVGResourceClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(CanvasRenderingContext2D);
-  USING_PRE_FINALIZER(CanvasRenderingContext2D, dispose);
 
  public:
   class Factory : public CanvasRenderingContextFactory {
@@ -142,8 +140,7 @@
   void restoreCanvasMatrixClipStack(PaintCanvas*) const override;
 
   // TaskObserver implementation
-  void didProcessTask() override;
-  void willProcessTask() override {}
+  void didProcessTask() final;
 
   void styleDidChange(const ComputedStyle* oldStyle,
                       const ComputedStyle& newStyle) override;
@@ -179,7 +176,9 @@
   void disableDeferral(DisableDeferralReason) final;
 
   AffineTransform baseTransform() const final;
-  void didDraw(const SkIRect& dirtyRect) final;
+  void didDraw(const SkIRect& dirtyRect) final;  // overrides
+                                                 // BaseRenderingContext2D and
+                                                 // CanvasRenderingContext
 
   bool stateHasFilter() final;
   sk_sp<SkImageFilter> stateGetFilter() final;
@@ -208,14 +207,11 @@
                            const CanvasContextCreationAttributes& attrs,
                            Document&);
 
-  void dispose();
-
   void dispatchContextLostEvent(TimerBase*);
   void dispatchContextRestoredEvent(TimerBase*);
   void tryRestoreContextEvent(TimerBase*);
 
   void pruneLocalFontCache(size_t targetSize);
-  void schedulePruneLocalFontCacheIfNeeded();
 
   void scrollPathIntoViewInternal(const Path&);
 
@@ -257,7 +253,7 @@
 
   FilterOperations m_filterOperations;
   HashMap<String, Font> m_fontsResolvedUsingCurrentStyle;
-  bool m_pruneLocalFontCacheScheduled;
+  bool m_shouldPruneLocalFontCache;
   ListHashSet<String> m_fontLRUList;
 };
 
diff --git a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
index 0863555..edd8a49 100644
--- a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
@@ -822,7 +822,7 @@
     context2d()->save();
     context2d()->translate(1.0f, 0.0f);
   }
-  canvasElement().doDeferredPaintInvalidation();  // To close the current frame
+  canvasElement().finalizeFrame();  // To close the current frame
 }
 
 TEST_F(CanvasRenderingContext2DTest, FallbackWithLargeState) {
@@ -841,7 +841,7 @@
     context2d()->save();
     context2d()->translate(1.0f, 0.0f);
   }
-  canvasElement().doDeferredPaintInvalidation();  // To close the current frame
+  canvasElement().finalizeFrame();  // To close the current frame
 }
 
 TEST_F(CanvasRenderingContext2DTest, OpaqueDisplayListFallsBackForText) {
diff --git a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h
index 37040aa4..cd30654 100644
--- a/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h
+++ b/third_party/WebKit/Source/modules/offscreencanvas2d/OffscreenCanvasRenderingContext2D.h
@@ -72,7 +72,9 @@
   void disableDeferral(DisableDeferralReason) final;
 
   AffineTransform baseTransform() const final;
-  void didDraw(const SkIRect& dirtyRect) final;
+  void didDraw(const SkIRect& dirtyRect) final;  // overrides
+                                                 // BaseRenderingContext2D and
+                                                 // CanvasRenderingContext
 
   bool stateHasFilter() final;
   sk_sp<SkImageFilter> stateGetFilter() final;
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
index 10c2c319..010b60b6f 100644
--- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
+++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -1399,15 +1399,12 @@
 
   LayoutBox* layoutBox = canvas()->layoutBox();
   if (layoutBox && layoutBox->hasAcceleratedCompositing()) {
-    m_markedCanvasDirty = true;
-    canvas()->clearCopiedImage();
     layoutBox->contentChanged(changeType);
-  } else {
-    if (!m_markedCanvasDirty) {
-      m_markedCanvasDirty = true;
-      canvas()->didDraw(
-          FloatRect(FloatPoint(0, 0), FloatSize(clampedCanvasSize())));
-    }
+  }
+  if (!m_markedCanvasDirty) {
+    m_markedCanvasDirty = true;
+    IntSize canvasSize = clampedCanvasSize();
+    didDraw(SkIRect::MakeXYWH(0, 0, canvasSize.width(), canvasSize.height()));
   }
 }
 
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DImageBufferSurface.h b/third_party/WebKit/Source/platform/graphics/Canvas2DImageBufferSurface.h
index f7d04ef..67c11ffd4 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DImageBufferSurface.h
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DImageBufferSurface.h
@@ -80,8 +80,9 @@
   ~Canvas2DImageBufferSurface() override { m_layerBridge->beginDestruction(); }
 
   // ImageBufferSurface implementation
-  void finalizeFrame(const FloatRect& dirtyRect) override {
-    m_layerBridge->finalizeFrame(dirtyRect);
+  void finalizeFrame() override { m_layerBridge->finalizeFrame(); }
+  void doPaintInvalidation(const FloatRect& dirtyRect) override {
+    m_layerBridge->doPaintInvalidation(dirtyRect);
   }
   void willOverwriteCanvas() override { m_layerBridge->willOverwriteCanvas(); }
   PaintCanvas* canvas() override { return m_layerBridge->canvas(); }
@@ -102,9 +103,6 @@
   void didDraw(const FloatRect& rect) override { m_layerBridge->didDraw(rect); }
   void flush(FlushReason) override { m_layerBridge->flush(); }
   void flushGpu(FlushReason) override { m_layerBridge->flushGpu(); }
-  void prepareSurfaceForPaintingIfNeeded() override {
-    m_layerBridge->prepareSurfaceForPaintingIfNeeded();
-  }
   bool writePixels(const SkImageInfo& origInfo,
                    const void* pixels,
                    size_t rowBytes,
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
index dab2786f..cbd9476bf 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.cpp
@@ -853,9 +853,9 @@
 
 bool Canvas2DLayerBridge::restoreSurface() {
   DCHECK(!m_destructionInProgress);
-  if (m_destructionInProgress)
+  if (m_destructionInProgress || !isAccelerated())
     return false;
-  DCHECK(isAccelerated() && !m_surface);
+  DCHECK(!m_surface);
 
   gpu::gles2::GLES2Interface* sharedGL = nullptr;
   m_layer->clearTexture();
@@ -899,12 +899,6 @@
     // 4. Here.
     return false;
   }
-  DCHECK(isAccelerated() || isHibernating() || m_softwareRenderingWhileHidden);
-
-  // if hibernating but not hidden, we want to wake up from
-  // hibernation
-  if ((isHibernating() || m_softwareRenderingWhileHidden) && isHidden())
-    return false;
 
   // If the context is lost, we don't know if we should be producing GPU or
   // software frames, until we get a new context, since the compositor will
@@ -914,6 +908,13 @@
           GL_NO_ERROR)
     return false;
 
+  DCHECK(isAccelerated() || isHibernating() || m_softwareRenderingWhileHidden);
+
+  // if hibernating but not hidden, we want to wake up from
+  // hibernation
+  if ((isHibernating() || m_softwareRenderingWhileHidden) && isHidden())
+    return false;
+
   sk_sp<SkImage> image =
       newImageSnapshot(PreferAcceleration, SnapshotReasonUnknown);
   if (!image || !image->getTexture())
@@ -1042,19 +1043,24 @@
   m_didDrawSinceLastGpuFlush = true;
 }
 
-void Canvas2DLayerBridge::prepareSurfaceForPaintingIfNeeded() {
-  getOrCreateSurface(PreferAcceleration);
-}
-
-void Canvas2DLayerBridge::finalizeFrame(const FloatRect& dirtyRect) {
+void Canvas2DLayerBridge::finalizeFrame() {
   DCHECK(!m_destructionInProgress);
-  if (m_layer && m_accelerationMode != DisableAcceleration)
-    m_layer->layer()->invalidateRect(enclosingIntRect(dirtyRect));
+
+  // Make sure surface is ready for painting: fix the rendering mode now
+  // because it will be too late during the paint invalidation phase.
+  getOrCreateSurface(PreferAcceleration);
+
   if (m_rateLimiter)
     m_rateLimiter->reset();
   m_renderingTaskCompletedForCurrentFrame = false;
 }
 
+void Canvas2DLayerBridge::doPaintInvalidation(const FloatRect& dirtyRect) {
+  DCHECK(!m_destructionInProgress);
+  if (m_layer && m_accelerationMode != DisableAcceleration)
+    m_layer->layer()->invalidateRect(enclosingIntRect(dirtyRect));
+}
+
 void Canvas2DLayerBridge::didProcessTask() {
   TRACE_EVENT0("cc", "Canvas2DLayerBridge::didProcessTask");
   DCHECK(m_isRegisteredTaskObserver);
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h
index aff8e6a7..d1a7f13 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridge.h
@@ -113,7 +113,8 @@
                        bool lostResource);
 
   // ImageBufferSurface implementation
-  void finalizeFrame(const FloatRect& dirtyRect);
+  void finalizeFrame();
+  void doPaintInvalidation(const FloatRect& dirtyRect);
   void willWritePixels();
   void willOverwriteAllPixels();
   void willOverwriteCanvas();
@@ -134,7 +135,6 @@
                    int y);
   void flush();
   void flushGpu();
-  void prepareSurfaceForPaintingIfNeeded();
   bool isHidden() { return m_isHidden; }
   OpacityMode opacityMode() { return m_opacityMode; }
   void dontUseIdleSchedulingForTesting() {
diff --git a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
index 75010c0..d234255 100644
--- a/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp
@@ -240,9 +240,7 @@
         Canvas2DLayerBridge::ForceAccelerationForTesting,
         gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType)));
 
-    // TODO(junov): The PrepareTextureMailbox() method will fail a DCHECK if we
-    // don't do this before calling it the first time when the context is lost.
-    bridge->prepareSurfaceForPaintingIfNeeded();
+    EXPECT_TRUE(bridge->isAccelerated());
 
     // When the context is lost we are not sure if we should still be producing
     // GL frames for the compositor or not, so fail to generate frames.
@@ -263,10 +261,8 @@
         Canvas2DLayerBridge::ForceAccelerationForTesting,
         gfx::ColorSpace::CreateSRGB(), false, kN32_SkColorType)));
 
-    // TODO(junov): The PrepareTextureMailbox() method will fail a DCHECK if we
-    // don't do this before calling it the first time when the context is lost.
-    bridge->prepareSurfaceForPaintingIfNeeded();
-
+    bridge->getOrCreateSurface();
+    EXPECT_TRUE(bridge->checkSurfaceValid());
     // When the context is lost we are not sure if we should still be producing
     // GL frames for the compositor or not, so fail to generate frames.
     gl.setIsContextLost(true);
@@ -420,7 +416,7 @@
                            Canvas2DLayerBridge::EnableAcceleration);
   // draw+flush to trigger the creation of a GPU surface
   (*bridgePtr)->didDraw(FloatRect(0, 0, 1, 1));
-  (*bridgePtr)->finalizeFrame(FloatRect(0, 0, 1, 1));
+  (*bridgePtr)->finalizeFrame();
   (*bridgePtr)->flush();
   doneEvent->signal();
 }
@@ -679,7 +675,7 @@
 
 void runRenderingTask(Canvas2DLayerBridge* bridge, WaitableEvent* doneEvent) {
   bridge->didDraw(FloatRect(0, 0, 1, 1));
-  bridge->finalizeFrame(FloatRect(0, 0, 1, 1));
+  bridge->finalizeFrame();
   bridge->flush();
   doneEvent->signal();
 }
diff --git a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
index fa041cb..2e38b19 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/ImageBuffer.cpp
@@ -148,18 +148,12 @@
   return m_surface->isValid();
 }
 
-bool ImageBuffer::isDirty() {
-  return m_client ? m_client->isDirty() : false;
+void ImageBuffer::finalizeFrame() {
+  m_surface->finalizeFrame();
 }
 
-void ImageBuffer::didFinalizeFrame() {
-  if (m_client)
-    m_client->didFinalizeFrame();
-}
-
-void ImageBuffer::finalizeFrame(const FloatRect& dirtyRect) {
-  m_surface->finalizeFrame(dirtyRect);
-  didFinalizeFrame();
+void ImageBuffer::doPaintInvalidation(const FloatRect& dirtyRect) {
+  m_surface->doPaintInvalidation(dirtyRect);
 }
 
 bool ImageBuffer::restoreSurface() const {
diff --git a/third_party/WebKit/Source/platform/graphics/ImageBuffer.h b/third_party/WebKit/Source/platform/graphics/ImageBuffer.h
index 9c53f8c..8e38810 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageBuffer.h
+++ b/third_party/WebKit/Source/platform/graphics/ImageBuffer.h
@@ -115,10 +115,8 @@
   void disableDeferral(DisableDeferralReason) const;
 
   // Called at the end of a task that rendered a whole frame
-  void finalizeFrame(const FloatRect& dirtyRect);
-  void didFinalizeFrame();
-
-  bool isDirty();
+  void finalizeFrame();
+  void doPaintInvalidation(const FloatRect& dirtyRect);
 
   bool writePixels(const SkImageInfo&,
                    const void* pixels,
diff --git a/third_party/WebKit/Source/platform/graphics/ImageBufferClient.h b/third_party/WebKit/Source/platform/graphics/ImageBufferClient.h
index a256f53..25bbb3e4 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageBufferClient.h
+++ b/third_party/WebKit/Source/platform/graphics/ImageBufferClient.h
@@ -40,9 +40,7 @@
  public:
   virtual ~ImageBufferClient() {}
   virtual void notifySurfaceInvalid() = 0;
-  virtual bool isDirty() = 0;
   virtual void didDisableAcceleration() = 0;
-  virtual void didFinalizeFrame() = 0;
   virtual void restoreCanvasMatrixClipStack(PaintCanvas*) const = 0;
 };
 
diff --git a/third_party/WebKit/Source/platform/graphics/ImageBufferSurface.h b/third_party/WebKit/Source/platform/graphics/ImageBufferSurface.h
index 870425fb..8ae2b2f 100644
--- a/third_party/WebKit/Source/platform/graphics/ImageBufferSurface.h
+++ b/third_party/WebKit/Source/platform/graphics/ImageBufferSurface.h
@@ -75,7 +75,8 @@
   virtual void setIsHidden(bool) {}
   virtual void setImageBuffer(ImageBuffer*) {}
   virtual sk_sp<PaintRecord> getRecord();
-  virtual void finalizeFrame(const FloatRect& dirtyRect) {}
+  virtual void finalizeFrame() {}
+  virtual void doPaintInvalidation(const FloatRect& dirtyRect) {}
   virtual void draw(GraphicsContext&,
                     const FloatRect& destRect,
                     const FloatRect& srcRect,
diff --git a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.cpp b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.cpp
index 2afaaeeb..8e56c8c 100644
--- a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.cpp
+++ b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.cpp
@@ -229,7 +229,6 @@
 
   FallbackReason fallbackReason = FallbackReasonUnknown;
   bool canUseRecord = finalizeFrameInternal(&fallbackReason);
-  m_imageBuffer->didFinalizeFrame();
 
   DCHECK(canUseRecord || m_fallbackFactory);
 
@@ -242,9 +241,9 @@
   return nullptr;
 }
 
-void RecordingImageBufferSurface::finalizeFrame(const FloatRect& dirtyRect) {
+void RecordingImageBufferSurface::finalizeFrame() {
   if (m_fallbackSurface) {
-    m_fallbackSurface->finalizeFrame(dirtyRect);
+    m_fallbackSurface->finalizeFrame();
     return;
   }
 
@@ -253,6 +252,13 @@
     fallBackToRasterCanvas(fallbackReason);
 }
 
+void RecordingImageBufferSurface::doPaintInvalidation(
+    const FloatRect& dirtyRect) {
+  if (m_fallbackSurface) {
+    m_fallbackSurface->doPaintInvalidation(dirtyRect);
+  }
+}
+
 static RecordingImageBufferSurface::FallbackReason flushReasonToFallbackReason(
     FlushReason reason) {
   switch (reason) {
@@ -298,9 +304,8 @@
   CHECK(m_currentFrame);
   DCHECK(m_currentFrame->getRecordingCanvas());
   DCHECK(fallbackReason);
-  DCHECK_EQ(*fallbackReason, FallbackReasonUnknown);
-
-  if (!m_imageBuffer->isDirty()) {
+  DCHECK(*fallbackReason == FallbackReasonUnknown);
+  if (!m_didRecordDrawCommandsInCurrentFrame) {
     if (!m_previousFrame) {
       // Create an initial blank frame
       m_previousFrame = m_currentFrame->finishRecordingAsPicture();
diff --git a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.h b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.h
index c348e216..41ae58f 100644
--- a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.h
+++ b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurface.h
@@ -65,7 +65,8 @@
                    int x,
                    int y) override;
   void willOverwriteCanvas() override;
-  virtual void finalizeFrame(const FloatRect&);
+  void finalizeFrame() override;
+  void doPaintInvalidation(const FloatRect&) override;
   void setImageBuffer(ImageBuffer*) override;
   sk_sp<SkImage> newImageSnapshot(AccelerationHint, SnapshotReason) override;
   void draw(GraphicsContext&,
diff --git a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurfaceTest.cpp b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurfaceTest.cpp
index 36f8203..47f063b 100644
--- a/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurfaceTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/RecordingImageBufferSurfaceTest.cpp
@@ -25,51 +25,6 @@
 
 namespace blink {
 
-class FakeImageBufferClient : public ImageBufferClient,
-                              public WebThread::TaskObserver {
- public:
-  FakeImageBufferClient(ImageBuffer* imageBuffer)
-      : m_isDirty(false), m_imageBuffer(imageBuffer), m_frameCount(0) {}
-
-  ~FakeImageBufferClient() override {}
-
-  // ImageBufferClient implementation
-  void notifySurfaceInvalid() override {}
-  bool isDirty() override { return m_isDirty; }
-  void didDisableAcceleration() override {}
-  void didFinalizeFrame() override {
-    if (m_isDirty) {
-      Platform::current()->currentThread()->removeTaskObserver(this);
-      m_isDirty = false;
-    }
-    ++m_frameCount;
-  }
-
-  // TaskObserver implementation
-  void willProcessTask() override { NOTREACHED(); }
-  void didProcessTask() override {
-    ASSERT_TRUE(m_isDirty);
-    FloatRect dirtyRect(0, 0, 1, 1);
-    m_imageBuffer->finalizeFrame(dirtyRect);
-    ASSERT_FALSE(m_isDirty);
-  }
-  void restoreCanvasMatrixClipStack(PaintCanvas*) const override {}
-
-  void fakeDraw() {
-    if (m_isDirty)
-      return;
-    m_isDirty = true;
-    Platform::current()->currentThread()->addTaskObserver(this);
-  }
-
-  int frameCount() { return m_frameCount; }
-
- private:
-  bool m_isDirty;
-  ImageBuffer* m_imageBuffer;
-  int m_frameCount;
-};
-
 class MockSurfaceFactory : public RecordingImageBufferFallbackSurfaceFactory {
  public:
   MockSurfaceFactory() : m_createSurfaceCount(0) {}
@@ -107,113 +62,13 @@
     // properly initialized with a GraphicsContext
     m_imageBuffer = ImageBuffer::create(std::move(testSurface));
     EXPECT_FALSE(!m_imageBuffer);
-    m_fakeImageBufferClient =
-        WTF::wrapUnique(new FakeImageBufferClient(m_imageBuffer.get()));
-    m_imageBuffer->setClient(m_fakeImageBufferClient.get());
+    m_testSurface->initializeCurrentFrame();
   }
 
  public:
-  void testEmptyRecord() {
-    m_testSurface->initializeCurrentFrame();
-    sk_sp<PaintRecord> record = m_testSurface->getRecord();
-    EXPECT_TRUE((bool)record.get());
-    EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-  }
-
-  void testNoFallbackWithClear() {
-    m_testSurface->initializeCurrentFrame();
-    m_testSurface->willOverwriteCanvas();
-    m_testSurface->getRecord();
-    EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-  }
-
-  void testNonAnimatedCanvasUpdate() {
-    m_testSurface->initializeCurrentFrame();
-    // Acquire record twice to simulate a static canvas: nothing drawn between
-    // updates.
-    m_fakeImageBufferClient->fakeDraw();
-    m_testSurface->getRecord();
-    m_testSurface->getRecord();
-    EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-  }
-
-  void testAnimatedWithoutClear() {
-    m_testSurface->initializeCurrentFrame();
-    m_fakeImageBufferClient->fakeDraw();
-    m_testSurface->getRecord();
-    EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
-    EXPECT_EQ(0, m_surfaceFactory->createSurfaceCount());
-    expectDisplayListEnabled(true);  // first frame has an implicit clear
-    m_fakeImageBufferClient->fakeDraw();
-    m_testSurface->getRecord();
-    EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(false);
-  }
-
-  void testFrameFinalizedByTaskObserver1() {
-    m_testSurface->initializeCurrentFrame();
-    expectDisplayListEnabled(true);
-    m_testSurface->getRecord();
-    EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-    m_fakeImageBufferClient->fakeDraw();
-    EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-    m_testSurface->getRecord();
-    EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-    m_fakeImageBufferClient->fakeDraw();
-    EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-    // Display list will be disabled only after exiting the runLoop
-  }
-  void testFrameFinalizedByTaskObserver2() {
-    EXPECT_EQ(3, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(false);
-    m_testSurface->getRecord();
-    EXPECT_EQ(3, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(false);
-    m_fakeImageBufferClient->fakeDraw();
-    EXPECT_EQ(3, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(false);
-  }
-
-  void testAnimatedWithClear() {
-    m_testSurface->initializeCurrentFrame();
-    m_testSurface->getRecord();
-    m_testSurface->willOverwriteCanvas();
-    m_fakeImageBufferClient->fakeDraw();
-    EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
-    m_testSurface->getRecord();
-    EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-    // clear after use
-    m_fakeImageBufferClient->fakeDraw();
-    m_testSurface->willOverwriteCanvas();
-    EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
-    m_testSurface->getRecord();
-    EXPECT_EQ(3, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-  }
-
-  void testClearRect() {
-    m_testSurface->initializeCurrentFrame();
-    m_testSurface->getRecord();
-    PaintFlags clearPaint;
-    clearPaint.setBlendMode(SkBlendMode::kClear);
-    m_imageBuffer->canvas()->drawRect(
-        SkRect::MakeWH(m_testSurface->size().width(),
-                       m_testSurface->size().height()),
-        clearPaint);
-    m_fakeImageBufferClient->fakeDraw();
-    EXPECT_EQ(1, m_fakeImageBufferClient->frameCount());
-    m_testSurface->getRecord();
-    EXPECT_EQ(2, m_fakeImageBufferClient->frameCount());
-    expectDisplayListEnabled(true);
-  }
+  RecordingImageBufferSurface* testSurface() { return m_testSurface; }
+  int createSurfaceCount() { return m_surfaceFactory->createSurfaceCount(); }
+  SkCanvas* canvas() { return m_imageBuffer->canvas(); }
 
   void expectDisplayListEnabled(bool displayListEnabled) {
     EXPECT_EQ(displayListEnabled, (bool)m_testSurface->m_currentFrame.get());
@@ -227,53 +82,62 @@
  private:
   MockSurfaceFactory* m_surfaceFactory;
   RecordingImageBufferSurface* m_testSurface;
-  std::unique_ptr<FakeImageBufferClient> m_fakeImageBufferClient;
   std::unique_ptr<ImageBuffer> m_imageBuffer;
 };
 
-#define CALL_TEST_TASK_WRAPPER(TEST_METHOD)                               \
-  {                                                                       \
-    ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> \
-        platform;                                                         \
-    Platform::current()->currentThread()->getWebTaskRunner()->postTask(   \
-        BLINK_FROM_HERE,                                                  \
-        WTF::bind(&RecordingImageBufferSurfaceTest::TEST_METHOD,          \
-                  WTF::unretained(this)));                                \
-    platform->runUntilIdle();                                             \
-  }
-
-TEST_F(RecordingImageBufferSurfaceTest, testEmptyRecord) {
-  testEmptyRecord();
+TEST_F(RecordingImageBufferSurfaceTest, testEmptyPicture) {
+  sk_sp<SkPicture> picture = testSurface()->getRecord();
+  EXPECT_TRUE((bool)picture.get());
+  expectDisplayListEnabled(true);
 }
 
 TEST_F(RecordingImageBufferSurfaceTest, testNoFallbackWithClear) {
-  testNoFallbackWithClear();
+  testSurface()->willOverwriteCanvas();
+  testSurface()->getRecord();
+  expectDisplayListEnabled(true);
 }
 
 TEST_F(RecordingImageBufferSurfaceTest, testNonAnimatedCanvasUpdate) {
-  CALL_TEST_TASK_WRAPPER(testNonAnimatedCanvasUpdate)
+  // Acquire picture twice to simulate a static canvas: nothing drawn between
+  // updates.
+  testSurface()->didDraw(FloatRect(0, 0, 1, 1));
+  testSurface()->getRecord();
+  testSurface()->getRecord();
   expectDisplayListEnabled(true);
 }
 
 TEST_F(RecordingImageBufferSurfaceTest, testAnimatedWithoutClear) {
-  CALL_TEST_TASK_WRAPPER(testAnimatedWithoutClear)
-  expectDisplayListEnabled(false);
-}
-
-TEST_F(RecordingImageBufferSurfaceTest, testFrameFinalizedByTaskObserver) {
-  CALL_TEST_TASK_WRAPPER(testFrameFinalizedByTaskObserver1)
-  expectDisplayListEnabled(false);
-  CALL_TEST_TASK_WRAPPER(testFrameFinalizedByTaskObserver2)
+  testSurface()->didDraw(FloatRect(0, 0, 1, 1));
+  testSurface()->getRecord();
+  EXPECT_EQ(0, createSurfaceCount());
+  expectDisplayListEnabled(true);  // first frame has an implicit clear
+  testSurface()->didDraw(FloatRect(0, 0, 1, 1));
+  testSurface()->getRecord();
   expectDisplayListEnabled(false);
 }
 
 TEST_F(RecordingImageBufferSurfaceTest, testAnimatedWithClear) {
-  CALL_TEST_TASK_WRAPPER(testAnimatedWithClear)
+  testSurface()->getRecord();
+  testSurface()->willOverwriteCanvas();
+  testSurface()->didDraw(FloatRect(0, 0, 1, 1));
+  testSurface()->getRecord();
+  expectDisplayListEnabled(true);
+  // clear after use
+  testSurface()->didDraw(FloatRect(0, 0, 1, 1));
+  testSurface()->willOverwriteCanvas();
+  testSurface()->getRecord();
   expectDisplayListEnabled(true);
 }
 
 TEST_F(RecordingImageBufferSurfaceTest, testClearRect) {
-  CALL_TEST_TASK_WRAPPER(testClearRect);
+  testSurface()->getRecord();
+  SkPaint clearPaint;
+  clearPaint.setBlendMode(SkBlendMode::kClear);
+  canvas()->drawRect(SkRect::MakeWH(testSurface()->size().width(),
+                                    testSurface()->size().height()),
+                     clearPaint);
+  testSurface()->didDraw(FloatRect(0, 0, 1, 1));
+  testSurface()->getRecord();
   expectDisplayListEnabled(true);
 }
 
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 318c7361..09c5d2ef 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -163,6 +163,8 @@
   settings.enable_color_correct_rendering =
       command_line->HasSwitch(cc::switches::kEnableColorCorrectRendering) ||
       command_line->HasSwitch(cc::switches::kEnableTrueColorRendering);
+  settings.renderer_settings.enable_color_correct_rendering =
+      settings.enable_color_correct_rendering;
 
   // UI compositor always uses partial raster if not using zero-copy. Zero copy
   // doesn't currently support partial raster.