diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 447d732..5c1b29be 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -606,6 +606,8 @@
     "paint/display_item_list_unittest.cc",
     "paint/paint_image_unittest.cc",
     "paint/paint_op_buffer_unittest.cc",
+    "paint/paint_shader_unittest.cc",
+    "paint/scoped_image_flags_unittest.cc",
     "paint/solid_color_analyzer_unittest.cc",
     "raster/playback_image_provider_unittest.cc",
     "raster/raster_buffer_provider_unittest.cc",
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index 387800c5..037891d4 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -45,6 +45,8 @@
     "paint_shader.h",
     "record_paint_canvas.cc",
     "record_paint_canvas.h",
+    "scoped_image_flags.cc",
+    "scoped_image_flags.h",
     "skia_paint_canvas.cc",
     "skia_paint_canvas.h",
     "skia_paint_image_generator.cc",
diff --git a/cc/paint/discardable_image_map.cc b/cc/paint/discardable_image_map.cc
index 0d23dd3..05dbe8b 100644
--- a/cc/paint/discardable_image_map.cc
+++ b/cc/paint/discardable_image_map.cc
@@ -27,6 +27,8 @@
   return dst;
 }
 
+// This canvas is used only for tracking transform/clip/filter state from the
+// non-drawing ops.
 class PaintTrackingCanvas final : public SkNoDrawCanvas {
  public:
   PaintTrackingCanvas(int width, int height) : SkNoDrawCanvas(width, height) {}
@@ -75,51 +77,13 @@
 
 class DiscardableImageGenerator {
  public:
-  DiscardableImageGenerator(int width, int height) : canvas_(width, height) {}
-  ~DiscardableImageGenerator() = default;
-
-  void GatherDiscardableImages(const PaintOpBuffer* buffer) {
-    if (!buffer->HasDiscardableImages())
-      return;
-
-    PlaybackParams params(nullptr, canvas_.getTotalMatrix());
-    canvas_.save();
-    // TODO(khushalsagar): Optimize out save/restore blocks if there are no
-    // images in the draw ops between them.
-    for (auto* op : PaintOpBuffer::Iterator(buffer)) {
-      if (op->IsDrawOp()) {
-        SkRect op_rect;
-        if (op->IsPaintOpWithFlags() && PaintOp::GetBounds(op, &op_rect)) {
-          AddImageFromFlags(op_rect,
-                            static_cast<const PaintOpWithFlags*>(op)->flags);
-        }
-
-        PaintOpType op_type = static_cast<PaintOpType>(op->type);
-        if (op_type == PaintOpType::DrawImage) {
-          auto* image_op = static_cast<DrawImageOp*>(op);
-          auto* sk_image = image_op->image.GetSkImage().get();
-          AddImage(image_op->image,
-                   SkRect::MakeIWH(sk_image->width(), sk_image->height()),
-                   SkRect::MakeXYWH(image_op->left, image_op->top,
-                                    sk_image->width(), sk_image->height()),
-                   nullptr, image_op->flags);
-        } else if (op_type == PaintOpType::DrawImageRect) {
-          auto* image_rect_op = static_cast<DrawImageRectOp*>(op);
-          SkMatrix matrix;
-          matrix.setRectToRect(image_rect_op->src, image_rect_op->dst,
-                               SkMatrix::kFill_ScaleToFit);
-          AddImage(image_rect_op->image, image_rect_op->src, image_rect_op->dst,
-                   &matrix, image_rect_op->flags);
-        } else if (op_type == PaintOpType::DrawRecord) {
-          GatherDiscardableImages(
-              static_cast<const DrawRecordOp*>(op)->record.get());
-        }
-      } else {
-        op->Raster(&canvas_, params);
-      }
-    }
-    canvas_.restore();
+  DiscardableImageGenerator(int width,
+                            int height,
+                            const PaintOpBuffer* buffer) {
+    PaintTrackingCanvas canvas(width, height);
+    GatherDiscardableImages(buffer, nullptr, &canvas);
   }
+  ~DiscardableImageGenerator() = default;
 
   std::vector<std::pair<DrawImage, gfx::Rect>> TakeImages() {
     return std::move(image_set_);
@@ -153,48 +117,112 @@
   }
 
  private:
-  void AddImageFromFlags(const SkRect& rect, const PaintFlags& flags) {
-    if (!flags.HasShader() ||
-        flags.getShader()->shader_type() != PaintShader::Type::kImage)
+  // Adds discardable images from |buffer| to the set of images tracked by
+  // this generator. If |buffer| is being used in a DrawOp that requires
+  // rasterization of the buffer as a pre-processing step for execution of the
+  // op (for instance, with PaintRecord backed PaintShaders),
+  // |top_level_op_rect| is set to the rect for that op. If provided, the
+  // |top_level_op_rect| will be used as the rect for tracking the position of
+  // this image in the top-level buffer.
+  void GatherDiscardableImages(const PaintOpBuffer* buffer,
+                               const gfx::Rect* top_level_op_rect,
+                               PaintTrackingCanvas* canvas) {
+    if (!buffer->HasDiscardableImages())
       return;
 
-    const PaintImage& paint_image = flags.getShader()->paint_image();
-    SkMatrix local_matrix = flags.getShader()->GetLocalMatrix();
-    AddImage(paint_image,
-             SkRect::MakeWH(paint_image.width(), paint_image.height()), rect,
-             &local_matrix, flags);
+    // Prevent PaintOpBuffers from having side effects back into the canvas.
+    SkAutoCanvasRestore save_restore(canvas, true);
+
+    PlaybackParams params(nullptr, canvas->getTotalMatrix());
+    // TODO(khushalsagar): Optimize out save/restore blocks if there are no
+    // images in the draw ops between them.
+    for (auto* op : PaintOpBuffer::Iterator(buffer)) {
+      if (!op->IsDrawOp()) {
+        op->Raster(canvas, params);
+        continue;
+      } else if (!PaintOp::OpHasDiscardableImages(op)) {
+        continue;
+      }
+
+      gfx::Rect op_rect;
+      base::Optional<gfx::Rect> local_op_rect;
+
+      if (top_level_op_rect) {
+        op_rect = *top_level_op_rect;
+      } else {
+        local_op_rect = ComputePaintRect(op, canvas);
+        if (local_op_rect.value().IsEmpty())
+          continue;
+
+        op_rect = local_op_rect.value();
+      }
+
+      const SkMatrix& ctm = canvas->getTotalMatrix();
+      if (op->IsPaintOpWithFlags()) {
+        AddImageFromFlags(op_rect,
+                          static_cast<const PaintOpWithFlags*>(op)->flags, ctm);
+      }
+
+      PaintOpType op_type = static_cast<PaintOpType>(op->type);
+      if (op_type == PaintOpType::DrawImage) {
+        auto* image_op = static_cast<DrawImageOp*>(op);
+        auto* sk_image = image_op->image.GetSkImage().get();
+        AddImage(image_op->image,
+                 SkRect::MakeIWH(sk_image->width(), sk_image->height()),
+                 op_rect, ctm, image_op->flags.getFilterQuality());
+      } else if (op_type == PaintOpType::DrawImageRect) {
+        auto* image_rect_op = static_cast<DrawImageRectOp*>(op);
+        SkMatrix matrix = ctm;
+        matrix.postConcat(SkMatrix::MakeRectToRect(image_rect_op->src,
+                                                   image_rect_op->dst,
+                                                   SkMatrix::kFill_ScaleToFit));
+        AddImage(image_rect_op->image, image_rect_op->src, op_rect, matrix,
+                 image_rect_op->flags.getFilterQuality());
+      } else if (op_type == PaintOpType::DrawRecord) {
+        GatherDiscardableImages(
+            static_cast<const DrawRecordOp*>(op)->record.get(),
+            top_level_op_rect, canvas);
+      }
+    }
   }
 
-  void AddImage(PaintImage paint_image,
-                const SkRect& src_rect,
-                const SkRect& rect,
-                const SkMatrix* local_matrix,
-                const PaintFlags& flags) {
-    if (!paint_image.IsLazyGenerated())
-      return;
+  // Given the |op_rect|, which is the rect for the draw op, returns the
+  // transformed rect accounting for the current transform, clip and paint
+  // state on |canvas_|.
+  gfx::Rect ComputePaintRect(const PaintOp* op, PaintTrackingCanvas* canvas) {
+    const SkRect& clip_rect = SkRect::Make(canvas->getDeviceClipBounds());
+    const SkMatrix& ctm = canvas->getTotalMatrix();
 
-    const SkRect& clip_rect = SkRect::Make(canvas_.getDeviceClipBounds());
-    const SkMatrix& ctm = canvas_.getTotalMatrix();
+    gfx::Rect transformed_rect;
+    SkRect op_rect;
+    if (!PaintOp::GetBounds(op, &op_rect)) {
+      // If we can't provide a conservative bounding rect for the op, assume it
+      // covers the complete current clip.
+      transformed_rect = gfx::ToEnclosingRect(gfx::SkRectToRectF(clip_rect));
+    } else {
+      const PaintFlags* flags =
+          op->IsPaintOpWithFlags()
+              ? &static_cast<const PaintOpWithFlags*>(op)->flags
+              : nullptr;
+      SkPaint paint;
+      if (flags)
+        paint = flags->ToSkPaint();
 
-    SkRect paint_rect = MapRect(ctm, rect);
-    SkPaint paint = flags.ToSkPaint();
-    bool computed_paint_bounds =
-        canvas_.ComputePaintBounds(paint_rect, &paint, &paint_rect);
-    if (!computed_paint_bounds) {
-      // TODO(vmpstr): UMA this case.
-      paint_rect = clip_rect;
+      SkRect paint_rect = MapRect(ctm, op_rect);
+      bool computed_paint_bounds =
+          canvas->ComputePaintBounds(paint_rect, &paint, &paint_rect);
+      if (!computed_paint_bounds) {
+        // TODO(vmpstr): UMA this case.
+        paint_rect = clip_rect;
+      }
+
+      // Clamp the image rect by the current clip rect.
+      if (!paint_rect.intersect(clip_rect))
+        return gfx::Rect();
+
+      transformed_rect = gfx::ToEnclosingRect(gfx::SkRectToRectF(paint_rect));
     }
 
-    // Clamp the image rect by the current clip rect.
-    if (!paint_rect.intersect(clip_rect))
-      return;
-
-    SkFilterQuality filter_quality = flags.getFilterQuality();
-
-    SkIRect src_irect;
-    src_rect.roundOut(&src_irect);
-    gfx::Rect image_rect = gfx::ToEnclosingRect(gfx::SkRectToRectF(paint_rect));
-
     // During raster, we use the device clip bounds on the canvas, which outsets
     // the actual clip by 1 due to the possibility of antialiasing. Account for
     // this here by outsetting the image rect by 1. Note that this only affects
@@ -205,7 +233,52 @@
     // raster time, since we might be sending a larger-than-one-item display
     // item to skia, which means that skia will internally determine whether to
     // raster the picture (using device clip bounds that are outset).
-    image_rect.Inset(-1, -1);
+    transformed_rect.Inset(-1, -1);
+    return transformed_rect;
+  }
+
+  void AddImageFromFlags(const gfx::Rect& op_rect,
+                         const PaintFlags& flags,
+                         const SkMatrix& ctm) {
+    if (!flags.getShader())
+      return;
+
+    if (flags.getShader()->shader_type() == PaintShader::Type::kImage) {
+      const PaintImage& paint_image = flags.getShader()->paint_image();
+      SkMatrix matrix = ctm;
+      matrix.postConcat(flags.getShader()->GetLocalMatrix());
+      AddImage(paint_image,
+               SkRect::MakeWH(paint_image.width(), paint_image.height()),
+               op_rect, matrix, flags.getFilterQuality());
+    } else if (flags.getShader()->shader_type() ==
+                   PaintShader::Type::kPaintRecord &&
+               flags.getShader()->paint_record()->HasDiscardableImages()) {
+      SkRect scaled_tile_rect;
+      if (!flags.getShader()->GetRasterizationTileRect(ctm,
+                                                       &scaled_tile_rect)) {
+        return;
+      }
+
+      PaintTrackingCanvas canvas(scaled_tile_rect.width(),
+                                 scaled_tile_rect.height());
+      canvas.setMatrix(SkMatrix::MakeRectToRect(flags.getShader()->tile(),
+                                                scaled_tile_rect,
+                                                SkMatrix::kFill_ScaleToFit));
+      GatherDiscardableImages(flags.getShader()->paint_record().get(), &op_rect,
+                              &canvas);
+    }
+  }
+
+  void AddImage(PaintImage paint_image,
+                const SkRect& src_rect,
+                const gfx::Rect& image_rect,
+                const SkMatrix& matrix,
+                SkFilterQuality filter_quality) {
+    if (!paint_image.IsLazyGenerated())
+      return;
+
+    SkIRect src_irect;
+    src_rect.roundOut(&src_irect);
 
     // Make a note if any image was originally specified in a non-sRGB color
     // space.
@@ -217,10 +290,6 @@
       color_stats_srgb_image_count_++;
     }
 
-    SkMatrix matrix = ctm;
-    if (local_matrix)
-      matrix.postConcat(*local_matrix);
-
     image_id_to_rect_[paint_image.stable_id()].Union(image_rect);
 
     if (paint_image.ShouldAnimate()) {
@@ -235,9 +304,6 @@
         image_rect);
   }
 
-  // This canvas is used only for tracking transform/clip/filter state from the
-  // non-drawing ops.
-  PaintTrackingCanvas canvas_;
   std::vector<std::pair<DrawImage, gfx::Rect>> image_set_;
   base::flat_map<PaintImage::Id, gfx::Rect> image_id_to_rect_;
   std::vector<DiscardableImageMap::AnimatedImageMetadata>
@@ -263,8 +329,8 @@
   if (!paint_op_buffer->HasDiscardableImages())
     return;
 
-  DiscardableImageGenerator generator(bounds.right(), bounds.bottom());
-  generator.GatherDiscardableImages(paint_op_buffer);
+  DiscardableImageGenerator generator(bounds.right(), bounds.bottom(),
+                                      paint_op_buffer);
   generator.RecordColorHistograms();
   image_id_to_rect_ = generator.TakeImageIdToRectMap();
   animated_images_metadata_ = generator.TakeAnimatedImagesMetadata();
diff --git a/cc/paint/discardable_image_map_unittest.cc b/cc/paint/discardable_image_map_unittest.cc
index 0e8a6d20e..4530746 100644
--- a/cc/paint/discardable_image_map_unittest.cc
+++ b/cc/paint/discardable_image_map_unittest.cc
@@ -747,6 +747,43 @@
   EXPECT_DCHECK_DEATH(images[2]->frame_index());
 }
 
+TEST_F(DiscardableImageMapTest, CapturesImagesInPaintRecordShaders) {
+  // Create the record to use in the shader.
+  auto shader_record = sk_make_sp<PaintOpBuffer>();
+  shader_record->push<ScaleOp>(2.0f, 2.0f);
+  PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(100, 100));
+  shader_record->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr);
+
+  gfx::Rect visible_rect(500, 500);
+  scoped_refptr<DisplayItemList> display_list = new DisplayItemList();
+  display_list->StartPaint();
+  display_list->push<ScaleOp>(2.0f, 2.0f);
+  PaintFlags flags;
+  SkRect tile = SkRect::MakeWH(100, 100);
+  flags.setShader(PaintShader::MakePaintRecord(
+      shader_record, tile, SkShader::TileMode::kClamp_TileMode,
+      SkShader::TileMode::kClamp_TileMode, nullptr));
+  display_list->push<DrawRectOp>(SkRect::MakeWH(200, 200), flags);
+  display_list->EndPaintOfUnpaired(visible_rect);
+  display_list->Finalize();
+
+  display_list->GenerateDiscardableImagesMetadata();
+  const auto& image_map = display_list->discardable_image_map();
+
+  // The image rect is set to the rect for the DrawRectOp.
+  std::vector<PositionScaleDrawImage> draw_images =
+      GetDiscardableImagesInRect(image_map, visible_rect);
+  std::vector<gfx::Rect> inset_rects = InsetImageRects(draw_images);
+  ASSERT_EQ(draw_images.size(), 1u);
+  EXPECT_EQ(draw_images[0].image, paint_image);
+  // The position of the image is the position of the DrawRectOp that uses the
+  // shader.
+  EXPECT_EQ(gfx::Rect(400, 400), inset_rects[0]);
+  // The scale of the image includes the scale at which the shader record is
+  // rasterized.
+  EXPECT_EQ(SkSize::Make(4.f, 4.f), draw_images[0].scale);
+}
+
 class DiscardableImageMapColorSpaceTest
     : public DiscardableImageMapTest,
       public testing::WithParamInterface<gfx::ColorSpace> {};
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 4631780..ab441cd 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -13,6 +13,7 @@
 #include "cc/paint/paint_op_reader.h"
 #include "cc/paint/paint_op_writer.h"
 #include "cc/paint/paint_record.h"
+#include "cc/paint/scoped_image_flags.h"
 #include "third_party/skia/include/core/SkAnnotation.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkRegion.h"
@@ -25,22 +26,6 @@
   return result;
 }
 
-bool IsImageShader(const PaintFlags& flags) {
-  return flags.HasShader() &&
-         flags.getShader()->shader_type() == PaintShader::Type::kImage;
-}
-
-bool IsImageOp(const PaintOp* op) {
-  if (op->GetType() == PaintOpType::DrawImage)
-    return true;
-  else if (op->GetType() == PaintOpType::DrawImageRect)
-    return true;
-  else if (op->IsDrawOp() && op->IsPaintOpWithFlags())
-    return IsImageShader(static_cast<const PaintOpWithFlags*>(op)->flags);
-
-  return false;
-}
-
 bool QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas) {
   DCHECK(op->IsDrawOp());
 
@@ -58,65 +43,6 @@
   return canvas->quickReject(rect);
 }
 
