Bug 1318573 - (Part 5) Move CanvasFillStrokeStyles to BasicRenderingContext2D. r?mstange draft
authorKevin Chen <kechen@mozilla.com>
Wed, 15 Feb 2017 14:20:38 +0800
changeset 499858 2259232b69001a1b3725ccde275468e8923e8c5a
parent 499857 9b05cefe9033fed528e2aa40b921b2a39233a07d
child 499859 fbf638c8a07d67bf9431efc4d3ff330521d8c68d
push id49564
push userbmo:kechen@mozilla.com
push dateThu, 16 Mar 2017 09:50:15 +0000
reviewersmstange
bugs1318573
milestone54.0a1
Bug 1318573 - (Part 5) Move CanvasFillStrokeStyles to BasicRenderingContext2D. r?mstange MozReview-Commit-ID: F9WhJO1H2kn
dom/canvas/BasicRenderingContext2D.cpp
dom/canvas/BasicRenderingContext2D.h
dom/canvas/CanvasGradient.cpp
dom/canvas/CanvasGradient.h
dom/canvas/CanvasPattern.cpp
dom/canvas/CanvasPattern.h
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
dom/canvas/moz.build
dom/html/HTMLCanvasElement.cpp
--- a/dom/canvas/BasicRenderingContext2D.cpp
+++ b/dom/canvas/BasicRenderingContext2D.cpp
@@ -1,30 +1,103 @@
 /* -*- 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"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/dom/HTMLImageElement.h"
+#include "mozilla/dom/HTMLVideoElement.h"
+#include "mozilla/dom/ImageBitmap.h"
+#include "nsContentUtils.h"
+#include "nsICanvasRenderingContextInternal.h"
+#include "nsPrintfCString.h"
+#include "nsStyleUtil.h"
+
+using mozilla::gfx::SourceSurface;
 
 namespace mozilla {
 namespace dom{
 
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient, Release)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
+
+
 // 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
  **/
 
+void
+BasicRenderingContext2D::GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
+                                         Style aWhichStyle)
+{
+  const ContextState &state = CurrentState();
+  if (state.patternStyles[aWhichStyle]) {
+    aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
+  } else if (state.gradientStyles[aWhichStyle]) {
+    aValue.SetAsCanvasGradient() = state.gradientStyles[aWhichStyle];
+  } else {
+    StyleColorToString(state.colorStyles[aWhichStyle], aValue.SetAsString());
+  }
+}
+
+void
+BasicRenderingContext2D::SetStyleFromString(const nsAString& aStr,
+                                            Style aWhichStyle)
+{
+  MOZ_ASSERT(!aStr.IsVoid());
+
+  nscolor color;
+  if (!ParseColor(aStr, &color)) {
+    return;
+  }
+
+  CurrentState().SetColorStyle(aWhichStyle, color);
+}
+
+// static
+void
+BasicRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
+{
+  // We can't reuse the normal CSS color stringification code,
+  // because the spec calls for a different algorithm for canvas.
+  if (NS_GET_A(aColor) == 255) {
+    CopyUTF8toUTF16(nsPrintfCString("#%02x%02x%02x",
+                                    NS_GET_R(aColor),
+                                    NS_GET_G(aColor),
+                                    NS_GET_B(aColor)),
+                                    aStr);
+  } else {
+    CopyUTF8toUTF16(nsPrintfCString("rgba(%d, %d, %d, ",
+                                    NS_GET_R(aColor),
+                                    NS_GET_G(aColor),
+                                    NS_GET_B(aColor)),
+                                    aStr);
+    aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
+    aStr.Append(')');
+  }
+}
+
 //
 // state
 //
+
 void
 BasicRenderingContext2D::Save()
 {
   EnsureTarget();
   mStyleStack[mStyleStack.Length() - 1].transform = mTarget->GetTransform();
   mStyleStack.SetCapacity(mStyleStack.Length() + 1);
   mStyleStack.AppendElement(CurrentState());
 
@@ -245,18 +318,184 @@ BasicRenderingContext2D::GetGlobalCompos
   else {
     aError.Throw(NS_ERROR_FAILURE);
   }
 
 #undef CANVAS_OP_TO_GFX_OP
 }
 
 //
