Add a PaintRecorder to CanvasResourceProvider

This CL is the first step in creating a PaintRecord backed resource
provider for canvas OOP-R.

Changes of ownership:
- PaintRecorder entirely moved from Canvas2DLayerBridge to
  CanvasResourceProvider.
- Management of needs_flush_ for MemoryManagedPaintRecorder moved to
  CanvasResourceProvider.
- RestoreCanvasMatrixClipStack logic centralized to CanvasResourceHost
  and can be accessed via callback
- Backing SkiaPaintCanvas now private to CanvasResourceProvider with no
  external access. Any users of the backing SkiaPaintCanvas in
  Canvas2DLayerBridge had their functionality moved to
  CanvasResourceProvider (ie. new RestoreBackBuffer() function)

Updates to test files:
- Addition of explicit FlushCanvas() calls in tests that used to expect
  SkiaPaintCanvas to immediately update.
- Some tests expected the CanvasResourceProvider to be created on first
  draw. This expectation has been changed since the
  CanvasResourceProvider now gets set up on SetCanvasResourceHost().

Other new functionality:
- Display Item List now has a bit that tracks if it contains any draw
  operations. This is used to see if there are draw ops to flush.

Bug: 1019288
Change-Id: I717b18e22d6699dc876d8f8121a25d147738579d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1891292
Commit-Queue: Khushal <khushalsagar@chromium.org>
Reviewed-by: Aaron Krajeski <aaronhk@chromium.org>
Reviewed-by: Juanmi Huertas <juanmihd@chromium.org>
Reviewed-by: Fernando Serboncini <fserb@chromium.org>
Reviewed-by: Khushal <khushalsagar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#743002}
diff --git a/cc/paint/display_item_list.cc b/cc/paint/display_item_list.cc
index 2c66534..9429fef1 100644
--- a/cc/paint/display_item_list.cc
+++ b/cc/paint/display_item_list.cc
@@ -241,6 +241,7 @@
   offsets_.shrink_to_fit();
   begin_paired_indices_.clear();
   begin_paired_indices_.shrink_to_fit();
+  has_draw_ops_ = false;
 }
 
 sk_sp<PaintRecord> DisplayItemList::ReleaseAsRecord() {
diff --git a/cc/paint/display_item_list.h b/cc/paint/display_item_list.h
index eefd3b69..7a77181 100644
--- a/cc/paint/display_item_list.h
+++ b/cc/paint/display_item_list.h
@@ -87,6 +87,8 @@
     if (usage_hint_ == kTopLevelDisplayItemList)
       offsets_.push_back(offset);
     const T* op = paint_op_buffer_.push<T>(std::forward<Args>(args)...);
+    if (op->IsDrawOp())
+      has_draw_ops_ = true;
     DCHECK(op->IsValid());
     return offset;
   }
@@ -197,6 +199,7 @@
                              int max_ops_to_analyze = 1);
 
   std::string ToString() const;
+  bool has_draw_ops() const { return has_draw_ops_; }
 
  private:
   friend class DisplayItemListTest;
@@ -246,6 +249,7 @@
 #endif
 
   UsageHint usage_hint_;
+  bool has_draw_ops_ = false;
 
   friend class base::RefCountedThreadSafe<DisplayItemList>;
   FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, BytesUsed);
diff --git a/cc/paint/paint_recorder.cc b/cc/paint/paint_recorder.cc
index 08f0bd9..1fb9514 100644
--- a/cc/paint/paint_recorder.cc
+++ b/cc/paint/paint_recorder.cc
@@ -47,4 +47,8 @@
   return std::make_unique<RecordPaintCanvas>(list, bounds);
 }
 
+bool PaintRecorder::ListHasDrawOps() const {
+  return display_item_list_->has_draw_ops();
+}
+
 }  // namespace cc
diff --git a/cc/paint/paint_recorder.h b/cc/paint/paint_recorder.h
index b57fcb2..6beb024 100644
--- a/cc/paint/paint_recorder.h
+++ b/cc/paint/paint_recorder.h
@@ -37,6 +37,8 @@
 
   sk_sp<PaintRecord> finishRecordingAsPicture();
 
+  bool ListHasDrawOps() const;
+
  protected:
   virtual std::unique_ptr<RecordPaintCanvas> CreateCanvas(DisplayItemList* list,
                                                           const SkRect& bounds);
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 967ff28..9d6cf1e 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -140,11 +140,11 @@
 
   if (context_) {
     UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.HasRendered", bool(ResourceProvider()));
-    if (ResourceProvider()) {
+    if (context_->Host()) {
       UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.IsComposited",
                             context_->IsComposited());
+      context_->DetachHost();
     }
-    context_->DetachHost();
     context_ = nullptr;
   }
 
@@ -1506,20 +1506,30 @@
   if (canvas2d_bridge_) {
     image = canvas2d_bridge_->NewImageSnapshot(kPreferNoAcceleration);
     // image can be null if allocation failed in which case we should just
-    // abort the surface switch to reatain the old surface which is still
+    // abort the surface switch to retain the old surface which is still
     // functional.
     if (!image)
       return;
   }
-  new_layer_bridge->SetCanvasResourceHost(this);
   ReplaceResourceProvider(nullptr);
   canvas2d_bridge_ = std::move(new_layer_bridge);
+  canvas2d_bridge_->SetCanvasResourceHost(this);
+
+  cc::PaintCanvas* canvas = canvas2d_bridge_->GetPaintCanvas();
+  // Paint canvas automatically has the clip re-applied. Since image already
+  // contains clip, it needs to be drawn before the clip stack is re-applied.
+  // Remove clip from canvas and restore it after the image is drawn.
+  canvas->restoreToCount(1);
+  canvas->save();
+
+  // TODO(jochin): Consider using ResourceProvider()->RestoreBackBuffer() here
+  // to avoid all of this clip stack manipulation.
   if (image)
     canvas2d_bridge_->DrawFullImage(image->PaintImageForCurrentFrame());
 