-// Encapsulates a ImageProvider::DecodedImageHolder and a SkPaint. Use of
-// this class ensures that the DecodedImageHolder outlives the dependent
-// SkPaint.
-class ScopedImageFlags {
- public:
-  ScopedImageFlags(ImageProvider* image_provider,
-                   const PaintFlags& flags,
-                   const SkMatrix& ctm) {
-    DCHECK(IsImageShader(flags));
-
-    const PaintImage& paint_image = flags.getShader()->paint_image();
-    SkMatrix matrix = flags.getShader()->GetLocalMatrix();
-
-    SkMatrix total_image_matrix = matrix;
-    total_image_matrix.preConcat(ctm);
-    SkRect src_rect =
-        SkRect::MakeIWH(paint_image.width(), paint_image.height());
-    DrawImage draw_image(paint_image, RoundOutRect(src_rect),
-                         flags.getFilterQuality(), total_image_matrix);
-    scoped_decoded_draw_image_ =
-        image_provider->GetDecodedDrawImage(draw_image);
-
-    if (!scoped_decoded_draw_image_)
-      return;
-    const auto& decoded_image = scoped_decoded_draw_image_.decoded_image();
-    DCHECK(decoded_image.image());
-
-    bool need_scale = !decoded_image.is_scale_adjustment_identity();
-    if (need_scale) {
-      matrix.preScale(1.f / decoded_image.scale_adjustment().width(),
-                      1.f / decoded_image.scale_adjustment().height());
-    }
-
-    sk_sp<SkImage> sk_image =
-        sk_ref_sp<SkImage>(const_cast<SkImage*>(decoded_image.image().get()));
-    PaintImage decoded_paint_image = PaintImageBuilder()
-                                         .set_id(paint_image.stable_id())
-                                         .set_image(std::move(sk_image))
-                                         .TakePaintImage();
-    decoded_flags_.emplace(flags);
-    decoded_flags_.value().setFilterQuality(decoded_image.filter_quality());
-    decoded_flags_.value().setShader(
-        PaintShader::MakeImage(decoded_paint_image, flags.getShader()->tx(),
-                               flags.getShader()->ty(), &matrix));
-  }
-
-  PaintFlags* decoded_flags() {
-    return decoded_flags_ ? &decoded_flags_.value() : nullptr;
-  }
-
-  ~ScopedImageFlags() = default;
-
- private:
-  base::Optional<PaintFlags> decoded_flags_;
-  ImageProvider::ScopedDecodedDrawImage scoped_decoded_draw_image_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedImageFlags);
-};
-
 void RasterWithAlpha(const PaintOp* op,
                      SkCanvas* canvas,
                      const PlaybackParams& params,
@@ -133,7 +59,7 @@
     // ImageProvider if it consists of an image shader.
     base::Optional<ScopedImageFlags> scoped_flags;
     const PaintFlags* decoded_flags = &flags_op->flags;
-    if (params.image_provider && IsImageShader(flags_op->flags)) {
+    if (params.image_provider && flags_op->HasDiscardableImagesFromFlags()) {
       scoped_flags.emplace(params.image_provider, flags_op->flags,
                            canvas->getTotalMatrix());
       decoded_flags = scoped_flags.value().decoded_flags();
@@ -1457,12 +1383,47 @@
   return false;
 }
 
+// static
+bool PaintOp::OpHasDiscardableImages(const PaintOp* op) {
+  if (op->IsPaintOpWithFlags() && static_cast<const PaintOpWithFlags*>(op)
+                                      ->HasDiscardableImagesFromFlags()) {
+    return true;
+  }
+
+  if (op->GetType() == PaintOpType::DrawImage &&
+      static_cast<const DrawImageOp*>(op)->HasDiscardableImages()) {
+    return true;
+  } else if (op->GetType() == PaintOpType::DrawImageRect &&
+             static_cast<const DrawImageRectOp*>(op)->HasDiscardableImages()) {
+    return true;
+  } else if (op->GetType() == PaintOpType::DrawRecord &&
+             static_cast<const DrawRecordOp*>(op)->HasDiscardableImages()) {
+    return true;
+  }
+
+  return false;
+}
+
 void PaintOp::DestroyThis() {
   auto func = g_destructor_functions[type];
   if (func)
     func(this);
 }
 
+bool PaintOpWithFlags::HasDiscardableImagesFromFlags() const {
+  if (!IsDrawOp())
+    return false;
+
+  if (!flags.HasShader())
+    return false;
+  else if (flags.getShader()->shader_type() == PaintShader::Type::kImage)
+    return flags.getShader()->paint_image().IsLazyGenerated();
+  else if (flags.getShader()->shader_type() == PaintShader::Type::kPaintRecord)
+    return flags.getShader()->paint_record()->HasDiscardableImages();
+
+  return false;
+}
+
 void PaintOpWithFlags::RasterWithFlags(SkCanvas* canvas,
                                        const PaintFlags* flags,
                                        const PlaybackParams& params) const {
@@ -1740,7 +1701,8 @@
           // general case we defer this to the SkCanvas but if we will be
           // using an ImageProvider for pre-decoding images, we can save
           // performing an expensive decode that will never be rasterized.
-          const bool skip_op = params.image_provider && IsImageOp(draw_op) &&
+          const bool skip_op = params.image_provider &&
+                               PaintOp::OpHasDiscardableImages(draw_op) &&
                                QuickRejectDraw(draw_op, canvas);
           if (skip_op) {
             // Now that we know this op will be skipped, we can push the save
@@ -1770,15 +1732,15 @@
       }
     }
 
-    if (params.image_provider && IsImageOp(op)) {
+    if (params.image_provider && PaintOp::OpHasDiscardableImages(op)) {
       if (QuickRejectDraw(op, canvas))
         continue;
 
       auto* flags_op = op->IsPaintOpWithFlags()
                            ? static_cast<const PaintOpWithFlags*>(op)
                            : nullptr;
-      if (flags_op && IsImageShader(flags_op->flags)) {
-        ScopedImageFlags scoped_flags(image_provider, flags_op->flags,
+      if (flags_op && flags_op->HasDiscardableImagesFromFlags()) {
+        ScopedImageFlags scoped_flags(params.image_provider, flags_op->flags,
                                       canvas->getTotalMatrix());
 
         // Only rasterize the op if we successfully decoded the image.
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index 17405ec..96eb7f7 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -15,6 +15,7 @@
 #include "base/memory/aligned_memory.h"
 #include "base/optional.h"
 #include "cc/base/math_util.h"
+#include "cc/paint/image_provider.h"
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/paint_export.h"
 #include "cc/paint/paint_flags.h"
@@ -29,7 +30,6 @@
 
 namespace cc {
 class ImageDecodeCache;
-class ImageProvider;
 
 class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix {
  public:
@@ -137,6 +137,10 @@
   // for the op.
   static bool GetBounds(const PaintOp* op, SkRect* rect);
 
+  // Returns true if executing this op will require decoding of any lazy
+  // generated images.
+  static bool OpHasDiscardableImages(const PaintOp* op);
+
   int CountSlowPaths() const { return 0; }
   int CountSlowPathsFromFlags() const { return 0; }
 
@@ -195,14 +199,7 @@
 
   int CountSlowPathsFromFlags() const { return flags.getPathEffect() ? 1 : 0; }
   bool HasNonAAPaint() const { return !flags.isAntiAlias(); }
-  bool HasDiscardableImagesFromFlags() const {
-    if (!IsDrawOp())
-      return false;
-
-    SkShader* shader = flags.getSkShader();
-    SkImage* image = shader ? shader->isAImage(nullptr, nullptr) : nullptr;
-    return image && image->isLazyGenerated();
-  }
+  bool HasDiscardableImagesFromFlags() const;
 
   void RasterWithFlags(SkCanvas* canvas,
                        const PaintFlags* flags,
@@ -981,6 +978,7 @@
   friend class DisplayItemList;
   friend class PaintOpBufferOffsetsTest;
   friend class SolidColorAnalyzer;
+  friend class ScopedImageFlags;
 
   // Replays the paint op buffer into the canvas. If |indices| is specified, it
   // contains indices in an increasing order and only the indices specified in
diff --git a/cc/paint/paint_shader.cc b/cc/paint/paint_shader.cc
index 7d0b8cf..6f0767086 100644
--- a/cc/paint/paint_shader.cc
+++ b/cc/paint/paint_shader.cc
@@ -6,9 +6,25 @@
 
 #include "base/memory/ptr_util.h"
 #include "cc/paint/paint_record.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
 
 namespace cc {
+namespace {
+
+sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record,
+                             const SkRect& bounds,
+                             const SkMatrix* matrix,
+                             ImageProvider* image_provider) {
+  SkPictureRecorder recorder;
+  SkCanvas* canvas = recorder.beginRecording(bounds);
+  if (matrix)
+    canvas->setMatrix(*matrix);
+  record->Playback(canvas, image_provider);
+  return recorder.finishRecordingAsPicture();
+}
+
+}  // namespace
 
 sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) {
   sk_sp<PaintShader> shader(new PaintShader(Type::kColor));
@@ -139,6 +155,96 @@
 PaintShader::PaintShader(Type type) : shader_type_(type) {}
 PaintShader::~PaintShader() = default;
 
+bool PaintShader::GetRasterizationTileRect(const SkMatrix& ctm,
+                                           SkRect* tile_rect) const {
+  DCHECK_EQ(shader_type_, Type::kPaintRecord);
+
+  // If we are using a fixed scale, the record is rasterized with the original
+  // tile size and scaling is applied to the generated output.
+  if (scaling_behavior_ == ScalingBehavior::kFixedScale) {
+    *tile_rect = tile_;
+    return true;
+  }
+
+  SkMatrix matrix = ctm;
+  if (local_matrix_.has_value())
+    matrix.preConcat(local_matrix_.value());
+
+  SkSize scale;
+  if (!matrix.decomposeScale(&scale)) {
+    // Decomposition failed, use an approximation.
+    scale.set(SkScalarSqrt(matrix.getScaleX() * matrix.getScaleX() +
+                           matrix.getSkewX() * matrix.getSkewX()),
+              SkScalarSqrt(matrix.getScaleY() * matrix.getScaleY() +
+                           matrix.getSkewY() * matrix.getSkewY()));
+  }
+  SkSize scaled_size =
+      SkSize::Make(SkScalarAbs(scale.width() * tile_.width()),
+                   SkScalarAbs(scale.height() * tile_.height()));
+
+  // Clamp the tile size to about 4M pixels.
+  // TODO(khushalsagar): We need to consider the max texture size as well.
+  static const SkScalar kMaxTileArea = 2048 * 2048;
+  SkScalar tile_area = scaled_size.width() * scaled_size.height();
+  if (tile_area > kMaxTileArea) {
+    SkScalar clamp_scale = SkScalarSqrt(kMaxTileArea / tile_area);
+    scaled_size.set(scaled_size.width() * clamp_scale,
+                    scaled_size.height() * clamp_scale);
+  }
+
+  scaled_size = scaled_size.toCeil();
+  if (scaled_size.isEmpty())
+    return false;
+
+  *tile_rect = SkRect::MakeWH(scaled_size.width(), scaled_size.height());
+  return true;
+}
+
+sk_sp<PaintShader> PaintShader::CreateDecodedPaintRecord(
+    const SkMatrix& ctm,
+    ImageProvider* image_provider) const {
+  DCHECK_EQ(shader_type_, Type::kPaintRecord);
+
+  // For creating a decoded PaintRecord shader, we need to do the following:
+  // 1) Figure out the scale at which the record should be rasterization given
+  //    the ctm and local_matrix on the shader.
+  // 2) Transform this record to an SkPicture with this scale and replace
+  //    encoded images in this record with decodes from the ImageProvider. This
+  //    is done by setting the rasterization_matrix_ for this shader to be used
+  //    in GetSkShader.
+  // 3) Since the SkShader will use a scaled SkPicture, we use a kFixedScale for
+  //    the decoded shader which creates an SkPicture backed SkImage for
+  //    creating the decoded SkShader.
+  // Note that the scaling logic here is replicated from
+  // SkPictureShader::refBitmapShader.
+  SkRect tile_rect;
+  if (!GetRasterizationTileRect(ctm, &tile_rect))
+    return nullptr;
+
+  sk_sp<PaintShader> shader(new PaintShader(Type::kPaintRecord));
+  shader->record_ = record_;
+  shader->tile_ = tile_rect;
+  // Use a fixed scale since we have already scaled the tile rect and fixed the
+  // raster scale.
+  shader->scaling_behavior_ = ScalingBehavior::kFixedScale;
+  shader->rasterization_matrix_.emplace();
+  shader->rasterization_matrix_.value().setRectToRect(
+      tile_, tile_rect, SkMatrix::kFill_ScaleToFit);
+  shader->tx_ = tx_;
+  shader->ty_ = ty_;
+
+  const SkSize tile_scale =
+      SkSize::Make(SkIntToScalar(tile_rect.width()) / tile_.width(),
+                   SkIntToScalar(tile_rect.height()) / tile_.height());
+  shader->local_matrix_ = GetLocalMatrix();
+  shader->local_matrix_->preScale(1 / tile_scale.width(),
+                                  1 / tile_scale.height());
+
+  shader->image_provider_ = image_provider;
+
+  return shader;
+}
+
 sk_sp<SkShader> PaintShader::GetSkShader() const {
   if (cached_shader_)
     return cached_shader_;
@@ -182,7 +288,12 @@
           tx_, ty_, local_matrix_ ? &*local_matrix_ : nullptr);
       break;
     case Type::kPaintRecord: {
-      auto picture = ToSkPicture(record_, tile_);
+      // Create a recording at the desired scale if this record has images which
+      // have been decoded before raster.
+      auto picture = ToSkPicture(
+          record_, tile_,
+          rasterization_matrix_ ? &rasterization_matrix_.value() : nullptr,
+          image_provider_);
 
       switch (scaling_behavior_) {
         // For raster scale, we create a picture shader directly.
@@ -191,7 +302,7 @@
               std::move(picture), tx_, ty_,
               local_matrix_ ? &*local_matrix_ : nullptr, nullptr);
           break;
-        // For fixed scale, we create an image shader with and image backed by
+        // For fixed scale, we create an image shader with an image backed by
         // the picture.
         case ScalingBehavior::kFixedScale: {
           auto image = SkImage::MakeFromPicture(
diff --git a/cc/paint/paint_shader.h b/cc/paint/paint_shader.h
index 8a133709..42ec44be 100644
--- a/cc/paint/paint_shader.h
+++ b/cc/paint/paint_shader.h
@@ -16,7 +16,7 @@
 #include "third_party/skia/include/core/SkShader.h"
 
 namespace cc {
-
+class ImageProvider;
 class PaintOpBuffer;
 using PaintRecord = PaintOpBuffer;
 
@@ -111,8 +111,12 @@
     return image_;
   }
 
+  const sk_sp<PaintRecord>& paint_record() const { return record_; }
+  bool GetRasterizationTileRect(const SkMatrix& ctm, SkRect* tile_rect) const;
+
   SkShader::TileMode tx() const { return tx_; }
   SkShader::TileMode ty() const { return ty_; }
+  SkRect tile() const { return tile_; }
 
   bool IsOpaque() const;
 
@@ -127,11 +131,17 @@
   friend class PaintOpReader;
   friend class PaintOpSerializationTestUtils;
   friend class PaintOpWriter;
+  friend class ScopedImageFlags;
+  FRIEND_TEST_ALL_PREFIXES(PaintShaderTest, DecodePaintRecord);
 
   explicit PaintShader(Type type);
 
   sk_sp<SkShader> GetSkShader() const;
 
+  sk_sp<PaintShader> CreateDecodedPaintRecord(
+      const SkMatrix& ctm,
+      ImageProvider* image_provider) const;
+
   void SetColorsAndPositions(const SkColor* colors,
                              const SkScalar* positions,
                              int count);
@@ -166,6 +176,12 @@
   std::vector<SkColor> colors_;
   std::vector<SkScalar> positions_;
 
+  // The following are only used during raster to replace the decoded images in
+  // the record for this shader. The |image_provider_| and
+  // |decoded_image_stash_| must outlive this shader.
+  ImageProvider* image_provider_ = nullptr;
+  base::Optional<SkMatrix> rasterization_matrix_;
+
   mutable sk_sp<SkShader> cached_shader_;
 
   DISALLOW_COPY_AND_ASSIGN(PaintShader);
diff --git a/cc/paint/paint_shader_unittest.cc b/cc/paint/paint_shader_unittest.cc
new file mode 100644
index 0000000..bda3fb6
--- /dev/null
+++ b/cc/paint/paint_shader_unittest.cc
@@ -0,0 +1,119 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_shader.h"
+
+#include "cc/paint/draw_image.h"
+#include "cc/paint/image_provider.h"
+#include "cc/paint/paint_image_builder.h"
+#include "cc/paint/paint_op_buffer.h"
+#include "cc/test/fake_paint_image_generator.h"
+#include "cc/test/skia_common.h"
+#include "cc/test/test_skcanvas.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace cc {
+namespace {
+
+class MockImageGenerator : public FakePaintImageGenerator {
+ public:
+  explicit MockImageGenerator(const gfx::Size& size)
+      : FakePaintImageGenerator(
+            SkImageInfo::MakeN32Premul(size.width(), size.height())) {}
+
+  MOCK_METHOD5(GetPixels,
+               bool(const SkImageInfo&, void*, size_t, size_t, uint32_t));
+};
+
+class MockImageProvider : public ImageProvider {
+ public:
+  MockImageProvider() = default;
+  ~MockImageProvider() override = default;
+
+  ScopedDecodedDrawImage GetDecodedDrawImage(
+      const DrawImage& draw_image) override {
+    draw_image_ = draw_image;
+
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(10, 10);
+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
+    return ScopedDecodedDrawImage(
+        DecodedDrawImage(image, SkSize::MakeEmpty(), SkSize::Make(1.0f, 1.0f),
+                         draw_image.filter_quality()));
+  }
+
+  const DrawImage& draw_image() const { return draw_image_; }
+
+ private:
+  DrawImage draw_image_;
+};
+
+}  // namespace
+
+TEST(PaintShaderTest, RasterizationRectForRecordShaders) {
+  SkMatrix local_matrix = SkMatrix::MakeScale(0.5f, 0.5f);
+  auto record_shader = PaintShader::MakePaintRecord(
+      sk_make_sp<PaintOpBuffer>(), SkRect::MakeWH(100, 100),
+      SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode,
+      &local_matrix);
+
+  SkRect tile_rect;
+  SkMatrix ctm = SkMatrix::MakeScale(0.5f, 0.5f);
+  EXPECT_TRUE(record_shader->GetRasterizationTileRect(ctm, &tile_rect));
+  EXPECT_EQ(tile_rect, SkRect::MakeWH(25, 25));
+}
+
+TEST(PaintShaderTest, DecodePaintRecord) {
+  auto record = sk_make_sp<PaintOpBuffer>();
+
+  // Use a strict mock for the generator. It should never be used when
+  // rasterizing this shader, since the decode should be done by the
+  // ImageProvider.
+  auto generator =
+      sk_make_sp<testing::StrictMock<MockImageGenerator>>(gfx::Size(100, 100));
+  PaintImage paint_image = PaintImageBuilder()
+                               .set_id(PaintImage::GetNextId())
+                               .set_paint_image_generator(generator)
+                               .TakePaintImage();
+
+  record->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr);
+  SkMatrix local_matrix = SkMatrix::MakeScale(0.5f, 0.5f);
+  auto record_shader = PaintShader::MakePaintRecord(
+      record, SkRect::MakeWH(100, 100), SkShader::TileMode::kClamp_TileMode,
+      SkShader::TileMode::kClamp_TileMode, &local_matrix);
+
+  PaintOpBuffer buffer;
+  PaintFlags flags;
+  flags.setShader(record_shader);
+  buffer.push<ScaleOp>(0.5f, 0.5f);
+  buffer.push<DrawRectOp>(SkRect::MakeWH(100, 100), flags);
+
+  MockImageProvider image_provider;
+  SaveCountingCanvas canvas;
+  buffer.Playback(&canvas, &image_provider);
+
+  EXPECT_EQ(canvas.draw_rect_, SkRect::MakeWH(100, 100));
+  SkShader* shader = canvas.paint_.getShader();
+  ASSERT_TRUE(shader);
+  SkMatrix decoded_local_matrix;
+  SkShader::TileMode xy[2];
+  SkImage* skia_image = shader->isAImage(&decoded_local_matrix, xy);
+  ASSERT_TRUE(skia_image);
+  EXPECT_TRUE(skia_image->isLazyGenerated());
+  EXPECT_EQ(xy[0], record_shader->tx());
+  EXPECT_EQ(xy[1], record_shader->ty());
+  EXPECT_EQ(decoded_local_matrix, SkMatrix::MakeScale(2.f, 2.f));
+
+  // The rasterization of the shader is internal to skia, so use a raster canvas
+  // to verify that the decoded paint does not have the encoded image.
+  auto surface = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(100, 100));
+  surface->getCanvas()->drawPaint(canvas.paint_);
+
+  // Using the shader requests decode for images at the correct scale.
+  EXPECT_EQ(image_provider.draw_image().paint_image(), paint_image);
+  EXPECT_EQ(image_provider.draw_image().scale(), SkSize::Make(0.25f, 0.25f));
+}
+
+}  // namespace cc
diff --git a/cc/paint/scoped_image_flags.cc b/cc/paint/scoped_image_flags.cc
new file mode 100644
index 0000000..cadb1da
--- /dev/null
+++ b/cc/paint/scoped_image_flags.cc
@@ -0,0 +1,103 @@
+// 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 "cc/paint/scoped_image_flags.h"
+
+#include "cc/paint/image_provider.h"
+#include "cc/paint/paint_image_builder.h"
+
+namespace cc {
+namespace {
+SkIRect RoundOutRect(const SkRect& rect) {
+  SkIRect result;
+  rect.roundOut(&result);
+  return result;
+}
+}  // namespace
+
+ScopedImageFlags::DecodeStashingImageProvider::DecodeStashingImageProvider(
+    ImageProvider* source_provider)
+    : source_provider_(source_provider) {}
+ScopedImageFlags::DecodeStashingImageProvider::~DecodeStashingImageProvider() =
+    default;
+
+ImageProvider::ScopedDecodedDrawImage
+ScopedImageFlags::DecodeStashingImageProvider::GetDecodedDrawImage(
+    const DrawImage& draw_image) {
+  auto decode = source_provider_->GetDecodedDrawImage(draw_image);
+  if (!decode)
+    return ScopedDecodedDrawImage();
+
+  // No need to add any destruction callback to the returned image. The images
+  // decoded here match the lifetime of this provider.
+  auto image_to_return = ScopedDecodedDrawImage(decode.decoded_image());
+  decoded_images_.push_back(std::move(decode));
+  return image_to_return;
+}
+
+ScopedImageFlags::ScopedImageFlags(ImageProvider* image_provider,
+                                   const PaintFlags& flags,
+                                   const SkMatrix& ctm)
+    : decode_stashing_image_provider_(image_provider) {
+  if (flags.getShader()->shader_type() == PaintShader::Type::kImage) {
+    DecodeImageShader(flags, ctm);
+  } else {
+    DCHECK_EQ(flags.getShader()->shader_type(),
+              PaintShader::Type::kPaintRecord);
+    DecodeRecordShader(flags, ctm);
+  }
+}
+
+ScopedImageFlags::~ScopedImageFlags() = default;
+
+void ScopedImageFlags::DecodeImageShader(const PaintFlags& flags,
+                                         const SkMatrix& ctm) {
+  const PaintImage& paint_image = flags.getShader()->paint_image();
+  SkMatrix matrix = flags.getShader()->GetLocalMatrix();
+
+  SkMatrix total_image_matrix = matrix;
+  total_image_matrix.preConcat(ctm);
+  SkRect src_rect = SkRect::MakeIWH(paint_image.width(), paint_image.height());
+  DrawImage draw_image(paint_image, RoundOutRect(src_rect),
+                       flags.getFilterQuality(), total_image_matrix);
+  auto decoded_draw_image =
+      decode_stashing_image_provider_.GetDecodedDrawImage(draw_image);
+
+  if (!decoded_draw_image)
+    return;
+
+  const auto& decoded_image = decoded_draw_image.decoded_image();
+  DCHECK(decoded_image.image());
+
+  bool need_scale = !decoded_image.is_scale_adjustment_identity();
+  if (need_scale) {
+    matrix.preScale(1.f / decoded_image.scale_adjustment().width(),
+                    1.f / decoded_image.scale_adjustment().height());
+  }
+
+  sk_sp<SkImage> sk_image =
+      sk_ref_sp<SkImage>(const_cast<SkImage*>(decoded_image.image().get()));
+  PaintImage decoded_paint_image = PaintImageBuilder()
+                                       .set_id(paint_image.stable_id())
+                                       .set_image(std::move(sk_image))
+                                       .TakePaintImage();
+  decoded_flags_.emplace(flags);
+  decoded_flags_.value().setFilterQuality(decoded_image.filter_quality());
+  decoded_flags_.value().setShader(
+      PaintShader::MakeImage(decoded_paint_image, flags.getShader()->tx(),
+                             flags.getShader()->ty(), &matrix));
+}
+
+void ScopedImageFlags::DecodeRecordShader(const PaintFlags& flags,
+                                          const SkMatrix& ctm) {
+  auto decoded_shader = flags.getShader()->CreateDecodedPaintRecord(
+      ctm, &decode_stashing_image_provider_);
+  if (!decoded_shader)
+    return;
+
+  decoded_flags_.emplace(flags);
+  decoded_flags_.value().setShader(std::move(decoded_shader));
+}
+
+}  // namespace cc
diff --git a/cc/paint/scoped_image_flags.h b/cc/paint/scoped_image_flags.h
new file mode 100644
index 0000000..9c39bf8
--- /dev/null
+++ b/cc/paint/scoped_image_flags.h
@@ -0,0 +1,66 @@
+// 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 CC_PAINT_SCOPED_IMAGE_FLAGS_H_
+#define CC_PAINT_SCOPED_IMAGE_FLAGS_H_
+
+#include "base/macros.h"
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_op_buffer.h"
+
+namespace cc {
+class ImageProvider;
+
+// A helper class to decode images inside the provided |flags| and provide a
+// PaintFlags with the decoded images that can directly be used for
+// rasterization.
+// This class should only be used if |flags| has any discardable images.
+class CC_PAINT_EXPORT ScopedImageFlags {
+ public:
+  // |image_provider| must outlive this class.
+  ScopedImageFlags(ImageProvider* image_provider,
+                   const PaintFlags& flags,
+                   const SkMatrix& ctm);
+  ~ScopedImageFlags();
+
+  // The usage of these flags should not extend beyond the lifetime of this
+  // object.
+  PaintFlags* decoded_flags() {
+    return decoded_flags_ ? &decoded_flags_.value() : nullptr;
+  }
+
+ private:
+  // An ImageProvider that passes decode requests through to the
+  // |source_provider| but keeps the decode cached throughtout its lifetime,
+  // instead of passing the ref to the caller.
+  class DecodeStashingImageProvider : public ImageProvider {
+   public:
+    // |source_provider| must outlive this class.
+    explicit DecodeStashingImageProvider(ImageProvider* source_provider);
+    ~DecodeStashingImageProvider() override;
+
+    // ImageProvider implementation.
+    ScopedDecodedDrawImage GetDecodedDrawImage(
+        const DrawImage& draw_image) override;
+
+   private:
+    ImageProvider* source_provider_;
+    std::vector<ScopedDecodedDrawImage> decoded_images_;
+
+    DISALLOW_COPY_AND_ASSIGN(DecodeStashingImageProvider);
+  };
+
+  void DecodeImageShader(const PaintFlags& flags, const SkMatrix& ctm);
+
+  void DecodeRecordShader(const PaintFlags& flags, const SkMatrix& ctm);
+
+  base::Optional<PaintFlags> decoded_flags_;
+  DecodeStashingImageProvider decode_stashing_image_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedImageFlags);
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_SCOPED_IMAGE_FLAGS_H_
diff --git a/cc/paint/scoped_image_flags_unittest.cc b/cc/paint/scoped_image_flags_unittest.cc
new file mode 100644
index 0000000..b8e919a9
--- /dev/null
+++ b/cc/paint/scoped_image_flags_unittest.cc
@@ -0,0 +1,70 @@
+// 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 "cc/paint/scoped_image_flags.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "cc/test/skia_common.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+class MockImageProvider : public ImageProvider {
+ public:
+  MockImageProvider() = default;
+  ~MockImageProvider() override { EXPECT_EQ(ref_count_, 0); }
+
+  ScopedDecodedDrawImage GetDecodedDrawImage(
+      const DrawImage& draw_image) override {
+    ref_count_++;
+
+    SkBitmap bitmap;
+    bitmap.allocN32Pixels(10, 10);
+    sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
+
+    return ScopedDecodedDrawImage(
+        DecodedDrawImage(image, SkSize::MakeEmpty(), SkSize::Make(1.0f, 1.0f),
+                         draw_image.filter_quality()),
+        base::BindOnce(&MockImageProvider::UnrefImage, base::Unretained(this)));
+  }
+
+  void UnrefImage(DecodedDrawImage decoded_image) {
+    ref_count_--;
+    CHECK_GE(ref_count_, 0);
+  }
+
+  int ref_count() const { return ref_count_; }
+
+ private:
+  int ref_count_ = 0;
+};
+}  // namespace
+
+TEST(ScopedImageFlagsTest, KeepsDecodesAlive) {
+  auto record = sk_make_sp<PaintOpBuffer>();
+  record->push<DrawImageOp>(CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f,
+                            0.f, nullptr);
+  record->push<DrawImageOp>(CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f,
+                            0.f, nullptr);
+  record->push<DrawImageOp>(CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f,
+                            0.f, nullptr);
+  auto record_shader = PaintShader::MakePaintRecord(
+      record, SkRect::MakeWH(100, 100), SkShader::TileMode::kClamp_TileMode,
+      SkShader::TileMode::kClamp_TileMode, &SkMatrix::I());
+
+  MockImageProvider provider;
+  PaintFlags flags;
+  flags.setShader(record_shader);
+  {
+    ScopedImageFlags scoped_flags(&provider, flags, SkMatrix::I());
+    ASSERT_TRUE(scoped_flags.decoded_flags());
+    SkPaint paint = scoped_flags.decoded_flags()->ToSkPaint();
+    ASSERT_TRUE(paint.getShader());
+    EXPECT_EQ(provider.ref_count(), 3);
+  }
+  EXPECT_EQ(provider.ref_count(), 0);
+}
+
+}  // namespace cc
diff --git a/chrome/browser/component_updater/widevine_cdm_component_installer.cc b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
index aad7ee0943..26e1d99 100644
--- a/chrome/browser/component_updater/widevine_cdm_component_installer.cc
+++ b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
@@ -241,8 +241,8 @@
       codecs, std::string(1, kCdmSupportedCodecsValueDelimiter),
       base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
   CdmRegistry::GetInstance()->RegisterCdm(
-      content::CdmInfo(kWidevineCdmType, cdm_version, cdm_path,
-                       supported_codecs, kWidevineKeySystem, false));
+      content::CdmInfo(kWidevineCdmType, kWidevineCdmGuid, cdm_version,
+                       cdm_path, supported_codecs, kWidevineKeySystem, false));
 }
 
 }  // namespace
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index 41af191..d44710f4 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -881,6 +881,16 @@
   TestNonPlaybackCases(kExternalClearKeyStorageIdTestKeySystem,
                        kUnitTestSuccess);
 }
+
+IN_PROC_BROWSER_TEST_P(ECKEncryptedMediaTest, MultipleCdmTypes) {
+  if (!IsUsingMojoCdm()) {
+    DVLOG(0) << "Skipping test; Mojo CDM specific.";
+    return;
+  }
+
+  base::StringPairs empty_query_params;
+  RunMediaTestPage("multiple_cdm_types.html", empty_query_params, kEnded, true);
+}
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
 }  // namespace chrome
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index 75242d3..82f3aa08a 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -551,26 +551,40 @@
       // same directory as the installed adapter.
       const base::Version version(WIDEVINE_CDM_VERSION_STRING);
       DCHECK(version.IsValid());
-      cdms->push_back(content::CdmInfo(kWidevineCdmType, version, cdm_path,
-                                       codecs_supported, kWidevineKeySystem,
-                                       false));
+      cdms->push_back(content::CdmInfo(kWidevineCdmType, kWidevineCdmGuid,
+                                       version, cdm_path, codecs_supported,
+                                       kWidevineKeySystem, false));
     }
 #endif  // defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT)
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
     // Register Clear Key CDM if specified in command line.
     base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-    std::string clear_key_cdm_path =
-        command_line->GetSwitchValueASCII(switches::kClearKeyCdmPathForTesting);
+    base::FilePath clear_key_cdm_path =
+        command_line->GetSwitchValuePath(switches::kClearKeyCdmPathForTesting);
     if (!clear_key_cdm_path.empty()) {
-      // TODO(crbug.com/764480): Remove this after we have a central place for
+      // TODO(crbug.com/764480): Remove these after we have a central place for
       // External Clear Key (ECK) related information.
+      // Normal External Clear Key key system.
       const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey";
+      // A variant of ECK key system that has a different GUID.
+      const char kExternalClearKeyDifferentGuidTestKeySystem[] =
+          "org.chromium.externalclearkey.differentguid";
+
+      // Register kExternalClearKeyDifferentGuidTestKeySystem first separately.
+      // Otherwise, it'll be treated as a sub-key-system of normal
+      // kExternalClearKeyKeySystem. See MultipleCdmTypes test in
+      // ECKEncryptedMediaTest.
+      cdms->push_back(content::CdmInfo(
+          media::kClearKeyCdmType, media::kClearKeyCdmDifferentGuid,
+          base::Version("0.1.0.0"), clear_key_cdm_path, {},
+          kExternalClearKeyDifferentGuidTestKeySystem, false));
+
       // Supported codecs are hard-coded in ExternalClearKeyProperties.
       cdms->push_back(
-          content::CdmInfo(media::kClearKeyCdmType, base::Version("0.1.0.0"),
-                           base::FilePath::FromUTF8Unsafe(clear_key_cdm_path),
-                           {}, kExternalClearKeyKeySystem, true));
+          content::CdmInfo(media::kClearKeyCdmType, media::kClearKeyCdmGuid,
+                           base::Version("0.1.0.0"), clear_key_cdm_path, {},
+                           kExternalClearKeyKeySystem, true));
     }
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
   }