+// gradients and patterns
+//
+
+already_AddRefed<CanvasGradient>
+BasicRenderingContext2D::CreateLinearGradient(double aX0, double aY0, double aX1, double aY1)
+{
+  RefPtr<CanvasGradient> grad =
+    new CanvasLinearGradient(this, Point(aX0, aY0), Point(aX1, aY1));
+
+  return grad.forget();
+}
+
+already_AddRefed<CanvasGradient>
+BasicRenderingContext2D::CreateRadialGradient(double aX0, double aY0, double aR0,
+                                              double aX1, double aY1, double aR1,
+                                              ErrorResult& aError)
+{
+  if (aR0 < 0.0 || aR1 < 0.0) {
+    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return nullptr;
+  }
+
+  RefPtr<CanvasGradient> grad =
+    new CanvasRadialGradient(this, Point(aX0, aY0), aR0, Point(aX1, aY1), aR1);
+
+  return grad.forget();
+}
+
+already_AddRefed<CanvasPattern>
+BasicRenderingContext2D::CreatePattern(const CanvasImageSource& aSource,
+                                       const nsAString& aRepeat,
+                                       ErrorResult& aError)
+{
+  CanvasPattern::RepeatMode repeatMode =
+    CanvasPattern::RepeatMode::NOREPEAT;
+
+  if (aRepeat.IsEmpty() || aRepeat.EqualsLiteral("repeat")) {
+    repeatMode = CanvasPattern::RepeatMode::REPEAT;
+  } else if (aRepeat.EqualsLiteral("repeat-x")) {
+    repeatMode = CanvasPattern::RepeatMode::REPEATX;
+  } else if (aRepeat.EqualsLiteral("repeat-y")) {
+    repeatMode = CanvasPattern::RepeatMode::REPEATY;
+  } else if (aRepeat.EqualsLiteral("no-repeat")) {
+    repeatMode = CanvasPattern::RepeatMode::NOREPEAT;
+  } else {
+    aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return nullptr;
+  }
+
+  Element* htmlElement;
+  if (aSource.IsHTMLCanvasElement()) {
+    HTMLCanvasElement* canvas = &aSource.GetAsHTMLCanvasElement();
+    htmlElement = canvas;
+
+    nsIntSize size = canvas->GetSize();
+    if (size.width == 0 || size.height == 0) {
+      aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return nullptr;
+    }
+
+    // Special case for Canvas, which could be an Azure canvas!
+    nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
+    if (srcCanvas) {
+      // This might not be an Azure canvas!
+      RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
+      if (!srcSurf) {
+        JSContext* context = nsContentUtils::GetCurrentJSContext();
+        if (context) {
+          JS_ReportWarningASCII(context,
+                                "CanvasRenderingContext2D.createPattern()"
+                                " failed to snapshot source canvas.");
+        }
+        aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+        return nullptr;
+      }
+
+      RefPtr<CanvasPattern> pat =
+        new CanvasPattern(this, srcSurf, repeatMode,
+                          htmlElement->NodePrincipal(),
+                          canvas->IsWriteOnly(), false);
+
+      return pat.forget();
+    }
+  } else if (aSource.IsHTMLImageElement()) {
+    HTMLImageElement* img = &aSource.GetAsHTMLImageElement();
+    if (img->IntrinsicState().HasState(NS_EVENT_STATE_BROKEN)) {
+      aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return nullptr;
+    }
+
+    htmlElement = img;
+  } else if (aSource.IsHTMLVideoElement()) {
+    auto& video = aSource.GetAsHTMLVideoElement();
+    video.MarkAsContentSource(mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_PATTERN);
+    htmlElement = &video;
+  } else {
+    // Special case for ImageBitmap
+    ImageBitmap& imgBitmap = aSource.GetAsImageBitmap();
+    EnsureTarget();
+    RefPtr<SourceSurface> srcSurf = imgBitmap.PrepareForDrawTarget(mTarget);
+    if (!srcSurf) {
+      JSContext* context = nsContentUtils::GetCurrentJSContext();
+      if (context) {
+        JS_ReportWarningASCII(context,
+                              "CanvasRenderingContext2D.createPattern()"
+                              " failed to prepare source ImageBitmap.");
+      }
+      aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return nullptr;
+    }
+
+    // An ImageBitmap never taints others so we set principalForSecurityCheck to
+    // nullptr and set CORSUsed to true for passing the security check in
+    // CanvasUtils::DoDrawImageSecurityCheck().
+    RefPtr<CanvasPattern> pat =
+      new CanvasPattern(this, srcSurf, repeatMode, nullptr, false, true);
+
+    return pat.forget();
+  }
+
+  EnsureTarget();
+
+  // The canvas spec says that createPattern should use the first frame
+  // of animated images
+  nsLayoutUtils::SurfaceFromElementResult res =
+    nsLayoutUtils::SurfaceFromElement(htmlElement,
+      nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget);
+
+  if (!res.GetSourceSurface()) {
+    return nullptr;
+  }
+
+  RefPtr<CanvasPattern> pat = new CanvasPattern(this, res.GetSourceSurface(), repeatMode,
+                                                res.mPrincipal, res.mIsWriteOnly,
+                                                res.mCORSUsed);
+  return pat.forget();
+}
+
+//
+// colors
+//
+
+void
+BasicRenderingContext2D::SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& aValue,
+                                           Style aWhichStyle)
+{
+  if (aValue.IsString()) {
+    SetStyleFromString(aValue.GetAsString(), aWhichStyle);
+    return;
+  }
+
+  if (aValue.IsCanvasGradient()) {
+    SetStyleFromGradient(aValue.GetAsCanvasGradient(), aWhichStyle);
+    return;
+  }
+
+  if (aValue.IsCanvasPattern()) {
+    SetStyleFromPattern(aValue.GetAsCanvasPattern(), aWhichStyle);
+    return;
+  }
+
+  MOZ_ASSERT_UNREACHABLE("Invalid union value");
+}
+
+//
 // path bits
 //
+
 void
 BasicRenderingContext2D::TransformWillUpdate()
 {
   EnsureTarget();
 
   // Store the matrix that would transform the current path to device
   // space.
   if (mPath || mPathBuilder) {
--- a/dom/canvas/BasicRenderingContext2D.h
+++ b/dom/canvas/BasicRenderingContext2D.h
@@ -1,51 +1,61 @@
 /* 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 "gfxTextRun.h"
+#include "mozilla/dom/CanvasGradient.h"
+#include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/gfx/2D.h"
 #include "nsStyleStruct.h"
 #include "nsSVGEffects.h"
 
 using mozilla::gfx::CompositionOp;
 using mozilla::gfx::FilterDescription;
 using mozilla::gfx::Matrix;
 
 namespace mozilla {
 namespace dom {
 
+ #define NS_BASICRENDERINGCONTEXT2D_IID \
+ { 0xbd7f3f74, 0x5ed8, 0x4451, \
+  { 0x89, 0x95, 0x1c, 0x6a, 0x82, 0xeb, 0xef, 0x9f} }
+
 class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
 typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap
   CanvasImageSource;
 
 extern const mozilla::gfx::Float SIGMA_MAX;
 
 /*
  * BasicRenderingContext2D
  */
-class BasicRenderingContext2D
+class BasicRenderingContext2D : public nsISupports
 {
 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;
 
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_BASICRENDERINGCONTEXT2D_IID)
+protected:
+  ~BasicRenderingContext2D(){}
 public:
   explicit BasicRenderingContext2D(layers::LayersBackend aCompositorBackend)
     : mPathTransformWillUpdate(false){};
   //
   // CanvasState
   //
   void Save();
   void Restore();
@@ -98,40 +108,50 @@ public:
       CurrentState().imageSmoothingEnabled = aImageSmoothingEnabled;
     }
   }
 
 
   //
   // CanvasFillStrokeStyles
   //