-  RestoreCanvasMatrixClipStack(canvas2d_bridge_->GetPaintCanvas());
-  canvas2d_bridge_->DidRestoreCanvasMatrixClipStack(
-      canvas2d_bridge_->GetPaintCanvas());
+  RestoreCanvasMatrixClipStack(canvas);
+  canvas2d_bridge_->DidRestoreCanvasMatrixClipStack(canvas);
+
   UpdateMemoryUsage();
 }
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index 7b0b4bf..5b176570 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -50,6 +50,11 @@
   ValidateStateStack();
   if (GetState().HasUnrealizedSaves()) {
     DCHECK_GE(state_stack_.size(), 1u);
+    // GetOrCreatePaintCanvas() can call RestoreMatrixClipStack which syncs
+    // canvas to state_stack_. Get the canvas before adjusting state_stack_ to
+    // ensure canvas is synced prior to adjusting state_stack_.
+    cc::PaintCanvas* canvas = GetOrCreatePaintCanvas();
+
     // Reduce the current state's unrealized count by one now,
     // to reflect the fact we are saving one state.
     state_stack_.back()->Restore();
@@ -62,7 +67,6 @@
     // state (in turn necessary to support correct resizing and unwinding of the
     // stack).
     state_stack_.back()->ResetUnrealizedSaveCount();
-    cc::PaintCanvas* canvas = GetOrCreatePaintCanvas();
     if (canvas)
       canvas->save();
     ValidateStateStack();
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index 02caa1d..d56313c 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -441,14 +441,14 @@
   if (isContextLost())
     return nullptr;
   if (canvas()->GetOrCreateCanvas2DLayerBridge())
-    return GetPaintCanvas();
+    return canvas()->GetCanvas2DLayerBridge()->GetPaintCanvas();
   return nullptr;
 }
 
 cc::PaintCanvas* CanvasRenderingContext2D::GetPaintCanvas() const {
-  if (isContextLost())
+  if (isContextLost() || !canvas()->GetCanvas2DLayerBridge())
     return nullptr;
-  if (canvas() && canvas()->GetCanvas2DLayerBridge())
+  if (canvas() && canvas()->GetCanvas2DLayerBridge()->ResourceProvider())
     return canvas()->GetCanvas2DLayerBridge()->GetPaintCanvas();
   return nullptr;
 }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
index 3b75ce5..bf6055d5 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
@@ -736,13 +736,10 @@
       size, CanvasColorParams(), kPreferNoAcceleration);
   fake_deaccelerate_surface->SetCanvasResourceHost(&host);
 
-  cc::PaintCanvas* paint_canvas_ptr =
-      fake_deaccelerate_surface->GetPaintCanvas();
   FakeCanvas2DLayerBridge* surface_ptr = fake_deaccelerate_surface.get();
 
   EXPECT_CALL(*fake_deaccelerate_surface, DrawFullImage(_)).Times(1);
-  EXPECT_CALL(*fake_deaccelerate_surface,
-              DidRestoreCanvasMatrixClipStack(paint_canvas_ptr))
+  EXPECT_CALL(*fake_deaccelerate_surface, DidRestoreCanvasMatrixClipStack(_))
       .Times(1);
 
   EXPECT_TRUE(CanvasElement().GetCanvas2DLayerBridge()->IsAccelerated());
@@ -1143,11 +1140,14 @@
   CreateContext(kNonOpaque);
   IntSize size(300, 300);
   std::unique_ptr<Canvas2DLayerBridge> bridge =
-      MakeBridge(size, Canvas2DLayerBridge::kEnableAcceleration);
+      std::make_unique<Canvas2DLayerBridge>(
+          size, Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams());
   // Force hibernatation to occur in an immediate task.
   bridge->DontUseIdleSchedulingForTesting();
   CanvasElement().SetResourceProviderForTesting(nullptr, std::move(bridge),
                                                 size);
+  CanvasElement().GetCanvas2DLayerBridge()->SetCanvasResourceHost(
+      canvas_element_);
 
   EXPECT_TRUE(CanvasElement().GetCanvas2DLayerBridge()->IsAccelerated());
   // Take a snapshot to trigger lazy resource provider creation
@@ -1188,12 +1188,14 @@
   CreateContext(kNonOpaque);
   IntSize size(300, 300);
   std::unique_ptr<Canvas2DLayerBridge> bridge =
-      MakeBridge(size, Canvas2DLayerBridge::kEnableAcceleration);
+      std::make_unique<Canvas2DLayerBridge>(
+          size, Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams());
   // Force hibernatation to occur in an immediate task.
   bridge->DontUseIdleSchedulingForTesting();
   CanvasElement().SetResourceProviderForTesting(nullptr, std::move(bridge),
                                                 size);
-
+  CanvasElement().GetCanvas2DLayerBridge()->SetCanvasResourceHost(
+      canvas_element_);
   EXPECT_TRUE(CanvasElement().GetCanvas2DLayerBridge()->IsAccelerated());
 
   EXPECT_TRUE(CanvasElement().GetLayoutBoxModelObject());
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
index 81170420..112234e 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
@@ -19,7 +19,6 @@
 #include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
-#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
@@ -86,20 +85,9 @@
       bernoulli_distribution_(kUMASampleProbability) {
   is_valid_size_ = IsValidImageSize(Host()->Size());
 
-  // A raw pointer is safe here because the callback is only used by the
-  // recorder_
-  set_needs_flush_callback_ =
-      WTF::BindRepeating(&OffscreenCanvasRenderingContext2D::SetNeedsFlush,
-                         WrapWeakPersistent(this));
-  StartRecording();
-
-  // Clear the background transparent or opaque. Similar code at
-  // CanvasResourceProvider::Clear().
+  // Clear the background transparent or opaque.
   if (IsCanvas2DBufferValid()) {
-    DCHECK(recorder_);
-    recorder_->getRecordingCanvas()->clear(
-        ColorParams().GetOpacityMode() == kOpaque ? SK_ColorBLACK
-                                                  : SK_ColorTRANSPARENT);
+    GetOrCreateCanvasResourceProvider()->Clear();
     DidDraw();
   }
 
@@ -131,29 +119,13 @@
   GetOffscreenFontCache().PruneLocalFontCache(kMaxCachedFonts);
 }
 
