Bug 1145195 part 1 - Create a helper function for PrependLocalTransformsTo in SVGContentUtils r=dholbert
authorRobert Longson <longsonr@gmail.com>
Wed, 02 Dec 2015 22:36:23 +0000
changeset 275277 dee213acd662a8e9f3535d4bbefd34102d9efc1e
parent 275276 177b0d0529e9fe4c206bd9f9e1d857e765ba8377
child 275278 689d9f449560c540ba39d9c7c346c6666c3fb480
push id68804
push userlongsonr@gmail.com
push dateWed, 02 Dec 2015 22:36:32 +0000
treeherdermozilla-inbound@dee213acd662 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1145195
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1145195 part 1 - Create a helper function for PrependLocalTransformsTo in SVGContentUtils r=dholbert
dom/svg/SVGContentUtils.cpp
dom/svg/SVGContentUtils.h
dom/svg/SVGForeignObjectElement.cpp
dom/svg/SVGForeignObjectElement.h
dom/svg/SVGFragmentIdentifier.cpp
dom/svg/SVGSVGElement.cpp
dom/svg/SVGSVGElement.h
dom/svg/SVGTransformableElement.cpp
dom/svg/SVGTransformableElement.h
dom/svg/SVGUseElement.cpp
dom/svg/SVGUseElement.h
dom/svg/moz.build
dom/svg/nsSVGElement.cpp
dom/svg/nsSVGElement.h
layout/svg/nsSVGClipPathFrame.cpp
layout/svg/nsSVGContainerFrame.cpp
layout/svg/nsSVGForeignObjectFrame.cpp
layout/svg/nsSVGOuterSVGFrame.cpp
layout/svg/nsSVGPathGeometryFrame.cpp
layout/svg/nsSVGPatternFrame.cpp
layout/svg/nsSVGSwitchFrame.cpp
layout/svg/nsSVGUtils.cpp
--- a/dom/svg/SVGContentUtils.cpp
+++ b/dom/svg/SVGContentUtils.cpp
@@ -390,17 +390,17 @@ SVGContentUtils::GetNearestViewportEleme
   }
   return nullptr;
 }
 
 static gfx::Matrix
 GetCTMInternal(nsSVGElement *aElement, bool aScreenCTM, bool aHaveRecursed)
 {
   gfxMatrix matrix = aElement->PrependLocalTransformsTo(gfxMatrix(),
-    aHaveRecursed ? nsSVGElement::eAllTransforms : nsSVGElement::eUserSpaceToParent);
+    aHaveRecursed ? eAllTransforms : eUserSpaceToParent);
   nsSVGElement *element = aElement;
   nsIContent *ancestor = aElement->GetFlattenedTreeParent();
 
   while (ancestor && ancestor->IsSVGElement() &&
                      !ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
     element = static_cast<nsSVGElement*>(ancestor);
     matrix *= element->PrependLocalTransformsTo(gfxMatrix()); // i.e. *A*ppend
     if (!aScreenCTM && SVGContentUtils::EstablishesViewport(element)) {
@@ -853,8 +853,43 @@ SVGContentUtils::GetPath(const nsAString
   return pathData.BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 1);
 }
 
 bool
 SVGContentUtils::ShapeTypeHasNoCorners(const nsIContent* aContent) {
   return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::circle,
                                                   nsGkAtoms::ellipse);
 }
+
+gfxMatrix
+SVGContentUtils::PrependLocalTransformsTo(
+  const gfxMatrix &aMatrix,
+  SVGTransformTypes aWhich,
+  const gfx::Matrix* aAnimateMotionTransform,
+  const nsSVGAnimatedTransformList* aTransforms)
+{
+  gfxMatrix result(aMatrix);
+
+  if (aWhich == eChildToUserSpace) {
+    // We don't have anything to prepend.
+    // eChildToUserSpace is not the common case, which is why we return
+    // 'result' to benefit from NRVO rather than returning aMatrix before
+    // creating 'result'.
+    return result;
+  }
+
+  MOZ_ASSERT(aWhich == eAllTransforms || aWhich == eUserSpaceToParent,
+             "Unknown TransformTypes");
+
+  // animateMotion's resulting transform is supposed to apply *on top of*
+  // any transformations from the |transform| attribute. So since we're
+  // PRE-multiplying, we need to apply the animateMotion transform *first*.
+  if (aAnimateMotionTransform) {
+    result.PreMultiply(ThebesMatrix(*aAnimateMotionTransform));
+  }
+
+  if (aTransforms) {
+    result.PreMultiply(
+      aTransforms->GetAnimValue().GetConsolidationMatrix());
+  }
+
+  return result;
+}
--- a/dom/svg/SVGContentUtils.h
+++ b/dom/svg/SVGContentUtils.h
@@ -22,30 +22,60 @@ class gfxTextContextPaint;
 class nsIContent;
 class nsIDocument;
 class nsIFrame;
 class nsStyleContext;
 class nsStyleCoord;
 class nsSVGElement;
 
 namespace mozilla {
+class nsSVGAnimatedTransformList;
 class SVGAnimatedPreserveAspectRatio;
 class SVGPreserveAspectRatio;
 namespace dom {
 class Element;
 class SVGSVGElement;
 } // namespace dom
 
 namespace gfx {
 class Matrix;
 } // namespace gfx
 } // namespace mozilla
 
 #define SVG_ZERO_LENGTH_PATH_FIX_FACTOR 512
 