-  virtual void GetStrokeStyle(
-    OwningStringOrCanvasGradientOrCanvasPattern& aValue) = 0;
-  virtual void SetStrokeStyle(
-    const StringOrCanvasGradientOrCanvasPattern& aValue) = 0;
-  virtual void GetFillStyle(
-    OwningStringOrCanvasGradientOrCanvasPattern& aValue) = 0;
-  virtual void SetFillStyle(
-    const StringOrCanvasGradientOrCanvasPattern& aValue) = 0;
-  virtual already_AddRefed<CanvasGradient> CreateLinearGradient(double aX0,
-                                                                double aY0,
-                                                                double aX1,
-                                                                double aY1) = 0;
-  virtual already_AddRefed<CanvasGradient> CreateRadialGradient(
-    double aX0,
-    double aY0,
-    double aR0,
-    double aX1,
-    double aY1,
-    double aR1,
-    ErrorResult& aError) = 0;
-  virtual already_AddRefed<CanvasPattern> CreatePattern(
-    const CanvasImageSource& aElement,
-    const nsAString& aRepeat,
-    ErrorResult& aError) = 0;
+  void
+  GetStrokeStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue)
+  {
+    GetStyleAsUnion(aValue, Style::STROKE);
+  }
+
+  void
+  SetStrokeStyle(const StringOrCanvasGradientOrCanvasPattern& aValue)
+  {
+    SetStyleFromUnion(aValue, Style::STROKE);
+  }
+
+  void
+  GetFillStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue)
+  {
+    GetStyleAsUnion(aValue, Style::FILL);
+  }
+
+  void
+  SetFillStyle(const StringOrCanvasGradientOrCanvasPattern& aValue)
+  {
+    SetStyleFromUnion(aValue, Style::FILL);
+  }
+
+  already_AddRefed<CanvasGradient> CreateLinearGradient(double aX0, double aY0,
+                                                        double aX1, double aY1);
+  already_AddRefed<CanvasGradient> CreateRadialGradient(double aX0, double aY0,
+                                                        double aR0, double aX1,
+                                                        double aY1, double aR1,
+                                                        ErrorResult& aError);
+  already_AddRefed<CanvasPattern> CreatePattern(const CanvasImageSource& aElement,
+                                                const nsAString& aRepeat,
+                                                ErrorResult& aError);
+
   //
   // CanvasShadowStyles
   //
   virtual double ShadowOffsetX() = 0;
   virtual void SetShadowOffsetX(double aShadowOffsetX) = 0;
   virtual double ShadowOffsetY() = 0;
   virtual void SetShadowOffsetY(double aShadowOffsetY) = 0;
   virtual double ShadowBlur() = 0;
@@ -473,14 +493,40 @@ protected:
 
   /**
    * Needs to be called before updating the transform. This makes a call to
    * EnsureTarget() so you don't have to.
    */
   void TransformWillUpdate();
 
   void SetTransformInternal(const Matrix& aTransform);
+
+  // Some helpers.  Doesn't modify a color on failure.
+  void SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& aValue,
+                         Style aWhichStyle);
+  void SetStyleFromString(const nsAString& aStr, Style aWhichStyle);
+
+  void SetStyleFromGradient(CanvasGradient& aGradient, Style aWhichStyle)
+  {
+    CurrentState().SetGradientStyle(aWhichStyle, &aGradient);
+  }
+
+  void SetStyleFromPattern(CanvasPattern& aPattern, Style aWhichStyle)
+  {
+    CurrentState().SetPatternStyle(aWhichStyle, &aPattern);
+  }
+
+  // Returns whether a color was successfully parsed.
+  virtual bool ParseColor(const nsAString& aString, nscolor* aColor) = 0;
+
+  static void StyleColorToString(const nscolor& aColor, nsAString& aStr);
+
+  void GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
+                       Style aWhichStyle);
 };
 