-void OffscreenCanvasRenderingContext2D::StartRecording() {
-  recorder_ =
-      std::make_unique<MemoryManagedPaintRecorder>(set_needs_flush_callback_);
-  cc::PaintCanvas* canvas = recorder_->beginRecording(Width(), Height());
-  // Always save an initial frame, to support resetting the top level matrix
-  // and clip.
-  canvas->save();
-  RestoreMatrixClipStack(canvas);
-}
-
 void OffscreenCanvasRenderingContext2D::FlushRecording() {
   if (!have_recorded_draw_commands_)
     return;
 
-  {  // Make a new scope so that PaintRecord gets deleted and that gets timed
-    CanvasResourceProvider* resource_provider = GetCanvasResourceProvider();
-    cc::PaintCanvas* canvas = resource_provider->Canvas();
-    canvas->drawPicture(recorder_->finishRecordingAsPicture());
-    resource_provider->FlushSkia();
-  }
+  GetCanvasResourceProvider()->FlushCanvas();
   GetCanvasResourceProvider()->ReleaseLockedImages();
 
-  StartRecording();
   have_recorded_draw_commands_ = false;
 }
 
@@ -165,7 +137,6 @@
   if (!GetOrCreateCanvasResourceProvider())
     return;
   FlushRecording();
-  needs_flush_ = false;
 }
 
 // BaseRenderingContext2D implementation
@@ -210,7 +181,6 @@
 void OffscreenCanvasRenderingContext2D::Reset() {
   Host()->DiscardResourceProvider();
   BaseRenderingContext2D::Reset();
-  StartRecording();
   // Because the host may have changed to a zero size
   is_valid_size_ = IsValidImageSize(Host()->Size());
 }
@@ -262,11 +232,7 @@
       return nullptr;
     }
   }
-
-  // "Transfer" means no retained buffer. Matrix transformations need to be
-  // preserved though.
   Host()->DiscardResourceProvider();
-  RestoreMatrixClipStack(recorder_->getRecordingCanvas());
 
   return MakeGarbageCollected<ImageBitmap>(std::move(image));
 }
@@ -293,17 +259,23 @@
   return ::blink::ParseColorOrCurrentColor(color, color_string, nullptr);
 }
 
-cc::PaintCanvas* OffscreenCanvasRenderingContext2D::GetPaintCanvas() const {
-  if (!is_valid_size_)
+cc::PaintCanvas* OffscreenCanvasRenderingContext2D::GetOrCreatePaintCanvas() {
+  if (!is_valid_size_ || !GetOrCreateCanvasResourceProvider())
     return nullptr;
-  return recorder_->getRecordingCanvas();
+  return GetPaintCanvas();
+}
+
+cc::PaintCanvas* OffscreenCanvasRenderingContext2D::GetPaintCanvas() const {
+  if (!is_valid_size_ || !GetCanvasResourceProvider())
+    return nullptr;
+  return GetCanvasResourceProvider()->Canvas();
 }
 
 void OffscreenCanvasRenderingContext2D::DidDraw() {
   have_recorded_draw_commands_ = true;
   dirty_rect_for_commit_.setWH(Width(), Height());
   Host()->DidDraw();
-  if (needs_flush_)
+  if (GetCanvasResourceProvider() && GetCanvasResourceProvider()->needs_flush())
     FinalizeFrame();
 }
 
@@ -311,7 +283,7 @@
   have_recorded_draw_commands_ = true;
   dirty_rect_for_commit_.join(dirty_rect);
   Host()->DidDraw(SkRect::Make(dirty_rect_for_commit_));
-  if (needs_flush_)
+  if (GetCanvasResourceProvider() && GetCanvasResourceProvider()->needs_flush())
     FinalizeFrame();
 }
 
@@ -369,13 +341,6 @@
   DCHECK(IsPaintable());
   FinalizeFrame();
   have_recorded_draw_commands_ = false;
-  // Add a save to initialize the transform/clip stack and then restore it after
-  // the draw. This is needed because each recording initializes and the resets
-  // this state after every flush.
-  cc::PaintCanvas* canvas = GetCanvasResourceProvider()->Canvas();
-  PaintCanvasAutoRestore auto_restore(canvas, true);
-  if (GetOrCreateCanvasResourceProvider())
-    RestoreMatrixClipStack(canvas);
 
   return offscreenCanvasForBinding()->ResourceProvider()->WritePixels(
       orig_info, pixels, row_bytes, x, y);
@@ -538,7 +503,7 @@
     double y,
     CanvasRenderingContext2DState::PaintType paint_type,
     double* max_width) {
-  cc::PaintCanvas* paint_canvas = GetPaintCanvas();
+  cc::PaintCanvas* paint_canvas = GetOrCreatePaintCanvas();
   if (!paint_canvas)
     return;
 
@@ -648,7 +613,4 @@
   return false;
 }
 
-void OffscreenCanvasRenderingContext2D::SetNeedsFlush() {
-  needs_flush_ = true;
-}
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
index 63fec77..b99acfd 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
@@ -12,7 +12,6 @@
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
 
 namespace blink {
 
@@ -107,7 +106,7 @@
 
   bool ParseColorOrCurrentColor(Color&, const String& color_string) const final;
 
-  cc::PaintCanvas* GetOrCreatePaintCanvas() final { return GetPaintCanvas(); }
+  cc::PaintCanvas* GetOrCreatePaintCanvas() final;
   cc::PaintCanvas* GetPaintCanvas() const final;
 
   void DidDraw() final;
@@ -139,8 +138,6 @@
                    int y) override;
 
  private:
-  void StartRecording();
-  std::unique_ptr<PaintRecorder> recorder_;
   bool have_recorded_draw_commands_;
   void FinalizeFrame() final;
   void FlushRecording();
@@ -165,10 +162,6 @@
 
   std::mt19937 random_generator_;
   std::bernoulli_distribution bernoulli_distribution_;
-
-  void SetNeedsFlush();
-  base::RepeatingClosure set_needs_flush_callback_;
-  bool needs_flush_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
index 96a6f66..b2b976a9 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc
@@ -46,7 +46,6 @@
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
-#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
@@ -124,22 +123,6 @@
   // Used by browser tests to detect the use of a Canvas2DLayerBridge.
   TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation",
                        TRACE_EVENT_SCOPE_GLOBAL);
-
-  // A raw pointer is safe here because the callback is only used by the
-  // |recorder_|.
-  set_needs_flush_callback_ = WTF::BindRepeating(
-      &Canvas2DLayerBridge::SetNeedsFlush, WTF::Unretained(this));
-  StartRecording();
-
-  // Clear the background transparent or opaque. Similar code at
-  // CanvasResourceProvider::Clear().
-  if (IsValid()) {
-    DCHECK(recorder_);
-    recorder_->getRecordingCanvas()->clear(
-        color_params_.GetOpacityMode() == kOpaque ? SK_ColorBLACK
-                                                  : SK_ColorTRANSPARENT);
-    DidDraw(FloatRect(0.f, 0.f, size_.Width(), size_.Height()));
-  }
 }
 
 Canvas2DLayerBridge::~Canvas2DLayerBridge() {
@@ -162,18 +145,12 @@
   layer_ = nullptr;
 }
 
