Bug 1318573 - (Part 1) Move CanvasState to BasicRenderingContext2D. r?mstange draft
authorKevin Chen <kechen@mozilla.com>
Wed, 25 Jan 2017 16:05:24 +0800
changeset 499854 98247214b24b7178889e2ee45541a5de2f6329fb
parent 489857 7ef1e9abd296a8edc39b7efc8d637767ba2f77ed
child 499855 7e81700007171830733cd5f430ac1d8e5cf20ccd
push id49564
push userbmo:kechen@mozilla.com
push dateThu, 16 Mar 2017 09:50:15 +0000
reviewersmstange
bugs1318573
milestone54.0a1
Bug 1318573 - (Part 1) Move CanvasState to BasicRenderingContext2D. r?mstange MozReview-Commit-ID: JxC8FY875o1
dom/canvas/BasicRenderingContext2D.cpp
dom/canvas/BasicRenderingContext2D.h
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
dom/canvas/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/canvas/BasicRenderingContext2D.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/BasicRenderingContext2D.h"
+
+namespace mozilla {
+namespace dom{
+
+// Cap sigma to avoid overly large temp surfaces.
+const mozilla::gfx::Float SIGMA_MAX = 100;
+
+const size_t MAX_STYLE_STACK_SIZE = 1024;
+
+/**
+ ** BasicRenderingContext2D impl
+ **/
+
+//
+// state
+//
+void
+BasicRenderingContext2D::Save()
+{
+  EnsureTarget();
+  mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
+  mStyleStack.SetCapacity(mStyleStack.Length() + 1);
+  mStyleStack.AppendElement(CurrentState());
+
+  if (mStyleStack.Length() > MAX_STYLE_STACK_SIZE) {
+    // This is not fast, but is better than OOMing and shouldn't be hit by
+    // reasonable code.
+    mStyleStack.RemoveElementAt(0);
+  }
+}
+
+void
+BasicRenderingContext2D::Restore()
+{
+  if (mStyleStack.Length() - 1 == 0)
+    return;
+
+  TransformWillUpdate();
+
+  for (const auto& clipOrTransform : CurrentState().clipsAndTransforms) {
+    if (clipOrTransform.IsClip()) {
+      mTarget->PopClip();
+    }
+  }
+
+  mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
+
+  mTarget->SetTransform(CurrentState().transform);
+}
+
+//
+// path bits
+//
+void
+BasicRenderingContext2D::TransformWillUpdate()
+{
+  EnsureTarget();
+
+  // Store the matrix that would transform the current path to device
+  // space.
+  if (mPath || mPathBuilder) {
+    if (!mPathTransformWillUpdate) {
+      // If the transform has already been updated, but a device space builder
+      // has not been created yet mPathToDS contains the right transform to
+      // transform the current mPath into device space.
+      // We should leave it alone.
+      mPathToDS = mTarget->GetTransform();
+    }
+    mPathTransformWillUpdate = true;
+  }
+}
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/canvas/BasicRenderingContext2D.h
+++ b/dom/canvas/BasicRenderingContext2D.h
@@ -1,35 +1,56 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef BasicRenderingContext2D_h
 #define BasicRenderingContext2D_h
 
+#include "FilterSupport.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
+#include "nsStyleStruct.h"
+#include "nsSVGEffects.h"
+
+using mozilla::gfx::FilterDescription;
 
 namespace mozilla {
 namespace dom {
 
 class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
 typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap
   CanvasImageSource;
 
+extern const mozilla::gfx::Float SIGMA_MAX;
+
 /*
  * BasicRenderingContext2D
  */
 class BasicRenderingContext2D
 {
 public:
+  enum RenderingMode {
+    SoftwareBackendMode,
+    OpenGLBackendMode,
+    DefaultBackendMode
+  };
+
+  // This is created lazily so it is necessary to call EnsureTarget before
+  // accessing it. In the event of an error it will be equal to
+  // sErrorTarget.
+  RefPtr<mozilla::gfx::DrawTarget> mTarget;
+
+public:
+  explicit BasicRenderingContext2D(layers::LayersBackend aCompositorBackend)
+    : mPathTransformWillUpdate(false){};
   //
   // CanvasState
   //
-  virtual void Save() = 0;
-  virtual void Restore() = 0;
+  void Save();
+  void Restore();
 
   //
   // CanvasTransform
   //
   virtual void Scale(double aX, double aY, mozilla::ErrorResult& aError) = 0;
   virtual void Rotate(double aAngle, mozilla::ErrorResult& aError) = 0;
   virtual void Translate(double aX,
                          double aY,
@@ -188,14 +209,264 @@ public:
                        double aY,
                        double aRadiusX,
                        double aRadiusY,
                        double aRotation,
                        double aStartAngle,
                        double aEndAngle,
                        bool aAnticlockwise,
                        ErrorResult& aError) = 0;
+protected:
+  enum class Style : uint8_t {
+    STROKE = 0,
+    FILL,
+    MAX
+  };
+
+  enum class TextAlign : uint8_t {
+    START,
+    END,
+    LEFT,
+    RIGHT,
+    CENTER
+  };
+
+  enum class TextBaseline : uint8_t {
+    TOP,
+    HANGING,
+    MIDDLE,
+    ALPHABETIC,
+    IDEOGRAPHIC,
+    BOTTOM
+  };
+
+  // A clip or a transform, recorded and restored in order.
+  struct ClipState {
+    explicit ClipState(mozilla::gfx::Path* aClip)
+      : clip(aClip)
+    {}
+
+    explicit ClipState(const mozilla::gfx::Matrix& aTransform)
+      : transform(aTransform)
+    {}
+
+    bool IsClip() const { return !!clip; }
+
+    RefPtr<mozilla::gfx::Path> clip;
+    mozilla::gfx::Matrix transform;
+  };
+
+  // state stack handling
+  class ContextState {
+  public:
+  ContextState() : textAlign(TextAlign::START),
+                   textBaseline(TextBaseline::ALPHABETIC),
+                   shadowColor(0),
+                   lineWidth(1.0f),
+                   miterLimit(10.0f),
+                   globalAlpha(1.0f),
+                   shadowBlur(0.0),
+                   dashOffset(0.0f),
+                   op(mozilla::gfx::CompositionOp::OP_OVER),
+                   fillRule(mozilla::gfx::FillRule::FILL_WINDING),
+                   lineCap(mozilla::gfx::CapStyle::BUTT),
+                   lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL),
+                   filterString(u"none"),
+                   filterSourceGraphicTainted(false),
+                   imageSmoothingEnabled(true),
+                   fontExplicitLanguage(false)
+  { }
+
+  ContextState(const ContextState& aOther)
+      : fontGroup(aOther.fontGroup),
+        fontLanguage(aOther.fontLanguage),
+        fontFont(aOther.fontFont),
+        gradientStyles(aOther.gradientStyles),
+        patternStyles(aOther.patternStyles),
+        colorStyles(aOther.colorStyles),
+        font(aOther.font),
+        textAlign(aOther.textAlign),
+        textBaseline(aOther.textBaseline),
+        shadowColor(aOther.shadowColor),
+        transform(aOther.transform),
+        shadowOffset(aOther.shadowOffset),
+        lineWidth(aOther.lineWidth),
+        miterLimit(aOther.miterLimit),
+        globalAlpha(aOther.globalAlpha),
+        shadowBlur(aOther.shadowBlur),
+        dash(aOther.dash),
+        dashOffset(aOther.dashOffset),
+        op(aOther.op),
+        fillRule(aOther.fillRule),
+        lineCap(aOther.lineCap),
+        lineJoin(aOther.lineJoin),
+        filterString(aOther.filterString),
+        filterChain(aOther.filterChain),
+        filterChainObserver(aOther.filterChainObserver),
+        filter(aOther.filter),
+        filterAdditionalImages(aOther.filterAdditionalImages),
+        filterSourceGraphicTainted(aOther.filterSourceGraphicTainted),
+        imageSmoothingEnabled(aOther.imageSmoothingEnabled),
+        fontExplicitLanguage(aOther.fontExplicitLanguage)
+  { }
+
+  void SetColorStyle(Style aWhichStyle, nscolor aColor)
+  {
+    colorStyles[aWhichStyle] = aColor;
+    gradientStyles[aWhichStyle] = nullptr;
+    patternStyles[aWhichStyle] = nullptr;
+  }
+
+  void SetPatternStyle(Style aWhichStyle, CanvasPattern* aPat)
+  {
+    gradientStyles[aWhichStyle] = nullptr;
+    patternStyles[aWhichStyle] = aPat;
+  }
+
+  void SetGradientStyle(Style aWhichStyle, CanvasGradient* aGrad)
+  {
+    gradientStyles[aWhichStyle] = aGrad;
+    patternStyles[aWhichStyle] = nullptr;
+  }
+
+  /**
+    * returns true iff the given style is a solid color.
+    */
+  bool StyleIsColor(Style aWhichStyle) const
+  {
+    return !(patternStyles[aWhichStyle] || gradientStyles[aWhichStyle]);
+  }
+
+  int32_t ShadowBlurRadius() const
+  {
+    static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
+    return (int32_t)floor(ShadowBlurSigma() * GAUSSIAN_SCALE_FACTOR + 0.5);
+  }
+
+  mozilla::gfx::Float ShadowBlurSigma() const
+  {
+    return std::min(SIGMA_MAX, shadowBlur / 2.0f);
+  }
+
+  nsTArray<ClipState> clipsAndTransforms;
+
+  RefPtr<gfxFontGroup> fontGroup;
+  nsCOMPtr<nsIAtom> fontLanguage;
+  nsFont fontFont;
+
+  EnumeratedArray<Style, Style::MAX, RefPtr<CanvasGradient>> gradientStyles;
+  EnumeratedArray<Style, Style::MAX, RefPtr<CanvasPattern>> patternStyles;
+  EnumeratedArray<Style, Style::MAX, nscolor> colorStyles;
+
+  nsString font;
+  TextAlign textAlign;
+  TextBaseline textBaseline;
+
+  nscolor shadowColor;
+
+  mozilla::gfx::Matrix transform;
+  mozilla::gfx::Point shadowOffset;
+  mozilla::gfx::Float lineWidth;
+  mozilla::gfx::Float miterLimit;
+  mozilla::gfx::Float globalAlpha;
+  mozilla::gfx::Float shadowBlur;
+  nsTArray<mozilla::gfx::Float> dash;
+  mozilla::gfx::Float dashOffset;
+
+  mozilla::gfx::CompositionOp op;
+  mozilla::gfx::FillRule fillRule;
+  mozilla::gfx::CapStyle lineCap;
+  mozilla::gfx::JoinStyle lineJoin;
+
+  nsString filterString;
+  nsTArray<nsStyleFilter> filterChain;
+  RefPtr<nsSVGFilterChainObserver> filterChainObserver;
+  mozilla::gfx::FilterDescription filter;
+  nsTArray<RefPtr<mozilla::gfx::SourceSurface>> filterAdditionalImages;
+
+  // This keeps track of whether the canvas was "tainted" or not when
+  // we last used a filter. This is a security measure, whereby the
+  // canvas is flipped to write-only if a cross-origin image is drawn to it.
+  // This is to stop bad actors from reading back data they shouldn't have
+  // access to.
+  //
+  // This also limits what filters we can apply to the context; in particular
+  // feDisplacementMap is restricted.
+  //
+  // We keep track of this to ensure that if this gets out of sync with the
+  // tainted state of the canvas itself, we update our filters accordingly.
+  bool filterSourceGraphicTainted;
+
+  bool imageSmoothingEnabled;
+  bool fontExplicitLanguage;
+  };
+
+  AutoTArray<ContextState, 3> mStyleStack;
+
+  /**
+    * We also have a device space pathbuilder. The reason for this is as
+    * follows, when a path is being built, but the transform changes, we
+    * can no longer keep a single path in userspace, considering there's
+    * several 'user spaces' now. We therefore transform the current path
+    * into device space, and add all operations to this path in device
+    * space.
+    *
+    * When then finally executing a render, the Azure drawing API expects
+    * the path to be in userspace. We could then set an identity transform
+    * on the DrawTarget and do all drawing in device space. This is
+    * undesirable because it requires transforming patterns, gradients,
+    * clips, etc. into device space and it would not work for stroking.
+    * What we do instead is convert the path back to user space when it is
+    * drawn, and draw it with the current transform. This makes all drawing
+    * occur correctly.
+    *
+    * There's never both a device space path builder and a user space path
+    * builder present at the same time. There is also never a path and a
+    * path builder present at the same time. When writing proceeds on an
+    * existing path the Path is cleared and a new builder is created.
+    *
+    * mPath is always in user-space.
+    */
+  RefPtr<mozilla::gfx::Path> mPath;
+  RefPtr<mozilla::gfx::PathBuilder> mPathBuilder;
+  bool mPathTransformWillUpdate;
+  mozilla::gfx::Matrix mPathToDS;
+
+protected:
+  virtual bool AlreadyShutDown() const = 0;
+
+  /**
+   * Create the backing surfacing, if it doesn't exist. If there is an error
+   * in creating the target then it will put sErrorTarget in place. If there
+   * is in turn an error in creating the sErrorTarget then they would both
+   * be null so IsTargetValid() would still return null.
+   *
+   * Returns the actual rendering mode being used by the created target.
+   */
+  virtual RenderingMode
+  EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
+               RenderingMode aRenderMode = RenderingMode::DefaultBackendMode) = 0;
+
+  /**
+   * Check if the target is valid after calling EnsureTarget.
+   */
+  virtual bool IsTargetValid() const = 0;
+
+  inline ContextState& CurrentState() {
+    return mStyleStack[mStyleStack.Length() - 1];
+  }
+
+  inline const ContextState& CurrentState() const {
+    return mStyleStack[mStyleStack.Length() - 1];
+  }
+
+
+  /**
+   * Needs to be called before updating the transform. This makes a call to
+   * EnsureTarget() so you don't have to.
+   */
+  void TransformWillUpdate();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* BasicRenderingContext2D_h */
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -155,21 +155,16 @@ using namespace mozilla::css;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace dom {
 
-// Cap sigma to avoid overly large temp surfaces.
-const Float SIGMA_MAX = 100;
-
-const size_t MAX_STYLE_STACK_SIZE = 1024;
-
 /* Memory reporter stuff */
 static int64_t gCanvasAzureMemoryUsed = 0;
 
 // This is KIND_OTHER because it's not always clear where in memory the pixels
 // of a canvas are stored.  Furthermore, this memory will be tracked by the
 // underlying surface implementations.  See bug 655638 for details.
 class Canvas2dPixelsReporter final : public nsIMemoryReporter
 {
@@ -1060,30 +1055,30 @@ NS_INTERFACE_MAP_END
 uint32_t CanvasRenderingContext2D::sNumLivingContexts = 0;
 DrawTarget* CanvasRenderingContext2D::sErrorTarget = nullptr;
 static bool sMaxContextsInitialized = false;
 static int32_t sMaxContexts = 0;
 
 
 
 CanvasRenderingContext2D::CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend)
-  : mRenderingMode(RenderingMode::OpenGLBackendMode)
+  : BasicRenderingContext2D(aCompositorBackend)
+  , mRenderingMode(RenderingMode::OpenGLBackendMode)
   , mCompositorBackend(aCompositorBackend)
   // these are the default values from the Canvas spec
   , mWidth(0), mHeight(0)
   , mZero(false), mOpaque(false)
   , mResetLayer(true)
   , mIPC(false)
   , mIsSkiaGL(false)
   , mHasPendingStableStateCallback(false)
   , mDrawObserver(nullptr)
   , mIsEntireFrameInvalid(false)
   , mPredictManyRedrawCalls(false)
   , mIsCapturedFrameInvalid(false)
-  , mPathTransformWillUpdate(false)
   , mInvalidateCount(0)
 {
   if (!sMaxContextsInitialized) {
     sMaxContexts = gfxPrefs::CanvasAzureAcceleratedLimit();
     sMaxContextsInitialized = true;
   }
 
   sNumLivingContexts++;
@@ -2128,54 +2123,16 @@ CanvasRenderingContext2D::GetInputStream
 
 SurfaceFormat
 CanvasRenderingContext2D::GetSurfaceFormat() const
 {
   return mOpaque ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
 }
 
 //
-// state
-//
-
-void
-CanvasRenderingContext2D::Save()
-{
-  EnsureTarget();
-  mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
-  mStyleStack.SetCapacity(mStyleStack.Length() + 1);
-  mStyleStack.AppendElement(CurrentState());
-
-  if (mStyleStack.Length() > MAX_STYLE_STACK_SIZE) {
-    // This is not fast, but is better than OOMing and shouldn't be hit by
-    // reasonable code.
-    mStyleStack.RemoveElementAt(0);
-  }
-}
-
-void
-CanvasRenderingContext2D::Restore()
-{
-  if (mStyleStack.Length() - 1 == 0)
-    return;
-
-  TransformWillUpdate();
-
-  for (const auto& clipOrTransform : CurrentState().clipsAndTransforms) {
-    if (clipOrTransform.IsClip()) {
-      mTarget->PopClip();
-    }
-  }
-
-  mStyleStack.RemoveElementAt(mStyleStack.Length() - 1);
-
-  mTarget->SetTransform(CurrentState().transform);
-}
-
-//
 // transformations
 //
 
 void
 CanvasRenderingContext2D::Scale(double aX, double aY, ErrorResult& aError)
 {
   TransformWillUpdate();
   if (!IsTargetValid()) {
@@ -3586,35 +3543,16 @@ CanvasRenderingContext2D::EnsureUserSpac
     mPathBuilder = mPath->CopyToBuilder(fillRule);
     mPath = mPathBuilder->Finish();
     mPathBuilder = nullptr;
   }
 
   NS_ASSERTION(mPath, "mPath should exist");
 }
 
-void
-CanvasRenderingContext2D::TransformWillUpdate()
-{
-  EnsureTarget();
-
-  // Store the matrix that would transform the current path to device
-  // space.
-  if (mPath || mPathBuilder) {
-    if (!mPathTransformWillUpdate) {
-      // If the transform has already been updated, but a device space builder
-      // has not been created yet mPathToDS contains the right transform to
-      // transform the current mPath into device space.
-      // We should leave it alone.
-      mPathToDS = mTarget->GetTransform();
-    }
-    mPathTransformWillUpdate = true;
-  }
-}
-
 //
 // text
 //
 
 void
 CanvasRenderingContext2D::SetFont(const nsAString& aFont,
                                   ErrorResult& aError)
 {
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -21,40 +21,34 @@
 #include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/UniquePtr.h"
 #include "gfx2DGlue.h"
 #include "imgIEncoder.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/EnumeratedArray.h"
-#include "FilterSupport.h"
-#include "nsSVGEffects.h"
 #include "Layers.h"
 
 class nsGlobalWindow;
 class nsXULElement;
 
 namespace mozilla {
 namespace gl {
 class SourceSurface;
 } // namespace gl
 
 namespace dom {
-class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
-typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource;
 class ImageData;
 class StringOrCanvasGradientOrCanvasPattern;
 class OwningStringOrCanvasGradientOrCanvasPattern;
 class TextMetrics;
 class CanvasFilterChainObserver;
 class CanvasPath;
 
-extern const mozilla::gfx::Float SIGMA_MAX;
-
 template<typename T> class Optional;
 
 struct CanvasBidiProcessor;
 class CanvasRenderingContext2DUserData;
 class CanvasDrawObserver;
 class CanvasShutdownObserver;
 
 /**
@@ -77,18 +71,16 @@ public:
     if (!mCanvasElement || mCanvasElement->IsInNativeAnonymousSubtree()) {
       return nullptr;
     }
 
     // corresponds to changes to the old bindings made in bug 745025
     return mCanvasElement->GetOriginalCanvas();
   }
 
-  void Save() override;
-  void Restore() override;
   void Scale(double aX, double aY, mozilla::ErrorResult& aError) override;
   void Rotate(double aAngle, mozilla::ErrorResult& aError) override;
   void Translate(double aX, double aY, mozilla::ErrorResult& aError) override;
   void Transform(double aM11, double aM12, double aM21, double aM22,
                  double aDx, double aDy, mozilla::ErrorResult& aError) override;
   void SetTransform(double aM11, double aM12, double aM21, double aM22,
                     double aDx, double aDy, mozilla::ErrorResult& aError) override;
   void ResetTransform(mozilla::ErrorResult& aError) override;
@@ -407,22 +399,16 @@ public:
     }
   }
 
   void DrawWindow(nsGlobalWindow& aWindow, double aX, double aY,
 		  double aW, double aH,
                   const nsAString& aBgColor, uint32_t aFlags,
                   mozilla::ErrorResult& aError);
 
-  enum RenderingMode {
-    SoftwareBackendMode,
-    OpenGLBackendMode,
-    DefaultBackendMode
-  };
-
   bool SwitchRenderingMode(RenderingMode aRenderingMode);
 
   // Eventually this should be deprecated. Keeping for now to keep the binding functional.
   void Demote();
 
   nsresult Redraw();
 
   virtual int32_t GetWidth() const override;
@@ -485,31 +471,24 @@ public:
    */
   virtual void DidRefresh() override;
 
   // this rect is in mTarget's current user space
   void RedrawUser(const gfxRect& aR);
 
   // nsISupports interface + CC
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(CanvasRenderingContext2D)
 
   enum class CanvasMultiGetterType : uint8_t {
     STRING = 0,
     PATTERN = 1,
     GRADIENT = 2
   };
 
-  enum class Style : uint8_t {
-    STROKE = 0,
-    FILL,
-    MAX
-  };
-
   nsINode* GetParentObject()
   {
     return mCanvasElement;
   }
 
   void LineTo(const mozilla::gfx::Point& aPoint)
   {
     if (mPathBuilder) {
@@ -624,35 +603,30 @@ protected:
    * device space.
    * After calling this function mPathTransformWillUpdate will be false
    */
   void EnsureWritablePath();
 
   // Ensures a path in UserSpace is available.
   void EnsureUserSpacePath(const CanvasWindingRule& aWinding = CanvasWindingRule::Nonzero);
 
-  /**
-   * Needs to be called before updating the transform. This makes a call to
-   * EnsureTarget() so you don't have to.
-   */
-  void TransformWillUpdate();
-
   // Report the fillRule has changed.
   void FillRuleChanged();
 
-   /**
+  /**
    * Create the backing surfacing, if it doesn't exist. If there is an error
    * in creating the target then it will put sErrorTarget in place. If there
    * is in turn an error in creating the sErrorTarget then they would both
    * be null so IsTargetValid() would still return null.
    *
    * Returns the actual rendering mode being used by the created target.
    */
-  RenderingMode EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
-                             RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
+  virtual RenderingMode
+  EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
+               RenderingMode aRenderMode = RenderingMode::DefaultBackendMode) override;
 
   void RestoreClipsAndTransformToTarget();
 
   bool TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
                        RefPtr<layers::PersistentBufferProvider>& aOutProvider);
 
   bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
                        RefPtr<layers::PersistentBufferProvider>& aOutProvider);
@@ -688,17 +662,17 @@ protected:
    * Returns the target to the buffer provider. i.e. this will queue a frame for
    * rendering.
    */
   void ReturnTarget(bool aForceReset = false);
 
   /**
    * Check if the target is valid after calling EnsureTarget.
    */
-  bool IsTargetValid() const {
+  bool IsTargetValid() const override{
     return !!mTarget && mTarget != sErrorTarget;
   }
 
   /**
     * Returns the surface format this canvas should be allocated using. Takes
     * into account mOpaque, platform requirements, etc.
     */
   mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
@@ -771,34 +745,29 @@ protected:
 
   bool mHasPendingStableStateCallback;
 
   nsTArray<CanvasRenderingContext2DUserData*> mUserDatas;
 
   // If mCanvasElement is not provided, then a docshell is
   nsCOMPtr<nsIDocShell> mDocShell;
 
-  // This is created lazily so it is necessary to call EnsureTarget before
-  // accessing it. In the event of an error it will be equal to
-  // sErrorTarget.
-  RefPtr<mozilla::gfx::DrawTarget> mTarget;
-
   RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider;
 
   uint32_t SkiaGLTex() const;
 
   // This observes our draw calls at the beginning of the canvas
   // lifetime and switches to software or GPU mode depending on
   // what it thinks is best
   CanvasDrawObserver* mDrawObserver;
   void RemoveDrawObserver();
 
   RefPtr<CanvasShutdownObserver> mShutdownObserver;
   void RemoveShutdownObserver();
-  bool AlreadyShutDown() const { return !mShutdownObserver; }
+  virtual bool AlreadyShutDown() const override{ return !mShutdownObserver; }
 
   /**
     * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
     * Redraw is called, reset to false when Render is called.
     */
   bool mIsEntireFrameInvalid;
   /**
     * When this is set, the first call to Redraw(gfxRect) should set
@@ -833,21 +802,17 @@ protected:
     *
     * There's never both a device space path builder and a user space path
     * builder present at the same time. There is also never a path and a
     * path builder present at the same time. When writing proceeds on an
     * existing path the Path is cleared and a new builder is created.
     *
     * mPath is always in user-space.
     */
-  RefPtr<mozilla::gfx::Path> mPath;
   RefPtr<mozilla::gfx::PathBuilder> mDSPathBuilder;
-  RefPtr<mozilla::gfx::PathBuilder> mPathBuilder;
-  bool mPathTransformWillUpdate;
-  mozilla::gfx::Matrix mPathToDS;
 
   /**
     * Number of times we've invalidated before calling redraw
     */
   uint32_t mInvalidateCount;
   static const uint32_t kCanvasMaxInvalidateCount = 100;
 
   /**
@@ -914,33 +879,16 @@ protected:
     }
 
     return CurrentState().op;
   }
 
   // text
 
 protected:
-  enum class TextAlign : uint8_t {
-    START,
-    END,
-    LEFT,
-    RIGHT,
-    CENTER
-  };
-
-  enum class TextBaseline : uint8_t {
-    TOP,
-    HANGING,
-    MIDDLE,
-    ALPHABETIC,
-    IDEOGRAPHIC,
-    BOTTOM
-  };
-
   enum class TextDrawOperation : uint8_t {
     FILL,
     STROKE,
     MEASURE
   };
 
 protected:
   gfxFontGroup *GetCurrentFontStyle();
@@ -953,187 +901,16 @@ protected:
                              float aX,
                              float aY,
                              const Optional<double>& aMaxWidth,
                              TextDrawOperation aOp,
                              float* aWidth);
 
   bool CheckSizeForSkiaGL(mozilla::gfx::IntSize aSize);
 
-  // A clip or a transform, recorded and restored in order.
-  struct ClipState {
-    explicit ClipState(mozilla::gfx::Path* aClip)
-      : clip(aClip)
-    {}
-
-    explicit ClipState(const mozilla::gfx::Matrix& aTransform)
-      : transform(aTransform)
-    {}
-
-    bool IsClip() const { return !!clip; }
-
-    RefPtr<mozilla::gfx::Path> clip;
-    mozilla::gfx::Matrix transform;
-  };
-
-  // state stack handling
-  class ContextState {
-  public:
-    ContextState() : textAlign(TextAlign::START),
-                     textBaseline(TextBaseline::ALPHABETIC),
-                     shadowColor(0),
-                     lineWidth(1.0f),
-                     miterLimit(10.0f),
-                     globalAlpha(1.0f),
-                     shadowBlur(0.0),
-                     dashOffset(0.0f),
-                     op(mozilla::gfx::CompositionOp::OP_OVER),
-                     fillRule(mozilla::gfx::FillRule::FILL_WINDING),
-                     lineCap(mozilla::gfx::CapStyle::BUTT),
-                     lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL),
-                     filterString(u"none"),
-                     filterSourceGraphicTainted(false),
-                     imageSmoothingEnabled(true),
-                     fontExplicitLanguage(false)
-    { }
-
-    ContextState(const ContextState& aOther)
-        : fontGroup(aOther.fontGroup),
-          fontLanguage(aOther.fontLanguage),
-          fontFont(aOther.fontFont),
-          gradientStyles(aOther.gradientStyles),
-          patternStyles(aOther.patternStyles),
-          colorStyles(aOther.colorStyles),
-          font(aOther.font),
-          textAlign(aOther.textAlign),
-          textBaseline(aOther.textBaseline),
-          shadowColor(aOther.shadowColor),
-          transform(aOther.transform),
-          shadowOffset(aOther.shadowOffset),
-          lineWidth(aOther.lineWidth),
-          miterLimit(aOther.miterLimit),
-          globalAlpha(aOther.globalAlpha),
-          shadowBlur(aOther.shadowBlur),
-          dash(aOther.dash),
-          dashOffset(aOther.dashOffset),
-          op(aOther.op),
-          fillRule(aOther.fillRule),
-          lineCap(aOther.lineCap),
-          lineJoin(aOther.lineJoin),
-          filterString(aOther.filterString),
-          filterChain(aOther.filterChain),
-          filterChainObserver(aOther.filterChainObserver),
-          filter(aOther.filter),
-          filterAdditionalImages(aOther.filterAdditionalImages),
-          filterSourceGraphicTainted(aOther.filterSourceGraphicTainted),
-          imageSmoothingEnabled(aOther.imageSmoothingEnabled),
-          fontExplicitLanguage(aOther.fontExplicitLanguage)
-    { }
-
-    void SetColorStyle(Style aWhichStyle, nscolor aColor)
-    {
-      colorStyles[aWhichStyle] = aColor;
-      gradientStyles[aWhichStyle] = nullptr;
-      patternStyles[aWhichStyle] = nullptr;
-    }
-
-    void SetPatternStyle(Style aWhichStyle, CanvasPattern* aPat)
-    {
-      gradientStyles[aWhichStyle] = nullptr;
-      patternStyles[aWhichStyle] = aPat;
-    }
-
-    void SetGradientStyle(Style aWhichStyle, CanvasGradient* aGrad)
-    {
-      gradientStyles[aWhichStyle] = aGrad;
-      patternStyles[aWhichStyle] = nullptr;
-    }
-
-    /**
-      * returns true iff the given style is a solid color.
-      */
-    bool StyleIsColor(Style aWhichStyle) const
-    {
-      return !(patternStyles[aWhichStyle] || gradientStyles[aWhichStyle]);
-    }
-
-    int32_t ShadowBlurRadius() const
-    {
-      static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
-      return (int32_t)floor(ShadowBlurSigma() * GAUSSIAN_SCALE_FACTOR + 0.5);
-    }
-
-    mozilla::gfx::Float ShadowBlurSigma() const
-    {
-      return std::min(SIGMA_MAX, shadowBlur / 2.0f);
-    }
-
-    nsTArray<ClipState> clipsAndTransforms;
-
-    RefPtr<gfxFontGroup> fontGroup;
-    nsCOMPtr<nsIAtom> fontLanguage;
-    nsFont fontFont;
-
-    EnumeratedArray<Style, Style::MAX, RefPtr<CanvasGradient>> gradientStyles;
-    EnumeratedArray<Style, Style::MAX, RefPtr<CanvasPattern>> patternStyles;
-    EnumeratedArray<Style, Style::MAX, nscolor> colorStyles;
-
-    nsString font;
-    TextAlign textAlign;
-    TextBaseline textBaseline;
-
-    nscolor shadowColor;
-
-    mozilla::gfx::Matrix transform;
-    mozilla::gfx::Point shadowOffset;
-    mozilla::gfx::Float lineWidth;
-    mozilla::gfx::Float miterLimit;
-    mozilla::gfx::Float globalAlpha;
-    mozilla::gfx::Float shadowBlur;
-    nsTArray<mozilla::gfx::Float> dash;
-    mozilla::gfx::Float dashOffset;
-
-    mozilla::gfx::CompositionOp op;
-    mozilla::gfx::FillRule fillRule;
-    mozilla::gfx::CapStyle lineCap;
-    mozilla::gfx::JoinStyle lineJoin;
-
-    nsString filterString;
-    nsTArray<nsStyleFilter> filterChain;
-    RefPtr<nsSVGFilterChainObserver> filterChainObserver;
-    mozilla::gfx::FilterDescription filter;
-    nsTArray<RefPtr<mozilla::gfx::SourceSurface>> filterAdditionalImages;
-
-    // This keeps track of whether the canvas was "tainted" or not when
-    // we last used a filter. This is a security measure, whereby the
-    // canvas is flipped to write-only if a cross-origin image is drawn to it.
-    // This is to stop bad actors from reading back data they shouldn't have
-    // access to.
-    //
-    // This also limits what filters we can apply to the context; in particular
-    // feDisplacementMap is restricted.
-    //
-    // We keep track of this to ensure that if this gets out of sync with the
-    // tainted state of the canvas itself, we update our filters accordingly.
-    bool filterSourceGraphicTainted;
-
-    bool imageSmoothingEnabled;
-    bool fontExplicitLanguage;
-  };
-
-  AutoTArray<ContextState, 3> mStyleStack;
-
-  inline ContextState& CurrentState() {
-    return mStyleStack[mStyleStack.Length() - 1];
-  }
-
-  inline const ContextState& CurrentState() const {
-    return mStyleStack[mStyleStack.Length() - 1];
-  }
-
   friend class CanvasGeneralPattern;
   friend class CanvasFilterChainObserver;
   friend class AdjustedTarget;
   friend class AdjustedTargetForShadow;
   friend class AdjustedTargetForFilter;
 
   // other helpers
   void GetAppUnitsValues(int32_t* aPerDevPixel, int32_t* aPerCSSPixel)
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -68,16 +68,17 @@ EXPORTS.mozilla.dom += [
     'ImageData.h',
     'OffscreenCanvas.h',
     'TextMetrics.h',
     'WebGLVertexArrayObject.h',
 ]
 
 # Canvas 2D and common sources
 UNIFIED_SOURCES += [
+    'BasicRenderingContext2D.cpp',
     'CanvasImageCache.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasRenderingContextHelper.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageBitmap.cpp',
     'ImageBitmapColorUtils.cpp',