+ NS_DEFINE_STATIC_IID_ACCESSOR(BasicRenderingContext2D,
+                               NS_BASICRENDERINGCONTEXT2D_IID)
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* BasicRenderingContext2D_h */
new file mode 100644
--- /dev/null
+++ b/dom/canvas/CanvasGradient.cpp
@@ -0,0 +1,69 @@
+/* -*- 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 "CanvasGradient.h"
+
+#include "mozilla/gfx/Types.h"
+#include "mozilla/dom/CanvasRenderingContext2D.h"
+#include "nsCSSParser.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::gfx::Color;
+using mozilla::gfx::GradientStop;
+using mozilla::gfx::GradientStops;
+
+void
+CanvasGradient::AddColorStop(float aOffset, const nsAString& aColorstr, ErrorResult& aRv)
+{
+  if (aOffset < 0.0 || aOffset > 1.0) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  nsCSSValue value;
+  nsCSSParser parser;
+  if (!parser.ParseColorString(aColorstr, nullptr, 0, value)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return;
+  }
+
+  nscolor color;
+  nsCOMPtr<nsIPresShell> presShell =
+    mContext ? static_cast<CanvasRenderingContext2D*>(mContext.get())->GetPresShell() : nullptr;
+  if (!nsRuleNode::ComputeColor(value, presShell ? presShell->GetPresContext() : nullptr,
+                                nullptr, color)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return;
+  }
+
+  mStops = nullptr;
+
+  GradientStop newStop;
+
+  newStop.offset = aOffset;
+  newStop.color = Color::FromABGR(color);
+
+  mRawStops.AppendElement(newStop);
+}
+
+mozilla::gfx::GradientStops *
+CanvasGradient::GetGradientStopsForTarget(mozilla::gfx::DrawTarget *aRT)
+{
+  if (mStops && mStops->GetBackendType() == aRT->GetBackendType()) {
+    return mStops;
+  }
+
+  mStops =
+    gfx::gfxGradientCache::GetOrCreateGradientStops(aRT,
+                                                    mRawStops,
+                                                    gfx::ExtendMode::CLAMP);
+
+  return mStops;
+}
+
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
--- a/dom/canvas/CanvasGradient.h
+++ b/dom/canvas/CanvasGradient.h
@@ -4,24 +4,29 @@
 
 #ifndef mozilla_dom_CanvasGradient_h
 #define mozilla_dom_CanvasGradient_h
 
 #include "mozilla/Attributes.h"
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
-#include "mozilla/dom/CanvasRenderingContext2D.h"
+#include "mozilla/dom/BasicRenderingContext2D.h"
 #include "mozilla/gfx/2D.h"
 #include "nsWrapperCache.h"
 #include "gfxGradientCache.h"
 
+using mozilla::gfx::Float;
+using mozilla::gfx::Point;
+
 namespace mozilla {
 namespace dom {
 
+class BasicRenderingContext2D;
+
 class CanvasGradient : public nsWrapperCache
 {
 public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CanvasGradient)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CanvasGradient)
 
   enum class Type : uint8_t {
     LINEAR = 0,
@@ -29,55 +34,84 @@ public:
   };
 
   Type GetType()
   {
     return mType;
   }
 
   mozilla::gfx::GradientStops *
-  GetGradientStopsForTarget(mozilla::gfx::DrawTarget *aRT)
-  {
-    if (mStops && mStops->GetBackendType() == aRT->GetBackendType()) {
-      return mStops;
-    }
-
-    mStops =
-      gfx::gfxGradientCache::GetOrCreateGradientStops(aRT,
-                                                      mRawStops,
-                                                      gfx::ExtendMode::CLAMP);
-
-    return mStops;
-  }
+  GetGradientStopsForTarget(mozilla::gfx::DrawTarget *aRT);
 
   // WebIDL
   void AddColorStop(float offset, const nsAString& colorstr, ErrorResult& rv);
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return CanvasGradientBinding::Wrap(aCx, this, aGivenProto);
   }
 
-  CanvasRenderingContext2D* GetParentObject()
+  BasicRenderingContext2D* GetParentObject()
   {
     return mContext;
   }
 
 protected:
   friend struct CanvasBidiProcessor;
 
-  CanvasGradient(CanvasRenderingContext2D* aContext, Type aType)
+  CanvasGradient(BasicRenderingContext2D* aContext, Type aType)
     : mContext(aContext)
     , mType(aType)
   {
   }
 
-  RefPtr<CanvasRenderingContext2D> mContext;
+  RefPtr<BasicRenderingContext2D> mContext;
   nsTArray<mozilla::gfx::GradientStop> mRawStops;
   RefPtr<mozilla::gfx::GradientStops> mStops;
   Type mType;
   virtual ~CanvasGradient() {}
 };
 
+class CanvasRadialGradient : public CanvasGradient
+{
+public:
+  CanvasRadialGradient(BasicRenderingContext2D* aContext,
+                       const Point& aBeginOrigin, Float aBeginRadius,
+                       const Point& aEndOrigin, Float aEndRadius)
+    : CanvasGradient(aContext, Type::RADIAL)
+    , mCenter1(aBeginOrigin)
+    , mCenter2(aEndOrigin)
+    , mRadius1(aBeginRadius)
+    , mRadius2(aEndRadius)
+  {
+  }
+
+  Point mCenter1;
+  Point mCenter2;
+  Float mRadius1;
+  Float mRadius2;
+};
+
+class CanvasLinearGradient : public CanvasGradient
+{
+public:
+  CanvasLinearGradient(BasicRenderingContext2D* aContext,
+                       const Point& aBegin, const Point& aEnd)
+    : CanvasGradient(aContext, Type::LINEAR)
+    , mBegin(aBegin)
+    , mEnd(aEnd)
+  {
+  }
+
+protected:
+  friend struct CanvasBidiProcessor;
+  friend class CanvasGeneralPattern;
+
+  // Beginning of linear gradient.
+  Point mBegin;
+  // End of linear gradient.
+  Point mEnd;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CanvasGradient_h
new file mode 100644
--- /dev/null
+++ b/dom/canvas/CanvasPattern.cpp
@@ -0,0 +1,23 @@
+/* -*- 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 "CanvasPattern.h"
+
+#include "gfx2DGlue.h"
+#include "mozilla/dom/SVGMatrix.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::gfx::ToMatrix;
+
+void
+CanvasPattern::SetTransform(SVGMatrix& aMatrix)
+{
+  mTransform = ToMatrix(aMatrix.GetMatrix());
+}
+
+} // namespace dom
+} // namespace mozilla
\ No newline at end of file
--- a/dom/canvas/CanvasPattern.h
+++ b/dom/canvas/CanvasPattern.h
@@ -2,46 +2,47 @@
  * 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 mozilla_dom_CanvasPattern_h
 #define mozilla_dom_CanvasPattern_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
-#include "mozilla/dom/CanvasRenderingContext2D.h"
+#include "mozilla/dom/BasicRenderingContext2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsISupports.h"
 #include "nsWrapperCache.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 
 namespace dom {
+class BasicRenderingContext2D;
 class SVGMatrix;
 
 class CanvasPattern final : public nsWrapperCache
 {
   ~CanvasPattern() {}
 public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CanvasPattern)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CanvasPattern)
 
   enum class RepeatMode : uint8_t {
     REPEAT,
     REPEATX,
     REPEATY,
     NOREPEAT
   };
 
-  CanvasPattern(CanvasRenderingContext2D* aContext,
+  CanvasPattern(BasicRenderingContext2D* aContext,
                 gfx::SourceSurface* aSurface,
                 RepeatMode aRepeat,
                 nsIPrincipal* principalForSecurityCheck,
                 bool forceWriteOnly,
                 bool CORSUsed)
     : mContext(aContext)
     , mSurface(aSurface)
     , mPrincipal(principalForSecurityCheck)
@@ -52,25 +53,25 @@ public:
   {
   }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return CanvasPatternBinding::Wrap(aCx, this, aGivenProto);
   }
 
-  CanvasRenderingContext2D* GetParentObject()
+  BasicRenderingContext2D* GetParentObject()
   {
     return mContext;
   }
 
   // WebIDL
-  void SetTransform(SVGMatrix& matrix);
+  void SetTransform(SVGMatrix& aMatrix);
 
-  RefPtr<CanvasRenderingContext2D> mContext;
+  RefPtr<BasicRenderingContext2D> mContext;
   RefPtr<gfx::SourceSurface> mSurface;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   mozilla::gfx::Matrix mTransform;
   const bool mForceWriteOnly;
   const bool mCORSUsed;
   const RepeatMode mRepeat;
 };
 
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -26,18 +26,16 @@
 #include "nsError.h"
 
 #include "nsCSSParser.h"
 #include "mozilla/css/StyleRule.h"
 #include "mozilla/css/Declaration.h"
 #include "nsComputedDOMStyle.h"
 #include "nsStyleSet.h"
 
-#include "nsPrintfCString.h"
-
 #include "nsReadableUtils.h"
 
 #include "nsColor.h"
 #include "nsGfxCIID.h"
 #include "nsIDocShell.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsDisplayList.h"
@@ -58,17 +56,16 @@
 
 #include "nsFrameLoader.h"
 #include "nsBidi.h"
 #include "nsBidiPresUtils.h"
 #include "Layers.h"
 #include "LayerUserData.h"
 #include "CanvasUtils.h"
 #include "nsIMemoryReporter.h"
-#include "nsStyleUtil.h"
 #include "CanvasImageCache.h"
 
 #include <algorithm>
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Conversions.h"
 #include "js/HeapAPI.h"
@@ -181,57 +178,16 @@ public:
       "(width * height * 4) bytes.");
 
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
 
-class CanvasRadialGradient : public CanvasGradient
-{
-public:
-  CanvasRadialGradient(CanvasRenderingContext2D* aContext,
-                       const Point& aBeginOrigin, Float aBeginRadius,
-                       const Point& aEndOrigin, Float aEndRadius)
-    : CanvasGradient(aContext, Type::RADIAL)
-    , mCenter1(aBeginOrigin)
-    , mCenter2(aEndOrigin)
-    , mRadius1(aBeginRadius)
-    , mRadius2(aEndRadius)
-  {
-  }
-
-  Point mCenter1;
-  Point mCenter2;
-  Float mRadius1;
-  Float mRadius2;
-};
-
-class CanvasLinearGradient : public CanvasGradient
-{
-public:
-  CanvasLinearGradient(CanvasRenderingContext2D* aContext,
-                       const Point& aBegin, const Point& aEnd)
-    : CanvasGradient(aContext, Type::LINEAR)
-    , mBegin(aBegin)
-    , mEnd(aEnd)
-  {
-  }
-
-protected:
-  friend struct CanvasBidiProcessor;
-  friend class CanvasGeneralPattern;
-
-  // Beginning of linear gradient.
-  Point mBegin;
-  // End of linear gradient.
-  Point mEnd;
-};
-
 bool
 CanvasRenderingContext2D::PatternIsOpaque(CanvasRenderingContext2D::Style aStyle) const
 {
   const ContextState& state = CurrentState();
   if (state.globalAlpha < 1.0) {
     return false;
   }
 
@@ -698,65 +654,16 @@ private:
     return gfx::Rect(extents.GetBounds());
   }
 
   RefPtr<DrawTarget> mTarget;
   UniquePtr<AdjustedTargetForShadow> mShadowTarget;
   UniquePtr<AdjustedTargetForFilter> mFilterTarget;
 };
 
-void
-CanvasPattern::SetTransform(SVGMatrix& aMatrix)
-{
-  mTransform = ToMatrix(aMatrix.GetMatrix());
-}
-
-void
-CanvasGradient::AddColorStop(float aOffset, const nsAString& aColorstr, ErrorResult& aRv)
-{
-  if (aOffset < 0.0 || aOffset > 1.0) {
-    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return;
-  }
-
-  nsCSSValue value;
-  nsCSSParser parser;
-  if (!parser.ParseColorString(aColorstr, nullptr, 0, value)) {
-    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-    return;
-  }
-
-  nscolor color;
-  nsCOMPtr<nsIPresShell> presShell = mContext ? mContext->GetPresShell() : nullptr;
-  if (!nsRuleNode::ComputeColor(value, presShell ? presShell->GetPresContext() : nullptr,
-                                nullptr, color)) {
-    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-    return;
-  }
-
-  mStops = nullptr;
-
-  GradientStop newStop;
-
-  newStop.offset = aOffset;
-  newStop.color = Color::FromABGR(color);
-
-  mRawStops.AppendElement(newStop);
-}
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient, Release)
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern, Release)
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)
-
 class CanvasShutdownObserver final : public nsIObserver
 {
 public:
   explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas)
   : mCanvas(aCanvas)
   {}
 
   NS_DECL_ISUPPORTS
@@ -1036,19 +943,20 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_
   return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CanvasRenderingContext2D)
   return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasRenderingContext2D)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICanvasRenderingContextInternal)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(BasicRenderingContext2D)
 NS_INTERFACE_MAP_END
 
 /**
  ** CanvasRenderingContext2D impl
  **/
 
 
 // Initialize our static variables.