diff --git a/chrome/renderer/media/chrome_key_systems.cc b/chrome/renderer/media/chrome_key_systems.cc
index 51f24da..0c6eb3f 100644
--- a/chrome/renderer/media/chrome_key_systems.cc
+++ b/chrome/renderer/media/chrome_key_systems.cc
@@ -84,6 +84,8 @@
       "org.chromium.externalclearkey.verifycdmhosttest";
   static const char kExternalClearKeyStorageIdTestKeySystem[] =
       "org.chromium.externalclearkey.storageidtest";
+  static const char kExternalClearKeyDifferentGuidTestKeySystem[] =
+      "org.chromium.externalclearkey.differentguid";
 
   std::vector<base::string16> additional_param_names;
   std::vector<base::string16> additional_param_values;
@@ -133,6 +135,10 @@
   // A key system that fetches the Storage ID in ClearKeyCdm.
   concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
       kExternalClearKeyStorageIdTestKeySystem));
+
+  // A key system that is registered with a different CDM GUID.
+  concrete_key_systems->emplace_back(new cdm::ExternalClearKeyProperties(
+      kExternalClearKeyDifferentGuidTestKeySystem));
 }
 
 #if defined(WIDEVINE_CDM_AVAILABLE)
diff --git a/chromeos/cryptohome/system_salt_getter.cc b/chromeos/cryptohome/system_salt_getter.cc
index 45461458..09d120b6 100644
--- a/chromeos/cryptohome/system_salt_getter.cc
+++ b/chromeos/cryptohome/system_salt_getter.cc
@@ -6,6 +6,8 @@
 
 #include <stdint.h>
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
@@ -76,13 +78,11 @@
 
 void SystemSaltGetter::DidGetSystemSalt(
     const GetSystemSaltCallback& callback,
-    DBusMethodCallStatus call_status,
-    const std::vector<uint8_t>& system_salt) {
-  if (call_status == DBUS_METHOD_CALL_SUCCESS &&
-      !system_salt.empty() &&
-      system_salt.size() % 2 == 0U) {
-      raw_salt_ = system_salt;
-    system_salt_ = ConvertRawSaltToHexString(system_salt);
+    base::Optional<std::vector<uint8_t>> system_salt) {
+  if (system_salt.has_value() && !system_salt->empty() &&
+      system_salt->size() % 2 == 0U) {
+    raw_salt_ = std::move(system_salt).value();
+    system_salt_ = ConvertRawSaltToHexString(raw_salt_);
 
     std::vector<base::Closure> callbacks;
     callbacks.swap(on_system_salt_ready_);
diff --git a/chromeos/cryptohome/system_salt_getter.h b/chromeos/cryptohome/system_salt_getter.h
index a77d85c..57cb91b7 100644
--- a/chromeos/cryptohome/system_salt_getter.h
+++ b/chromeos/cryptohome/system_salt_getter.h
@@ -13,8 +13,8 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "chromeos/chromeos_export.h"
-#include "chromeos/dbus/dbus_method_call_status.h"
 
 namespace chromeos {
 
@@ -59,8 +59,7 @@
   void DidWaitForServiceToBeAvailable(const GetSystemSaltCallback& callback,
                                       bool service_is_available);
   void DidGetSystemSalt(const GetSystemSaltCallback& callback,
-                        DBusMethodCallStatus call_status,
-                        const RawSalt& system_salt);
+                        base::Optional<std::vector<uint8_t>> system_salt);
 
   RawSalt raw_salt_;
   std::string system_salt_;
diff --git a/chromeos/dbus/cryptohome_client.cc b/chromeos/dbus/cryptohome_client.cc
index c49cefa5..f84b7f7 100644
--- a/chromeos/dbus/cryptohome_client.cc
+++ b/chromeos/dbus/cryptohome_client.cc
@@ -178,13 +178,14 @@
   }
 
   // CryptohomeClient override.
-  void GetSystemSalt(const GetSystemSaltCallback& callback) override {
+  void GetSystemSalt(
+      DBusMethodCallback<std::vector<uint8_t>> callback) override {
     dbus::MethodCall method_call(cryptohome::kCryptohomeInterface,
                                  cryptohome::kCryptohomeGetSystemSalt);
     proxy_->CallMethod(
         &method_call, kTpmDBusTimeoutMs,
         base::BindOnce(&CryptohomeClientImpl::OnGetSystemSalt,
-                       weak_ptr_factory_.GetWeakPtr(), callback));
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
   // CryptohomeClient override,
@@ -1020,21 +1021,20 @@
   }
 
   // Handles the result of GetSystemSalt().
-  void OnGetSystemSalt(const GetSystemSaltCallback& callback,
+  void OnGetSystemSalt(DBusMethodCallback<std::vector<uint8_t>> callback,
                        dbus::Response* response) {
     if (!response) {
-      callback.Run(DBUS_METHOD_CALL_FAILURE, std::vector<uint8_t>());
+      std::move(callback).Run(base::nullopt);
       return;
     }
     dbus::MessageReader reader(response);
-    const uint8_t* bytes = NULL;
+    const uint8_t* bytes = nullptr;
     size_t length = 0;
     if (!reader.PopArrayOfBytes(&bytes, &length)) {
-      callback.Run(DBUS_METHOD_CALL_FAILURE, std::vector<uint8_t>());
+      std::move(callback).Run(base::nullopt);
       return;
     }
-    callback.Run(DBUS_METHOD_CALL_SUCCESS,
-                 std::vector<uint8_t>(bytes, bytes + length));
+    std::move(callback).Run(std::vector<uint8_t>(bytes, bytes + length));
   }
 
   // Calls a method without result values.
diff --git a/chromeos/dbus/cryptohome_client.h b/chromeos/dbus/cryptohome_client.h
index 1d712b4..79ef865 100644
--- a/chromeos/dbus/cryptohome_client.h
+++ b/chromeos/dbus/cryptohome_client.h
@@ -63,10 +63,6 @@
   // handler invocation later.
   using AsyncMethodCallback = DBusMethodCallback<int /* async_id */>;
 
-  // A callback for GetSystemSalt().
-  typedef base::Callback<void(DBusMethodCallStatus call_status,
-                              const std::vector<uint8_t>& system_salt)>
-      GetSystemSaltCallback;
   // A callback to handle LowDiskSpace signals.
   typedef base::Callback<void(uint64_t disk_free_bytes)> LowDiskSpaceHandler;
   // A callback to handle responses of Pkcs11GetTpmTokenInfo method.  The result
@@ -183,7 +179,8 @@
 
   // Calls GetSystemSalt method.  |callback| is called after the method call
   // succeeds.
-  virtual void GetSystemSalt(const GetSystemSaltCallback& callback) = 0;
+  virtual void GetSystemSalt(
+      DBusMethodCallback<std::vector<uint8_t>> callback) = 0;
 
   // Calls GetSanitizedUsername method.  |callback| is called after the method
   // call succeeds.
diff --git a/chromeos/dbus/fake_cryptohome_client.cc b/chromeos/dbus/fake_cryptohome_client.cc
index 9ccb5722..0048bce 100644
--- a/chromeos/dbus/fake_cryptohome_client.cc
+++ b/chromeos/dbus/fake_cryptohome_client.cc
@@ -140,9 +140,9 @@
 }
 
 void FakeCryptohomeClient::GetSystemSalt(
-    const GetSystemSaltCallback& callback) {
+    DBusMethodCallback<std::vector<uint8_t>> callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, system_salt_));
+      FROM_HERE, base::BindOnce(std::move(callback), system_salt_));
 }
 
 void FakeCryptohomeClient::GetSanitizedUsername(
diff --git a/chromeos/dbus/fake_cryptohome_client.h b/chromeos/dbus/fake_cryptohome_client.h
index c94a254..e92390d 100644
--- a/chromeos/dbus/fake_cryptohome_client.h
+++ b/chromeos/dbus/fake_cryptohome_client.h
@@ -51,7 +51,8 @@
                         const ProtobufMethodCallback& callback) override;
   void GetAccountDiskUsage(const cryptohome::Identification& account_id,
                            const ProtobufMethodCallback& callback) override;
-  void GetSystemSalt(const GetSystemSaltCallback& callback) override;
+  void GetSystemSalt(
+      DBusMethodCallback<std::vector<uint8_t>> callback) override;
   void GetSanitizedUsername(const cryptohome::Identification& cryptohome_id,
                             DBusMethodCallback<std::string> callback) override;
   std::string BlockingGetSanitizedUsername(
diff --git a/content/browser/media/cdm_registry_impl_unittest.cc b/content/browser/media/cdm_registry_impl_unittest.cc
index 260a9678..066d5bd 100644
--- a/content/browser/media/cdm_registry_impl_unittest.cc
+++ b/content/browser/media/cdm_registry_impl_unittest.cc
@@ -19,7 +19,8 @@
 
 namespace content {
 
-const char kTestCdmType[] = "test";
+const char kTestCdmType[] = "Test CDM";
+const char kTestCdmGuid[] = "62FE9C4B-384E-48FD-B28A-9F6F248BC8CC";
 const char kTestPath[] = "/aa/bb";
 const char kVersion1[] = "1.1.1.1";
 const char kVersion2[] = "1.1.1.2";
@@ -44,9 +45,10 @@
     const std::vector<std::string> codecs =
         base::SplitString(supported_codecs, kCodecDelimiter,
                           base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-    cdm_registry_.RegisterCdm(CdmInfo(
-        type, base::Version(version), base::FilePath::FromUTF8Unsafe(path),
-        codecs, supported_key_system, supports_sub_key_systems));
+    cdm_registry_.RegisterCdm(
+        CdmInfo(type, kTestCdmGuid, base::Version(version),
+                base::FilePath::FromUTF8Unsafe(path), codecs,
+                supported_key_system, supports_sub_key_systems));
   }
 
   bool IsRegistered(const std::string& type, const std::string& version) {
diff --git a/content/browser/media/media_interface_proxy.cc b/content/browser/media/media_interface_proxy.cc
index 7505e28..31ce607a 100644
--- a/content/browser/media/media_interface_proxy.cc
+++ b/content/browser/media/media_interface_proxy.cc
@@ -27,6 +27,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#include "base/guid.h"
 #include "content/public/browser/cdm_registry.h"
 #include "content/public/common/cdm_info.h"
 #include "media/base/key_system_names.h"
@@ -46,7 +47,7 @@
 
   binding_.set_connection_error_handler(error_handler);
 
-  // |interface_factory_ptr_| and |cdm_interface_factory_ptr_| will be lazily
+  // |interface_factory_ptr_| and |cdm_interface_factory_map_| will be lazily
   // connected in GetMediaInterfaceFactory() and GetCdmInterfaceFactory().
 }
 
@@ -58,7 +59,7 @@
 void MediaInterfaceProxy::CreateAudioDecoder(
     media::mojom::AudioDecoderRequest request) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  media::mojom::InterfaceFactory* factory = GetMediaInterfaceFactory();
+  InterfaceFactory* factory = GetMediaInterfaceFactory();
   if (factory)
     factory->CreateAudioDecoder(std::move(request));
 }
@@ -66,7 +67,7 @@
 void MediaInterfaceProxy::CreateVideoDecoder(
     media::mojom::VideoDecoderRequest request) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  media::mojom::InterfaceFactory* factory = GetMediaInterfaceFactory();
+  InterfaceFactory* factory = GetMediaInterfaceFactory();
   if (factory)
     factory->CreateVideoDecoder(std::move(request));
 }
@@ -75,7 +76,7 @@
     const std::string& audio_device_id,
     media::mojom::RendererRequest request) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  media::mojom::InterfaceFactory* factory = GetMediaInterfaceFactory();
+  InterfaceFactory* factory = GetMediaInterfaceFactory();
   if (factory)
     factory->CreateRenderer(audio_device_id, std::move(request));
 }
@@ -84,48 +85,18 @@
     const std::string& key_system,
     media::mojom::ContentDecryptionModuleRequest request) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  media::mojom::InterfaceFactory* factory = GetCdmInterfaceFactory(key_system);
+
+  InterfaceFactory* factory =
+#if !BUILDFLAG(ENABLE_STANDALONE_CDM_SERVICE)
+      GetMediaInterfaceFactory();
+#else
+      GetCdmInterfaceFactory(key_system);
+#endif
+
   if (factory)
     factory->CreateCdm(key_system, std::move(request));
 }
 
-media::mojom::InterfaceFactory*
-MediaInterfaceProxy::GetMediaInterfaceFactory() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  if (!interface_factory_ptr_)
-    ConnectToMediaService();
-
-  return interface_factory_ptr_.get();
-}
-
-media::mojom::InterfaceFactory* MediaInterfaceProxy::GetCdmInterfaceFactory(
-    const std::string& key_system) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-#if !BUILDFLAG(ENABLE_STANDALONE_CDM_SERVICE)
-  return GetMediaInterfaceFactory();
-#else
-  if (!cdm_interface_factory_ptr_)
-    ConnectToCdmService(key_system);
-
-  return cdm_interface_factory_ptr_.get();
-#endif
-}
-
-void MediaInterfaceProxy::OnMediaServiceConnectionError() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  interface_factory_ptr_.reset();
-}
-
-void MediaInterfaceProxy::OnCdmServiceConnectionError() {
-  DVLOG(1) << __func__;
-  DCHECK(thread_checker_.CalledOnValidThread());
-
-  cdm_interface_factory_ptr_.reset();
-}
-
 service_manager::mojom::InterfaceProviderPtr
 MediaInterfaceProxy::GetFrameServices() {
   // Register frame services.
@@ -154,6 +125,16 @@
   return interfaces;
 }
 
+media::mojom::InterfaceFactory*
+MediaInterfaceProxy::GetMediaInterfaceFactory() {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  if (!interface_factory_ptr_)
+    ConnectToMediaService();
+
+  return interface_factory_ptr_.get();
+}
+
 void MediaInterfaceProxy::ConnectToMediaService() {
   DVLOG(1) << __func__;
   DCHECK(!interface_factory_ptr_);
@@ -173,7 +154,18 @@
                      base::Unretained(this)));
 }
 
+void MediaInterfaceProxy::OnMediaServiceConnectionError() {
+  DVLOG(1) << __func__;
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  interface_factory_ptr_.reset();
+}
+
+#if BUILDFLAG(ENABLE_STANDALONE_CDM_SERVICE)
+
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
+// Returns CdmInfo registered for |key_system|. Returns null if no CdmInfo is
+// registered for |key_system|, or if the CdmInfo registered is invalid.
 static std::unique_ptr<CdmInfo> GetCdmInfoForKeySystem(
     const std::string& key_system) {
   DVLOG(2) << __func__ << ": key_system = " << key_system;
@@ -189,45 +181,84 @@
 }
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
-void MediaInterfaceProxy::ConnectToCdmService(const std::string& key_system) {
-  DVLOG(1) << __func__ << ": key_system = " << key_system;
-  DCHECK(!cdm_interface_factory_ptr_);
+media::mojom::InterfaceFactory* MediaInterfaceProxy::GetCdmInterfaceFactory(
+    const std::string& key_system) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  // Set the CDM GUID to be the default user ID used when connecting to mojo
+  // services. This is needed when ENABLE_STANDALONE_CDM_SERVICE is true but
+  // ENABLE_LIBRARY_CDMS is false.
+  std::string cdm_guid = service_manager::mojom::kInheritUserID;
+
+  base::FilePath cdm_path;
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
-  // The |key_system| comes from the render process which we cannot fully trust.
-  // Handle failure cases gracefully!
-  auto cdm_info = GetCdmInfoForKeySystem(key_system);
+  std::unique_ptr<CdmInfo> cdm_info = GetCdmInfoForKeySystem(key_system);
   if (!cdm_info) {
-    NOTREACHED() << "Key system " << key_system << " not registered.";
-    return;
+    NOTREACHED() << "No valid CdmInfo for " << key_system;
+    return nullptr;
   }
-  base::FilePath cdm_path = cdm_info->path;
-  if (cdm_path.empty()) {
+  if (cdm_info->path.empty()) {
     NOTREACHED() << "CDM path for " << key_system << " is empty.";
-    return;
+    return nullptr;
   }
+  if (!base::IsValidGUID(cdm_info->guid)) {
+    NOTREACHED() << "Invalid CDM GUID " << cdm_info->guid;
+    return nullptr;
+  }
+  cdm_guid = cdm_info->guid;
+  cdm_path = cdm_info->path;
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
+  auto found = cdm_interface_factory_map_.find(cdm_guid);
+  if (found != cdm_interface_factory_map_.end())
+    return found->second.get();
+
+  return ConnectToCdmService(cdm_guid, cdm_path);
+}
+
+media::mojom::InterfaceFactory* MediaInterfaceProxy::ConnectToCdmService(
+    const std::string& cdm_guid,
+    const base::FilePath& cdm_path) {
+  DVLOG(1) << __func__ << ": cdm_guid = " << cdm_guid;
+
+  DCHECK(!cdm_interface_factory_map_.count(cdm_guid));
+  service_manager::Identity identity(media::mojom::kCdmServiceName, cdm_guid);
+
   // TODO(slan): Use the BrowserContext Connector instead. See crbug.com/638950.
   service_manager::Connector* connector =
       ServiceManagerConnection::GetForProcess()->GetConnector();
 
-  // TODO(crbug.com/510604): Use different "User ID" for different CDM types to
-  // run each type of CDM in its own process.
   media::mojom::MediaServicePtr media_service;
-  connector->BindInterface(media::mojom::kCdmServiceName, &media_service);
+  connector->BindInterface(identity, &media_service);
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   // LoadCdm() should always be called before CreateInterfaceFactory().
   media_service->LoadCdm(cdm_path);
 #endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
 
-  media_service->CreateInterfaceFactory(
-      MakeRequest(&cdm_interface_factory_ptr_), GetFrameServices());
-
-  cdm_interface_factory_ptr_.set_connection_error_handler(
+  InterfaceFactoryPtr interface_factory_ptr;
+  media_service->CreateInterfaceFactory(MakeRequest(&interface_factory_ptr),
+                                        GetFrameServices());
+  interface_factory_ptr.set_connection_error_handler(
       base::BindOnce(&MediaInterfaceProxy::OnCdmServiceConnectionError,
-                     base::Unretained(this)));
+                     base::Unretained(this), cdm_guid));
+
+  InterfaceFactory* cdm_interface_factory = interface_factory_ptr.get();
+  cdm_interface_factory_map_.emplace(cdm_guid,
+                                     std::move(interface_factory_ptr));
+  return cdm_interface_factory;
 }
 
+void MediaInterfaceProxy::OnCdmServiceConnectionError(
+    const std::string& cdm_guid) {
+  DVLOG(1) << __func__;
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  DCHECK(cdm_interface_factory_map_.count(cdm_guid));
+  cdm_interface_factory_map_.erase(cdm_guid);
+}
+
+#endif  // BUILDFLAG(ENABLE_STANDALONE_CDM_SERVICE)
+
 }  // namespace content
diff --git a/content/browser/media/media_interface_proxy.h b/content/browser/media/media_interface_proxy.h
index 859fb07..431a3ba0 100644
--- a/content/browser/media/media_interface_proxy.h
+++ b/content/browser/media/media_interface_proxy.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_MEDIA_MEDIA_INTERFACE_PROXY_H_
 #define CONTENT_BROWSER_MEDIA_MEDIA_INTERFACE_PROXY_H_
 
+#include <map>
 #include <memory>
 #include <vector>
 
@@ -23,9 +24,13 @@
 
 class RenderFrameHost;
 
-// This implements the media::mojom::InterfaceFactory interface for
-// RenderFrameHostImpl. Upon media::mojom::InterfaceFactory calls, it will
-// figure out where to forward to the interface requests.
+// This implements the media::mojom::InterfaceFactory interface for a
+// RenderFrameHostImpl. Upon InterfaceFactory calls, it will
+// figure out where to forward to the interface requests. For example,
+// - When |enable_standalone_cdm_service| is true, forward CDM request to a
+//   standalone CDM service rather than the general media service.
+// - Forward CDM requests to different CDM service instances based on library
+//   CDM types.
 class MediaInterfaceProxy : public media::mojom::InterfaceFactory {
  public:
   // Constructs MediaInterfaceProxy and bind |this| to the |request|. When
@@ -45,42 +50,59 @@
                  media::mojom::ContentDecryptionModuleRequest request) final;
 
  private:
-  // Get the |interface_factory_ptr_|. Returns null if expected error happened.
-  media::mojom::InterfaceFactory* GetMediaInterfaceFactory();
+  using InterfaceFactoryPtr = media::mojom::InterfaceFactoryPtr;
 
-  // Get the |cdm_interface_factory_ptr_|. Returns null if expected error
-  // happened.
-  media::mojom::InterfaceFactory* GetCdmInterfaceFactory(
-      const std::string& key_system);
-
-  // Callback for connection error from |interface_factory_ptr_| or
-  // |cdm_interface_factory_ptr_|.
-  void OnMediaServiceConnectionError();
-  void OnCdmServiceConnectionError();
-
+  // Gets services provided by the browser (at RenderFrameHost level) to the
+  // mojo media (or CDM) service running remotely.
   service_manager::mojom::InterfaceProviderPtr GetFrameServices();
 
+  // Gets the MediaService |interface_factory_ptr_|. Returns null if unexpected
+  // error happened.
+  InterfaceFactory* GetMediaInterfaceFactory();
+
   void ConnectToMediaService();
-  void ConnectToCdmService(const std::string& key_system);
+
+  // Callback for connection error from |interface_factory_ptr_|.
+  void OnMediaServiceConnectionError();
+
+#if BUILDFLAG(ENABLE_STANDALONE_CDM_SERVICE)
+  // Gets a CDM InterfaceFactory pointer for |key_system|. Returns null if
+  // unexpected error happened.
+  InterfaceFactory* GetCdmInterfaceFactory(const std::string& key_system);
+
+  // Connects to the CDM service associated with |key_system|, adds the new
+  // InterfaceFactoryPtr to the |cdm_interface_factory_map_|, and returns the
+  // newly created InterfaceFactory pointer. Returns nullptr if unexpected error
+  // happened.
+  InterfaceFactory* ConnectToCdmService(const std::string& cdm_guid,
+                                        const base::FilePath& cdm_path);
+
+  // Callback for connection error from the InterfaceFactoryPtr in the
+  // |cdm_interface_factory_map_| associated with |cdm_guid|.
+  void OnCdmServiceConnectionError(const std::string& cdm_guid);
+#endif  // BUILDFLAG(ENABLE_STANDALONE_CDM_SERVICE)
 
   // Safe to hold a raw pointer since |this| is owned by RenderFrameHostImpl.
-  RenderFrameHost* render_frame_host_;
+  RenderFrameHost* const render_frame_host_;
+
+  // Binding for incoming InterfaceFactoryRequest from the the RenderFrameImpl.
+  mojo::Binding<InterfaceFactory> binding_;
 
   // TODO(xhwang): Replace InterfaceProvider with a dedicated host interface.
   // See http://crbug.com/660573
   std::vector<std::unique_ptr<media::MediaInterfaceProvider>> media_registries_;
 
-  mojo::Binding<media::mojom::InterfaceFactory> binding_;
-
-  // InterfacePtr to the remote media::mojom::InterfaceFactory implementation
+  // InterfacePtr to the remote InterfaceFactory implementation
   // in the service named kMediaServiceName hosted in the process specified by
   // the "mojo_media_host" gn argument. Available options are browser, GPU and
   // utility processes.
-  media::mojom::InterfaceFactoryPtr interface_factory_ptr_;
+  InterfaceFactoryPtr interface_factory_ptr_;
 
-  // InterfacePtr to the remote media::mojom::InterfaceFactory implementation
-  // in the service named kCdmServiceName hosted in the utility process.
-  media::mojom::InterfaceFactoryPtr cdm_interface_factory_ptr_;
+#if BUILDFLAG(ENABLE_STANDALONE_CDM_SERVICE)
+  // CDM GUID to CDM InterfaceFactoryPtr mapping, where the InterfaceFactory
+  // instances live in the standalone kCdmServiceName service instances.
+  std::map<std::string, InterfaceFactoryPtr> cdm_interface_factory_map_;
+#endif  // BUILDFLAG(ENABLE_STANDALONE_CDM_SERVICE)
 
   base::ThreadChecker thread_checker_;
 
diff --git a/content/child/service_worker/service_worker_subresource_loader_unittest.cc b/content/child/service_worker/service_worker_subresource_loader_unittest.cc
index 833d1249..bfa5b48 100644
--- a/content/child/service_worker/service_worker_subresource_loader_unittest.cc
+++ b/content/child/service_worker/service_worker_subresource_loader_unittest.cc
@@ -12,14 +12,62 @@
 #include "content/public/common/content_features.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_url_loader_client.h"
+#include "mojo/common/data_pipe_utils.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "net/http/http_util.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "storage/browser/blob/blob_data_builder.h"
+#include "storage/browser/blob/blob_data_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace content {
 
 namespace {
 
+// This class need to set ChildURLLoaderFactoryGetter. CreateLoaderAndStart()
+// need to implement. todo(emim): Merge this and the one in
+// service_worker_url_loader_job_unittest.cc.
+class FakeNetworkURLLoaderFactory final : public mojom::URLLoaderFactory {
+ public:
+  FakeNetworkURLLoaderFactory() = default;
+
+  // mojom::URLLoaderFactory implementation.
+  void CreateLoaderAndStart(mojom::URLLoaderRequest request,
+                            int32_t routing_id,
+                            int32_t request_id,
+                            uint32_t options,
+                            const ResourceRequest& url_request,
+                            mojom::URLLoaderClientPtr client,
+                            const net::MutableNetworkTrafficAnnotationTag&
+                                traffic_annotation) override {
+    std::string headers = "HTTP/1.1 200 OK\n\n";
+    net::HttpResponseInfo info;
+    info.headers = new net::HttpResponseHeaders(
+        net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.length()));
+    ResourceResponseHead response;
+    response.headers = info.headers;
+    response.headers->GetMimeType(&response.mime_type);
+    client->OnReceiveResponse(response, base::nullopt, nullptr);
+
+    std::string body = "this body came from the network";
+    uint32_t bytes_written = body.size();
+    mojo::DataPipe data_pipe;
+    data_pipe.producer_handle->WriteData(body.data(), &bytes_written,
+                                         MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
+    client->OnStartLoadingResponseBody(std::move(data_pipe.consumer_handle));
+
+    ResourceRequestCompletionStatus status;
+    status.error_code = net::OK;
+    client->OnComplete(status);
+  }
+
+  void Clone(mojom::URLLoaderFactoryRequest factory) override { NOTREACHED(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FakeNetworkURLLoaderFactory);
+};
+
 class FakeControllerServiceWorker : public mojom::ControllerServiceWorker {
  public:
   FakeControllerServiceWorker() = default;
@@ -31,6 +79,22 @@
 
   void CloseAllBindings() { bindings_.CloseAllBindings(); }
 
+  // Tells this controller to respond to fetch events with network fallback.
+  // i.e., simulate the service worker not calling respondWith().
+  void RespondWithFallback() {
+    response_mode_ = ResponseMode::kFallbackResponse;
+  }
+
+  // Tells this controller to respond to fetch events with the specified stream.
+  void RespondWithStream(
+      blink::mojom::ServiceWorkerStreamCallbackRequest callback_request,
+      mojo::ScopedDataPipeConsumerHandle consumer_handle) {
+    response_mode_ = ResponseMode::kStream;
+    stream_handle_ = blink::mojom::ServiceWorkerStreamHandle::New();
+    stream_handle_->callback_request = std::move(callback_request);
+    stream_handle_->stream = std::move(consumer_handle);
+  }
+
   // mojom::ControllerServiceWorker:
   void DispatchFetchEvent(
       const ServiceWorkerFetchRequest& request,
@@ -38,7 +102,31 @@
       DispatchFetchEventCallback callback) override {
     fetch_event_count_++;
     fetch_event_request_ = request;
-    std::move(callback).Run(SERVICE_WORKER_OK, base::Time());
+    switch (response_mode_) {
+      case ResponseMode::kDefault:
+        std::move(callback).Run(SERVICE_WORKER_OK, base::Time());
+        return;
+      case ResponseMode::kStream:
+        response_callback->OnResponseStream(
+            ServiceWorkerResponse(
+                base::MakeUnique<std::vector<GURL>>(), 200, "OK",
+                network::mojom::FetchResponseType::kDefault,
+                base::MakeUnique<ServiceWorkerHeaderMap>(), "" /* blob_uuid */,
+                0 /* blob_size */, nullptr /* blob */,
+                blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
+                false /* response_is_in_cache_storage */,
+                std::string() /* response_cache_storage_cache_name */,
+                base::MakeUnique<
+                    ServiceWorkerHeaderList>() /* cors_exposed_header_names */),
+            std::move(stream_handle_), base::Time::Now());
+        std::move(callback).Run(SERVICE_WORKER_OK, base::Time());
+        return;
+      case ResponseMode::kFallbackResponse:
+        response_callback->OnFallback(base::Time::Now());
+        std::move(callback).Run(SERVICE_WORKER_OK, base::Time::Now());
+        return;
+    }
+    NOTREACHED();
   }
 
   int fetch_event_count() const { return fetch_event_count_; }
@@ -47,9 +135,16 @@
   }
 
  private:
+  enum class ResponseMode { kDefault, kStream, kFallbackResponse };
+
+  ResponseMode response_mode_ = ResponseMode::kDefault;
+
   int fetch_event_count_ = 0;
   ServiceWorkerFetchRequest fetch_event_request_;
   mojo::BindingSet<mojom::ControllerServiceWorker> bindings_;
+  // For ResponseMode::kStream.
+  blink::mojom::ServiceWorkerStreamHandlePtr stream_handle_;
+
   DISALLOW_COPY_AND_ASSIGN(FakeControllerServiceWorker);
 };
 
@@ -97,8 +192,26 @@
 
 }  // namespace
 
+// Returns typical response info for a resource load that went through a service
+// worker.
+std::unique_ptr<ResourceResponseHead> CreateResponseInfoFromServiceWorker() {
+  auto head = std::make_unique<ResourceResponseHead>();
+  head->was_fetched_via_service_worker = true;
+  head->was_fallback_required_by_service_worker = false;
+  head->url_list_via_service_worker = std::vector<GURL>();
+  head->response_type_via_service_worker =
+      network::mojom::FetchResponseType::kDefault;
+  // TODO(emim): start and ready time should be set.
+  head->service_worker_start_time = base::TimeTicks();
+  head->service_worker_ready_time = base::TimeTicks();
+  head->is_in_cache_storage = false;
+  head->cache_storage_cache_name = std::string();
+  head->did_service_worker_navigation_preload = false;
+  return head;
+}
+
 class ServiceWorkerSubresourceLoaderTest : public ::testing::Test {
- public:
+ protected:
   ServiceWorkerSubresourceLoaderTest()
       : fake_container_host_(&fake_controller_) {}
   ~ServiceWorkerSubresourceLoaderTest() override = default;
@@ -106,8 +219,12 @@
   void SetUp() override {
     feature_list_.InitAndEnableFeature(features::kNetworkService);
 
+    mojom::URLLoaderFactoryPtr fake_loader_factory;
+    mojo::MakeStrongBinding(base::MakeUnique<FakeNetworkURLLoaderFactory>(),
+                            MakeRequest(&fake_loader_factory));
     loader_factory_getter_ =
-        base::MakeRefCounted<ChildURLLoaderFactoryGetterImpl>();
+        base::MakeRefCounted<ChildURLLoaderFactoryGetterImpl>(
+            std::move(fake_loader_factory), nullptr);
     controller_connector_ =
         base::MakeRefCounted<ControllerServiceWorkerConnector>(
             &fake_container_host_);
@@ -123,7 +240,6 @@
     request.method = method;
 
     mojom::URLLoaderPtr url_loader;
-    TestURLLoaderClient url_loader_client;
 
     ServiceWorkerSubresourceLoaderFactory loader_factory(
         controller_connector_, loader_factory_getter_, request.url.GetOrigin(),
@@ -131,7 +247,7 @@
             base::RefCountedData<storage::mojom::BlobRegistryPtr>>());
     loader_factory.CreateLoaderAndStart(
         mojo::MakeRequest(&url_loader), 0, 0, mojom::kURLLoadOptionNone,
-        request, url_loader_client.CreateInterfacePtr(),
+        request, url_loader_client_.CreateInterfacePtr(),
         net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
     base::RunLoop().RunUntilIdle();
 
@@ -139,7 +255,27 @@
     EXPECT_EQ(request.method, fake_controller_.fetch_event_request().method);
   }
 
- protected:
+  void ExpectResponseInfo(const ResourceResponseHead& info,
+                          const ResourceResponseHead& expected_info) {
+    EXPECT_EQ(expected_info.was_fetched_via_service_worker,
+              info.was_fetched_via_service_worker);
+    EXPECT_EQ(expected_info.was_fallback_required_by_service_worker,
+              info.was_fallback_required_by_service_worker);
+    EXPECT_EQ(expected_info.url_list_via_service_worker,
+              info.url_list_via_service_worker);
+    EXPECT_EQ(expected_info.response_type_via_service_worker,
+              info.response_type_via_service_worker);
+    EXPECT_EQ(expected_info.service_worker_start_time,
+              info.service_worker_start_time);
+    EXPECT_EQ(expected_info.service_worker_ready_time,
+              info.service_worker_ready_time);
+    EXPECT_EQ(expected_info.is_in_cache_storage, info.is_in_cache_storage);
+    EXPECT_EQ(expected_info.cache_storage_cache_name,
+              info.cache_storage_cache_name);
+    EXPECT_EQ(expected_info.did_service_worker_navigation_preload,
+              info.did_service_worker_navigation_preload);
+  }
+
   TestBrowserThreadBundle thread_bundle_;
   scoped_refptr<ChildURLLoaderFactoryGetter> loader_factory_getter_;
 
@@ -148,6 +284,8 @@
   scoped_refptr<ControllerServiceWorkerConnector> controller_connector_;
   base::test::ScopedFeatureList feature_list_;
 
+  TestURLLoaderClient url_loader_client_;
+
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerSubresourceLoaderTest);
 };
 
@@ -159,9 +297,11 @@
 
 TEST_F(ServiceWorkerSubresourceLoaderTest, DropController) {
   TestRequest(GURL("https://www.example.com/foo.html"), "GET");
+  url_loader_client_.Unbind();
   EXPECT_EQ(1, fake_controller_.fetch_event_count());
   EXPECT_EQ(1, fake_container_host_.get_controller_service_worker_count());
   TestRequest(GURL("https://www.example.com/foo2.html"), "GET");
+  url_loader_client_.Unbind();
   EXPECT_EQ(2, fake_controller_.fetch_event_count());
   EXPECT_EQ(1, fake_container_host_.get_controller_service_worker_count());
 
@@ -175,6 +315,52 @@
   EXPECT_EQ(2, fake_container_host_.get_controller_service_worker_count());
 }
 
+TEST_F(ServiceWorkerSubresourceLoaderTest, StreamResponse) {
+  // Construct the Stream to respond with.
+  const char kResponseBody[] = "Here is sample text for the Stream.";
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  fake_controller_.RespondWithStream(mojo::MakeRequest(&stream_callback),
+                                     std::move(data_pipe.consumer_handle));
+
+  // Perform the request.
+  TestRequest(GURL("https://www.example.com/foo.html"), "GET");
+
+  const ResourceResponseHead& info = url_loader_client_.response_head();
+  EXPECT_EQ(200, info.headers->response_code());
+  ExpectResponseInfo(info, *CreateResponseInfoFromServiceWorker());
+
+  // Write the body stream.
+  uint32_t written_bytes = sizeof(kResponseBody) - 1;
+  MojoResult mojo_result = data_pipe.producer_handle->WriteData(
+      kResponseBody, &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_OK, mojo_result);
+  EXPECT_EQ(sizeof(kResponseBody) - 1, written_bytes);
+  stream_callback->OnCompleted();
+  data_pipe.producer_handle.reset();
+
+  url_loader_client_.RunUntilComplete();
+  EXPECT_EQ(net::OK, url_loader_client_.completion_status().error_code);
+
+  // Test the body.
+  std::string response;
+  EXPECT_TRUE(url_loader_client_.response_body().is_valid());
+  EXPECT_TRUE(mojo::common::BlockingCopyToString(
+      url_loader_client_.response_body_release(), &response));
+  EXPECT_EQ(kResponseBody, response);
+}
+
+// Test when the service worker responds with network fallback.
+// i.e., does not call respondWith().
+TEST_F(ServiceWorkerSubresourceLoaderTest, FallbackResponse) {
+  fake_controller_.RespondWithFallback();
+
+  // Perform the request.
+  TestRequest(GURL("https://www.example.com/foo.html"), "GET");
+  // TODO(emim): It should add some expression to check whether this actually
+  // performed the fallback.
+}
+
 // TODO(kinuko): Add more tests.
 
 }  // namespace content
diff --git a/content/common/media/cdm_info.cc b/content/common/media/cdm_info.cc
index fb03e84..3d8fdc3 100644
--- a/content/common/media/cdm_info.cc
+++ b/content/common/media/cdm_info.cc
@@ -4,20 +4,27 @@
 
 #include "content/public/common/cdm_info.h"
 