+/**
+ * SVGTransformTypes controls the transforms that PrependLocalTransformsTo
+ * applies.
+ *
+ * If aWhich is eAllTransforms, then all the transforms from the coordinate
+ * space established by this element for its children to the coordinate
+ * space established by this element's parent element for this element, are
+ * included.
+ *
+ * If aWhich is eUserSpaceToParent, then only the transforms from this
+ * element's userspace to the coordinate space established by its parent is
+ * included. This includes any transforms introduced by the 'transform'
+ * attribute, transform animations and animateMotion, but not any offsets
+ * due to e.g. 'x'/'y' attributes, or any transform due to a 'viewBox'
+ * attribute. (SVG userspace is defined to be the coordinate space in which
+ * coordinates on an element apply.)
+ *
+ * If aWhich is eChildToUserSpace, then only the transforms from the
+ * coordinate space established by this element for its childre to this
+ * elements userspace are included. This includes any offsets due to e.g.
+ * 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but
+ * does not include any transforms due to the 'transform' attribute.
+ */
+enum SVGTransformTypes {
+   eAllTransforms,
+   eUserSpaceToParent,
+   eChildToUserSpace
+};
+
 inline bool
 IsSVGWhitespace(char aChar)
 {
   return aChar == '\x20' || aChar == '\x9' ||
          aChar == '\xD'  || aChar == '\xA';
 }
 
 inline bool
@@ -344,11 +374,22 @@ public:
   static already_AddRefed<mozilla::gfx::Path>
   GetPath(const nsAString& aPathString);
 
   /**
    *  Returns true if aContent is one of the elements whose stroke is guaranteed
    *  to have no corners: circle or ellipse
    */
   static bool ShapeTypeHasNoCorners(const nsIContent* aContent);
+
+  /**
+   *  Prepends an element's local transforms to the transform matrix.
+   *  This is a helper for nsSVGElement::PrependLocalTransformsTo.
+   *  Any callers probably really want to call that method instead of this one.
+   */
+  static gfxMatrix PrependLocalTransformsTo(
+    const gfxMatrix &aMatrix,
+    SVGTransformTypes aWhich,
+    const Matrix* aAnimateMotionTransform,
+    const mozilla::nsSVGAnimatedTransformList* aTransforms);
 };
 
 #endif