@@ -1201,67 +1109,17 @@ void
 CanvasRenderingContext2D::RemoveShutdownObserver()
 {
   if (mShutdownObserver) {
     nsContentUtils::UnregisterShutdownObserver(mShutdownObserver);
     mShutdownObserver = nullptr;
   }
 }
 
-void
-CanvasRenderingContext2D::SetStyleFromString(const nsAString& aStr,
-                                             Style aWhichStyle)
-{
-  MOZ_ASSERT(!aStr.IsVoid());
-
-  nscolor color;
-  if (!ParseColor(aStr, &color)) {
-    return;
-  }
-
-  CurrentState().SetColorStyle(aWhichStyle, color);
-}
-
-void
-CanvasRenderingContext2D::GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
-                                          Style aWhichStyle)
-{
-  const ContextState &state = CurrentState();
-  if (state.patternStyles[aWhichStyle]) {
-    aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
-  } else if (state.gradientStyles[aWhichStyle]) {
-    aValue.SetAsCanvasGradient() = state.gradientStyles[aWhichStyle];
-  } else {
-    StyleColorToString(state.colorStyles[aWhichStyle], aValue.SetAsString());
-  }
-}
-
 // static
-void
-CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& aStr)
-{
-  // We can't reuse the normal CSS color stringification code,
-  // because the spec calls for a different algorithm for canvas.
-  if (NS_GET_A(aColor) == 255) {
-    CopyUTF8toUTF16(nsPrintfCString("#%02x%02x%02x",
-                                    NS_GET_R(aColor),
-                                    NS_GET_G(aColor),
-                                    NS_GET_B(aColor)),
-                    aStr);
-  } else {
-    CopyUTF8toUTF16(nsPrintfCString("rgba(%d, %d, %d, ",
-                                    NS_GET_R(aColor),
-                                    NS_GET_G(aColor),
-                                    NS_GET_B(aColor)),
-                    aStr);
-    aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
-    aStr.Append(')');
-  }
-}
-
 nsresult
 CanvasRenderingContext2D::Redraw()
 {
   mIsCapturedFrameInvalid = true;
 
   if (mIsEntireFrameInvalid) {
     return NS_OK;
   }
@@ -2245,39 +2103,16 @@ CanvasRenderingContext2D::GetMozCurrentT
   }
 
   MatrixToJSObject(aCx, ctm, aResult, aError);
 }
 
 //
 // colors
 //