-void Canvas2DLayerBridge::StartRecording() {
-  recorder_ =
-      std::make_unique<MemoryManagedPaintRecorder>(set_needs_flush_callback_);
-  cc::PaintCanvas* canvas =
-      recorder_->beginRecording(size_.Width(), size_.Height());
+void Canvas2DLayerBridge::SetCanvasResourceHost(CanvasResourceHost* host) {
+  resource_host_ = host;
 
-  // Always save an initial frame, to support resetting the top level matrix
-  // and clip.
-  canvas->save();
-
-  if (resource_host_)
-    resource_host_->RestoreCanvasMatrixClipStack(canvas);
+  if (resource_host_ && GetOrCreateResourceProvider()) {
+    EnsureCleared();
+  }
 }
 
 void Canvas2DLayerBridge::ResetResourceProvider() {
@@ -352,9 +329,11 @@
   // circular callstack from HTMLCanvasElement.
   resource_provider =
       resource_host_->GetOrCreateCanvasResourceProviderImpl(adjusted_hint);
-  if (!resource_provider)
+  if (!resource_provider || !resource_provider->IsValid())
     return nullptr;
 
+  EnsureCleared();
+
   if (IsAccelerated() && !layer_) {
     layer_ = cc::TextureLayer::CreateForMailbox(this);
     layer_->SetIsDrawable(true);
@@ -392,9 +371,11 @@
   return resource_provider;
 }
 
-cc::PaintCanvas* Canvas2DLayerBridge::GetPaintCanvas() const {
+cc::PaintCanvas* Canvas2DLayerBridge::GetPaintCanvas() {
   DCHECK(resource_host_);
-  return recorder_->getRecordingCanvas();
+  if (GetOrCreateResourceProvider())
+    return ResourceProvider()->Canvas();
+  return nullptr;
 }
 
 void Canvas2DLayerBridge::UpdateFilterQuality() {
@@ -491,14 +472,6 @@
 
   last_record_tainted_by_write_pixels_ = true;
   have_recorded_draw_commands_ = false;
-  // Apply clipstack to canvas_ and then restore it to original state once
-  // we leave this scope. This is needed because each recording initializes and
-  // resets this state after every flush.
-  cc::PaintCanvas* canvas = ResourceProvider()->Canvas();
-  PaintCanvasAutoRestore auto_restore(canvas, true);
-  if (GetOrCreateResourceProvider()) {
-    resource_host_->RestoreCanvasMatrixClipStack(canvas);
-  }
 
   ResourceProvider()->WritePixels(orig_info, pixels, row_bytes, x, y);
   return true;
@@ -506,8 +479,7 @@
 
 void Canvas2DLayerBridge::SkipQueuedDrawCommands() {
   if (have_recorded_draw_commands_) {
-    recorder_->finishRecordingAsPicture();
-    StartRecording();
+    ResourceProvider()->SkipQueuedDrawCommands();
     have_recorded_draw_commands_ = false;
   }
 
@@ -515,8 +487,19 @@
     rate_limiter_->Reset();
 }
 
-void Canvas2DLayerBridge::CalculateDirtyRegion(int canvas_width,
-                                               int canvas_height) {
+void Canvas2DLayerBridge::EnsureCleared() {
+  if (cleared_)
+    return;
+  cleared_ = true;
+  ResourceProvider()->Clear();
+  DidDraw(FloatRect(0.f, 0.f, size_.Width(), size_.Height()));
+}
+
+void Canvas2DLayerBridge::CalculateDirtyRegion() {
+  // 1 pixel is added around the rect for anti-alias effect
+  // TODO(khushalsagar) : Remove the need for this 1 pixel addition.
+  int canvas_width = size_.Width() + 1;
+  int canvas_height = size_.Height() + 1;
   base::CheckedNumeric<int> area(canvas_width);
   area *= canvas_height;
   if (!area.IsValid() || area.ValueOrDie() < kMinAreaForComputingDirtyRegion)
@@ -656,24 +639,15 @@
     }
     timer.emplace();
   }
-  {  // Make a new scope so that PaintRecord gets deleted and that gets timed
-    cc::PaintCanvas* canvas = ResourceProvider()->Canvas();
-    last_recording_ = recorder_->finishRecordingAsPicture();
-    SkScalar canvas_width = canvas->getLocalClipBounds().width();
-    SkScalar canvas_height = canvas->getLocalClipBounds().height();
-    DCHECK_GE(canvas_width, size_.Width());
-    DCHECK_GE(canvas_height, size_.Height());
-    if (will_measure) {
-      CalculateDirtyRegion(canvas_width, canvas_height);
-    }
 
-    canvas->drawPicture(last_recording_);
-    last_record_tainted_by_write_pixels_ = false;
-    if (!clear_frame_ || !resource_host_ || !resource_host_->IsPrinting()) {
-      last_recording_ = nullptr;
-      clear_frame_ = false;
-    }
-    ResourceProvider()->FlushSkia();
+  ResourceProvider()->FlushCanvas();
+  last_recording_ = ResourceProvider()->last_recording();
+  if (last_recording_ && will_measure)
+    CalculateDirtyRegion();
+  last_record_tainted_by_write_pixels_ = false;
+  if (!clear_frame_ || !resource_host_ || !resource_host_->IsPrinting()) {
+    last_recording_ = nullptr;
+    clear_frame_ = false;
   }
 
   // Finish up the timing operation
@@ -697,7 +671,6 @@
   if (GetOrCreateResourceProvider())
     ResourceProvider()->ReleaseLockedImages();
 
-  StartRecording();
   have_recorded_draw_commands_ = false;
 }
 
@@ -819,7 +792,7 @@
 }
 
 void Canvas2DLayerBridge::DidDraw(const FloatRect& /* rect */) {
-  if (needs_flush_)
+  if (ResourceProvider() && ResourceProvider()->needs_flush())
     FinalizeFrame();
   have_recorded_draw_commands_ = true;
 }
@@ -848,8 +821,6 @@
 
   if (rate_limiter_)
     rate_limiter_->Tick();
-
-  needs_flush_ = false;
 }
 
 void Canvas2DLayerBridge::DoPaintInvalidation(const FloatRect& dirty_rect) {
@@ -880,10 +851,6 @@
   SkipQueuedDrawCommands();
 }
 
-void Canvas2DLayerBridge::SetNeedsFlush() {
-  needs_flush_ = true;
-}
-
 void Canvas2DLayerBridge::Logger::ReportHibernationEvent(
     HibernationEvent event) {
   UMA_HISTOGRAM_ENUMERATION("Blink.Canvas.HibernationEvents", event);
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
index 6b39d2a..d295f57 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h
@@ -45,7 +45,6 @@
 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_host.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
-#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
@@ -114,7 +113,8 @@
   virtual void DidRestoreCanvasMatrixClipStack(cc::PaintCanvas*) {}
   virtual bool IsAccelerated() const;
 
-  cc::PaintCanvas* GetPaintCanvas() const;
+  // This may recreate CanvasResourceProvider
+  cc::PaintCanvas* GetPaintCanvas();
   bool IsValid();
   bool WritePixels(const SkImageInfo&,
                    const void* pixels,
@@ -124,9 +124,7 @@
   void DontUseIdleSchedulingForTesting() {
     dont_use_idle_scheduling_for_testing_ = true;
   }
-  void SetCanvasResourceHost(CanvasResourceHost* host) {
-    resource_host_ = host;
-  }
+  void SetCanvasResourceHost(CanvasResourceHost* host);
 
   void Hibernate();
   bool IsHibernating() const { return hibernation_image_ != nullptr; }
@@ -138,14 +136,6 @@
 
   cc::TextureLayer* layer_for_testing() { return layer_.get(); }
 
-  // TODO(jochin): Remove this function completely once recorder_ has been
-  // moved into CanvasResourceProvider.
-  sk_sp<cc::PaintRecord> record_for_testing() {
-    sk_sp<cc::PaintRecord> record = recorder_->finishRecordingAsPicture();
-    StartRecording();
-    return record;
-  }
-
   // The values of the enum entries must not change because they are used for
   // usage metrics histograms. New values can be added to the end.
   enum HibernationEvent {
@@ -199,13 +189,12 @@
   bool CheckResourceProviderValid();
   void ResetResourceProvider();
 
-  void StartRecording();
   void SkipQueuedDrawCommands();
+  void EnsureCleared();
 
   bool ShouldAccelerate(AccelerationHint) const;
-  void CalculateDirtyRegion(int canvas_width, int canvas_height);
+  void CalculateDirtyRegion();
 
-  std::unique_ptr<PaintRecorder> recorder_;
   sk_sp<SkImage> hibernation_image_;
   scoped_refptr<cc::TextureLayer> layer_;
   std::unique_ptr<SharedContextRateLimiter> rate_limiter_;
@@ -256,11 +245,11 @@
   Deque<RasterTimer> pending_raster_timers_;
 
   sk_sp<cc::PaintRecord> last_recording_;
-  cc::InvalidationRegion dirty_invalidate_region_;
 
-  void SetNeedsFlush();
-  base::RepeatingClosure set_needs_flush_callback_;
-  bool needs_flush_ = false;
+  // This tracks whether the canvas has been cleared once after
+  // this bridge was created.
+  bool cleared_ = false;
+  cc::InvalidationRegion dirty_invalidate_region_;
 
   base::WeakPtrFactory<Canvas2DLayerBridge> weak_ptr_factory_{this};
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
index a8ed0b5..7c955f9 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc
@@ -360,6 +360,13 @@
   EXPECT_FALSE(bridge->IsAccelerated());
 }
 
+void DrawSomething(Canvas2DLayerBridge* bridge) {
+  bridge->DidDraw(FloatRect(0, 0, 1, 1));
+  bridge->FinalizeFrame();
+  // Grabbing an image forces a flush
+  bridge->NewImageSnapshot(kPreferAcceleration);
+}
+
 TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) {
   {
     // No fallback case.
@@ -380,14 +387,19 @@
                         ->ContextProvider()
                         ->GetGrContext();
     std::unique_ptr<Canvas2DLayerBridge> bridge =
-        MakeBridge(IntSize(300, 150), Canvas2DLayerBridge::kEnableAcceleration,
-                   CanvasColorParams());
+        std::make_unique<Canvas2DLayerBridge>(
+            IntSize(300, 150), Canvas2DLayerBridge::kEnableAcceleration,
+            CanvasColorParams());
+    bridge->DontUseIdleSchedulingForTesting();
     EXPECT_TRUE(bridge->IsValid());
     EXPECT_TRUE(bridge->IsAccelerated());  // We don't yet know that
                                            // allocation will fail.
     // This will cause SkSurface_Gpu creation to fail without
     // Canvas2DLayerBridge otherwise detecting that anything was disabled.
     gr->abandonContext();
+    host_ = std::make_unique<FakeCanvasResourceHost>(IntSize(300, 150));
+    bridge->SetCanvasResourceHost(host_.get());
+    DrawSomething(bridge.get());
     scoped_refptr<StaticBitmapImage> snapshot =
         bridge->NewImageSnapshot(kPreferAcceleration);
     EXPECT_FALSE(bridge->IsAccelerated());
@@ -403,13 +415,6 @@
   ~MockLogger() override = default;
 };
 
-void DrawSomething(Canvas2DLayerBridge* bridge) {
-  bridge->DidDraw(FloatRect(0, 0, 1, 1));
-  bridge->FinalizeFrame();
-  // Grabbing an image forces a flush
-  bridge->NewImageSnapshot(kPreferAcceleration);
-}
-
 #if CANVAS2D_HIBERNATION_ENABLED
 TEST_F(Canvas2DLayerBridgeTest, HibernationLifeCycle)
 #else
@@ -845,13 +850,17 @@
 
   viz::TransferableResource resources[3];
   std::unique_ptr<viz::SingleReleaseCallback> callbacks[3];
+  PaintFlags flags;
 
   std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
       IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
       CanvasColorParams());
+  bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags);
   DrawSomething(bridge.get());
   ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[0],
                                                   &callbacks[0]));