--- a/dom/svg/SVGForeignObjectElement.cpp
+++ b/dom/svg/SVGForeignObjectElement.cpp
@@ -68,18 +68,18 @@ SVGForeignObjectElement::Height()
 {
   return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 /* virtual */ gfxMatrix
-SVGForeignObjectElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                                                  TransformTypes aWhich) const
+SVGForeignObjectElement::PrependLocalTransformsTo(
+  const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const
 {
   // 'transform' attribute:
   gfxMatrix fromUserSpace =
     SVGGraphicsElement::PrependLocalTransformsTo(aMatrix, aWhich);
   if (aWhich == eUserSpaceToParent) {
     return fromUserSpace;
   }
   // our 'x' and 'y' attributes:
--- a/dom/svg/SVGForeignObjectElement.h
+++ b/dom/svg/SVGForeignObjectElement.h
@@ -25,18 +25,19 @@ class SVGForeignObjectElement final : pu
 protected:
   friend nsresult (::NS_NewSVGForeignObjectElement(nsIContent **aResult,
                                                    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   explicit SVGForeignObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
   // nsSVGElement specializations:
-  virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                      TransformTypes aWhich = eAllTransforms) const override;
+  virtual gfxMatrix PrependLocalTransformsTo(
+    const gfxMatrix &aMatrix,
+    SVGTransformTypes aWhich = eAllTransforms) const override;
   virtual bool HasValidDimensions() const override;
 
   // nsIContent interface
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) override;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override;
 
--- a/dom/svg/SVGFragmentIdentifier.cpp
+++ b/dom/svg/SVGFragmentIdentifier.cpp
@@ -7,17 +7,19 @@
 #include "SVGFragmentIdentifier.h"
 
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/SVGViewElement.h"
 #include "nsContentUtils.h" // for nsCharSeparatedTokenizerTemplate
 #include "nsSVGAnimatedTransformList.h"
 #include "nsCharSeparatedTokenizer.h"
 
-using namespace mozilla;
+namespace mozilla {
+
+using namespace dom;
 
 static bool
 IsMatchingParameter(const nsAString& aString, const nsAString& aParameterName)
 {
   // The first two tests ensure aString.Length() > aParameterName.Length()
   // so it's then safe to do the third test
   return StringBeginsWith(aString, aParameterName) &&
          aString.Last() == ')' &&
@@ -25,112 +27,112 @@ IsMatchingParameter(const nsAString& aSt
 }
 
 inline bool
 IgnoreWhitespace(char16_t aChar)
 {
   return false;
 }
 
-static dom::SVGViewElement*
+static SVGViewElement*
 GetViewElement(nsIDocument* aDocument, const nsAString& aId)
 {
-  dom::Element* element = aDocument->GetElementById(aId);
+  Element* element = aDocument->GetElementById(aId);
   return (element && element->IsSVGElement(nsGkAtoms::view)) ?
-            static_cast<dom::SVGViewElement*>(element) : nullptr;
+            static_cast<SVGViewElement*>(element) : nullptr;
 }
 
 void
-SVGFragmentIdentifier::SaveOldPreserveAspectRatio(dom::SVGSVGElement* root)
+SVGFragmentIdentifier::SaveOldPreserveAspectRatio(SVGSVGElement* root)
 {
   if (root->mPreserveAspectRatio.IsExplicitlySet()) {
     root->SetPreserveAspectRatioProperty(root->mPreserveAspectRatio.GetBaseValue());
   }
 }
 
 void 
-SVGFragmentIdentifier::RestoreOldPreserveAspectRatio(dom::SVGSVGElement* root)
+SVGFragmentIdentifier::RestoreOldPreserveAspectRatio(SVGSVGElement* root)
 {
   const SVGPreserveAspectRatio* oldPARPtr = root->GetPreserveAspectRatioProperty();
   if (oldPARPtr) {
     root->mPreserveAspectRatio.SetBaseValue(*oldPARPtr, root);
   } else if (root->mPreserveAspectRatio.IsExplicitlySet()) {
-    mozilla::ErrorResult error;
+    ErrorResult error;
     root->RemoveAttribute(NS_LITERAL_STRING("preserveAspectRatio"), error);
   }
 }
 
 void 
-SVGFragmentIdentifier::SaveOldViewBox(dom::SVGSVGElement* root)
+SVGFragmentIdentifier::SaveOldViewBox(SVGSVGElement* root)
 {
   if (root->mViewBox.IsExplicitlySet()) {
     root->SetViewBoxProperty(root->mViewBox.GetBaseValue());
   }
 }
 
 void 
-SVGFragmentIdentifier::RestoreOldViewBox(dom::SVGSVGElement* root)
+SVGFragmentIdentifier::RestoreOldViewBox(SVGSVGElement* root)
 {
   const nsSVGViewBoxRect* oldViewBoxPtr = root->GetViewBoxProperty();
   if (oldViewBoxPtr) {
     root->mViewBox.SetBaseValue(*oldViewBoxPtr, root);
   } else if (root->mViewBox.IsExplicitlySet()) {
-    mozilla::ErrorResult error;
+    ErrorResult error;
     root->RemoveAttribute(NS_LITERAL_STRING("viewBox"), error);
   }
 }
 
 void 
-SVGFragmentIdentifier::SaveOldZoomAndPan(dom::SVGSVGElement* root)
+SVGFragmentIdentifier::SaveOldZoomAndPan(SVGSVGElement* root)
 {
-  if (root->mEnumAttributes[dom::SVGSVGElement::ZOOMANDPAN].IsExplicitlySet()) {
-    root->SetZoomAndPanProperty(root->mEnumAttributes[dom::SVGSVGElement::ZOOMANDPAN].GetBaseValue());
+  if (root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].IsExplicitlySet()) {
+    root->SetZoomAndPanProperty(root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].GetBaseValue());
   }
 }
 
 void
-SVGFragmentIdentifier::RestoreOldZoomAndPan(dom::SVGSVGElement* root)
+SVGFragmentIdentifier::RestoreOldZoomAndPan(SVGSVGElement* root)
 {
   uint16_t oldZoomAndPan = root->GetZoomAndPanProperty();
   if (oldZoomAndPan != SVG_ZOOMANDPAN_UNKNOWN) {
-    root->mEnumAttributes[dom::SVGSVGElement::ZOOMANDPAN].SetBaseValue(oldZoomAndPan, root);
-  } else if (root->mEnumAttributes[dom::SVGSVGElement::ZOOMANDPAN].IsExplicitlySet()) {
-    mozilla::ErrorResult error;
+    root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].SetBaseValue(oldZoomAndPan, root);
+  } else if (root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].IsExplicitlySet()) {
+    ErrorResult error;
     root->RemoveAttribute(NS_LITERAL_STRING("zoomAndPan"), error);
   }
 }
 
 void 
-SVGFragmentIdentifier::SaveOldTransform(dom::SVGSVGElement* root)
+SVGFragmentIdentifier::SaveOldTransform(SVGSVGElement* root)
 {
   nsSVGAnimatedTransformList* transformList = root->GetAnimatedTransformList();
 
   if (transformList && transformList->IsExplicitlySet()) {
     root->SetTransformProperty(transformList->GetBaseValue());
   }
 }
 
 void 
-SVGFragmentIdentifier::RestoreOldTransform(dom::SVGSVGElement* root)
+SVGFragmentIdentifier::RestoreOldTransform(SVGSVGElement* root)
 {
   const SVGTransformList* oldTransformPtr = root->GetTransformProperty();
   if (oldTransformPtr) {
     root->GetAnimatedTransformList(nsSVGElement::DO_ALLOCATE)->SetBaseValue(*oldTransformPtr);
   } else {
     nsSVGAnimatedTransformList* transformList = root->GetAnimatedTransformList();
     if (transformList && transformList->IsExplicitlySet()) {
-      mozilla::ErrorResult error;
+      ErrorResult error;
       root->RemoveAttribute(NS_LITERAL_STRING("transform"), error);
     }
   }
 }
 
 bool
 SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString& aViewSpec,
-                                          dom::SVGSVGElement* root)
+                                          SVGSVGElement* root)
 {
   if (!IsMatchingParameter(aViewSpec, NS_LITERAL_STRING("svgView"))) {
     return false;
   }
 
   // SVGViewAttributes may occur in any order, but each type may only occur
   // at most one time in a correctly formed SVGViewSpec.
   // If we encounter any attribute more than once or get any syntax errors
@@ -187,21 +189,21 @@ SVGFragmentIdentifier::ProcessSVGViewSpe
     } else if (IsMatchingParameter(token, NS_LITERAL_STRING("zoomAndPan"))) {
       if (zoomAndPanFound) {
         return false;
       }
       nsIAtom* valAtom = NS_GetStaticAtom(params);
       if (!valAtom) {
         return false;
       }
-      const nsSVGEnumMapping* mapping = dom::SVGSVGElement::sZoomAndPanMap;
+      const nsSVGEnumMapping* mapping = SVGSVGElement::sZoomAndPanMap;
       while (mapping->mKey) {
         if (valAtom == *(mapping->mKey)) {
           // If we've got a valid zoomAndPan value, then set it on our root element.
-          if (NS_FAILED(root->mEnumAttributes[dom::SVGSVGElement::ZOOMANDPAN].SetBaseValue(
+          if (NS_FAILED(root->mEnumAttributes[SVGSVGElement::ZOOMANDPAN].SetBaseValue(
                           mapping->mVal, root))) {
             return false;
           }
           break;
         }
         mapping++;
       }
       if (!mapping->mKey) {
@@ -237,26 +239,26 @@ SVGFragmentIdentifier::ProcessSVGViewSpe
 
 bool
 SVGFragmentIdentifier::ProcessFragmentIdentifier(nsIDocument* aDocument,
                                                  const nsAString& aAnchorName)
 {
   MOZ_ASSERT(aDocument->GetRootElement()->IsSVGElement(nsGkAtoms::svg),
              "expecting an SVG root element");
 
-  dom::SVGSVGElement* rootElement =
-    static_cast<dom::SVGSVGElement*>(aDocument->GetRootElement());
+  SVGSVGElement* rootElement =
+    static_cast<SVGSVGElement*>(aDocument->GetRootElement());
 
   if (!rootElement->mUseCurrentView) {
     SaveOldViewBox(rootElement);
     SaveOldPreserveAspectRatio(rootElement);
     SaveOldZoomAndPan(rootElement);
   }
 
-  const dom::SVGViewElement* viewElement = GetViewElement(aDocument, aAnchorName);
+  const SVGViewElement* viewElement = GetViewElement(aDocument, aAnchorName);
 
   if (viewElement) {
     if (!rootElement->mCurrentViewID) {
       rootElement->mCurrentViewID = new nsString();
     }
     *rootElement->mCurrentViewID = aAnchorName;
     rootElement->mUseCurrentView = true;
     rootElement->InvalidateTransformNotifyFrame();
@@ -280,8 +282,10 @@ SVGFragmentIdentifier::ProcessFragmentId
   rootElement->ClearZoomAndPanProperty();
   RestoreOldTransform(rootElement);
   rootElement->ClearTransformProperty();
   if (wasOverridden) {
     rootElement->InvalidateTransformNotifyFrame();
   }
   return false;
 }
+
+} // namespace mozilla
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -938,18 +938,18 @@ SVGSVGElement::GetLength(uint8_t aCtxTyp
   }
   return 0;
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 /* virtual */ gfxMatrix
-SVGSVGElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                                        TransformTypes aWhich) const
+SVGSVGElement::PrependLocalTransformsTo(
+  const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const
 {
   // 'transform' attribute:
   gfxMatrix fromUserSpace =
     SVGSVGElementBase::PrependLocalTransformsTo(aMatrix, aWhich);
   if (aWhich == eUserSpaceToParent) {
     return fromUserSpace;
   }
 
--- a/dom/svg/SVGSVGElement.h
+++ b/dom/svg/SVGSVGElement.h
@@ -133,18 +133,19 @@ public:
 
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override;
   virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override;
 
   virtual bool IsEventAttributeName(nsIAtom* aName) override;
 
   // nsSVGElement specializations:
-  virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                      TransformTypes aWhich = eAllTransforms) const override;
+  virtual gfxMatrix PrependLocalTransformsTo(
+    const gfxMatrix &aMatrix,
+    SVGTransformTypes aWhich = eAllTransforms) const override;
   virtual bool HasValidDimensions() const override;
 
   // SVGSVGElement methods:
   float GetLength(uint8_t mCtxType);
 
   // public helpers:
 
   /**
--- a/dom/svg/SVGTransformableElement.cpp
+++ b/dom/svg/SVGTransformableElement.cpp
@@ -85,44 +85,22 @@ SVGTransformableElement::IsEventAttribut
 {
   return nsContentUtils::IsEventAttributeName(aName, EventNameType_SVGGraphic);
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement overrides
 
 gfxMatrix
-SVGTransformableElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                                                  TransformTypes aWhich) const
+SVGTransformableElement::PrependLocalTransformsTo(
+  const gfxMatrix &aMatrix,
+  SVGTransformTypes aWhich) const
 {
-  gfxMatrix result(aMatrix);
-
-  if (aWhich == eChildToUserSpace) {
-    // We don't have anything to prepend.
-    // eChildToUserSpace is not the common case, which is why we return
-    // 'result' to benefit from NRVO rather than returning aMatrix before
-    // creating 'result'.
-    return result;
-  }
-
-  MOZ_ASSERT(aWhich == eAllTransforms || aWhich == eUserSpaceToParent,
-             "Unknown TransformTypes");
-
-  // animateMotion's resulting transform is supposed to apply *on top of*
-  // any transformations from the |transform| attribute. So since we're
-  // PRE-multiplying, we need to apply the animateMotion transform *first*.
-  if (mAnimateMotionTransform) {
-    result.PreMultiply(ThebesMatrix(*mAnimateMotionTransform));
-  }
-
-  if (mTransforms) {
-    result.PreMultiply(mTransforms->GetAnimValue().GetConsolidationMatrix());
-  }
-
-  return result;
+  return SVGContentUtils::PrependLocalTransformsTo(
+    aMatrix, aWhich, mAnimateMotionTransform, mTransforms);
 }
 
 const gfx::Matrix*
 SVGTransformableElement::GetAnimateMotionTransform() const
 {
   return mAnimateMotionTransform.get();
 }
 
--- a/dom/svg/SVGTransformableElement.h
+++ b/dom/svg/SVGTransformableElement.h
@@ -48,18 +48,19 @@ public:
   nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
                                       int32_t aModType) const override;
 
 
   // nsSVGElement overrides
   virtual bool IsEventAttributeName(nsIAtom* aName) override;
 
 
-  virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                      TransformTypes aWhich = eAllTransforms) const override;
+  virtual gfxMatrix PrependLocalTransformsTo(
+    const gfxMatrix &aMatrix,
+    SVGTransformTypes aWhich = eAllTransforms) const override;
   virtual const gfx::Matrix* GetAnimateMotionTransform() const override;
   virtual void SetAnimateMotionTransform(const gfx::Matrix* aMatrix) override;
 
   virtual nsSVGAnimatedTransformList*
     GetAnimatedTransformList(uint32_t aFlags = 0) override;
   virtual nsIAtom* GetTransformListAttrName() const override {
     return nsGkAtoms::transform;
   }
--- a/dom/svg/SVGUseElement.cpp
+++ b/dom/svg/SVGUseElement.cpp
@@ -426,18 +426,18 @@ SVGUseElement::UnlinkSource()
   }
   mSource.Unlink();
 }
 
 //----------------------------------------------------------------------
 // nsSVGElement methods
 
 /* virtual */ gfxMatrix
-SVGUseElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                                        TransformTypes aWhich) const
+SVGUseElement::PrependLocalTransformsTo(
+  const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const
 {
   // 'transform' attribute:
   gfxMatrix fromUserSpace =
     SVGUseElementBase::PrependLocalTransformsTo(aMatrix, aWhich);
   if (aWhich == eUserSpaceToParent) {
     return fromUserSpace;
   }
   // our 'x' and 'y' attributes:
--- a/dom/svg/SVGUseElement.h
+++ b/dom/svg/SVGUseElement.h
@@ -55,18 +55,19 @@ public:
   NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
 
   // for nsSVGUseFrame's nsIAnonymousContentCreator implementation.
   nsIContent* CreateAnonymousContent();
   nsIContent* GetAnonymousContent() const { return mClone; }
   void DestroyAnonymousContent();
 
   // nsSVGElement specializations:
-  virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                      TransformTypes aWhich = eAllTransforms) const override;
+  virtual gfxMatrix PrependLocalTransformsTo(
+    const gfxMatrix &aMatrix,
+    SVGTransformTypes aWhich = eAllTransforms) const override;
   virtual bool HasValidDimensions() const override;
 
   // nsIContent interface
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedString> Href();
--- a/dom/svg/moz.build
+++ b/dom/svg/moz.build
@@ -6,16 +6,17 @@
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 
 EXPORTS += [
     'nsSVGClass.h',
     'nsSVGElement.h',
     'nsSVGFeatures.h',
     'SVGAttrValueWrapper.h',
+    'SVGContentUtils.h',
     'SVGPreserveAspectRatio.h',
     'SVGStringList.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'nsSVGAnimatedTransformList.h',
     'SVGAElement.h',
     'SVGAltGlyphElement.h',
--- a/dom/svg/nsSVGElement.cpp
+++ b/dom/svg/nsSVGElement.cpp
@@ -1549,18 +1549,18 @@ nsSVGElement::GetCtx() const
     ancestor = ancestor->GetFlattenedTreeParent();
   }
 
   // we don't have an ancestor <svg> element...
   return nullptr;
 }
 
 /* virtual */ gfxMatrix
-nsSVGElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                                       TransformTypes aWhich) const
+nsSVGElement::PrependLocalTransformsTo(
+  const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const
 {
   return aMatrix;
 }
 
 nsSVGElement::LengthAttributesInfo
 nsSVGElement::GetLengthInfo()
 {
   return LengthAttributesInfo(nullptr, nullptr, 0);
--- a/dom/svg/nsSVGElement.h
+++ b/dom/svg/nsSVGElement.h
@@ -20,16 +20,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsError.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/ElementInlines.h"
 #include "nsISupportsImpl.h"
 #include "nsStyledElement.h"
 #include "nsSVGClass.h"
 #include "nsIDOMSVGElement.h"
+#include "SVGContentUtils.h"
 
 class nsSVGAngle;
 class nsSVGBoolean;
 class nsSVGEnum;
 class nsSVGInteger;
 class nsSVGIntegerPair;
 class nsSVGLength2;
 class nsSVGNumber2;
@@ -134,21 +135,16 @@ public:
   NS_FORWARD_NSIDOMELEMENT_TO_GENERIC
   NS_DECL_NSIDOMSVGELEMENT
 
   // Gets the element that establishes the rectangular viewport against which
   // we should resolve percentage lengths (our "coordinate context"). Returns
   // nullptr for outer <svg> or SVG without an <svg> parent (invalid SVG).
   mozilla::dom::SVGSVGElement* GetCtx() const;
 
-  enum TransformTypes {
-     eAllTransforms
-    ,eUserSpaceToParent
-    ,eChildToUserSpace
-  };
   /**
    * Returns aMatrix pre-multiplied by (explicit or implicit) transforms that
    * are introduced by attributes on this element.
    *
    * If aWhich is eAllTransforms, then all the transforms from the coordinate
    * space established by this element for its children to the coordinate
    * space established by this element's parent element for this element, are
    * included.
@@ -162,18 +158,18 @@ public:
    * coordinates on an element apply.)
    *
    * If aWhich is eChildToUserSpace, then only the transforms from the
    * coordinate space established by this element for its childre to this
    * elements userspace are included. This includes any offsets due to e.g.
    * 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but
    * does not include any transforms due to the 'transform' attribute.
    */
-  virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix,
-                      TransformTypes aWhich = eAllTransforms) const;
+  virtual gfxMatrix PrependLocalTransformsTo(
+    const gfxMatrix &aMatrix, SVGTransformTypes aWhich = eAllTransforms) const;
 
   // Setter for to set the current <animateMotion> transformation
   // Only visible for nsSVGGraphicElement, so it's a no-op here, and that
   // subclass has the useful implementation.
   virtual void SetAnimateMotionTransform(const mozilla::gfx::Matrix* aMatrix) {/*no-op*/}
   virtual const mozilla::gfx::Matrix* GetAnimateMotionTransform() const { return nullptr; }
 
   bool IsStringAnimatable(uint8_t aAttrEnum) {
--- a/layout/svg/nsSVGClipPathFrame.cpp
+++ b/layout/svg/nsSVGClipPathFrame.cpp
@@ -54,18 +54,17 @@ nsSVGClipPathFrame::ApplyClipOrPaintClip
     gfxContextMatrixAutoSaveRestore autoRestore(&aContext);
     RefPtr<Path> clipPath;
     if (singleClipPathChild) {
       nsSVGPathGeometryFrame* pathFrame = do_QueryFrame(singleClipPathChild);
       if (pathFrame) {
         nsSVGPathGeometryElement* pathElement =
           static_cast<nsSVGPathGeometryElement*>(pathFrame->GetContent());
         gfxMatrix toChildsUserSpace = pathElement->
-          PrependLocalTransformsTo(mMatrixForChildren,
-                                   nsSVGElement::eUserSpaceToParent);
+          PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent);
         gfxMatrix newMatrix =
           aContext.CurrentMatrix().PreMultiply(toChildsUserSpace).NudgeToIntegers();
         if (!newMatrix.IsSingular()) {
           aContext.SetMatrix(newMatrix);
           clipPath = pathElement->GetOrBuildPath(aDrawTarget,
                                                  nsSVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule));
         }
       }
@@ -132,18 +131,17 @@ nsSVGClipPathFrame::ApplyClipOrPaintClip
       }
 
       gfxMatrix toChildsUserSpace = mMatrixForChildren;
       nsIFrame* child = do_QueryFrame(SVGFrame);
       nsIContent* childContent = child->GetContent();
       if (childContent->IsSVGElement()) {
         toChildsUserSpace =
           static_cast<const nsSVGElement*>(childContent)->
-            PrependLocalTransformsTo(mMatrixForChildren,
-                                     nsSVGElement::eUserSpaceToParent);
+            PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent);
       }
       SVGFrame->PaintSVG(aContext, toChildsUserSpace);
 
       if (clipPathFrame) {
         if (!isTrivial) {
           aContext.PopGroupAndBlend();
         }
         aContext.Restore();
@@ -242,17 +240,17 @@ nsSVGClipPathFrame::PointIsInsideClipPat
   }
 
   for (nsIFrame* kid = mFrames.FirstChild(); kid;
        kid = kid->GetNextSibling()) {
     nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
     if (SVGFrame) {
       gfxPoint pointForChild = point;
       gfxMatrix m = static_cast<nsSVGElement*>(kid->GetContent())->
-        PrependLocalTransformsTo(gfxMatrix(), nsSVGElement::eUserSpaceToParent);
+        PrependLocalTransformsTo(gfxMatrix(), eUserSpaceToParent);
       if (!m.IsIdentity()) {
         if (!m.Invert()) {
           return false;
         }
         pointForChild = m.Transform(point);
       }
       if (SVGFrame->GetFrameForPoint(pointForChild)) {
         return true;
--- a/layout/svg/nsSVGContainerFrame.cpp
+++ b/layout/svg/nsSVGContainerFrame.cpp
@@ -230,18 +230,19 @@ nsSVGDisplayContainerFrame::IsSVGTransfo
   // mContent could be a XUL element so check for an SVG element before casting
   if (mContent->IsSVGElement()) {
     nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
     nsSVGAnimatedTransformList* transformList =
       content->GetAnimatedTransformList();
     if ((transformList && transformList->HasTransform()) ||
         content->GetAnimateMotionTransform()) {
       if (aOwnTransform) {
-        *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
-                                    nsSVGElement::eUserSpaceToParent));
+        *aOwnTransform = gfx::ToMatrix(
+                           content->PrependLocalTransformsTo(
+                             gfxMatrix(), eUserSpaceToParent));
       }
       foundTransform = true;
     }
   }
   return foundTransform;
 }
 
 //----------------------------------------------------------------------
@@ -261,36 +262,34 @@ nsSVGDisplayContainerFrame::PaintSVG(gfx
   const nsStyleDisplay *display = StyleDisplay();
   if (display->mOpacity == 0.0) {
     return NS_OK;
   }
 
   gfxMatrix matrix = aTransform;
   if (GetContent()->IsSVGElement()) { // must check before cast
     matrix = static_cast<const nsSVGElement*>(GetContent())->
-               PrependLocalTransformsTo(matrix,
-                                        nsSVGElement::eChildToUserSpace);
+               PrependLocalTransformsTo(matrix, eChildToUserSpace);
     if (matrix.IsSingular()) {
       return NS_OK;
     }
   }
 
   for (nsIFrame* kid = mFrames.FirstChild(); kid;
        kid = kid->GetNextSibling()) {
     gfxMatrix m = matrix;
     // PaintFrameWithEffects() expects the transform that is passed to it to
     // include the transform to the passed frame's user space, so add it:
     const nsIContent* content = kid->GetContent();
     if (content->IsSVGElement()) { // must check before cast
       const nsSVGElement* element = static_cast<const nsSVGElement*>(content);
       if (!element->HasValidDimensions()) {
         continue; // nothing to paint for kid
       }
-      m = element->
-            PrependLocalTransformsTo(m, nsSVGElement::eUserSpaceToParent);
+      m = element->PrependLocalTransformsTo(m, eUserSpaceToParent);
       if (m.IsSingular()) {
         continue;
       }
     }
     nsSVGUtils::PaintFrameWithEffects(kid, aContext, m, aDirtyRect);
   }
 
   return NS_OK;
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -184,18 +184,19 @@ nsSVGForeignObjectFrame::IsSVGTransforme
   }
 
   nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
   nsSVGAnimatedTransformList* transformList =
     content->GetAnimatedTransformList();
   if ((transformList && transformList->HasTransform()) ||
       content->GetAnimateMotionTransform()) {
     if (aOwnTransform) {
-      *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
-                                  nsSVGElement::eUserSpaceToParent));
+      *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(
+                                       gfxMatrix(),
+                                       eUserSpaceToParent));
     }
     foundTransform = true;
   }
   return foundTransform;
 }
 
 nsresult
 nsSVGForeignObjectFrame::PaintSVG(gfxContext& aContext,
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -977,14 +977,13 @@ nsSVGOuterSVGAnonChildFrame::HasChildren
   SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
 
   bool hasTransform = content->HasChildrenOnlyTransform();
 
   if (hasTransform && aTransform) {
     // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
     gfxMatrix identity;
     *aTransform = gfx::ToMatrix(
-      content->PrependLocalTransformsTo(identity,
-                                        nsSVGElement::eChildToUserSpace));
+      content->PrependLocalTransformsTo(identity, eChildToUserSpace));
   }
 
   return hasTransform;
 }