-
-void
-CanvasRenderingContext2D::SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& aValue,
-                                            Style aWhichStyle)
-{
-  if (aValue.IsString()) {
-    SetStyleFromString(aValue.GetAsString(), aWhichStyle);
-    return;
-  }
-
-  if (aValue.IsCanvasGradient()) {
-    SetStyleFromGradient(aValue.GetAsCanvasGradient(), aWhichStyle);
-    return;
-  }
-
-  if (aValue.IsCanvasPattern()) {
-    SetStyleFromPattern(aValue.GetAsCanvasPattern(), aWhichStyle);
-    return;
-  }
-
-  MOZ_ASSERT_UNREACHABLE("Invalid union value");
-}
-
 void
 CanvasRenderingContext2D::SetFillRule(const nsAString& aString)
 {
   FillRule rule;
 
   if (aString.EqualsLiteral("evenodd"))
     rule = FillRule::FILL_EVEN_ODD;
   else if (aString.EqualsLiteral("nonzero"))
@@ -2293,151 +2128,16 @@ CanvasRenderingContext2D::GetFillRule(ns
 {
   switch (CurrentState().fillRule) {
   case FillRule::FILL_WINDING:
     aString.AssignLiteral("nonzero"); break;
   case FillRule::FILL_EVEN_ODD:
     aString.AssignLiteral("evenodd"); break;
   }
 }
-//
-// gradients and patterns
-//
-already_AddRefed<CanvasGradient>
-CanvasRenderingContext2D::CreateLinearGradient(double aX0, double aY0, double aX1, double aY1)
-{
-  RefPtr<CanvasGradient> grad =
-    new CanvasLinearGradient(this, Point(aX0, aY0), Point(aX1, aY1));
-
-  return grad.forget();
-}
-
-already_AddRefed<CanvasGradient>
-CanvasRenderingContext2D::CreateRadialGradient(double aX0, double aY0, double aR0,
-                                               double aX1, double aY1, double aR1,
-                                               ErrorResult& aError)
-{
-  if (aR0 < 0.0 || aR1 < 0.0) {
-    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return nullptr;
-  }
-
-  RefPtr<CanvasGradient> grad =
-    new CanvasRadialGradient(this, Point(aX0, aY0), aR0, Point(aX1, aY1), aR1);
-
-  return grad.forget();
-}
-
-already_AddRefed<CanvasPattern>
-CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& aSource,
-                                        const nsAString& aRepeat,
-                                        ErrorResult& aError)
-{
-  CanvasPattern::RepeatMode repeatMode =
-    CanvasPattern::RepeatMode::NOREPEAT;
-
-  if (aRepeat.IsEmpty() || aRepeat.EqualsLiteral("repeat")) {
-    repeatMode = CanvasPattern::RepeatMode::REPEAT;
-  } else if (aRepeat.EqualsLiteral("repeat-x")) {
-    repeatMode = CanvasPattern::RepeatMode::REPEATX;
-  } else if (aRepeat.EqualsLiteral("repeat-y")) {
-    repeatMode = CanvasPattern::RepeatMode::REPEATY;
-  } else if (aRepeat.EqualsLiteral("no-repeat")) {
-    repeatMode = CanvasPattern::RepeatMode::NOREPEAT;
-  } else {
-    aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-    return nullptr;
-  }
-
-  Element* htmlElement;
-  if (aSource.IsHTMLCanvasElement()) {
-    HTMLCanvasElement* canvas = &aSource.GetAsHTMLCanvasElement();
-    htmlElement = canvas;
-
-    nsIntSize size = canvas->GetSize();
-    if (size.width == 0 || size.height == 0) {
-      aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-      return nullptr;
-    }
-
-    // Special case for Canvas, which could be an Azure canvas!
-    nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
-    if (srcCanvas) {
-      // This might not be an Azure canvas!
-      RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
-      if (!srcSurf) {
-        JSContext* context = nsContentUtils::GetCurrentJSContext();
-        if (context) {
-          JS_ReportWarningASCII(context,
-                                "CanvasRenderingContext2D.createPattern()"
-                                " failed to snapshot source canvas.");
-        }
-        aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-        return nullptr;
-      }
-
-      RefPtr<CanvasPattern> pat =
-        new CanvasPattern(this, srcSurf, repeatMode, htmlElement->NodePrincipal(), canvas->IsWriteOnly(), false);
-
-      return pat.forget();
-    }
-  } else if (aSource.IsHTMLImageElement()) {
-    HTMLImageElement* img = &aSource.GetAsHTMLImageElement();
-    if (img->IntrinsicState().HasState(NS_EVENT_STATE_BROKEN)) {
-      aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-      return nullptr;
-    }
-
-    htmlElement = img;
-  } else if (aSource.IsHTMLVideoElement()) {
-    auto& video = aSource.GetAsHTMLVideoElement();
-    video.MarkAsContentSource(mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_PATTERN);
-    htmlElement = &video;
-  } else {
-    // Special case for ImageBitmap
-    ImageBitmap& imgBitmap = aSource.GetAsImageBitmap();
-    EnsureTarget();
-    RefPtr<SourceSurface> srcSurf = imgBitmap.PrepareForDrawTarget(mTarget);
-    if (!srcSurf) {
-      JSContext* context = nsContentUtils::GetCurrentJSContext();
-      if (context) {
-        JS_ReportWarningASCII(context,
-                              "CanvasRenderingContext2D.createPattern()"
-                              " failed to prepare source ImageBitmap.");
-      }
-      aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-      return nullptr;
-    }
-
-    // An ImageBitmap never taints others so we set principalForSecurityCheck to
-    // nullptr and set CORSUsed to true for passing the security check in
-    // CanvasUtils::DoDrawImageSecurityCheck().
-    RefPtr<CanvasPattern> pat =
-      new CanvasPattern(this, srcSurf, repeatMode, nullptr, false, true);
-
-    return pat.forget();
-  }
-
-  EnsureTarget();
-
-  // The canvas spec says that createPattern should use the first frame
-  // of animated images
-  nsLayoutUtils::SurfaceFromElementResult res =
-    nsLayoutUtils::SurfaceFromElement(htmlElement,
-      nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget);
-
-  if (!res.GetSourceSurface()) {
-    return nullptr;
-  }
-
-  RefPtr<CanvasPattern> pat = new CanvasPattern(this, res.GetSourceSurface(), repeatMode,
-                                                res.mPrincipal, res.mIsWriteOnly,
-                                                res.mCORSUsed);
-  return pat.forget();
-}
 
 //
 // shadows
 //
 void
 CanvasRenderingContext2D::SetShadowColor(const nsAString& aShadowColor)
 {
   nscolor color;
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -11,17 +11,16 @@
 #include "nsICanvasRenderingContextInternal.h"
 #include "mozilla/RefPtr.h"
 #include "nsColor.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "gfxTextRun.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BasicRenderingContext2D.h"
-#include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/UniquePtr.h"
 #include "gfx2DGlue.h"
 #include "imgIEncoder.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/EnumeratedArray.h"
@@ -50,18 +49,18 @@ class CanvasRenderingContext2DUserData;
 class CanvasDrawObserver;
 class CanvasShutdownObserver;
 
 /**
  ** CanvasRenderingContext2D
  **/
 class CanvasRenderingContext2D final :
   public nsICanvasRenderingContextInternal,
-  public nsWrapperCache,
-  public BasicRenderingContext2D
+  public BasicRenderingContext2D,
+  public nsWrapperCache
 {
   virtual ~CanvasRenderingContext2D();
 
 public:
   explicit CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend);
 
   virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
@@ -70,50 +69,16 @@ public:
     if (!mCanvasElement || mCanvasElement->IsInNativeAnonymousSubtree()) {
       return nullptr;
     }
 
     // corresponds to changes to the old bindings made in bug 745025
     return mCanvasElement->GetOriginalCanvas();
   }
 
-  void
-  GetStrokeStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue) override
-  {
-    GetStyleAsUnion(aValue, Style::STROKE);
-  }
-
-  void
-  SetStrokeStyle(const StringOrCanvasGradientOrCanvasPattern& aValue) override
-  {
-    SetStyleFromUnion(aValue, Style::STROKE);
-  }
-
-  void
-  GetFillStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue) override
-  {
-    GetStyleAsUnion(aValue, Style::FILL);
-  }
-
-  void
-  SetFillStyle(const StringOrCanvasGradientOrCanvasPattern& aValue) override
-  {
-    SetStyleFromUnion(aValue, Style::FILL);
-  }
-
-  already_AddRefed<CanvasGradient>
-    CreateLinearGradient(double aX0, double aY0, double aX1, double aY1) override;
-  already_AddRefed<CanvasGradient>
-    CreateRadialGradient(double aX0, double aY0, double aR0,
-                         double aX1, double aY1, double aR1,
-                         ErrorResult& aError) override;
-  already_AddRefed<CanvasPattern>
-    CreatePattern(const CanvasImageSource& aElement,
-                  const nsAString& aRepeat, ErrorResult& aError) override;
-
   double ShadowOffsetX() override
   {
     return CurrentState().shadowOffset.x;
   }
 
   void SetShadowOffsetX(double aShadowOffsetX) override
   {
     CurrentState().shadowOffset.x = ToFloat(aShadowOffsetX);
@@ -429,17 +394,18 @@ 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)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(CanvasRenderingContext2D,
+                                                                   nsICanvasRenderingContextInternal)
 
   enum class CanvasMultiGetterType : uint8_t {
     STRING = 0,
     PATTERN = 1,
     GRADIENT = 2
   };
 
   nsINode* GetParentObject()