+
+  bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags);
   DrawSomething(bridge.get());
   ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[1],
                                                   &callbacks[1]));
@@ -861,6 +870,7 @@
   // Now release the first resource and draw again. It should be reused due to
   // recycling.
   callbacks[0]->Run(gpu::SyncToken(), false);
+  bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags);
   DrawSomething(bridge.get());
   ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[2],
                                                   &callbacks[2]));
@@ -880,13 +890,16 @@
 
   viz::TransferableResource resources[2];
   std::unique_ptr<viz::SingleReleaseCallback> callbacks[2];
+  PaintFlags flags;
 
   std::unique_ptr<Canvas2DLayerBridge> bridge = MakeBridge(
       IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting,
       CanvasColorParams());
+  bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags);
   DrawSomething(bridge.get());
   ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[0],
                                                   &callbacks[0]));
+  bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags);
   DrawSomething(bridge.get());
   ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[1],
                                                   &callbacks[1]));
@@ -1002,17 +1015,14 @@
   // First 2 images are budgeted, they should remain locked after the op.
   bridge->GetPaintCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr);
   bridge->GetPaintCanvas()->drawImage(images[1].paint_image(), 0u, 0u, nullptr);
-  // TODO(jochin): Can just call provider::FlushSkia() once we move recorder_
-  // to the resource provider. The following is a temp workaround.
-  cc::PaintCanvas* canvas = bridge->GetOrCreateResourceProvider()->Canvas();
-  canvas->drawPicture(bridge->record_for_testing());
+  bridge->GetOrCreateResourceProvider()->FlushCanvas();
   EXPECT_EQ(image_decode_cache_.num_locked_images(), 2);
 
   // Next image is not budgeted, we should unlock all images other than the last
   // image.
   image_decode_cache_.set_budget_exceeded(true);
   bridge->GetPaintCanvas()->drawImage(images[2].paint_image(), 0u, 0u, nullptr);
-  canvas->drawPicture(bridge->record_for_testing());
+  bridge->GetOrCreateResourceProvider()->FlushCanvas();
   EXPECT_EQ(image_decode_cache_.num_locked_images(), 1);
 
   // Ask the provider to release everything, no locked images should remain.
@@ -1033,10 +1043,7 @@
                     SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace());
   bridge->GetPaintCanvas()->drawImage(image.paint_image(), 0u, 0u, nullptr);
 
-  // TODO(jochin): Can just call provider::FlushSkia() once we move recorder_
-  // to the resource provider. The following is a temp workaround.
-  cc::PaintCanvas* canvas = bridge->GetOrCreateResourceProvider()->Canvas();
-  canvas->drawPicture(bridge->record_for_testing());
+  bridge->GetOrCreateResourceProvider()->FlushCanvas();
   EXPECT_EQ(image_decode_cache_.num_locked_images(), 1);
 
   base::RunLoop().RunUntilIdle();
@@ -1109,23 +1116,26 @@
                  std::move(host));
   PaintFlags flags;
 
+  // MakeBridge() results in a call to restore the matrix. So we already have 1.
   EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX),
-            0);
+            5);
+  // Drawline so WritePixels has something to flush
+  bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags);
+  bridge->DidDraw(FloatRect(0, 0, 1, 1));
 
-  cc::PaintCanvas* canvas = bridge->GetOrCreateResourceProvider()->Canvas();
+  // WritePixels flushes recording. Post flush, a new drawing canvas is created
+  // that should have the matrix restored onto it.
   bridge->WritePixels(SkImageInfo::MakeN32Premul(10, 10), nullptr, 10, 0, 0);
-  // Recording canvas maintain clip stack, while underlying SkCanvas should not.
-  EXPECT_EQ(canvas->getTotalMatrix().get(SkMatrix::kMTransX), 0);
   EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX),
             5);
 
   bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags);
-  // Flush recording. Recording canvas should maintain matrix, while SkCanvas
-  // should not.
+  // Standard flush recording. Post flush, a new drawing canvas is created that
+  // should have the matrix restored onto it.
   DrawSomething(bridge.get());