--- a/layout/svg/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/nsSVGPathGeometryFrame.cpp
@@ -211,18 +211,20 @@ nsSVGPathGeometryFrame::IsSVGTransformed
   }
 
   nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
   nsSVGAnimatedTransformList* transformList =
     content->GetAnimatedTransformList();
   if ((transformList && transformList->HasTransform()) ||
       content->GetAnimateMotionTransform()) {
     if (aOwnTransform) {
-      *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
-                                  nsSVGElement::eUserSpaceToParent));
+      *aOwnTransform = gfx::ToMatrix(
+                         content->PrependLocalTransformsTo(
+                           gfxMatrix(),
+                           eUserSpaceToParent));
     }
     foundTransform = true;
   }
   return foundTransform;
 }
 
 void
 nsSVGPathGeometryFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
--- a/layout/svg/nsSVGPatternFrame.cpp
+++ b/layout/svg/nsSVGPatternFrame.cpp
@@ -402,17 +402,17 @@ nsSVGPatternFrame::PaintPattern(const Dr
       // The CTM of each frame referencing us can be different
       nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
       if (SVGFrame) {
         SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
       }
       gfxMatrix tm = *(patternWithChildren->mCTM);
       if (kid->GetContent()->IsSVGElement()) {
         tm = static_cast<nsSVGElement*>(kid->GetContent())->
-              PrependLocalTransformsTo(tm, nsSVGElement::eUserSpaceToParent);
+               PrependLocalTransformsTo(tm, eUserSpaceToParent);
       }
       nsSVGUtils::PaintFrameWithEffects(kid, *gfx, tm);
     }
     patternWithChildren->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
   }
 
   patternWithChildren->mSource = nullptr;
 