+#include "base/guid.h"
+#include "base/logging.h"
+
 namespace content {
 
 CdmInfo::CdmInfo(const std::string& type,
+                 const std::string& guid,
                  const base::Version& version,
                  const base::FilePath& path,
                  const std::vector<std::string>& supported_codecs,
                  const std::string& supported_key_system,
                  bool supports_sub_key_systems)
     : type(type),
+      guid(guid),
       version(version),
       path(path),
       supported_codecs(supported_codecs),
       supported_key_system(supported_key_system),
-      supports_sub_key_systems(supports_sub_key_systems) {}
+      supports_sub_key_systems(supports_sub_key_systems) {
+  DCHECK(base::IsValidGUID(guid));
+}
 
 CdmInfo::CdmInfo(const CdmInfo& other) = default;
 
diff --git a/content/public/common/cdm_info.h b/content/public/common/cdm_info.h
index ba61eeb..cd4f1d4f9 100644
--- a/content/public/common/cdm_info.h
+++ b/content/public/common/cdm_info.h
@@ -17,6 +17,7 @@
 // Represents a Content Decryption Module implementation and its capabilities.
 struct CONTENT_EXPORT CdmInfo {
   CdmInfo(const std::string& type,
+          const std::string& guid,
           const base::Version& version,
           const base::FilePath& path,
           const std::vector<std::string>& supported_codecs,
@@ -28,6 +29,9 @@
   // Type of the CDM (e.g. Widevine).
   std::string type;
 
+  // A version 4 GUID to uniquely identify this type of CDM.
+  std::string guid;
+
   // Version of the CDM. May be empty if the version is not known.
   base::Version version;
 
diff --git a/content/utility/utility_service_factory.cc b/content/utility/utility_service_factory.cc
index 2534d2b..e89d74b 100644
--- a/content/utility/utility_service_factory.cc
+++ b/content/utility/utility_service_factory.cc
@@ -4,6 +4,8 @@
 
 #include "content/utility/utility_service_factory.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "content/child/child_process.h"
@@ -24,18 +26,12 @@
 #include "services/video_capture/service_impl.h"
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
-#include "base/memory/ptr_util.h"
-#include "media/base/scoped_callback_runner.h"
 #include "media/cdm/cdm_adapter_factory.h"           // nogncheck
-#include "media/cdm/cdm_helpers.h"                   // nogncheck
 #include "media/mojo/features.h"                     // nogncheck
 #include "media/mojo/interfaces/constants.mojom.h"   // nogncheck
-#include "media/mojo/interfaces/output_protection.mojom.h"      // nogncheck
-#include "media/mojo/interfaces/platform_verification.mojom.h"  // nogncheck
 #include "media/mojo/services/media_service.h"       // nogncheck
-#include "media/mojo/services/mojo_cdm_allocator.h"  // nogncheck
+#include "media/mojo/services/mojo_cdm_helper.h"     // nogncheck
 #include "media/mojo/services/mojo_media_client.h"   // nogncheck
-#include "services/service_manager/public/cpp/connect.h"
 #endif
 
 namespace {
@@ -55,112 +51,9 @@
 static_assert(BUILDFLAG(ENABLE_STANDALONE_CDM_SERVICE), "");
 static_assert(BUILDFLAG(ENABLE_MOJO_CDM), "");
 
-// Helper class that connects the CDM to various auxiliary services. All
-// additional services (FileIO, memory allocation, output protection, and
-// platform verification) are lazily created.
-class MojoCdmHelper : public media::CdmAuxiliaryHelper {
- public:
-  explicit MojoCdmHelper(
-      service_manager::mojom::InterfaceProvider* interface_provider)
-      : interface_provider_(interface_provider) {}
-  ~MojoCdmHelper() override = default;
-
-  // CdmAuxiliaryHelper implementation.
-  std::unique_ptr<media::CdmFileIO> CreateCdmFileIO(
-      cdm::FileIOClient* client) override {
-    // TODO(jrummell): Hook up File IO. http://crbug.com/479923.
-    return nullptr;
-  }
-
-  cdm::Buffer* CreateCdmBuffer(size_t capacity) override {
-    return GetAllocator()->CreateCdmBuffer(capacity);
-  }
-
-  std::unique_ptr<media::VideoFrameImpl> CreateCdmVideoFrame() override {
-    return GetAllocator()->CreateCdmVideoFrame();
-  }
-
-  void QueryStatus(QueryStatusCB callback) override {
-    QueryStatusCB scoped_callback =
-        media::ScopedCallbackRunner(std::move(callback), false, 0, 0);
-    if (!ConnectToOutputProtection())
-      return;
-
-    output_protection_->QueryStatus(std::move(scoped_callback));
-  }
-
-  void EnableProtection(uint32_t desired_protection_mask,
-                        EnableProtectionCB callback) override {
-    EnableProtectionCB scoped_callback =
-        media::ScopedCallbackRunner(std::move(callback), false);
-    if (!ConnectToOutputProtection())
-      return;
-
-    output_protection_->EnableProtection(desired_protection_mask,
-                                         std::move(scoped_callback));
-  }
-
-  void ChallengePlatform(const std::string& service_id,
-                         const std::string& challenge,
-                         ChallengePlatformCB callback) override {
-    ChallengePlatformCB scoped_callback =
-        media::ScopedCallbackRunner(std::move(callback), false, "", "", "");
-    if (!ConnectToPlatformVerification())
-      return;
-
-    platform_verification_->ChallengePlatform(service_id, challenge,
-                                              std::move(scoped_callback));
-  }
-
-  void GetStorageId(uint32_t version, StorageIdCB callback) override {
-    StorageIdCB scoped_callback = media::ScopedCallbackRunner(
-        std::move(callback), version, std::vector<uint8_t>());
-    // TODO(jrummell): Hook up GetStorageId() once added to the mojo interface.
-    // http://crbug.com/478960.
-  }
-
- private:
-  // All services are created lazily.
-  media::CdmAllocator* GetAllocator() {
-    if (!allocator_)
-      allocator_ = base::MakeUnique<media::MojoCdmAllocator>();
-    return allocator_.get();
-  }
-
-  bool ConnectToOutputProtection() {
-    if (!output_protection_attempted_) {
-      output_protection_attempted_ = true;
-      service_manager::GetInterface<media::mojom::OutputProtection>(
-          interface_provider_, &output_protection_);
-    }
-    return output_protection_.is_bound();
-  }
-
-  bool ConnectToPlatformVerification() {
-    if (!platform_verification_attempted_) {
-      platform_verification_attempted_ = true;
-      service_manager::GetInterface<media::mojom::PlatformVerification>(
-          interface_provider_, &platform_verification_);
-    }
-    return platform_verification_.is_bound();
-  }
-
-  // Provides interfaces when needed.
-  service_manager::mojom::InterfaceProvider* interface_provider_;
-
-  // Keep track if connection to the Mojo service has been attempted once.
-  // The service may not exist, or may fail later.
-  bool output_protection_attempted_ = false;
-  bool platform_verification_attempted_ = false;
-
-  std::unique_ptr<media::CdmAllocator> allocator_;
-  media::mojom::OutputProtectionPtr output_protection_;
-  media::mojom::PlatformVerificationPtr platform_verification_;
-};
-
 std::unique_ptr<media::CdmAuxiliaryHelper> CreateCdmHelper(
     service_manager::mojom::InterfaceProvider* interface_provider) {
-  return base::MakeUnique<MojoCdmHelper>(interface_provider);
+  return std::make_unique<media::MojoCdmHelper>(interface_provider);
 }
 
 class CdmMojoMediaClient final : public media::MojoMediaClient {
@@ -170,7 +63,7 @@
 
   std::unique_ptr<media::CdmFactory> CreateCdmFactory(
       service_manager::mojom::InterfaceProvider* host_interfaces) override {
-    return base::MakeUnique<media::CdmAdapterFactory>(
+    return std::make_unique<media::CdmAdapterFactory>(
         base::Bind(&CreateCdmHelper, host_interfaces));
   }
 };
@@ -204,7 +97,7 @@
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
   service_manager::EmbeddedServiceInfo info;
   info.factory = base::Bind(&CreateCdmService);
-  services->insert(std::make_pair(media::mojom::kCdmServiceName, info));
+  services->emplace(media::mojom::kCdmServiceName, info);
 #endif
 
   service_manager::EmbeddedServiceInfo shape_detection_info;
diff --git a/gpu/ipc/service/gpu_vsync_provider_win.cc b/gpu/ipc/service/gpu_vsync_provider_win.cc
index 9adfece..9fec2d2 100644
--- a/gpu/ipc/service/gpu_vsync_provider_win.cc
+++ b/gpu/ipc/service/gpu_vsync_provider_win.cc
@@ -25,6 +25,14 @@
 // Default v-sync interval used when there is no history of v-sync timestamps.
 const int kDefaultInterval = 16666;
 
+// Occasionally DWM stops advancing qpcVBlank timestamp. The existing
+// implementation can cope with that by adjusting the qpcVBlank value forward
+// by a number of v-sync intervals, although the accuracy of adjustment depends
+// on accuracy of the calculated v-sync interval. To avoid accumulating the
+// error, any DWM values that are more than the threshold number of intervals
+// in the past are ignored.
+const int kMissingDwmTimestampsThreshold = 7;
+
 // from <D3dkmthk.h>
 typedef LONG NTSTATUS;
 typedef UINT D3DKMT_HANDLE;
@@ -93,7 +101,7 @@
   void AddTimestamp(base::TimeTicks timestamp);
   void AddInterval(base::TimeDelta interval);
   base::TimeDelta GetAverageInterval() const;
-  void ClearIntervalHistory();
+  void ClearHistory();
 
   bool GetDisplayFrequency(const wchar_t* device_name, DWORD* frequency);
   void UpdateCurrentDisplayFrequency();
@@ -139,15 +147,59 @@
   base::TimeDelta min_accepted_interval_;
   base::TimeDelta max_accepted_interval_;
 
-  // History of recent deltas between timestamps which is used to calculate the
-  // average v-sync interval and organized as a circular buffer.
-  static const size_t kIntervalHistorySize = 60;
-  base::TimeDelta interval_history_[kIntervalHistorySize];
-  size_t history_index_ = 0;
-  size_t history_size_ = 0;
+  // A simple circular buffer for storing a number of recent v-sync intervals
+  // or DWM adjustment deltas and finding an average of them.
+  class TimeDeltaRingBuffer {
+   public:
+    TimeDeltaRingBuffer() = default;
+    ~TimeDeltaRingBuffer() = default;
 
-  // Rolling sum of intervals in the circular buffer above.
-  base::TimeDelta rolling_interval_sum_;
+    void Add(base::TimeDelta value) {
+      if (size_ == kMaxSize) {
+        rolling_sum_ -= values_[next_index_];
+      } else {
+        size_++;
+      }
+
+      values_[next_index_] = value;
+      rolling_sum_ += value;
+      next_index_ = (next_index_ + 1) % kMaxSize;
+    }
+
+    void Clear() {
+      rolling_sum_ = base::TimeDelta();
+      next_index_ = 0;
+      size_ = 0;
+    }
+
+    base::TimeDelta GetAverage() const {
+      if (size_ == 0)
+        return base::TimeDelta();
+
+      return rolling_sum_ / size_;
+    }
+
+   private:
+    enum { kMaxSize = 60 };
+
+    base::TimeDelta values_[kMaxSize];
+    size_t next_index_ = 0;
+    size_t size_ = 0;
+
+    // Rolling sum of TimeDelta values in the circular buffer above.
+    base::TimeDelta rolling_sum_;
+
+    DISALLOW_COPY_AND_ASSIGN(TimeDeltaRingBuffer);
+  };
+
+  // History of recent deltas between timestamps used to calculate the average
+  // v-sync interval.
+  TimeDeltaRingBuffer recent_intervals_;
+
+  // History of recent DWM adjustments used to calculate the average adjustment.
+  TimeDeltaRingBuffer recent_adjustments_;
+
+  DISALLOW_COPY_AND_ASSIGN(GpuVSyncWorker);
 };
 
 GpuVSyncWorker::GpuVSyncWorker(
@@ -289,27 +341,19 @@
   if (interval < min_accepted_interval_ || interval > max_accepted_interval_)
     return;
 
-  if (history_size_ == kIntervalHistorySize) {
-    rolling_interval_sum_ -= interval_history_[history_index_];
-  } else {
-    history_size_++;
-  }
-
-  interval_history_[history_index_] = interval;
-  rolling_interval_sum_ += interval;
-  history_index_ = (history_index_ + 1) % kIntervalHistorySize;
+  recent_intervals_.Add(interval);
 }
 
-void GpuVSyncWorker::ClearIntervalHistory() {
+void GpuVSyncWorker::ClearHistory() {
   last_timestamp_ = base::TimeTicks();
-  rolling_interval_sum_ = base::TimeDelta();
-  history_index_ = 0;
-  history_size_ = 0;
+  recent_intervals_.Clear();
+  recent_adjustments_.Clear();
 }
 
 base::TimeDelta GpuVSyncWorker::GetAverageInterval() const {
-  return !rolling_interval_sum_.is_zero()
-             ? rolling_interval_sum_ / history_size_
+  base::TimeDelta average_interval = recent_intervals_.GetAverage();
+  return !average_interval.is_zero()
+             ? average_interval
              : base::TimeDelta::FromMicroseconds(kDefaultInterval);
 }
 
@@ -341,11 +385,10 @@
     current_display_frequency_ = frequency;
     base::TimeDelta interval = base::TimeDelta::FromMicroseconds(
         base::Time::kMicrosecondsPerSecond / static_cast<double>(frequency));
-    ClearIntervalHistory();
+    ClearHistory();
 
     min_accepted_interval_ = interval * 0.8;
     max_accepted_interval_ = interval * 1.2;
-    AddInterval(interval);
   }
 }
 
@@ -366,14 +409,22 @@
   base::TimeDelta adjustment;
 
   if (use_dwm && GetDwmVBlankTimestamp(&timestamp)) {
-    // Timestamp comes from DwmGetCompositionTimingInfo and apparently it might
-    // be up to 2-3 vsync cycles in the past or in the future.
-    // The adjustment formula was suggested here:
-    // http://www.vsynctester.com/firefoxisbroken.html
     base::TimeDelta interval = GetAverageInterval();
-    adjustment =
-        ((now - timestamp + interval / 8) % interval + interval) % interval -
-        interval / 8;
+    if (now - timestamp > interval * kMissingDwmTimestampsThreshold) {
+      // DWM timestamp is too far in the past. Ignore it and use average
+      // historical adjustment to be applied to |now| time to estimate
+      // the v-sync timestamp.
+      adjustment = recent_adjustments_.GetAverage();
+    } else {
+      // Timestamp comes from DwmGetCompositionTimingInfo and apparently it
+      // might be a few v-sync cycles in the past or in the future.
+      // The adjustment formula was suggested here:
+      // http://www.vsynctester.com/firefoxisbroken.html
+      adjustment =
+          ((now - timestamp + interval / 8) % interval + interval) % interval -
+          interval / 8;
+      recent_adjustments_.Add(adjustment);
+    }
     timestamp = now - adjustment;
   } else {
     // Not using DWM.
@@ -382,11 +433,13 @@
 
   AddTimestamp(timestamp);
 
-  TRACE_EVENT1("gpu", "GpuVSyncWorker::SendGpuVSyncUpdate", "adjustment",
-               adjustment.ToInternalValue());
+  base::TimeDelta average_interval = GetAverageInterval();
+  TRACE_EVENT2("gpu", "GpuVSyncWorker::SendGpuVSyncUpdate", "adjustment",
+               adjustment.InMicroseconds(), "interval",
+               average_interval.InMicroseconds());
 
-  DCHECK_GT(GetAverageInterval().InMillisecondsF(), 0);
-  InvokeCallbackAndReschedule(timestamp, GetAverageInterval());
+  DCHECK_GT(average_interval.InMillisecondsF(), 0);
+  InvokeCallbackAndReschedule(timestamp, average_interval);
 }
 
 void GpuVSyncWorker::InvokeCallbackAndReschedule(base::TimeTicks timestamp,
@@ -461,7 +514,7 @@
     current_adapter_handle_ = 0;
     current_device_name_.clear();
 
-    ClearIntervalHistory();
+    ClearHistory();
   }
 }
 
diff --git a/ios/tools/coverage/coverage.py b/ios/tools/coverage/coverage.py
index 3bb3bf5d..5706d30 100755
--- a/ios/tools/coverage/coverage.py
+++ b/ios/tools/coverage/coverage.py
@@ -40,6 +40,10 @@
 import subprocess
 import webbrowser
 
+sys.path.append(os.path.join(os.path.dirname(__file__), os.path.pardir,
+                             os.path.pardir, os.path.pardir, 'third_party'))
+import jinja2
+
 BUILD_DIRECTORY = 'out/Coverage-iphonesimulator'
 DEFAULT_GOMA_JOBS = 50
 
@@ -64,6 +68,95 @@
 # TODO(crbug.com/763957): Make test file identifiers configurable.
 TEST_FILES_POSTFIXES = ['unittest.mm', 'unittest.cc', 'egtest.mm']
 
+# The default name of the html coverage report for a directory.
+DIRECTORY_COVERAGE_HTML_REPORT_NAME = 'coverage.html'
+
+
+class _DirectoryCoverageReportHtmlGenerator(object):
+  """Excapsulates code coverage html report generation for a directory.
+
+  The generated html has a table that contains all the code coverage of its
+  sub-directories and files. Please refer to ./example.html for an example of
+  the generated html file.
+  """
+
+  def __init__(self, css_path):
+    """Initializes _DirectoryCoverageReportHtmlGenerator object.
+
+    This class assumes that the css file generated by 'llvm-cov show' is reused.
+
+    Args:
+      css_path: A path to the css file.
+    """
+    assert os.path.exists(css_path), ('css file: {} doesn\'t exist.'
+                                      .format(css_path))
+    self._css_path = css_path
+    self._table_entries = []
+    template_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+                                'html_templates')
+
+    jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir),
+                                   trim_blocks=True)
+    self._header_template = jinja_env.get_template('header.html')
+    self._table_template = jinja_env.get_template('table.html')
+    self._footer_template = jinja_env.get_template('footer.html')
+
+  def AddTableEntry(self, html_report_path, name, total_lines, executed_lines):
+    """Adds a file or directory entry to the directory html coverage report.
+
+    Args:
+      html_report_path: A path to the file or directory's html report.
+      name: Base name of the file or directory.
+      total_lines: total number of lines.
+      executed_lines: executed number of lines.
+    """
+    coverage = float(executed_lines) / total_lines
+    if coverage < 0.8:
+      color_class = 'column-entry-red'
+    elif coverage < 1:
+      color_class = 'column-entry-yellow'
+    elif coverage == 1:
+      color_class = 'column-entry-green'
+    else:
+      assert False, ('coverage cannot be greater than 100%, however, {} has '
+                     'coverage: {}').format(html_report_path, coverage)
+    percentage_coverage = round(coverage * 100, 2)
+
+    self._table_entries.append({'href': html_report_path,
+                                'name': name,
+                                'color_class': color_class,
+                                'total_lines': total_lines,
+                                'executed_lines': executed_lines,
+                                'percentage_coverage': percentage_coverage})
+
+  def WriteHtmlCoverageReport(self, output_path):
+    """Write html coverage report for the directory.
+
+    In the report, sub-directories are displayed before files and within each
+    category, entries are sorted by coverage in ascending order.
+
+    Args:
+      output_path: A path to the html report.
+    """
+    dir_entries = [entry for entry in self._table_entries
+                   if os.path.basename(entry['href']) ==
+                   DIRECTORY_COVERAGE_HTML_REPORT_NAME]
+    file_entries = [entry for entry in self._table_entries
+                    if entry not in dir_entries]
+
+    file_entries.sort(
+        key=lambda entry: float(entry['executed_lines']) / entry['total_lines'])
+    dir_entries.sort(
+        key=lambda entry: float(entry['executed_lines']) / entry['total_lines'])
+
+    html_header = self._header_template.render(css_path=self._css_path)
+    html_table = self._table_template.render(dir_entries=dir_entries,
+                                             file_entries=file_entries)
+    html_footer = self._footer_template.render()
+
+    with open(output_path, 'w') as html_file:
+      html_file.write(html_header + html_table + html_footer)
+
 
 class _FileLineCoverageReport(object):
   """Encapsulates coverage calculations for files."""
@@ -165,7 +258,10 @@
 
   def _CalculateCoverageForDirectory(self, path, line_coverage_result,
                                      file_line_coverage_report):
-    """Recursively calculate the line coverage for a directory.
+    """Recursively calculates the line coverage for a directory.
+
+    Only directories that have non-zero number of total lines are included in
+    the report.
 
     Args:
       path: path to the directory.
@@ -188,7 +284,7 @@
         self._CalculateCoverageForDirectory(sub_path, line_coverage_result,
                                             file_line_coverage_report)
 
-      if os.path.isdir(sub_path):
+      if sub_path in line_coverage_result:
         sum_total_lines += line_coverage_result[sub_path]['total']
         sum_executed_lines += line_coverage_result[sub_path]['executed']
       elif file_line_coverage_report.ContainsFile(sub_path):
@@ -197,8 +293,28 @@
         sum_total_lines += total_lines
         sum_executed_lines += executed_lines
 
-    line_coverage_result[path] = {'total': sum_total_lines,
-                                  'executed': sum_executed_lines}
+    if sum_total_lines != 0:
+      line_coverage_result[path] = {'total': sum_total_lines,
+                                    'executed': sum_executed_lines}
+
+  def GetListOfDirectories(self):
+    """Returns a list of directories in the report.
+
+    Returns:
+      A list of directories.
+    """
+    return self._coverage.keys()
+
+  def ContainsDirectory(self, path):
+    """Returns True if the path is in the report.
+
+    Args:
+      path: path to the directory.
+
+    Returns:
+      True if the path is in the report.
+    """
+    return path in self._coverage
 
   def GetCoverageForDirectory(self, path):
     """Returns tuple representing coverage for a directory.
@@ -215,10 +331,10 @@
 
 def _GenerateLineByLineFileCoverageInHtml(
     target, profdata_path, file_line_coverage_report, output_dir):
-  """Generate per file line-by-line coverage in html using 'llvm-cov show'.
+  """Generates per file line-by-line coverage in html using 'llvm-cov show'.
 
   For a file with absolute path /a/b/x.cc, a html report is generated as:
-  output_dir/a/b/x.cc.html. An index html file is also generated as:
+  output_dir/coverage/a/b/x.cc.html. An index html file is also generated as:
   output_dir/index.html.
 
   Args:
@@ -226,8 +342,9 @@
     profdata_path: A string representing the path to the profdata file.
     file_line_coverage_report: A FileLineCoverageReport object.
     output_dir: output directory for generated html files, the path needs to be
-                absolute or relative to the root of checkout.
+                an absolute path.
   """
+  assert os.path.isabs(output_dir), 'output_dir must be an absolute path.'
   application_path = _GetApplicationBundlePath(target)
   binary_path = os.path.join(application_path, target)
   cmd = ['xcrun', 'llvm-cov', 'show', '-instr-profile', profdata_path,
@@ -238,6 +355,113 @@
   subprocess.check_call(cmd)
 
 
+def _GeneratePerDirectoryCoverageHtmlReport(dir_line_coverage_report,
+                                            file_line_coverage_report,
+                                            output_dir):
+  """Generates per directory coverage report in html.
+
+  For each directory, all its files or sub-directories that has non-zero number
+  of total lines are displayed.
+
+  Args:
+    dir_line_coverage_report: A DirectoryLineCoverageReport object.
+    file_line_coverage_report: A FileLineCoverageReport object.
+    output_dir: output directory for generated html files, the path needs to be
+                an absolute path.
+  """
+  assert os.path.isabs(output_dir), 'output_dir must be an absolute path.'
+  for dir_path in dir_line_coverage_report.GetListOfDirectories():
+    _GenerateCoverageHtmlReportForDirectory(dir_path, dir_line_coverage_report,
+                                            file_line_coverage_report,
+                                            output_dir)
+
+
+def _GenerateCoverageHtmlReportForDirectory(dir_path, dir_line_coverage_report,
+                                            file_line_coverage_report,
+                                            output_dir):
+  """Generates coverage report for directory in html.
+
+  Args:
+    dir_path: A path to the directory.
+    dir_line_coverage_report: A DirectoryLineCoverageReport object.
+    file_line_coverage_report: A FileLineCoverageReport object.
+    output_dir: output directory for generated html files, the path needs to be
+                an absolute path.
+  """
+  assert os.path.isabs(output_dir), 'output_dir must be an absolute path.'
+  css_path = os.path.join(output_dir, 'style.css')
+  html_generator = _DirectoryCoverageReportHtmlGenerator(css_path)
+
+  for sub_name in os.listdir(dir_path):
+    sub_path = os.path.join(dir_path, sub_name)
+    sub_path_html_report_path = _GetCoverageHtmlReportPath(
+        sub_path, output_dir)
+
+    if dir_line_coverage_report.ContainsDirectory(sub_path):
+      total_lines, executed_lines = (
+          dir_line_coverage_report.GetCoverageForDirectory(sub_path))
+      html_generator.AddTableEntry(sub_path_html_report_path,
+                                   os.path.basename(sub_path),
+                                   total_lines,
+                                   executed_lines)
+    elif file_line_coverage_report.ContainsFile(sub_path):
+      total_lines, executed_lines = (
+          file_line_coverage_report.GetCoverageForFile(sub_path))
+      html_generator.AddTableEntry(sub_path_html_report_path,
+                                   os.path.basename(sub_path),
+                                   total_lines,
+                                   executed_lines)
+
+  html_generator.WriteHtmlCoverageReport(
+      _GetCoverageHtmlReportPath(dir_path, output_dir))
+
+
+def _GetCoverageHtmlReportPath(file_or_dir_path, output_dir):
+  """Given a file or directory, returns the corresponding html report path.
+
+  Args:
+    file_or_dir_path: os path to a file or directory.
+    output_dir: output directory for generated html files, the path needs to be
+                an absolute path.
+  Returns:
+    A path to the corresponding coverage html report.
+  """
+  assert os.path.isabs(output_dir), 'output_dir must be an absolute path.'
+  html_path = (os.path.join(os.path.abspath(output_dir), 'coverage') +
+               os.path.abspath(file_or_dir_path))
+  if os.path.isdir(file_or_dir_path):
+    return os.path.join(html_path, DIRECTORY_COVERAGE_HTML_REPORT_NAME)
+  else:
+    return html_path + '.html'
+
+
+def _OverwriteHtmlReportsIndexFile(top_level_dir, dir_line_coverage_report,
+                                   output_dir):
+  """Overwrites the index file to link to the report of the top level directory.
+
+  Args:
+    top_level_dir: The top level directory to generate code coverage for.
+    dir_line_coverage_report: A DirectoryLineCoverageReport object.
+    output_dir: output directory for generated html files, the path needs to be
+                an absolute path.
+  """
+  assert os.path.isabs(output_dir), 'output_dir must be an absolute path.'
+  css_path = os.path.join(output_dir, 'style.css')
+  index_html_generator = _DirectoryCoverageReportHtmlGenerator(css_path)
+
+  html_report_path = _GetCoverageHtmlReportPath(top_level_dir,
+                                                output_dir)
+  total_lines, executed_lines = (
+      dir_line_coverage_report.GetCoverageForDirectory(top_level_dir))
+  index_html_generator.AddTableEntry(
+      html_report_path, os.path.basename(top_level_dir), total_lines,
+      executed_lines)
+
+  index_file_path = os.path.join(output_dir,
+                                 'index.html')
+  index_html_generator.WriteHtmlCoverageReport(index_file_path)
+
+
 def _CreateCoverageProfileDataForTarget(target, output_dir, jobs_count=None,
                                         gtest_filter=None):
   """Builds and runs target to generate the coverage profile data.
@@ -245,7 +469,7 @@
   Args:
     target: A string representing the name of the target to be tested.
     output_dir: A directory to store created coverage profile data file, the
-                path needs to be absolute or relative to the root of checkout.
+                path needs to be an absolute path.
     jobs_count: Number of jobs to run in parallel for building. If None, a
                 default value is derived based on CPUs availability.
     gtest_filter: If present, only run unit tests whose full name matches the
@@ -254,6 +478,7 @@
   Returns:
     A string representing the absolute path to the generated profdata file.
   """
+  assert os.path.isabs(output_dir), 'output_dir must be an absolute path.'
   _BuildTargetWithCoverageConfiguration(target, jobs_count)
   profraw_path = _GetProfileRawDataPathByRunningTarget(target, gtest_filter)
   profdata_path = _CreateCoverageProfileDataFromProfRawData(profraw_path,
@@ -286,6 +511,9 @@
   ------------------------------------------------------------------------------
   In Total\t89\t85\t4.49%\t7\t6\t14.29%\t7\t6\t14.29%\t107\t99\t7.48%
 
+  NOTE: This method only includes files with non-zero total number of lines to
+  the report.
+
   Args:
     target: A string representing the name of the target to be tested.
     profdata_path: A string representing the path to the profdata file.
@@ -326,8 +554,9 @@
     except ValueError:
       continue
 
-    executed_lines = total_lines - missed_lines
-    file_line_coverage_report.AddFile(file_name, total_lines, executed_lines)
+    if total_lines > 0:
+      executed_lines = total_lines - missed_lines
+      file_line_coverage_report.AddFile(file_name, total_lines, executed_lines)
 
   return file_line_coverage_report
 
@@ -442,7 +671,7 @@
     profraw_path: A string representing the absolute path to the profraw data
                   file that is to be merged.
     output_dir: A directory to store created coverage profile data file, the
-                path needs to be absolute or relative to the root of checkout.
+                path needs to be an absolute path.
 
   Returns:
     A string representing the absolute path to the generated profdata file.
@@ -452,6 +681,7 @@
   """
   print 'Creating the profile data file'
 
+  assert os.path.isabs(output_dir), 'output_dir must be an absolute path.'
   if not os.path.exists(output_dir):
     os.makedirs(output_dir)
   profdata_path = os.path.join(output_dir, PROFDATA_FILE_NAME)
@@ -471,8 +701,8 @@
   Returns:
     A string representing the absolute path to the root of checkout.
   """
-  return os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir,
-                                      os.pardir, os.pardir))
+  return os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir,
+                                      os.path.pardir, os.path.pardir))
 
 
 def _GetApplicationBundlePath(target):
@@ -808,14 +1038,15 @@
   include_sources = include_paths + _GetSourcesOfBuildTargets(include_targets)
   exclude_sources = exclude_paths + _GetSourcesOfBuildTargets(exclude_targets)
 
+  output_dir_abspath = os.path.abspath(args.output_dir)
   profdata_path = args.reuse_profdata
   if profdata_path:
     assert os.path.exists(profdata_path), ('The provided profile data file: {} '
                                            'doesn\'t exist.').format(
                                                profdata_path)
   else:
-    profdata_path = _CreateCoverageProfileDataForTarget(target, args.output_dir,
-                                                        jobs, args.gtest_filter)
+    profdata_path = _CreateCoverageProfileDataForTarget(
+        target, output_dir_abspath, jobs, args.gtest_filter)
 
   print 'Generating code coverge report'
   file_line_coverage_report = _GeneratePerFileLineCoverageReport(
@@ -835,15 +1066,19 @@
   _PrintLineCoverageStats(total, executed)
 
   _GenerateLineByLineFileCoverageInHtml(
-      target, profdata_path, file_line_coverage_report, args.output_dir)
+      target, profdata_path, file_line_coverage_report, output_dir_abspath)
+  _GeneratePerDirectoryCoverageHtmlReport(
+      dir_line_coverage_report, file_line_coverage_report, output_dir_abspath)
 
+  # The default index file is generated only for the list of source files, needs
+  # to overwrite it to display per directory code coverage breakdown.
+  _OverwriteHtmlReportsIndexFile(top_level_dir, dir_line_coverage_report,
+                                 output_dir_abspath)
   html_index_file_path = 'file://' + os.path.abspath(
       os.path.join(args.output_dir, 'index.html'))
   print 'index file for html report is generated as: {}'.format(
       html_index_file_path)
-
   webbrowser.open(html_index_file_path)
 
-
 if __name__ == '__main__':
   sys.exit(Main())
diff --git a/ios/tools/coverage/example.html b/ios/tools/coverage/example.html
new file mode 100644
index 0000000..a5a3b3d3
--- /dev/null
+++ b/ios/tools/coverage/example.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<html>
+  <head>
+    <meta name='viewport' content='width=device-width,initial-scale=1'>
+    <meta charset='UTF-8'>
+    <link rel='stylesheet' type='text/css' href='/Users/liaoyuke/bling/src/out/Coverage-iphonesimulator/html/style.css'>
+  </head>
+  <body>
+    <h2>Coverage Report</h2>
+    <p>Click <a href='http://clang.llvm.org/docs/SourceBasedCodeCoverage.html#interpreting-reports'>here</a> for information about interpreting this report.</p>
+    <div class='centered'>
+      <table>
+        <tr>
+          <td class='column-entry-left'>Directory Name</td>
+          <td class='column-entry'>Line Coverage</td>
+        </tr>
+        <tr class='light-row'>
+          <td>
+            <pre><a href='/Users/liaoyuke/bling/src/out/Coverage-iphonesimulator/html/coverage/Users/liaoyuke/bling/src/url/third_party/coverage.html'>third_party</a></pre>
+          </td>
+          <td class='column-entry-yellow'>
+            <pre>  90.31% (699/774)</pre>
+          </td>
+        </tr>
+        <tr>
+          <td class='column-entry-left'>File Name</td>
+          <td class='column-entry'>Line Coverage</td>
+        </tr>
+        <tr class='light-row'>
+          <td>
+            <pre><a href='/Users/liaoyuke/bling/src/out/Coverage-iphonesimulator/html/coverage/Users/liaoyuke/bling/src/url/url_canon_stdurl.cc.html'>url_canon_stdurl.cc</a></pre>
+          </td>
+          <td class='column-entry-yellow'>
+            <pre>  93.94% (124/132)</pre>
+          </td>
+        </tr>
+      </table>
+    </div>
+  </body>
+</html>
\ No newline at end of file
diff --git a/ios/tools/coverage/html_templates/footer.html b/ios/tools/coverage/html_templates/footer.html
new file mode 100644
index 0000000..79532637
--- /dev/null
+++ b/ios/tools/coverage/html_templates/footer.html
@@ -0,0 +1,4 @@
+      </table>
+    </div>
+  </body>
+</html>
\ No newline at end of file
diff --git a/ios/tools/coverage/html_templates/header.html b/ios/tools/coverage/html_templates/header.html
new file mode 100644
index 0000000..0d16a2fd
--- /dev/null
+++ b/ios/tools/coverage/html_templates/header.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+  <head>
+    <meta name='viewport' content='width=device-width,initial-scale=1'>
+    <meta charset='UTF-8'>
+    <link rel='stylesheet' type='text/css' href='{{ css_path }}'>
+  </head>
+  <body>
+    <h2>Coverage Report</h2>
+    <p>Click <a href='http://clang.llvm.org/docs/SourceBasedCodeCoverage.html#interpreting-reports'>here</a> for information about interpreting this report.</p>
+    <div class='centered'>
+      <table>
\ No newline at end of file
diff --git a/ios/tools/coverage/html_templates/table.html b/ios/tools/coverage/html_templates/table.html
new file mode 100644
index 0000000..3fcdfaa
--- /dev/null
+++ b/ios/tools/coverage/html_templates/table.html
@@ -0,0 +1,30 @@
+<table>
+  <tr>
+    <td class='column-entry-left'>Directory Name</td>
+    <td class='column-entry'>Line Coverage</td>
+  </tr>
+  {% for dir_entry in dir_entries %}
+  <tr class='light-row'>
+    <td>
+      <pre><a href='{{ dir_entry.href }}'>{{ dir_entry.name }}</a></pre>
+    </td>
+    <td class='{{ dir_entry.color_class }}'>
+      <pre> {{ dir_entry.percentage_coverage }}% ({{ dir_entry.executed_lines }}/{{ dir_entry.total_lines }})</pre>
+    </td>
+  </tr>
+  {% endfor %}
+  <tr>
+    <td class='column-entry-left'>File Name</td>
+    <td class='column-entry'>Line Coverage</td>
+  </tr>
+  {% for file_entry in file_entries %}
+  <tr class='light-row'>
+    <td>
+      <pre><a href='{{ file_entry.href }}'>{{ file_entry.name }}</a></pre>
+    </td>
+    <td class='{{ file_entry.color_class }}'>
+      <pre>  {{ file_entry.percentage_coverage }}% ({{ file_entry.executed_lines }}/{{ file_entry.total_lines }})</pre>
+    </td>
+  </tr>
+  {% endfor %}
+</table>
\ No newline at end of file
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 0d8c66f..8aab9bd 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -281,7 +281,7 @@
 // supported platforms. On platforms that do not support ECK, this feature has
 // no effect.
 const base::Feature kExternalClearKeyForTesting{
-    "external-clear-key-for-testing", base::FEATURE_DISABLED_BY_DEFAULT};
+    "ExternalClearKeyForTesting", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kSupportExperimentalCdmInterface{
     "SupportExperimentalCdmInterface", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/media/cdm/cdm_paths.cc b/media/cdm/cdm_paths.cc
index 0c1bdb9..c72f49f 100644
--- a/media/cdm/cdm_paths.cc
+++ b/media/cdm/cdm_paths.cc
@@ -25,9 +25,9 @@
 #endif
 
 const char kClearKeyCdmDisplayName[] = "Clear Key CDM";
-
 const char kClearKeyCdmType[] = "Clear Key CDM";
-
+const char kClearKeyCdmGuid[] = "C1A6B4E3-FE48-4D53-9F52-244AEEAD5335";
+const char kClearKeyCdmDifferentGuid[] = "747C565D-34EE-4B0D-AC1E-4F9FB17DDB40";
 const char kClearKeyCdmPepperMimeType[] = "application/x-ppapi-clearkey-cdm";
 
 // Note: This file must be in sync with cdm_paths.gni.
diff --git a/media/cdm/cdm_paths.h b/media/cdm/cdm_paths.h
index 0e6b9be5c..06e4985 100644
--- a/media/cdm/cdm_paths.h
+++ b/media/cdm/cdm_paths.h
@@ -25,6 +25,13 @@
 // The CDM type used to register Clear Key CDM.
 extern const char kClearKeyCdmType[];
 
+// The default GUID for Clear Key Cdm.
+extern const char kClearKeyCdmGuid[];
+
+// A different GUID for Clear Key Cdm for testing running different types of
+// CDMs in the system.
+extern const char kClearKeyCdmDifferentGuid[];
+
 // Pepper type for Clear Key CDM.
 // TODO(xhwang): Remove after switching to mojo CDM.
 extern const char kClearKeyCdmPepperMimeType[];
diff --git a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc
index d96df62..22f1994 100644
--- a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc
+++ b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc
@@ -70,6 +70,8 @@
     "org.chromium.externalclearkey.verifycdmhosttest";
 const char kExternalClearKeyStorageIdTestKeySystem[] =
     "org.chromium.externalclearkey.storageidtest";
+const char kExternalClearKeyDifferentGuidTestKeySystem[] =
+    "org.chromium.externalclearkey.differentguid";
 
 const int64_t kSecondsPerMinute = 60;
 const int64_t kMsPerSecond = 1000;
@@ -256,7 +258,8 @@
       key_system_string != kExternalClearKeyPlatformVerificationTestKeySystem &&
       key_system_string != kExternalClearKeyCrashKeySystem &&
       key_system_string != kExternalClearKeyVerifyCdmHostTestKeySystem &&
-      key_system_string != kExternalClearKeyStorageIdTestKeySystem) {
+      key_system_string != kExternalClearKeyStorageIdTestKeySystem &&
+      key_system_string != kExternalClearKeyDifferentGuidTestKeySystem) {
     DVLOG(1) << "Unsupported key system:" << key_system_string;
     return nullptr;
   }
@@ -693,7 +696,7 @@
   // that the session is properly closed.
   if (!last_session_id_.empty() &&
       key_system_ == kExternalClearKeyCrashKeySystem) {
-    CHECK(false);
+    CHECK(false) << "Crash in decrypt-and-decode with crash key system.";
   }
 
   scoped_refptr<media::DecoderBuffer> buffer;