+
   EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX),
             5);
-  EXPECT_EQ(canvas->getTotalMatrix().get(SkMatrix::kMTransX), 0);
 }
 
 TEST_F(Canvas2DLayerBridgeTest, DisplayedCanvasIsRateLimited) {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc
index f7c2031..d636062 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc
@@ -19,6 +19,18 @@
       std::move(resource_provider_);
   resource_provider_ = std::move(new_resource_provider);
   UpdateMemoryUsage();
+  if (resource_provider_) {
+    resource_provider_->Canvas()->restoreToCount(1);
+    InitializeForRecording(resource_provider_->Canvas());
+    // Using unretained here since CanvasResourceHost owns |resource_provider_|
+    // and is guaranteed to outlive it
+    resource_provider_->SetRestoreClipStackCallback(base::BindRepeating(
+        &CanvasResourceHost::InitializeForRecording, base::Unretained(this)));
+  }
+  if (old_resource_provider) {
+    old_resource_provider->SetRestoreClipStackCallback(
+        CanvasResourceProvider::RestoreMatrixClipStackCb());
+  }
   return old_resource_provider;
 }
 
@@ -27,4 +39,9 @@
   UpdateMemoryUsage();
 }
 
+void CanvasResourceHost::InitializeForRecording(cc::PaintCanvas* canvas) {
+  canvas->save();
+  RestoreCanvasMatrixClipStack(canvas);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
index 9c1e69a6..16dbe14 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
@@ -43,6 +43,8 @@
   virtual bool IsPrinting() const { return false; }
 
  private:
+  void InitializeForRecording(cc::PaintCanvas* canvas);
+
   std::unique_ptr<CanvasResourceProvider> resource_provider_;
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 2c855dd..bcbd2a5 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -17,9 +17,11 @@
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/config/gpu_driver_bug_workaround_type.h"
 #include "gpu/config/gpu_feature_info.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
+#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
@@ -229,6 +231,7 @@
     if (IsGpuContextLost())
       return nullptr;
 
+    FlushCanvas();
     // Its important to end read access and ref the resource before the WillDraw
     // call below. Since it relies on resource ref-count to trigger
     // copy-on-write and asserts that we only have write access when the
@@ -264,6 +267,7 @@
       return SnapshotInternal(orientation);
 
     if (!cached_snapshot_) {
+      FlushCanvas();
       EndWriteAccess();
       cached_snapshot_ = resource_->Bitmap();
     }
@@ -562,8 +566,8 @@
                  "CanvasResourceProviderSwapChain::ProduceCanvasResource");
     if (!IsValid())
       return nullptr;
+    FlushCanvas();
     if (dirty_) {
-      FlushSkia();
       resource_->PresentSwapChain();
       dirty_ = false;
     }
@@ -1022,11 +1026,11 @@
   return surface_.get();
 }
 
-void CanvasResourceProvider::InitializePaintCanvas() {
-  // Since canvas_ has a reference to canvas_image_provider_, canvas must be
-  // deleted before the image_provider.
-  canvas_ = nullptr;
-  canvas_image_provider_ = nullptr;
+void CanvasResourceProvider::EnsureSkiaCanvas() {
+  WillDraw();
+
+  if (skia_canvas_)
+    return;
 
   // Create an ImageDecodeCache for half float images only if the canvas is
   // using half float back storage.
@@ -1046,26 +1050,27 @@
     context_flushes.enable = true;
     context_flushes.max_draws_before_flush = kMaxDrawsBeforeContextFlush;
   }
-  canvas_ = std::make_unique<cc::SkiaPaintCanvas>(GetSkSurface()->getCanvas(),
-                                                  canvas_image_provider_.get(),
-                                                  context_flushes);
+  skia_canvas_ = std::make_unique<cc::SkiaPaintCanvas>(
+      GetSkSurface()->getCanvas(), canvas_image_provider_.get(),
+      context_flushes);
 }
 
 cc::PaintCanvas* CanvasResourceProvider::Canvas() {
-  WillDraw();
+  if (!recorder_) {
+    // A raw pointer is safe here because the callback is only used by the
+    // |recorder_|.
+    recorder_ = std::make_unique<MemoryManagedPaintRecorder>(WTF::BindRepeating(
+        &CanvasResourceProvider::SetNeedsFlush, WTF::Unretained(this)));
 
-  if (!canvas_) {
-    TRACE_EVENT0("blink", "CanvasResourceProvider::Canvas");
-    DCHECK(!canvas_image_provider_);
-    InitializePaintCanvas();
+    return recorder_->beginRecording(Size().Width(), Size().Height());
   }
-  return canvas_.get();
+  return recorder_->getRecordingCanvas();
 }
 
 void CanvasResourceProvider::OnContextDestroyed() {
   if (canvas_image_provider_) {
-    DCHECK(canvas_);
-    canvas_->reset_image_provider();
+    DCHECK(skia_canvas_);
+    skia_canvas_->reset_image_provider();
     canvas_image_provider_.reset();
   }
 }
@@ -1087,6 +1092,7 @@
 }
 
 cc::PaintImage CanvasResourceProvider::MakeImageSnapshot() {
+  FlushCanvas();
   auto sk_image = GetSkSurface()->makeImageSnapshot();
   if (!sk_image)
     return cc::PaintImage();
@@ -1126,8 +1132,18 @@
   return context_provider_wrapper_->ContextProvider()->GetGrContext();
 }
 