--- a/layout/svg/nsSVGSwitchFrame.cpp
+++ b/layout/svg/nsSVGSwitchFrame.cpp
@@ -117,17 +117,17 @@ nsSVGSwitchFrame::PaintSVG(gfxContext& a
   if (StyleDisplay()->mOpacity == 0.0)
     return NS_OK;
 
   nsIFrame *kid = GetActiveChildFrame();
   if (kid) {
     gfxMatrix tm = aTransform;
     if (kid->GetContent()->IsSVGElement()) {
       tm = static_cast<nsSVGElement*>(kid->GetContent())->
-             PrependLocalTransformsTo(tm, nsSVGElement::eUserSpaceToParent);
+             PrependLocalTransformsTo(tm, eUserSpaceToParent);
     }
     nsSVGUtils::PaintFrameWithEffects(kid, aContext, tm, aDirtyRect);
   }
   return NS_OK;
 }
 
 
 nsIFrame*
@@ -140,19 +140,19 @@ nsSVGSwitchFrame::GetFrameForPoint(const
 
   nsIFrame *kid = GetActiveChildFrame();
   nsISVGChildFrame* svgFrame = do_QueryFrame(kid);
   if (svgFrame) {
     // Transform the point from our SVG user space to our child's.
     gfxPoint point = aPoint;
     gfxMatrix m =
       static_cast<const nsSVGElement*>(mContent)->
-        PrependLocalTransformsTo(gfxMatrix(), nsSVGElement::eChildToUserSpace);
+        PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
     m = static_cast<const nsSVGElement*>(kid->GetContent())->
-          PrependLocalTransformsTo(m, nsSVGElement::eUserSpaceToParent);
+          PrependLocalTransformsTo(m, eUserSpaceToParent);
     if (!m.IsIdentity()) {
       if (!m.Invert()) {
         return nullptr;
       }
       point = m.Transform(point);
     }
     return svgFrame->GetFrameForPoint(point);
   }
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -419,17 +419,17 @@ nsSVGUtils::GetUserToCanvasTM(nsIFrame *
   nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame);
   NS_ASSERTION(svgFrame, "bad frame");
 
   gfxMatrix tm;
   if (svgFrame) {
     nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
     tm = content->PrependLocalTransformsTo(
                     GetCanvasTM(aFrame->GetParent()),
-                    nsSVGElement::eUserSpaceToParent);
+                    eUserSpaceToParent);
   }
   return tm;
 }
 
 void 
 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags)
 {
   nsIFrame *kid = aFrame->GetFirstPrincipalChild();
@@ -750,17 +750,17 @@ nsSVGUtils::HitTestChildren(nsSVGDisplay
                             const gfxPoint& aPoint)
 {
   // First we transform aPoint into the coordinate space established by aFrame
   // for its children (e.g. take account of any 'viewBox' attribute):
   gfxPoint point = aPoint;
   if (aFrame->GetContent()->IsSVGElement()) { // must check before cast
     gfxMatrix m = static_cast<const nsSVGElement*>(aFrame->GetContent())->
                     PrependLocalTransformsTo(gfxMatrix(),
-                                             nsSVGElement::eChildToUserSpace);
+                                             eChildToUserSpace);
     if (!m.IsIdentity()) {
       if (!m.Invert()) {
         return nullptr;
       }
       point = m.Transform(point);
     }
   }
 
@@ -778,17 +778,17 @@ nsSVGUtils::HitTestChildren(nsSVGDisplay
         continue;
       }
       // GetFrameForPoint() expects a point in its frame's SVG user space, so
       // we need to convert to that space:
       gfxPoint p = point;
       if (content->IsSVGElement()) { // must check before cast
         gfxMatrix m = static_cast<const nsSVGElement*>(content)->
                         PrependLocalTransformsTo(gfxMatrix(),
-                                                 nsSVGElement::eUserSpaceToParent);
+                                                 eUserSpaceToParent);
         if (!m.IsIdentity()) {
           if (!m.Invert()) {
             continue;
           }
           p = m.Transform(p);
         }
       }
       result = SVGFrame->GetFrameForPoint(p);