@@ -513,37 +479,21 @@ protected:
     * The number of living nsCanvasRenderingContexts.  When this goes down to
     * 0, we free the premultiply and unpremultiply tables, if they exist.
     */
   static uint32_t sNumLivingContexts;
 
   static mozilla::gfx::DrawTarget* sErrorTarget;
 
   // Some helpers.  Doesn't modify a color on failure.
-  void SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& aValue,
-                         Style aWhichStyle);
-  void SetStyleFromString(const nsAString& aStr, Style aWhichStyle);
-
-  void SetStyleFromGradient(CanvasGradient& aGradient, Style aWhichStyle)
-  {
-    CurrentState().SetGradientStyle(aWhichStyle, &aGradient);
-  }
-
-  void SetStyleFromPattern(CanvasPattern& aPattern, Style aWhichStyle)
-  {
-    CurrentState().SetPatternStyle(aWhichStyle, &aPattern);
-  }
-
   void GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
                        Style aWhichStyle);
 
   // Returns whether a color was successfully parsed.
-  bool ParseColor(const nsAString& aString, nscolor* aColor);
-
-  static void StyleColorToString(const nscolor& aColor, nsAString& aStr);
+  virtual bool ParseColor(const nsAString& aString, nscolor* aColor) override;
 
    // Returns whether a filter was successfully parsed.
   bool ParseFilter(const nsAString& aString,
                    nsTArray<nsStyleFilter>& aFilterChain,
                    ErrorResult& aError);
 
   // Returns whether the font was successfully updated.
   bool SetFontInternal(const nsAString& aFont, mozilla::ErrorResult& aError);
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -69,17 +69,19 @@ EXPORTS.mozilla.dom += [
     'OffscreenCanvas.h',
     'TextMetrics.h',
     'WebGLVertexArrayObject.h',
 ]
 
 # Canvas 2D and common sources
 UNIFIED_SOURCES += [
     'BasicRenderingContext2D.cpp',
+    'CanvasGradient.cpp',
     'CanvasImageCache.cpp',
+    'CanvasPattern.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasRenderingContextHelper.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageBitmap.cpp',
     'ImageBitmapColorUtils.cpp',
     'ImageBitmapRenderingContext.cpp',
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -565,17 +565,18 @@ HTMLCanvasElement::CopyInnerTo(Element* 
   NS_ENSURE_SUCCESS(rv, rv);
   if (aDest->OwnerDoc()->IsStaticDocument()) {
     HTMLCanvasElement* dest = static_cast<HTMLCanvasElement*>(aDest);
     dest->mOriginalCanvas = this;
 
     nsCOMPtr<nsISupports> cxt;
     dest->GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(cxt));
     RefPtr<CanvasRenderingContext2D> context2d =
-      static_cast<CanvasRenderingContext2D*>(cxt.get());
+      static_cast<CanvasRenderingContext2D*>(
+        static_cast<nsICanvasRenderingContextInternal*>(cxt.get()));
     if (context2d && !mPrintCallback) {
       CanvasImageSource source;
       source.SetAsHTMLCanvasElement() = this;
       ErrorResult err;
       context2d->DrawImage(source,
                            0.0, 0.0, err);
       rv = err.StealNSResult();
     }