@@ -857,6 +860,13 @@
                                       CdmKeysInfo keys_info) {
   DVLOG(1) << __func__ << ": size = " << keys_info.size();
 
+  // Crash if the special key ID "crash" is present.
+  const std::vector<uint8_t> kCrashKeyId{'c', 'r', 'a', 's', 'h'};
+  for (const auto& key_info : keys_info) {
+    if (key_info->key_id == kCrashKeyId)
+      CHECK(false) << "Crash on special crash key ID.";
+  }
+
   std::vector<cdm::KeyInformation> keys_vector;
   ConvertCdmKeysInfo(keys_info, &keys_vector);
   host_->OnSessionKeysChange(session_id.data(), session_id.length(),
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index 533ac35..b4c22eb 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -112,6 +112,8 @@
     sources += [
       "mojo_cdm_allocator.cc",
       "mojo_cdm_allocator.h",
+      "mojo_cdm_helper.cc",
+      "mojo_cdm_helper.h",
     ]
     deps += [ "//media/cdm:cdm_api" ]
   }
diff --git a/media/mojo/services/mojo_cdm_helper.cc b/media/mojo/services/mojo_cdm_helper.cc
new file mode 100644
index 0000000..0bb4836e
--- /dev/null
+++ b/media/mojo/services/mojo_cdm_helper.cc
@@ -0,0 +1,97 @@
+// 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 "media/mojo/services/mojo_cdm_helper.h"
+
+#include "media/base/scoped_callback_runner.h"
+#include "media/cdm/cdm_helpers.h"
+#include "media/mojo/services/mojo_cdm_allocator.h"
+#include "services/service_manager/public/cpp/connect.h"
+
+namespace media {
+
+MojoCdmHelper::MojoCdmHelper(
+    service_manager::mojom::InterfaceProvider* interface_provider)
+    : interface_provider_(interface_provider) {}
+
+MojoCdmHelper::~MojoCdmHelper() {}
+
+std::unique_ptr<CdmFileIO> MojoCdmHelper::CreateCdmFileIO(
+    cdm::FileIOClient* client) {
+  // TODO(jrummell): Hook up File IO. http://crbug.com/479923.
+  return nullptr;
+}
+
+cdm::Buffer* MojoCdmHelper::CreateCdmBuffer(size_t capacity) {
+  return GetAllocator()->CreateCdmBuffer(capacity);
+}
+
+std::unique_ptr<VideoFrameImpl> MojoCdmHelper::CreateCdmVideoFrame() {
+  return GetAllocator()->CreateCdmVideoFrame();
+}
+
+void MojoCdmHelper::QueryStatus(QueryStatusCB callback) {
+  QueryStatusCB scoped_callback =
+      ScopedCallbackRunner(std::move(callback), false, 0, 0);
+  if (!ConnectToOutputProtection())
+    return;
+
+  output_protection_->QueryStatus(std::move(scoped_callback));
+}
+
+void MojoCdmHelper::EnableProtection(uint32_t desired_protection_mask,
+                                     EnableProtectionCB callback) {
+  EnableProtectionCB scoped_callback =
+      ScopedCallbackRunner(std::move(callback), false);
+  if (!ConnectToOutputProtection())
+    return;
+
+  output_protection_->EnableProtection(desired_protection_mask,
+                                       std::move(scoped_callback));
+}
+
+void MojoCdmHelper::ChallengePlatform(const std::string& service_id,
+                                      const std::string& challenge,
+                                      ChallengePlatformCB callback) {
+  ChallengePlatformCB scoped_callback =
+      ScopedCallbackRunner(std::move(callback), false, "", "", "");
+  if (!ConnectToPlatformVerification())
+    return;
+
+  platform_verification_->ChallengePlatform(service_id, challenge,
+                                            std::move(scoped_callback));
+}
+
+void MojoCdmHelper::GetStorageId(uint32_t version, StorageIdCB callback) {
+  StorageIdCB scoped_callback = ScopedCallbackRunner(
+      std::move(callback), version, std::vector<uint8_t>());
+  // TODO(jrummell): Hook up GetStorageId() once added to the mojo interface.
+  // http://crbug.com/478960.
+}
+
+CdmAllocator* MojoCdmHelper::GetAllocator() {
+  if (!allocator_)
+    allocator_ = base::MakeUnique<MojoCdmAllocator>();
+  return allocator_.get();
+}
+
+bool MojoCdmHelper::ConnectToOutputProtection() {
+  if (!output_protection_attempted_) {
+    output_protection_attempted_ = true;
+    service_manager::GetInterface<mojom::OutputProtection>(interface_provider_,
+                                                           &output_protection_);
+  }
+  return output_protection_.is_bound();
+}
+
+bool MojoCdmHelper::ConnectToPlatformVerification() {
+  if (!platform_verification_attempted_) {
+    platform_verification_attempted_ = true;
+    service_manager::GetInterface<mojom::PlatformVerification>(
+        interface_provider_, &platform_verification_);
+  }
+  return platform_verification_.is_bound();
+}
+
+}  // namespace media
diff --git a/media/mojo/services/mojo_cdm_helper.h b/media/mojo/services/mojo_cdm_helper.h
new file mode 100644
index 0000000..167fdfd
--- /dev/null
+++ b/media/mojo/services/mojo_cdm_helper.h
@@ -0,0 +1,68 @@
+// 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 MEDIA_MOJO_SERVICES_MOJO_CDM_HELPER_H_
+#define MEDIA_MOJO_SERVICES_MOJO_CDM_HELPER_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "media/cdm/cdm_auxiliary_helper.h"
+#include "media/mojo/interfaces/output_protection.mojom.h"
+#include "media/mojo/interfaces/platform_verification.mojom.h"
+#include "media/mojo/services/media_mojo_export.h"
+
+namespace service_manager {
+namespace mojom {
+class InterfaceProvider;
+}
+}  // namespace service_manager
+
+namespace media {
+
+// Helper class that connects the CDM to various auxiliary services. All
+// additional services (FileIO, memory allocation, output protection, and
+// platform verification) are lazily created.
+class MEDIA_MOJO_EXPORT MojoCdmHelper final : public CdmAuxiliaryHelper {
+ public:
+  explicit MojoCdmHelper(
+      service_manager::mojom::InterfaceProvider* interface_provider);
+  ~MojoCdmHelper() final;
+
+  // CdmAuxiliaryHelper implementation.
+  std::unique_ptr<media::CdmFileIO> CreateCdmFileIO(
+      cdm::FileIOClient* client) final;
+  cdm::Buffer* CreateCdmBuffer(size_t capacity) final;
+  std::unique_ptr<VideoFrameImpl> CreateCdmVideoFrame() final;
+  void QueryStatus(QueryStatusCB callback) final;
+  void EnableProtection(uint32_t desired_protection_mask,
+                        EnableProtectionCB callback) final;
+  void ChallengePlatform(const std::string& service_id,
+                         const std::string& challenge,
+                         ChallengePlatformCB callback) final;
+  void GetStorageId(uint32_t version, StorageIdCB callback) final;
+
+ private:
+  // All services are created lazily.
+  CdmAllocator* GetAllocator();
+  bool ConnectToOutputProtection();
+  bool ConnectToPlatformVerification();
+
+  // Provides interfaces when needed.
+  service_manager::mojom::InterfaceProvider* interface_provider_;
+
+  // Keep track if connection to the Mojo service has been attempted once.
+  // The service may not exist, or may fail later.
+  bool output_protection_attempted_ = false;
+  bool platform_verification_attempted_ = false;
+
+  std::unique_ptr<media::CdmAllocator> allocator_;
+  media::mojom::OutputProtectionPtr output_protection_;
+  media::mojom::PlatformVerificationPtr platform_verification_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_MOJO_SERVICES_MOJO_CDM_HELPER_H_
diff --git a/media/test/data/multiple_cdm_types.html b/media/test/data/multiple_cdm_types.html
new file mode 100644
index 0000000..ad313ea6
--- /dev/null
+++ b/media/test/data/multiple_cdm_types.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<title>Test running different types of CDM in the system</title>
+<div id="console"></div>
+<script src='eme_player_js/app_loader.js' type='text/javascript'></script>
+<script type='text/javascript'>
+
+  function log(message) {
+    let consoleElement = document.getElementById('console');
+    let entry = document.createElement('div');
+    entry.appendChild(document.createTextNode(message));
+    consoleElement.appendChild(entry);
+    console.log(message);
+  }
+
+  const EXTERNAL_CLEARKEY_DIFFERENTGUID
+      = 'org.chromium.externalclearkey.differentguid';
+  const crashKeyId = 'crash';
+  const normalJwkSet = Utils.createJWKData(KEY_ID, KEY);
+  const crashJwkSet = Utils.createJWKData(crashKeyId, KEY);
+
+  var config = {
+    initDataTypes : [ 'keyids' ],
+    videoCapabilities: [{contentType: 'video/webm; codecs="vp8"'}],
+    persistentState: 'optional',
+    sessionTypes: ['temporary'],
+  };
+
+  function createMediaKeySession(key_system) {
+    var mediaKeySession;
+    return navigator.requestMediaKeySystemAccess(key_system, [config])
+        .then(function(access) {
+      initDataType = access.getConfiguration().initDataTypes[0];
+      initData = Utils.createKeyIdsInitializationData(KEY_ID)
+      return access.createMediaKeys();
+    }).then(function(result) {
+      log('CDM created');
+      var mediaKeys = result;
+      mediaKeySession = mediaKeys.createSession();
+      return mediaKeySession.generateRequest(initDataType, initData);
+    }).then(function() {
+      mediaKeySession.update(normalJwkSet);
+    }).then(function() {
+      return mediaKeySession;
+    });
+  }
+
+  log('Start test');
+
+  // Using EXTERNAL_CLEARKEY
+  var session1;
+
+  // Both using EXTERNAL_CLEARKEY_DIFFERENTGUID
+  var session2;
+  var session3;
+
+  // The following creates 3 MediaKeys instances each with a MediaKeySession.
+  // MediaKeys using different CDM GUID will run in different processes.
+  // |session1| uses EXTERNAL_CLEARKEY that is registered with the default GUID
+  // for Clear Key CDM. |session2/3| use EXTERNAL_CLEARKEY_DIFFERENTGUID that is
+  // registered with a different GUID. So |session1| will run in process1, and
+  // |session2/3| will run in process2.
+  //
+  // Then we send a special response |crashJwkSet| to session2 which will cause
+  // the process2 to crash. This will close both |session2/3| as they run in the
+  // same process. |session1| should not be affected. Then we try to create
+  // another MediaKeySession using EXTERNAL_CLEARKEY_DIFFERENTGUID, and the
+  // creation should work as a new process should be started.
+
+  createMediaKeySession(EXTERNAL_CLEARKEY).then(function(session) {
+    log('Session1 created');
+    session1 = session;
+    return createMediaKeySession(EXTERNAL_CLEARKEY_DIFFERENTGUID);
+  }).then(function(session) {
+    log('Session2 created');
+    session2 = session;
+    return createMediaKeySession(EXTERNAL_CLEARKEY_DIFFERENTGUID);
+  }).then(function(session) {
+    log('Session3 created');
+    session3 = session;
+    log('Crash session2');
+    return session.update(crashJwkSet);
+  }).then(function() {
+    log('Session2 crashed');
+    return session2.closed;
+  }).then(function() {
+    log('Session2 closed');
+    return session3.closed;
+  }).then(function() {
+    log('Session3 also closed');
+    return session1.update(normalJwkSet);
+  }).then(function() {
+    log('Session1 still works');
+    return createMediaKeySession(EXTERNAL_CLEARKEY_DIFFERENTGUID);
+  }).then(function(session) {
+    log('Can still create a session after crash');
+    Utils.setResultInTitle('ENDED');
+  }).catch(function(error) {
+    log('Error: ' + error);
+    Utils.failTest('Test failed: ' + error);
+  });
+
+</script>
+</html>
diff --git a/third_party/WebKit/LayoutTests/compositing/squashing/selection-repaint-with-gaps.html b/third_party/WebKit/LayoutTests/compositing/squashing/selection-repaint-with-gaps.html
index b6fd71d9..192b55d21 100644
--- a/third_party/WebKit/LayoutTests/compositing/squashing/selection-repaint-with-gaps.html
+++ b/third_party/WebKit/LayoutTests/compositing/squashing/selection-repaint-with-gaps.html
@@ -36,7 +36,7 @@
 </div>
 <script>
     if (window.testRunner) {
-        testRunner.dumpAsText();
+        testRunner.dumpAsTextWithPixelResults();
         testRunner.waitUntilDone();
     }
 
diff --git a/third_party/WebKit/LayoutTests/images/color-profile-background-image-space-expected.png b/third_party/WebKit/LayoutTests/images/color-profile-background-image-space-expected.png
index a811512..c5d0b0f0 100644
--- a/third_party/WebKit/LayoutTests/images/color-profile-background-image-space-expected.png
+++ b/third_party/WebKit/LayoutTests/images/color-profile-background-image-space-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-in-composited-scrolling-container.html b/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-in-composited-scrolling-container.html
index 54afdb7..d4afee2 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-in-composited-scrolling-container.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-in-composited-scrolling-container.html
@@ -1,9 +1,9 @@
-<!DOCTYPE html> 
+<!DOCTYPE html>
 <script src="../resources/text-based-repaint.js"></script>
 <input id="target" size="5"  value="test test test">
 <script>
 window.testIsAsync = true;
-onload = runRepaintTest;
+onload = runRepaintAndPixelTest;
 if (window.internals)
   window.internals.settings.setPreferCompositingToLCDTextEnabled(true);
 
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-in-non-composited-scrolling-container.html b/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-in-non-composited-scrolling-container.html
index be54fc7..53da250 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-in-non-composited-scrolling-container.html
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-in-non-composited-scrolling-container.html
@@ -1,9 +1,9 @@
-<!DOCTYPE html> 
+<!DOCTYPE html>
 <script src="../resources/text-based-repaint.js"></script>
 <input id="target"size="5"  value="test test test">
 <script>
 window.testIsAsync = true;
-onload = runRepaintTest;
+onload = runRepaintAndPixelTest;
 if (window.internals)
   window.internals.settings.setPreferCompositingToLCDTextEnabled(false);
 
diff --git a/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..4525ea5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/squashing/selection-repaint-with-gaps-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/squashing/selection-repaint-with-gaps-expected.png
new file mode 100644
index 0000000..cf84bf13
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/squashing/selection-repaint-with-gaps-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/http/tests/misc/slow-loading-image-in-pattern-expected.png b/third_party/WebKit/LayoutTests/platform/linux/http/tests/misc/slow-loading-image-in-pattern-expected.png
index 424b4db..cabe47b 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/http/tests/misc/slow-loading-image-in-pattern-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/http/tests/misc/slow-loading-image-in-pattern-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/linux/images/color-profile-svg-fill-text-expected.png
index c7e1f7b..5d8eaeae 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..effd75a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..044e2c9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
index 66498a7..9c73ba8 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png
index 424b4db..cabe47b 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/compositing/squashing/selection-repaint-with-gaps-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/compositing/squashing/selection-repaint-with-gaps-expected.png
new file mode 100644
index 0000000..1bbbef8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/compositing/squashing/selection-repaint-with-gaps-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png
index a773132..08343bfa 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..e2c6c700
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..da7762af
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
index 07a3c2c..05ebe12 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..74794c1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..dd74d55
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/css3/masking/mask-repeat-space-padding-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/css3/masking/mask-repeat-space-padding-expected.png
index 47ed2d1..eedee93 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/css3/masking/mask-repeat-space-padding-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/css3/masking/mask-repeat-space-padding-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/images/color-profile-svg-fill-text-expected.png
index cde34596..b2aeb5a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..d2cc6b04
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..4fcaf52
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
index 0f4a16b..1df1cd43 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..74794c1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..dd74d55
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/selection-repaint-with-gaps-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/selection-repaint-with-gaps-expected.png
new file mode 100644
index 0000000..4f49e56
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/squashing/selection-repaint-with-gaps-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/css3/masking/mask-repeat-space-padding-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/masking/mask-repeat-space-padding-expected.png
index eb90563..636b74b 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/css3/masking/mask-repeat-space-padding-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/css3/masking/mask-repeat-space-padding-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/background-repeat-with-background-color-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/background-repeat-with-background-color-expected.png
index 4dba2a8b..6f502775 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/background-repeat-with-background-color-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/backgrounds/background-repeat-with-background-color-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/http/tests/misc/slow-loading-image-in-pattern-expected.png b/third_party/WebKit/LayoutTests/platform/mac/http/tests/misc/slow-loading-image-in-pattern-expected.png
index d8181498..5c897cc 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/http/tests/misc/slow-loading-image-in-pattern-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/http/tests/misc/slow-loading-image-in-pattern-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/color-profile-svg-fill-text-expected.png
index 75c356c..5773e7e 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/cross-fade-background-size-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/cross-fade-background-size-expected.png
index 13c9b2e..3359ed2 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/images/cross-fade-background-size-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..02490f3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..80def35
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png
new file mode 100644
index 0000000..7b65507
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
index 41f9297..8cde405 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/cross-fade-background-size-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/cross-fade-background-size-expected.png
index f268586..73cbaf7a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/cross-fade-background-size-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
index 94f1612..e138bc81 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png
index d8181498..5c897cc 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/selection-repaint-with-gaps-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/selection-repaint-with-gaps-expected.png
new file mode 100644
index 0000000..6db86be
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/squashing/selection-repaint-with-gaps-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/css3/masking/mask-repeat-space-padding-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/masking/mask-repeat-space-padding-expected.png
index 391076d..6dd1e66 100644
--- a/third_party/WebKit/LayoutTests/platform/win/css3/masking/mask-repeat-space-padding-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/css3/masking/mask-repeat-space-padding-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/background-repeat-with-background-color-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/background-repeat-with-background-color-expected.png
index f6ab2241..47bfe99 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/background-repeat-with-background-color-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/backgrounds/background-repeat-with-background-color-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/http/tests/misc/slow-loading-image-in-pattern-expected.png b/third_party/WebKit/LayoutTests/platform/win/http/tests/misc/slow-loading-image-in-pattern-expected.png
index 2304502..fe19eb5 100644
--- a/third_party/WebKit/LayoutTests/platform/win/http/tests/misc/slow-loading-image-in-pattern-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/http/tests/misc/slow-loading-image-in-pattern-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/color-profile-svg-fill-text-expected.png
index 15b2a7c8..7d0f474 100644
--- a/third_party/WebKit/LayoutTests/platform/win/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/images/cross-fade-background-size-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/cross-fade-background-size-expected.png
index 6dd3d20b..af34a7c 100644
--- a/third_party/WebKit/LayoutTests/platform/win/images/cross-fade-background-size-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..59ebd4a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
new file mode 100644
index 0000000..209e87d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-in-non-composited-scrolling-container-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png
index 8b8e6e0a..a8f389c6 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
index e6b8478..6a319b8 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/cross-fade-background-size-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/cross-fade-background-size-expected.png
index 6965947..2699d76 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/cross-fade-background-size-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
index 54806de..0123a81 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png
index 2304502..fe19eb5 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/mojo-loading/http/tests/misc/slow-loading-image-in-pattern-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/shadow-dom/focus-navigation-slot-shadow-in-fallback.html b/third_party/WebKit/LayoutTests/shadow-dom/focus-navigation-slot-shadow-in-fallback.html
new file mode 100644
index 0000000..f84b925b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/shadow-dom/focus-navigation-slot-shadow-in-fallback.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script src='resources/shadow-dom.js'></script>
+<script src='resources/focus-utils.js'></script>
+<div id="log"></div>
+
+<input id="i0" tabindex=0 value="i0">
+<div id="fallback">
+  <template data-mode="open">
+    <slot id="s0" name="s0">
+      <div id="x-foo">
+        <template data-mode="open">
+          <input id="a2" tabindex=3 value="a2">
+          <slot id="s1" name="s1" tabindex=2>
+            <slot id="s2" name="s2">
+              <input id="a1" slot="s2" value="a1">
+            </slot>
+          </slot>
+          <input id="a0" tabindex=1 value="a0">
+        </template>
+      </div>
+    </slot>
+  </template>
+</div>
+
+<script>
+'use strict';
+
+test(() => {
+    let fallback = document.getElementById('fallback');
+    convertTemplatesToShadowRootsWithin(fallback);
+
+    let elements = [
+        'i0',
+        'fallback/x-foo/a0',
+        'fallback/x-foo/a1',
+        'fallback/x-foo/a2'
+    ];
+
+    assert_focus_navigation_forward(elements);
+    elements.reverse();
+    assert_focus_navigation_backward(elements);
+}, 'Focus should cover assigned elements of an assigned slot, as well as elements that are directly assigned to a slot.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/shadow-dom/focus-navigation-slot-shadow-in-slot.html b/third_party/WebKit/LayoutTests/shadow-dom/focus-navigation-slot-shadow-in-slot.html
new file mode 100644
index 0000000..48811e6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/shadow-dom/focus-navigation-slot-shadow-in-slot.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<script src='../resources/testharness.js'></script>
+<script src='../resources/testharnessreport.js'></script>
+<script src='resources/shadow-dom.js'></script>
+<script src='resources/focus-utils.js'></script>
+<div id="log"></div>
+
+<input id="i0" tabindex=0 value="i0">
+<div id="assigned">
+  <template data-mode="open">
+    <slot id="s0" name="s0">
+      <div id="x-foo">
+        <input id="a1" slot="s2" value="a1">
+        <template data-mode="open">
+          <input id="a2" tabindex=3 value="a2">
+          <slot id="s1" name="s1" tabindex=2>
+            <slot id="s2" name="s2"></slot>
+          </slot>
+          <input id="a0" tabindex=1 value="a0">
+        </template>
+      </div>
+    </slot>
+  </template>
+</div>
+
+<script>
+'use strict';
+
+test(() => {
+    let assigned = document.getElementById('assigned');
+    convertTemplatesToShadowRootsWithin(assigned);
+
+    let elements = [
+        'i0',
+        'assigned/x-foo/a0',
+        'assigned/a1',
+        'assigned/x-foo/a2'
+    ];
+
+    assert_focus_navigation_forward(elements);
+    elements.reverse();
+    assert_focus_navigation_backward(elements);
+}, 'Focus should cover assigned elements of an assigned slot, as well as elements that are directly assigned to a slot.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png b/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png
deleted file mode 100644
index 9e5603dc..0000000
--- a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/color-profile-background-image-space-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
index 0afb30b..66bfe74 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp
@@ -61,6 +61,7 @@
 #include "platform/loader/fetch/AccessControlStatus.h"
 #include "platform/runtime_enabled_features.h"
 #include "platform/scheduler/child/web_scheduler.h"
+#include "platform/weborigin/KURL.h"
 #include "platform/weborigin/SecurityViolationReportingPolicy.h"
 #include "platform/wtf/AddressSanitizer.h"
 #include "platform/wtf/Assertions.h"
@@ -395,9 +396,12 @@
 
   String specifier = ToCoreStringWithNullCheck(v8_specifier);
   v8::Local<v8::Value> v8_referrer_url = v8_referrer->GetResourceName();
-  String referrer_url;
-  if (v8_referrer_url->IsString())
-    referrer_url = ToCoreString(v8::Local<v8::String>::Cast(v8_referrer_url));
+  KURL referrer_url;
+  if (v8_referrer_url->IsString()) {
+    String referrer_url_str =
+        ToCoreString(v8::Local<v8::String>::Cast(v8_referrer_url));
+    referrer_url = KURL(NullURL(), referrer_url_str);
+  }
   ReferrerScriptInfo referrer_info =
       ReferrerScriptInfo::FromV8HostDefinedOptions(
           context, v8_referrer->GetHostDefinedOptions());
diff --git a/third_party/WebKit/Source/core/CoreInitializer.h b/third_party/WebKit/Source/core/CoreInitializer.h
index 2c99ec4..9b9d7c5 100644
--- a/third_party/WebKit/Source/core/CoreInitializer.h
+++ b/third_party/WebKit/Source/core/CoreInitializer.h
@@ -31,6 +31,7 @@
 #ifndef CoreInitializer_h
 #define CoreInitializer_h
 
+#include "base/macros.h"
 #include "core/CoreExport.h"
 #include "platform/wtf/Allocator.h"
 
@@ -60,7 +61,7 @@
 
 class CORE_EXPORT CoreInitializer {
   USING_FAST_MALLOC(CoreInitializer);
-  WTF_MAKE_NONCOPYABLE(CoreInitializer);
+  DISALLOW_COPY_AND_ASSIGN(CoreInitializer);
 
  public:
   // Initialize must be called before GetInstance.
diff --git a/third_party/WebKit/Source/core/dom/DynamicModuleResolver.cpp b/third_party/WebKit/Source/core/dom/DynamicModuleResolver.cpp
index 8176720..90cea65 100644
--- a/third_party/WebKit/Source/core/dom/DynamicModuleResolver.cpp
+++ b/third_party/WebKit/Source/core/dom/DynamicModuleResolver.cpp
@@ -142,7 +142,7 @@
 
 void DynamicModuleResolver::ResolveDynamically(
     const String& specifier,
-    const String& referrer_url_str,
+    const KURL& referrer_url,
     const ReferrerScriptInfo& referrer_info,
     ScriptPromiseResolver* promise_resolver) {
   DCHECK(modulator_->GetScriptState()->GetIsolate()->InContext())
@@ -157,7 +157,6 @@
 
   // Step 2.1. "Let url be the result of resolving a module specifier
   // given referencing script and specifier." [spec text]
-  KURL referrer_url = KURL(NullURL(), referrer_url_str);
   DCHECK(referrer_url.IsValid());
   KURL url = Modulator::ResolveModuleSpecifier(specifier, referrer_url);
   if (!url.IsValid()) {
diff --git a/third_party/WebKit/Source/core/dom/DynamicModuleResolver.h b/third_party/WebKit/Source/core/dom/DynamicModuleResolver.h
index 912a105..9bddfe2a 100644
--- a/third_party/WebKit/Source/core/dom/DynamicModuleResolver.h
+++ b/third_party/WebKit/Source/core/dom/DynamicModuleResolver.h
@@ -8,6 +8,7 @@
 #include "core/CoreExport.h"
 #include "platform/heap/Handle.h"
 #include "platform/heap/Visitor.h"
+#include "platform/weborigin/KURL.h"
 #include "platform/wtf/text/WTFString.h"
 
 namespace blink {
@@ -31,7 +32,7 @@
   // Implements "HostImportModuleDynamically" semantics.
   // Should be called w/ a valid V8 context.
   void ResolveDynamically(const String& specifier,
-                          const String& referrer_url,
+                          const KURL& referrer_url,
                           const ReferrerScriptInfo& referrer_info,
                           ScriptPromiseResolver*);
 
diff --git a/third_party/WebKit/Source/core/dom/DynamicModuleResolverTest.cpp b/third_party/WebKit/Source/core/dom/DynamicModuleResolverTest.cpp
index f60a51fbf..cb90687 100644
--- a/third_party/WebKit/Source/core/dom/DynamicModuleResolverTest.cpp
+++ b/third_party/WebKit/Source/core/dom/DynamicModuleResolverTest.cpp
@@ -23,6 +23,13 @@
 constexpr const char* kTestReferrerURL = "https://example.com/referrer.js";
 constexpr const char* kTestDependencyURL = "https://example.com/dependency.js";
 
+const KURL TestReferrerURL() {
+  return KURL(kParsedURLString, kTestReferrerURL);
+}
+const KURL TestDependencyURL() {
+  return KURL(kParsedURLString, kTestDependencyURL);
+}
+
 class DynamicModuleResolverTestModulator final : public DummyModulator {
  public:
   explicit DynamicModuleResolverTestModulator(ScriptState* script_state)
@@ -42,7 +49,7 @@
   ScriptState* GetScriptState() final { return script_state_.get(); }
 
   ModuleScript* GetFetchedModuleScript(const KURL& url) final {
-    EXPECT_EQ(kTestReferrerURL, url.GetString());
+    EXPECT_EQ(TestReferrerURL(), url);
     ModuleScript* module_script = ModuleScript::CreateForTest(
         this, ScriptModule(), url, "nonce", kParserInserted,
         WebURLRequest::kFetchCredentialsModeOmit);
@@ -51,7 +58,7 @@
 
   void FetchTree(const ModuleScriptFetchRequest& request,
                  ModuleTreeClient* client) final {
-    EXPECT_EQ(kTestDependencyURL, request.Url().GetString());
+    EXPECT_EQ(TestDependencyURL(), request.Url());
 
     pending_client_ = client;
   }
@@ -194,19 +201,18 @@
                NotReached::CreateFunction(scope.GetScriptState()));
 
   auto resolver = DynamicModuleResolver::Create(modulator);
-  resolver->ResolveDynamically("./dependency.js", kTestReferrerURL,
+  resolver->ResolveDynamically("./dependency.js", TestReferrerURL(),
                                ReferrerScriptInfo(), promise_resolver);
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
   EXPECT_FALSE(capture->WasCalled());
 
-  KURL url(kParsedURLString, kTestDependencyURL);
   ScriptModule record = ScriptModule::Compile(
-      scope.GetIsolate(), "export const foo = 'hello';", url.GetString(),
+      scope.GetIsolate(), "export const foo = 'hello';", TestReferrerURL(),
       kSharableCrossOrigin, WebURLRequest::kFetchCredentialsModeOmit, "",
       kParserInserted, TextPosition::MinimumPosition(), ASSERT_NO_EXCEPTION);
   ModuleScript* module_script = ModuleScript::CreateForTest(
-      modulator, record, url, "nonce", kNotParserInserted,
+      modulator, record, TestDependencyURL(), "nonce", kNotParserInserted,
       WebURLRequest::kFetchCredentialsModeOmit);
   record.Instantiate(scope.GetScriptState());
   EXPECT_FALSE(module_script->IsErrored());
@@ -230,7 +236,7 @@
                capture->Bind());
 
   auto resolver = DynamicModuleResolver::Create(modulator);
-  resolver->ResolveDynamically("invalid-specifier", kTestReferrerURL,
+  resolver->ResolveDynamically("invalid-specifier", TestReferrerURL(),
                                ReferrerScriptInfo(), promise_resolver);
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
@@ -252,7 +258,7 @@
                capture->Bind());
 
   auto resolver = DynamicModuleResolver::Create(modulator);
-  resolver->ResolveDynamically("./dependency.js", kTestReferrerURL,
+  resolver->ResolveDynamically("./dependency.js", TestReferrerURL(),
                                ReferrerScriptInfo(), promise_resolver);
 
   EXPECT_FALSE(capture->WasCalled());
@@ -278,18 +284,17 @@
                capture->Bind());
 
   auto resolver = DynamicModuleResolver::Create(modulator);
-  resolver->ResolveDynamically("./dependency.js", kTestReferrerURL,
+  resolver->ResolveDynamically("./dependency.js", TestReferrerURL(),
                                ReferrerScriptInfo(), promise_resolver);
 
   EXPECT_FALSE(capture->WasCalled());
 
-  KURL url(kParsedURLString, kTestDependencyURL);
   ScriptModule record = ScriptModule::Compile(
-      scope.GetIsolate(), "throw Error('bar')", url.GetString(),
+      scope.GetIsolate(), "throw Error('bar')", TestReferrerURL(),
       kSharableCrossOrigin, WebURLRequest::kFetchCredentialsModeOmit, "",
       kParserInserted, TextPosition::MinimumPosition(), ASSERT_NO_EXCEPTION);
   ModuleScript* module_script = ModuleScript::CreateForTest(
-      modulator, record, url, "nonce", kNotParserInserted,
+      modulator, record, TestDependencyURL(), "nonce", kNotParserInserted,
       WebURLRequest::kFetchCredentialsModeOmit);
   record.Instantiate(scope.GetScriptState());
   EXPECT_FALSE(module_script->IsErrored());
diff --git a/third_party/WebKit/Source/core/dom/Modulator.h b/third_party/WebKit/Source/core/dom/Modulator.h
index 0003b4bd..c7931fe 100644
--- a/third_party/WebKit/Source/core/dom/Modulator.h
+++ b/third_party/WebKit/Source/core/dom/Modulator.h
@@ -111,7 +111,7 @@
 
   // https://tc39.github.io/proposal-dynamic-import/#sec-hostimportmoduledynamically
   virtual void ResolveDynamically(const String& specifier,
-                                  const String& referrer_url,
+                                  const KURL&,
                                   const ReferrerScriptInfo&,
                                   ScriptPromiseResolver*) = 0;
 
diff --git a/third_party/WebKit/Source/core/dom/ModulatorImplBase.cpp b/third_party/WebKit/Source/core/dom/ModulatorImplBase.cpp
index 24d8fcdf..5f50bcde 100644
--- a/third_party/WebKit/Source/core/dom/ModulatorImplBase.cpp
+++ b/third_party/WebKit/Source/core/dom/ModulatorImplBase.cpp
@@ -108,7 +108,7 @@
 
 void ModulatorImplBase::ResolveDynamically(
     const String& specifier,
-    const String& referrer_url,
+    const KURL& referrer_url,
     const ReferrerScriptInfo& referrer_info,
     ScriptPromiseResolver* resolver) {
   dynamic_module_resolver_->ResolveDynamically(specifier, referrer_url,
diff --git a/third_party/WebKit/Source/core/dom/ModulatorImplBase.h b/third_party/WebKit/Source/core/dom/ModulatorImplBase.h
index 3c6c9fe..f751ddf 100644
--- a/third_party/WebKit/Source/core/dom/ModulatorImplBase.h
+++ b/third_party/WebKit/Source/core/dom/ModulatorImplBase.h
@@ -60,7 +60,7 @@
                             ModuleGraphLevel,
                             ModuleScriptLoaderClient*) override;
   void ResolveDynamically(const String& specifier,
-                          const String& referrer_url,
+                          const KURL&,
                           const ReferrerScriptInfo&,
                           ScriptPromiseResolver*) override;
   ScriptModule CompileModule(const String& script,
diff --git a/third_party/WebKit/Source/core/paint/ObjectPaintInvalidator.h b/third_party/WebKit/Source/core/paint/ObjectPaintInvalidator.h
index 85142d09..c6e93b6e 100644
--- a/third_party/WebKit/Source/core/paint/ObjectPaintInvalidator.h
+++ b/third_party/WebKit/Source/core/paint/ObjectPaintInvalidator.h
@@ -5,6 +5,7 @@
 #ifndef ObjectPaintInvalidator_h
 #define ObjectPaintInvalidator_h
 
+#include "base/macros.h"
 #include "core/CoreExport.h"
 #include "platform/graphics/PaintInvalidationReason.h"
 #include "platform/wtf/Allocator.h"
@@ -114,7 +115,7 @@
 // will always invalidate raster after paint.
 class DisablePaintInvalidationStateAsserts {
   STACK_ALLOCATED();
-  WTF_MAKE_NONCOPYABLE(DisablePaintInvalidationStateAsserts);
+  DISALLOW_COPY_AND_ASSIGN(DisablePaintInvalidationStateAsserts);
 
  public:
   DisablePaintInvalidationStateAsserts();
diff --git a/third_party/WebKit/Source/core/testing/DummyModulator.cpp b/third_party/WebKit/Source/core/testing/DummyModulator.cpp
index d2fb721..18a21b5 100644
--- a/third_party/WebKit/Source/core/testing/DummyModulator.cpp
+++ b/third_party/WebKit/Source/core/testing/DummyModulator.cpp
@@ -95,7 +95,7 @@
 }
 
 void DummyModulator::ResolveDynamically(const String&,
-                                        const String&,
+                                        const KURL&,
                                         const ReferrerScriptInfo&,
                                         ScriptPromiseResolver*) {
   NOTREACHED();
diff --git a/third_party/WebKit/Source/core/testing/DummyModulator.h b/third_party/WebKit/Source/core/testing/DummyModulator.h
index d9c235e..3a49e25 100644
--- a/third_party/WebKit/Source/core/testing/DummyModulator.h
+++ b/third_party/WebKit/Source/core/testing/DummyModulator.h
@@ -49,7 +49,7 @@
                             ModuleScriptLoaderClient*) override;
   bool HasValidContext() override;
   void ResolveDynamically(const String& specifier,
-                          const String& referrer_url,
+                          const KURL&,
                           const ReferrerScriptInfo&,
                           ScriptPromiseResolver*) override;
   ScriptModule CompileModule(const String& script,
diff --git a/third_party/WebKit/Source/core/workers/DedicatedWorkerThread.cpp b/third_party/WebKit/Source/core/workers/DedicatedWorkerThread.cpp
index 6fe83ad..f2a9abe 100644
--- a/third_party/WebKit/Source/core/workers/DedicatedWorkerThread.cpp
+++ b/third_party/WebKit/Source/core/workers/DedicatedWorkerThread.cpp
@@ -57,14 +57,14 @@
 
 DedicatedWorkerThread::~DedicatedWorkerThread() {}
 
+void DedicatedWorkerThread::ClearWorkerBackingThread() {
+  worker_backing_thread_ = nullptr;
+}
+
 WorkerOrWorkletGlobalScope* DedicatedWorkerThread::CreateWorkerGlobalScope(
     std::unique_ptr<GlobalScopeCreationParams> creation_params) {
   return DedicatedWorkerGlobalScope::Create(this, std::move(creation_params),
                                             time_origin_);
 }
 
-void DedicatedWorkerThread::ClearWorkerBackingThread() {
-  worker_backing_thread_ = nullptr;
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/DedicatedWorkerThread.h b/third_party/WebKit/Source/core/workers/DedicatedWorkerThread.h
index a827208..a0bccd4 100644
--- a/third_party/WebKit/Source/core/workers/DedicatedWorkerThread.h
+++ b/third_party/WebKit/Source/core/workers/DedicatedWorkerThread.h
@@ -53,14 +53,13 @@
     return worker_object_proxy_;
   }
 
- protected:
+ private:
+  friend class DedicatedWorkerThreadForTest;
+
   DedicatedWorkerThread(ThreadableLoadingContext*, InProcessWorkerObjectProxy&);
   WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
       std::unique_ptr<GlobalScopeCreationParams>) override;
 
- private:
-  friend class DedicatedWorkerThreadForTest;
-
   std::unique_ptr<WorkerBackingThread> worker_backing_thread_;
   InProcessWorkerObjectProxy& worker_object_proxy_;
 };
diff --git a/third_party/WebKit/Source/core/workers/SharedWorkerThread.h b/third_party/WebKit/Source/core/workers/SharedWorkerThread.h
index e8c6083..f14365e 100644
--- a/third_party/WebKit/Source/core/workers/SharedWorkerThread.h
+++ b/third_party/WebKit/Source/core/workers/SharedWorkerThread.h
@@ -51,11 +51,10 @@
   }
   void ClearWorkerBackingThread() override;
 
- protected:
+ private:
   WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
       std::unique_ptr<GlobalScopeCreationParams>) override;
 
- private:
   std::unique_ptr<WorkerBackingThread> worker_backing_thread_;
   String name_;
 };
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletTest.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorkletTest.cpp
index f6c5bc7d..e43e274 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedWorkletTest.cpp
+++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletTest.cpp
@@ -65,18 +65,6 @@
 
   void ClearWorkerBackingThread() override {}
 
-  WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
-      std::unique_ptr<GlobalScopeCreationParams> creation_params) final {
-    RefPtr<SecurityOrigin> security_origin =
-        SecurityOrigin::Create(creation_params->script_url);
-    return new ThreadedWorkletGlobalScope(
-        creation_params->script_url, creation_params->user_agent,
-        std::move(security_origin), this->GetIsolate(), this,
-        creation_params->worker_clients);
-  }
-
-  bool IsOwningBackingThread() const final { return false; }
-
   static void EnsureSharedBackingThread() {
     DCHECK(IsMainThread());
     WorkletThreadHolder<ThreadedWorkletThreadForTest>::CreateForTest(
@@ -121,6 +109,19 @@
         ->Get(TaskType::kUnspecedTimer)
         ->PostTask(BLINK_FROM_HERE, CrossThreadBind(&testing::ExitRunLoop));
   }
+
+ private:
+  WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
+      std::unique_ptr<GlobalScopeCreationParams> creation_params) final {
+    RefPtr<SecurityOrigin> security_origin =
+        SecurityOrigin::Create(creation_params->script_url);
+    return new ThreadedWorkletGlobalScope(
+        creation_params->script_url, creation_params->user_agent,
+        std::move(security_origin), this->GetIsolate(), this,
+        creation_params->worker_clients);
+  }
+
+  bool IsOwningBackingThread() const final { return false; }
 };
 
 class ThreadedWorkletMessagingProxyForTest
diff --git a/third_party/WebKit/Source/core/workers/WorkerThread.h b/third_party/WebKit/Source/core/workers/WorkerThread.h
index e75af1c..72e29b95 100644
--- a/third_party/WebKit/Source/core/workers/WorkerThread.h
+++ b/third_party/WebKit/Source/core/workers/WorkerThread.h
@@ -184,18 +184,6 @@
  protected:
   WorkerThread(ThreadableLoadingContext*, WorkerReportingProxy&);
 
-  // Factory method for creating a new worker context for the thread.
-  // Called on the worker thread.
-  virtual WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
-      std::unique_ptr<GlobalScopeCreationParams>) = 0;
-
-  // Returns true when this WorkerThread owns the associated
-  // WorkerBackingThread exclusively. If this function returns true, the
-  // WorkerThread initializes / shutdowns the backing thread. Otherwise
-  // workerBackingThread() should be initialized / shutdown properly
-  // out of this class.
-  virtual bool IsOwningBackingThread() const { return true; }
-
   // Official moment of creation of worker: when the worker thread is created.
   // (https://w3c.github.io/hr-time/#time-origin)
   const double time_origin_;
@@ -220,6 +208,18 @@
     kReadyToShutdown,
   };
 
+  // Factory method for creating a new worker context for the thread.
+  // Called on the worker thread.
+  virtual WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
+      std::unique_ptr<GlobalScopeCreationParams>) = 0;
+
+  // Returns true when this WorkerThread owns the associated
+  // WorkerBackingThread exclusively. If this function returns true, the
+  // WorkerThread initializes / shutdowns the backing thread. Otherwise
+  // the backing thread should be initialized / shutdown properly out of this
+  // class.
+  virtual bool IsOwningBackingThread() const { return true; }
+
   // Posts a delayed task to forcibly terminate script execution in case the
   // normal shutdown sequence does not start within a certain time period.
   void ScheduleToTerminateScriptExecution();
diff --git a/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThread.cpp b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThread.cpp
index ac2d9b6..ee3c221 100644
--- a/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThread.cpp
+++ b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThread.cpp
@@ -39,24 +39,6 @@
 
 AnimationWorkletThread::~AnimationWorkletThread() {}
 
-WorkerOrWorkletGlobalScope* AnimationWorkletThread::CreateWorkerGlobalScope(
-    std::unique_ptr<GlobalScopeCreationParams> creation_params) {
-  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("animation-worklet"),
-               "AnimationWorkletThread::createWorkerGlobalScope");
-
-  RefPtr<SecurityOrigin> security_origin =
-      SecurityOrigin::Create(creation_params->script_url);
-  if (creation_params->starter_origin_privilege_data) {
-    security_origin->TransferPrivilegesFrom(
-        std::move(creation_params->starter_origin_privilege_data));
-  }
-
-  return AnimationWorkletGlobalScope::Create(
-      creation_params->script_url, creation_params->user_agent,
-      std::move(security_origin), this->GetIsolate(), this,
-      creation_params->worker_clients);
-}
-
 WorkerBackingThread& AnimationWorkletThread::GetWorkerBackingThread() {
   return *WorkletThreadHolder<AnimationWorkletThread>::GetInstance()
               ->GetThread();
@@ -95,4 +77,22 @@
       Platform::Current()->CompositorThread());
 }
 
+WorkerOrWorkletGlobalScope* AnimationWorkletThread::CreateWorkerGlobalScope(
+    std::unique_ptr<GlobalScopeCreationParams> creation_params) {
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("animation-worklet"),
+               "AnimationWorkletThread::createWorkerGlobalScope");
+
+  RefPtr<SecurityOrigin> security_origin =
+      SecurityOrigin::Create(creation_params->script_url);
+  if (creation_params->starter_origin_privilege_data) {
+    security_origin->TransferPrivilegesFrom(
+        std::move(creation_params->starter_origin_privilege_data));
+  }
+
+  return AnimationWorkletGlobalScope::Create(
+      creation_params->script_url, creation_params->user_agent,
+      std::move(security_origin), this->GetIsolate(), this,
+      creation_params->worker_clients);
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThread.h b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThread.h
index 263b4a8..6e5a82c 100644
--- a/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThread.h
+++ b/third_party/WebKit/Source/modules/compositorworker/AnimationWorkletThread.h
@@ -34,14 +34,13 @@
 
   static void CreateSharedBackingThreadForTest();
 
- protected:
+ private:
+  AnimationWorkletThread(ThreadableLoadingContext*, WorkerReportingProxy&);
+
   WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
       std::unique_ptr<GlobalScopeCreationParams>) final;
 
   bool IsOwningBackingThread() const override { return false; }
-
- private:
-  AnimationWorkletThread(ThreadableLoadingContext*, WorkerReportingProxy&);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerThread.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerThread.h
index 379e3ab..10bcb7c 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerThread.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerThread.h
@@ -57,13 +57,12 @@
   }
   void ClearWorkerBackingThread() override;
 