@@ -965,18 +965,17 @@ nsSVGUtils::GetBBox(nsIFrame *aFrame, ui
         aFrame->GetType() == nsGkAtoms::svgUseFrame) {
       // The spec says getBBox "Returns the tight bounding box in *current user
       // space*". So we should really be doing this for all elements, but that
       // needs investigation to check that we won't break too much content.
       // NOTE: When changing this to apply to other frame types, make sure to
       // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
       MOZ_ASSERT(content->IsSVGElement(), "bad cast");
       nsSVGElement *element = static_cast<nsSVGElement*>(content);
-      matrix = element->PrependLocalTransformsTo(matrix,
-                          nsSVGElement::eChildToUserSpace);
+      matrix = element->PrependLocalTransformsTo(matrix, eChildToUserSpace);
     }
     bbox = svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
     // Account for 'clipped'.
     if (aFlags & nsSVGUtils::eBBoxIncludeClipped) {
       gfxRect clipRect(0, 0, 0, 0);
       float x, y, width, height;
       gfxMatrix tm;
       gfxRect fillBBox = 
@@ -1056,18 +1055,17 @@ nsSVGUtils::FrameSpaceInCSSPxToUserSpace
                                          nsPresContext::AppUnitsPerCSSPixel()).TopLeft();
   }
 
   // For foreignObject frames, nsSVGUtils::GetBBox applies their local
   // transform, so we need to do the same here.
   if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame ||
       aFrame->GetType() == nsGkAtoms::svgUseFrame) {
     gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())->