-void CanvasResourceProvider::FlushSkia() const {
+void CanvasResourceProvider::FlushCanvas() {
+  if (!recorder_ || !recorder_->ListHasDrawOps())
+    return;
+  EnsureSkiaCanvas();
+  last_recording_ = recorder_->finishRecordingAsPicture();
+  skia_canvas_->drawPicture(last_recording_);
+  cc::PaintCanvas* canvas =
+      recorder_->beginRecording(Size().Width(), Size().Height());
+  if (restore_clip_stack_callback_)
+    restore_clip_stack_callback_.Run(canvas);
   GetSkSurface()->flush();
+  needs_flush_ = false;
 }
 
 bool CanvasResourceProvider::IsGpuContextLost() const {
@@ -1144,6 +1160,18 @@
   TRACE_EVENT0("blink", "CanvasResourceProvider::WritePixels");
 
   DCHECK(IsValid());
+  DCHECK(!recorder_->ListHasDrawOps());
+
+  EnsureSkiaCanvas();
+
+  // Apply clipstack to skia_canvas_ and then restore it to original state once
+  // we leave this scope. This is needed because each recording initializes and
+  // resets this state after every flush. restore_clip_stack_callback_ sets the
+  // initial save required for a restore.
+  cc::PaintCanvasAutoRestore auto_restore(skia_canvas_.get(), false);
+  if (restore_clip_stack_callback_)
+    restore_clip_stack_callback_.Run(skia_canvas_.get());
+
   return GetSkSurface()->getCanvas()->writePixels(orig_info, pixels, row_bytes,
                                                   x, y);
 }
@@ -1253,12 +1281,29 @@
   return canvas_resources_.back();
 }
 
+void CanvasResourceProvider::SkipQueuedDrawCommands() {
+  if (!recorder_)
+    return;
+  recorder_->finishRecordingAsPicture();
+  cc::PaintCanvas* canvas =
+      recorder_->beginRecording(Size().Width(), Size().Height());
+  if (restore_clip_stack_callback_)
+    restore_clip_stack_callback_.Run(canvas);
+}
+
+void CanvasResourceProvider::SetRestoreClipStackCallback(
+    RestoreMatrixClipStackCb callback) {
+  DCHECK(restore_clip_stack_callback_.is_null() || callback.is_null());
+  restore_clip_stack_callback_ = std::move(callback);
+}
+
 void CanvasResourceProvider::RestoreBackBuffer(const cc::PaintImage& image) {
   DCHECK_EQ(image.height(), Size().Height());
   DCHECK_EQ(image.width(), Size().Width());
+  EnsureSkiaCanvas();
   cc::PaintFlags copy_paint;
   copy_paint.setBlendMode(SkBlendMode::kSrc);
-  Canvas()->drawImage(image, 0, 0, &copy_paint);
+  skia_canvas_->drawImage(image, 0, 0, &copy_paint);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
index f6787de..1427ccf 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
@@ -9,6 +9,7 @@
 #include "cc/raster/playback_image_provider.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
 #include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
 class GrContext;
@@ -93,6 +94,9 @@
     kMaxValue = kSwapChain,
   };
 
+  using RestoreMatrixClipStackCb =
+      base::RepeatingCallback<void(cc::PaintCanvas*)>;
+
   void static RecordTypeToUMA(ResourceProviderType type);
 
   // TODO(juanmihd): Clean up creation methods/usage. See crbug.com/1035589.
@@ -138,9 +142,8 @@
   void OnContextDestroyed() override;
 
   cc::PaintCanvas* Canvas();
-  void InitializePaintCanvas();
   void ReleaseLockedImages();
-  void FlushSkia() const;
+  void FlushCanvas();
   const CanvasColorParams& ColorParams() const { return color_params_; }
   void SetFilterQuality(SkFilterQuality quality) { filter_quality_ = quality; }
   const IntSize& Size() const { return size_; }
@@ -210,6 +213,12 @@
     return canvas_resources_.size();
   }
 
+  void SkipQueuedDrawCommands();
+  const sk_sp<cc::PaintRecord>& last_recording() const {
+    return last_recording_;
+  }
+  void SetRestoreClipStackCallback(RestoreMatrixClipStackCb);
+  bool needs_flush() const { return needs_flush_; }
   void RestoreBackBuffer(const cc::PaintImage&);
 
  protected:
@@ -261,6 +270,8 @@
 
   cc::ImageDecodeCache* ImageDecodeCacheRGBA8();
   cc::ImageDecodeCache* ImageDecodeCacheF16();
+  void EnsureSkiaCanvas();
+  void SetNeedsFlush() { needs_flush_ = true; }
 
   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
   base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher_;
@@ -270,7 +281,11 @@
   const CanvasColorParams color_params_;
   const bool is_origin_top_left_;
   std::unique_ptr<CanvasImageProvider> canvas_image_provider_;
-  std::unique_ptr<cc::SkiaPaintCanvas> canvas_;
+  std::unique_ptr<cc::SkiaPaintCanvas> skia_canvas_;
+  std::unique_ptr<PaintRecorder> recorder_;
+  sk_sp<cc::PaintRecord> last_recording_;
+
+  bool needs_flush_ = false;
 
   const cc::PaintImage::Id snapshot_paint_image_id_;
   cc::PaintImage::ContentId snapshot_paint_image_content_id_ =
@@ -289,6 +304,8 @@
   // underlying GrContext is flushed.
   static constexpr int kMaxDrawsBeforeContextFlush = 50;
 
+  RestoreMatrixClipStackCb restore_clip_stack_callback_;
+
   base::WeakPtrFactory<CanvasResourceProvider> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(CanvasResourceProvider);
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
index 8852771..53bd355 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
@@ -246,6 +246,7 @@
 
   // Resource updated after draw.
   provider->Canvas()->clear(SK_ColorWHITE);
+  provider->FlushCanvas();
   new_image = provider->Snapshot();
   EXPECT_NE(new_image->GetMailboxHolder().mailbox,
             image->GetMailboxHolder().mailbox);
@@ -254,6 +255,7 @@
   auto original_mailbox = image->GetMailboxHolder().mailbox;
   image.reset();
   provider->Canvas()->clear(SK_ColorBLACK);
+  provider->FlushCanvas();
   EXPECT_EQ(original_mailbox, provider->Snapshot()->GetMailboxHolder().mailbox);
 }