- protected:
+ private:
   WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
       std::unique_ptr<GlobalScopeCreationParams>) override;
 
   InstalledScriptsManager* GetInstalledScriptsManager() override;
 
- private:
   Persistent<ServiceWorkerGlobalScopeProxy> global_scope_proxy_;
   std::unique_ptr<WorkerBackingThread> worker_backing_thread_;
   std::unique_ptr<ServiceWorkerInstalledScriptsManager>
diff --git a/third_party/WebKit/Source/modules/webaudio/AudioWorkletThread.h b/third_party/WebKit/Source/modules/webaudio/AudioWorkletThread.h
index f8cd5a9..477cd13b 100644
--- a/third_party/WebKit/Source/modules/webaudio/AudioWorkletThread.h
+++ b/third_party/WebKit/Source/modules/webaudio/AudioWorkletThread.h
@@ -43,15 +43,14 @@
   // the customers.
   static WebThread* GetSharedBackingThread();
 
- protected:
+ private:
+  AudioWorkletThread(ThreadableLoadingContext*, WorkerReportingProxy&);
+
   WorkerOrWorkletGlobalScope* CreateWorkerGlobalScope(
       std::unique_ptr<GlobalScopeCreationParams>) final;
 
   bool IsOwningBackingThread() const override { return false; }
 
- private:
-  AudioWorkletThread(ThreadableLoadingContext*, WorkerReportingProxy&);
-
   // This raw pointer gets assigned in EnsureSharedBackingThread() and manually
   // released by ClearSharedBackingThread().
   static WebThread* s_backing_thread_;
diff --git a/third_party/WebKit/Source/platform/EventDispatchForbiddenScope.h b/third_party/WebKit/Source/platform/EventDispatchForbiddenScope.h
index 6a384841..7486b0a 100644
--- a/third_party/WebKit/Source/platform/EventDispatchForbiddenScope.h
+++ b/third_party/WebKit/Source/platform/EventDispatchForbiddenScope.h
@@ -5,6 +5,7 @@
 #ifndef EventDispatchForbiddenScope_h
 #define EventDispatchForbiddenScope_h
 
+#include "base/macros.h"
 #include "platform/PlatformExport.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/Assertions.h"
@@ -16,7 +17,7 @@
 
 class EventDispatchForbiddenScope {
   STACK_ALLOCATED();
-  WTF_MAKE_NONCOPYABLE(EventDispatchForbiddenScope);
+  DISALLOW_COPY_AND_ASSIGN(EventDispatchForbiddenScope);
 
  public:
   EventDispatchForbiddenScope() {
@@ -55,7 +56,7 @@
 
 class EventDispatchForbiddenScope {
   STACK_ALLOCATED();
-  WTF_MAKE_NONCOPYABLE(EventDispatchForbiddenScope);
+  DISALLOW_COPY_AND_ASSIGN(EventDispatchForbiddenScope);
 
  public:
   EventDispatchForbiddenScope() {}
diff --git a/third_party/WebKit/Source/platform/ScriptForbiddenScope.h b/third_party/WebKit/Source/platform/ScriptForbiddenScope.h
index f2bfd70..cdeb2c05 100644
--- a/third_party/WebKit/Source/platform/ScriptForbiddenScope.h
+++ b/third_party/WebKit/Source/platform/ScriptForbiddenScope.h
@@ -5,6 +5,7 @@
 #ifndef ScriptForbiddenScope_h
 #define ScriptForbiddenScope_h
 
+#include "base/macros.h"
 #include "platform/PlatformExport.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/AutoReset.h"
@@ -16,7 +17,7 @@
 // and only to be used by the main thread.
 class PLATFORM_EXPORT ScriptForbiddenScope final {
   STACK_ALLOCATED();
-  WTF_MAKE_NONCOPYABLE(ScriptForbiddenScope);
+  DISALLOW_COPY_AND_ASSIGN(ScriptForbiddenScope);
 
  public:
   ScriptForbiddenScope() { Enter(); }
@@ -24,7 +25,7 @@
 
   class PLATFORM_EXPORT AllowUserAgentScript final {
     STACK_ALLOCATED();
-    WTF_MAKE_NONCOPYABLE(AllowUserAgentScript);
+    DISALLOW_COPY_AND_ASSIGN(AllowUserAgentScript);
 
    public:
     AllowUserAgentScript() {
@@ -64,7 +65,7 @@
 // its behalf.
 class PLATFORM_EXPORT ScriptForbiddenIfMainThreadScope final {
   STACK_ALLOCATED();
-  WTF_MAKE_NONCOPYABLE(ScriptForbiddenIfMainThreadScope);
+  DISALLOW_COPY_AND_ASSIGN(ScriptForbiddenIfMainThreadScope);
 
  public:
   ScriptForbiddenIfMainThreadScope() {
diff --git a/third_party/WebKit/Source/platform/audio/AudioSourceProvider.h b/third_party/WebKit/Source/platform/audio/AudioSourceProvider.h
index 0034dc8..6f20c5b 100644
--- a/third_party/WebKit/Source/platform/audio/AudioSourceProvider.h
+++ b/third_party/WebKit/Source/platform/audio/AudioSourceProvider.h
@@ -30,6 +30,7 @@
 #define AudioSourceProvider_h
 
 #include <cstddef>
+#include "base/macros.h"
 #include "platform/PlatformExport.h"
 #include "platform/wtf/Allocator.h"
 
@@ -41,7 +42,7 @@
 // Abstract base-class for a pull-model client.
 class PLATFORM_EXPORT AudioSourceProvider {
   USING_FAST_MALLOC(AudioSourceProvider);
-  WTF_MAKE_NONCOPYABLE(AudioSourceProvider);
+  DISALLOW_COPY_AND_ASSIGN(AudioSourceProvider);
 
  public:
   AudioSourceProvider() {}
diff --git a/third_party/WebKit/Source/platform/graphics/Path.h b/third_party/WebKit/Source/platform/graphics/Path.h
index b9e1463..c44c2ae 100644
--- a/third_party/WebKit/Source/platform/graphics/Path.h
+++ b/third_party/WebKit/Source/platform/graphics/Path.h
@@ -29,6 +29,7 @@
 #ifndef Path_h
 #define Path_h
 
+#include "base/macros.h"
 #include "platform/PlatformExport.h"
 #include "platform/geometry/FloatRoundedRect.h"
 #include "platform/graphics/GraphicsTypes.h"
@@ -98,7 +99,7 @@
   // vary depending on curvature and number of segments, but should never be
   // worse than that of the state-less method on Path.
   class PLATFORM_EXPORT PositionCalculator {
-    WTF_MAKE_NONCOPYABLE(PositionCalculator);
+    DISALLOW_COPY_AND_ASSIGN(PositionCalculator);
     USING_FAST_MALLOC(PositionCalculator);
 
    public:
diff --git a/third_party/WebKit/Source/platform/graphics/PathTraversalState.h b/third_party/WebKit/Source/platform/graphics/PathTraversalState.h
index 26251c9..912d8044 100644
--- a/third_party/WebKit/Source/platform/graphics/PathTraversalState.h
+++ b/third_party/WebKit/Source/platform/graphics/PathTraversalState.h
@@ -26,6 +26,7 @@
 #ifndef PathTraversalState_h
 #define PathTraversalState_h
 
+#include "base/macros.h"
 #include "platform/PlatformExport.h"
 #include "platform/geometry/FloatPoint.h"
 #include "platform/wtf/Allocator.h"
@@ -34,7 +35,7 @@
 
 class PLATFORM_EXPORT PathTraversalState final {
   STACK_ALLOCATED();
-  WTF_MAKE_NONCOPYABLE(PathTraversalState);
+  DISALLOW_COPY_AND_ASSIGN(PathTraversalState);
 
  public:
   enum PathTraversalAction {
diff --git a/third_party/WebKit/Source/platform/heap/StackFrameDepth.h b/third_party/WebKit/Source/platform/heap/StackFrameDepth.h
index 4e0d5678..47ed658 100644
--- a/third_party/WebKit/Source/platform/heap/StackFrameDepth.h
+++ b/third_party/WebKit/Source/platform/heap/StackFrameDepth.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 #include <cstddef>
+#include "base/macros.h"
 #include "build/build_config.h"
 #include "platform/PlatformExport.h"
 #include "platform/wtf/Allocator.h"
@@ -86,7 +87,7 @@
 
 class StackFrameDepthScope {
   STACK_ALLOCATED();
-  WTF_MAKE_NONCOPYABLE(StackFrameDepthScope);
+  DISALLOW_COPY_AND_ASSIGN(StackFrameDepthScope);
 
  public:
   explicit StackFrameDepthScope(StackFrameDepth* depth) : depth_(depth) {
diff --git a/third_party/WebKit/Source/platform/wtf/Assertions.h b/third_party/WebKit/Source/platform/wtf/Assertions.h
index 506fb0d8..9b81828 100644
--- a/third_party/WebKit/Source/platform/wtf/Assertions.h
+++ b/third_party/WebKit/Source/platform/wtf/Assertions.h
@@ -31,7 +31,6 @@
 
 #include "base/compiler_specific.h"
 #include "base/logging.h"
-#include "platform/wtf/Noncopyable.h"
 #include "platform/wtf/WTFExport.h"
 
 // New code shouldn't use this function. This function will be deprecated.
@@ -74,25 +73,13 @@
 // Allow equality comparisons of Objects by reference or pointer,
 // interchangeably.  This can be only used on types whose equality makes no
 // other sense than pointer equality.
-#define DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES(thisType)    \
-  inline bool operator==(const thisType& a, const thisType& b) { \
-    return &a == &b;                                             \
-  }                                                              \
-  inline bool operator==(const thisType& a, const thisType* b) { \
-    return &a == b;                                              \
-  }                                                              \
-  inline bool operator==(const thisType* a, const thisType& b) { \
-    return a == &b;                                              \
-  }                                                              \
-  inline bool operator!=(const thisType& a, const thisType& b) { \
-    return !(a == b);                                            \
-  }                                                              \
-  inline bool operator!=(const thisType& a, const thisType* b) { \
-    return !(a == b);                                            \
-  }                                                              \
-  inline bool operator!=(const thisType* a, const thisType& b) { \
-    return !(a == b);                                            \
-  }
+#define DEFINE_COMPARISON_OPERATORS_WITH_REFERENCES(Type)                    \
+  inline bool operator==(const Type& a, const Type& b) { return &a == &b; }  \
+  inline bool operator==(const Type& a, const Type* b) { return &a == b; }   \
+  inline bool operator==(const Type* a, const Type& b) { return a == &b; }   \
+  inline bool operator!=(const Type& a, const Type& b) { return !(a == b); } \
+  inline bool operator!=(const Type& a, const Type* b) { return !(a == b); } \
+  inline bool operator!=(const Type* a, const Type& b) { return !(a == b); }
 
 // DEFINE_TYPE_CASTS
 //
@@ -108,68 +95,68 @@
 // * it's hard to prevent from passing unexpected objects,
 // * proceeding with the following code doesn't make sense, and
 // * cost of runtime type check is acceptable.
-#define DEFINE_TYPE_CASTS(thisType, argumentType, argument, pointerPredicate, \
-                          referencePredicate)                                 \
-  inline thisType* To##thisType(argumentType* argument) {                     \
-    SECURITY_DCHECK(!argument || (pointerPredicate));                         \
-    return static_cast<thisType*>(argument);                                  \
-  }                                                                           \
-  inline const thisType* To##thisType(const argumentType* argument) {         \
-    SECURITY_DCHECK(!argument || (pointerPredicate));                         \
-    return static_cast<const thisType*>(argument);                            \
-  }                                                                           \
-  inline thisType& To##thisType(argumentType& argument) {                     \
-    SECURITY_DCHECK(referencePredicate);                                      \
-    return static_cast<thisType&>(argument);                                  \
-  }                                                                           \
-  inline const thisType& To##thisType(const argumentType& argument) {         \
-    SECURITY_DCHECK(referencePredicate);                                      \
-    return static_cast<const thisType&>(argument);                            \
-  }                                                                           \
-  void To##thisType(const thisType*);                                         \
-  void To##thisType(const thisType&);                                         \
-                                                                              \
-  inline thisType* To##thisType##OrNull(argumentType* argument) {             \
-    if (!(argument) || !(pointerPredicate))                                   \
-      return nullptr;                                                         \
-    return static_cast<thisType*>(argument);                                  \
-  }                                                                           \
-  inline const thisType* To##thisType##OrNull(const argumentType* argument) { \
-    if (!(argument) || !(pointerPredicate))                                   \
-      return nullptr;                                                         \
-    return static_cast<const thisType*>(argument);                            \
-  }                                                                           \
-  inline thisType* To##thisType##OrNull(argumentType& argument) {             \
-    if (!(referencePredicate))                                                \
-      return nullptr;                                                         \
-    return static_cast<thisType*>(&argument);                                 \
-  }                                                                           \
-  inline const thisType* To##thisType##OrNull(const argumentType& argument) { \
-    if (!(referencePredicate))                                                \
-      return nullptr;                                                         \
-    return static_cast<const thisType*>(&argument);                           \
-  }                                                                           \
-  void To##thisType##OrNull(const thisType*);                                 \
-  void To##thisType##OrNull(const thisType&);                                 \
-                                                                              \
-  inline thisType* To##thisType##OrDie(argumentType* argument) {              \
-    CHECK(!argument || (pointerPredicate));                                   \
-    return static_cast<thisType*>(argument);                                  \
-  }                                                                           \
-  inline const thisType* To##thisType##OrDie(const argumentType* argument) {  \
-    CHECK(!argument || (pointerPredicate));                                   \
-    return static_cast<const thisType*>(argument);                            \
-  }                                                                           \
-  inline thisType& To##thisType##OrDie(argumentType& argument) {              \
-    CHECK(referencePredicate);                                                \
-    return static_cast<thisType&>(argument);                                  \
-  }                                                                           \
-  inline const thisType& To##thisType##OrDie(const argumentType& argument) {  \
-    CHECK(referencePredicate);                                                \
-    return static_cast<const thisType&>(argument);                            \
-  }                                                                           \
-  void To##thisType##OrDie(const thisType*);                                  \
-  void To##thisType##OrDie(const thisType&)
+#define DEFINE_TYPE_CASTS(Type, ArgType, argument, pointerPredicate, \
+                          referencePredicate)                        \
+  inline Type* To##Type(ArgType* argument) {                         \
+    SECURITY_DCHECK(!argument || (pointerPredicate));                \
+    return static_cast<Type*>(argument);                             \
+  }                                                                  \
+  inline const Type* To##Type(const ArgType* argument) {             \
+    SECURITY_DCHECK(!argument || (pointerPredicate));                \
+    return static_cast<const Type*>(argument);                       \
+  }                                                                  \
+  inline Type& To##Type(ArgType& argument) {                         \
+    SECURITY_DCHECK(referencePredicate);                             \
+    return static_cast<Type&>(argument);                             \
+  }                                                                  \
+  inline const Type& To##Type(const ArgType& argument) {             \
+    SECURITY_DCHECK(referencePredicate);                             \
+    return static_cast<const Type&>(argument);                       \
+  }                                                                  \
+  void To##Type(const Type*);                                        \
+  void To##Type(const Type&);                                        \
+                                                                     \
+  inline Type* To##Type##OrNull(ArgType* argument) {                 \
+    if (!(argument) || !(pointerPredicate))                          \
+      return nullptr;                                                \
+    return static_cast<Type*>(argument);                             \
+  }                                                                  \
+  inline const Type* To##Type##OrNull(const ArgType* argument) {     \
+    if (!(argument) || !(pointerPredicate))                          \
+      return nullptr;                                                \
+    return static_cast<const Type*>(argument);                       \
+  }                                                                  \
+  inline Type* To##Type##OrNull(ArgType& argument) {                 \
+    if (!(referencePredicate))                                       \
+      return nullptr;                                                \
+    return static_cast<Type*>(&argument);                            \
+  }                                                                  \
+  inline const Type* To##Type##OrNull(const ArgType& argument) {     \
+    if (!(referencePredicate))                                       \
+      return nullptr;                                                \
+    return static_cast<const Type*>(&argument);                      \
+  }                                                                  \
+  void To##Type##OrNull(const Type*);                                \
+  void To##Type##OrNull(const Type&);                                \
+                                                                     \
+  inline Type* To##Type##OrDie(ArgType* argument) {                  \
+    CHECK(!argument || (pointerPredicate));                          \
+    return static_cast<Type*>(argument);                             \
+  }                                                                  \
+  inline const Type* To##Type##OrDie(const ArgType* argument) {      \
+    CHECK(!argument || (pointerPredicate));                          \
+    return static_cast<const Type*>(argument);                       \
+  }                                                                  \
+  inline Type& To##Type##OrDie(ArgType& argument) {                  \
+    CHECK(referencePredicate);                                       \
+    return static_cast<Type&>(argument);                             \
+  }                                                                  \
+  inline const Type& To##Type##OrDie(const ArgType& argument) {      \
+    CHECK(referencePredicate);                                       \
+    return static_cast<const Type&>(argument);                       \
+  }                                                                  \
+  void To##Type##OrDie(const Type*);                                 \
+  void To##Type##OrDie(const Type&)
 
 // Check at compile time that related enums stay in sync.
 #define STATIC_ASSERT_ENUM(a, b)                            \
diff --git a/third_party/widevine/cdm/widevine_cdm_common.h b/third_party/widevine/cdm/widevine_cdm_common.h
index 8ab44bdb..84179fd 100644
--- a/third_party/widevine/cdm/widevine_cdm_common.h
+++ b/third_party/widevine/cdm/widevine_cdm_common.h
@@ -18,6 +18,8 @@
 // This type is used to register the Widevine CDM.
 const char kWidevineCdmType[] = "Widevine";
 
+const char kWidevineCdmGuid[] = "AD87877A-0213-49A8-8849-9E93B075E477";
+
 // Widevine CDM files are in a directory with this name.
 const char kWidevineCdmBaseDirectory[] = "WidevineCdm";