-        PrependLocalTransformsTo(gfxMatrix(),
-                                 nsSVGElement::eChildToUserSpace);
+        PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
     NS_ASSERTION(!transform.HasNonTranslation(), "we're relying on this being an offset-only transform");
     return transform.GetTranslation();
   }
 
   return gfxPoint();
 }
 
 static gfxRect
@@ -1658,17 +1656,17 @@ nsSVGUtils::PaintSVGGlyph(Element* aElem
   }
   aContext->GetDrawTarget()->AddUserData(&gfxTextContextPaint::sUserDataKey,
                                          aContextPaint, nullptr);
   gfxMatrix m;
   if (frame->GetContent()->IsSVGElement()) {
     // PaintSVG() expects the passed transform to be the transform to its own
     // SVG user space, so we need to account for any 'transform' attribute:
     m = static_cast<nsSVGElement*>(frame->GetContent())->
-          PrependLocalTransformsTo(gfxMatrix(), nsSVGElement::eUserSpaceToParent);
+          PrependLocalTransformsTo(gfxMatrix(), eUserSpaceToParent);
   }
   nsresult rv = svgFrame->PaintSVG(*aContext, m);
   return NS_SUCCEEDED(rv);
 }
 
 bool
 nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
                                const gfxMatrix& aSVGToAppSpace,