Bug 265894 - Part 2. Implement SVGViewportElementBase, and let SVGSVGElement inherit from it. draft
authorcku <cku@mozilla.com>
Wed, 14 Jun 2017 20:38:10 +0800
changeset 597162 77c3ad45e00713769a9d87786c8a1e17044ce96c
parent 597161 61c2a3e1294a12fd657b59aef07e5a0c466ccec2
child 597163 5f22b22f50f778871e275ead555e70da78063656
push id64856
push userbmo:cku@mozilla.com
push dateTue, 20 Jun 2017 07:46:45 +0000
bugs265894
milestone56.0a1
Bug 265894 - Part 2. Implement SVGViewportElementBase, and let SVGSVGElement inherit from it. MozReview-Commit-ID: I6BIgEMhN6Q
dom/svg/SVGSVGElement.cpp
dom/svg/SVGSVGElement.h
dom/svg/SVGSymbolElement.cpp
dom/svg/SVGSymbolElement.h
dom/svg/SVGViewElement.h
dom/svg/SVGViewportElement.cpp
dom/svg/SVGViewportElement.h
dom/svg/moz.build
layout/svg/nsSVGInnerSVGFrame.cpp
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -1,85 +1,70 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 <stdint.h>
-#include "mozilla/ArrayUtils.h"
 #include "mozilla/ContentEvents.h"
-#include "mozilla/EventDispatcher.h"
-#include "mozilla/Likely.h"
-
-#include "nsGkAtoms.h"
-#include "nsLayoutUtils.h"
-#include "nsLayoutStylesheetCache.h"
-#include "DOMSVGNumber.h"
-#include "DOMSVGLength.h"
-#include "nsSVGAngle.h"
-#include "nsCOMPtr.h"
-#include "nsIPresShell.h"
-#include "nsContentUtils.h"
-#include "nsIDocument.h"
-#include "mozilla/dom/SVGMatrix.h"
-#include "DOMSVGPoint.h"
-#include "nsIFrame.h"
-#include "nsFrameSelection.h"
-#include "nsISVGSVGFrame.h" //XXX
-#include "mozilla/dom/SVGRect.h"
-#include "nsError.h"
-#include "nsSVGDisplayableFrame.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/dom/SVGSVGElementBinding.h"
-#include "nsSVGUtils.h"
+#include "mozilla/dom/SVGMatrix.h"
 #include "mozilla/dom/SVGViewElement.h"
-#include "nsStyleUtil.h"
-#include "SVGContentUtils.h"
+#include "mozilla/EventDispatcher.h"
 
+#include "DOMSVGLength.h"
+#include "DOMSVGNumber.h"
+#include "DOMSVGPoint.h"
+#include "nsLayoutStylesheetCache.h"
+#include "nsSVGAngle.h"
+#include "nsFrameSelection.h"
+#include "nsIFrame.h"
+#include "nsISVGSVGFrame.h"
+#include "nsSMILAnimationController.h"
 #include "nsSMILTimeContainer.h"
-#include "nsSMILAnimationController.h"
-#include "nsSMILTypes.h"
+#include "nsSVGDisplayableFrame.h"
+#include "nsSVGUtils.h"
 #include "SVGAngle.h"
-#include <algorithm>
-#include "prtime.h"
 
 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT_CHECK_PARSER(SVG)
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
 
-class SVGAnimatedLength;
+nsSVGEnumMapping SVGSVGElement::sZoomAndPanMap[] = {
+  {&nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE},
+  {&nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY},
+  {nullptr, 0}
+};
 
-JSObject*
-SVGSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
+nsSVGElement::EnumInfo SVGSVGElement::sEnumInfo[1] =
 {
-  return SVGSVGElementBinding::Wrap(aCx, this, aGivenProto);
-}
+  { &nsGkAtoms::zoomAndPan,
+    sZoomAndPanMap,
+    SVG_ZOOMANDPAN_MAGNIFY
+  }
+};
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMSVGTranslatePoint, nsISVGPoint,
                                    mElement)
 
 NS_IMPL_ADDREF_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
 NS_IMPL_RELEASE_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTranslatePoint)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   // We have to qualify nsISVGPoint because NS_GET_IID looks for a class in the
   // global namespace
   NS_INTERFACE_MAP_ENTRY(mozilla::nsISVGPoint)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-SVGSVGElement::~SVGSVGElement()
-{
-}
-
 DOMSVGPoint*
 DOMSVGTranslatePoint::Copy()
 {
   return new DOMSVGPoint(mPt.GetX(), mPt.GetY());
 }
 
 nsISupports*
 DOMSVGTranslatePoint::GetParentObject()
@@ -106,45 +91,21 @@ DOMSVGTranslatePoint::MatrixTransform(SV
   float d = matrix.D(), e = matrix.E(), f = matrix.F();
   float x = mPt.GetX();
   float y = mPt.GetY();
 
   nsCOMPtr<nsISVGPoint> point = new DOMSVGPoint(a*x + c*y + e, b*x + d*y + f);
   return point.forget();
 }
 
-SVGView::SVGView()
-{
-  mZoomAndPan.Init(SVGSVGElement::ZOOMANDPAN,
-                   SVG_ZOOMANDPAN_MAGNIFY);
-  mViewBox.Init();
-  mPreserveAspectRatio.Init();
-}
-
-nsSVGElement::LengthInfo SVGSVGElement::sLengthInfo[4] =
+JSObject*
+SVGSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
-  { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
-  { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
-  { &nsGkAtoms::width, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X },
-  { &nsGkAtoms::height, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y },
-};
-
-nsSVGEnumMapping SVGSVGElement::sZoomAndPanMap[] = {
-  {&nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE},
-  {&nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY},
-  {nullptr, 0}
-};
-
-nsSVGElement::EnumInfo SVGSVGElement::sEnumInfo[1] =
-{
-  { &nsGkAtoms::zoomAndPan,
-    sZoomAndPanMap,
-    SVG_ZOOMANDPAN_MAGNIFY
-  }
-};
+  return SVGSVGElementBinding::Wrap(aCx, this, aGivenProto);
+}
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGSVGElement)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGSVGElement,
                                                 SVGSVGElementBase)
@@ -162,33 +123,42 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_ADDREF_INHERITED(SVGSVGElement,SVGSVGElementBase)
 NS_IMPL_RELEASE_INHERITED(SVGSVGElement,SVGSVGElementBase)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGSVGElement)
   NS_INTERFACE_TABLE_INHERITED(SVGSVGElement, nsIDOMNode, nsIDOMElement,
                                nsIDOMSVGElement)
 NS_INTERFACE_TABLE_TAIL_INHERITING(SVGSVGElementBase)
 
+SVGView::SVGView()
+{
+  mZoomAndPan.Init(SVGSVGElement::ZOOMANDPAN,
+                   SVG_ZOOMANDPAN_MAGNIFY);
+  mViewBox.Init();
+  mPreserveAspectRatio.Init();
+}
+
 //----------------------------------------------------------------------
 // Implementation
 
 SVGSVGElement::SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                              FromParser aFromParser)
   : SVGSVGElementBase(aNodeInfo),
-    mViewportWidth(0),
-    mViewportHeight(0),
     mCurrentTranslate(0.0f, 0.0f),
     mCurrentScale(1.0f),
     mPreviousTranslate(0.0f, 0.0f),
     mPreviousScale(1.0f),
     mStartAnimationOnBindToTree(aFromParser == NOT_FROM_PARSER ||
                                 aFromParser == FROM_PARSER_FRAGMENT ||
                                 aFromParser == FROM_PARSER_XSLT),
-    mImageNeedsTransformInvalidation(false),
-    mHasChildrenOnlyTransform(false)
+    mImageNeedsTransformInvalidation(false)
+{
+}
+
+SVGSVGElement::~SVGSVGElement()
 {
 }
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT_AND_PARSER(SVGSVGElement)
 
@@ -213,17 +183,16 @@ SVGSVGElement::Width()
   return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
 }
 
 already_AddRefed<SVGAnimatedLength>
 SVGSVGElement::Height()
 {
   return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
 }
-
 float
 SVGSVGElement::PixelUnitToMillimeterX()
 {
   return MM_PER_INCH_FLOAT / 96;
 }
 
 float
 SVGSVGElement::PixelUnitToMillimeterY()
@@ -237,17 +206,16 @@ SVGSVGElement::ScreenPixelToMillimeterX(
   return MM_PER_INCH_FLOAT / 96;
 }
 
 float
 SVGSVGElement::ScreenPixelToMillimeterY()
 {
   return ScreenPixelToMillimeterX();
 }
-
 bool
 SVGSVGElement::UseCurrentView()
 {
   return mSVGView || mCurrentViewID;
 }
 
 float
 SVGSVGElement::CurrentScale()
@@ -420,78 +388,46 @@ SVGSVGElement::CreateSVGTransform()
 already_AddRefed<SVGTransform>
 SVGSVGElement::CreateSVGTransformFromMatrix(SVGMatrix& matrix)
 {
   RefPtr<SVGTransform> transform = new SVGTransform(matrix.GetMatrix());
   return transform.forget();
 }
 
 //----------------------------------------------------------------------
-
-already_AddRefed<SVGAnimatedRect>
-SVGSVGElement::ViewBox()
-{
-  return mViewBox.ToSVGAnimatedRect(this);
-}
-
-already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
-SVGSVGElement::PreserveAspectRatio()
-{
-  return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
-}
-
-uint16_t
-SVGSVGElement::ZoomAndPan()
-{
-  return mEnumAttributes[ZOOMANDPAN].GetAnimValue();
-}
-
-void
-SVGSVGElement::SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv)
-{
-  if (aZoomAndPan == SVG_ZOOMANDPAN_DISABLE ||
-      aZoomAndPan == SVG_ZOOMANDPAN_MAGNIFY) {
-    mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this);
-    return;
-  }
-
-  rv.ThrowRangeError<MSG_INVALID_ZOOMANDPAN_VALUE_ERROR>();
-}
-
-//----------------------------------------------------------------------
 // helper method for implementing SetCurrentScale/Translate
 
 void
 SVGSVGElement::SetCurrentScaleTranslate(float s, float x, float y)
 {
   if (s == mCurrentScale &&
       x == mCurrentTranslate.GetX() && y == mCurrentTranslate.GetY()) {
     return;
   }
 
   // Prevent bizarre behaviour and maxing out of CPU and memory by clamping
   if (s < CURRENT_SCALE_MIN)
     s = CURRENT_SCALE_MIN;
   else if (s > CURRENT_SCALE_MAX)
     s = CURRENT_SCALE_MAX;
-  
+
   // IMPORTANT: If either mCurrentTranslate *or* mCurrentScale is changed then
   // mPreviousTranslate_x, mPreviousTranslate_y *and* mPreviousScale must all
   // be updated otherwise SVGZoomEvents will end up with invalid data. I.e. an
   // SVGZoomEvent's properties previousScale and previousTranslate must contain
   // the state of currentScale and currentTranslate immediately before the
   // change that caused the event's dispatch, which is *not* necessarily the
   // same thing as the values of currentScale and currentTranslate prior to
   // their own last change.
   //
   // XXX This comment is out-of-date due to removal of SVGZoomEvent.  Can we
   // remove some of this code?
   mPreviousScale = mCurrentScale;
   mPreviousTranslate = mCurrentTranslate;
-  
+
   mCurrentScale = s;
   mCurrentTranslate = SVGPoint(x, y);
 
   // now dispatch the appropriate event if we are the root element
   nsIDocument* doc = GetUncomposedDoc();
   if (doc) {
     nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
     if (presShell && IsRoot()) {
@@ -506,16 +442,37 @@ SVGSVGElement::SetCurrentScaleTranslate(
 }
 
 void
 SVGSVGElement::SetCurrentTranslate(float x, float y)
 {
   SetCurrentScaleTranslate(mCurrentScale, x, y);
 }
 
+//----------------------------------------------------------------------
+// SVGZoomAndPanValues
+uint16_t
+SVGSVGElement::ZoomAndPan()
+{
+  return mEnumAttributes[ZOOMANDPAN].GetAnimValue();
+}
+
+void
+SVGSVGElement::SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv)
+{
+  if (aZoomAndPan == SVG_ZOOMANDPAN_DISABLE ||
+      aZoomAndPan == SVG_ZOOMANDPAN_MAGNIFY) {
+    mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this);
+    return;
+  }
+
+  rv.ThrowRangeError<MSG_INVALID_ZOOMANDPAN_VALUE_ERROR>();
+}
+
+//----------------------------------------------------------------------
 nsSMILTimeContainer*
 SVGSVGElement::GetTimedDocumentRoot()
 {
   if (mTimedDocumentRoot) {
     return mTimedDocumentRoot;
   }
 
   // We must not be the outermost <svg> element, try to find it
@@ -523,185 +480,18 @@ SVGSVGElement::GetTimedDocumentRoot()
     SVGContentUtils::GetOuterSVGElement(this);
 
   if (outerSVGElement) {
     return outerSVGElement->GetTimedDocumentRoot();
   }
   // invalid structure
   return nullptr;
 }
-
 //----------------------------------------------------------------------
-// nsIContent methods
-
-NS_IMETHODIMP_(bool)
-SVGSVGElement::IsAttributeMapped(const nsIAtom* name) const
-{
-  // We want to map the 'width' and 'height' attributes into style for
-  // outer-<svg>, except when the attributes aren't set (since their default
-  // values of '100%' can cause unexpected and undesirable behaviour for SVG
-  // inline in HTML). We rely on nsSVGElement::UpdateContentStyleRule() to
-  // prevent mapping of the default values into style (it only maps attributes
-  // that are set). We also rely on a check in nsSVGElement::
-  // UpdateContentStyleRule() to prevent us mapping the attributes when they're
-  // given a <length> value that is not currently recognized by the SVG
-  // specification.
-
-  if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) {
-    return true;
-  }
-
-  static const MappedAttributeEntry* const map[] = {
-    sColorMap,
-    sFEFloodMap,
-    sFillStrokeMap,
-    sFiltersMap,
-    sFontSpecificationMap,
-    sGradientStopMap,
-    sGraphicsMap,
-    sLightingEffectsMap,
-    sMarkersMap,
-    sTextContentElementsMap,
-    sViewportsMap
-  };
-
-  return FindAttributeDependence(name, map) ||
-    SVGSVGElementBase::IsAttributeMapped(name);
-}
-
-//----------------------------------------------------------------------
-// nsIContent methods:
-
-nsresult
-SVGSVGElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
-{
-  if (aVisitor.mEvent->mMessage == eSVGLoad) {
-    if (mTimedDocumentRoot) {
-      mTimedDocumentRoot->Begin();
-      // Set 'resample needed' flag, so that if any script calls a DOM method
-      // that requires up-to-date animations before our first sample callback,
-      // we'll force a synchronous sample.
-      AnimationNeedsResample();
-    }
-  }
-  return SVGSVGElementBase::GetEventTargetParent(aVisitor);
-}
-
-bool
-SVGSVGElement::IsEventAttributeName(nsIAtom* aName)
-{
-  /* The events in EventNameType_SVGSVG are for events that are only
-     applicable to outermost 'svg' elements. We don't check if we're an outer
-     'svg' element in case we're not inserted into the document yet, but since
-     the target of the events in question will always be the outermost 'svg'
-     element, this shouldn't cause any real problems.
-  */
-  return nsContentUtils::IsEventAttributeName(aName,
-         (EventNameType_SVGGraphic | EventNameType_SVGSVG));
-}
-
-//----------------------------------------------------------------------
-// nsSVGElement overrides
-
-// Helper for GetViewBoxTransform on root <svg> node
-// * aLength: internal value for our <svg> width or height attribute.
-// * aViewportLength: length of the corresponding dimension of the viewport.
-// * aSelf: the outermost <svg> node itself.
-// NOTE: aSelf is not an ancestor viewport element, so it can't be used to
-// resolve percentage lengths. (It can only be used to resolve
-// 'em'/'ex'-valued units).
-inline float
-ComputeSynthesizedViewBoxDimension(const nsSVGLength2& aLength,
-                                   float aViewportLength,
-                                   const SVGSVGElement* aSelf)
-{
-  if (aLength.IsPercentage()) {
-    return aViewportLength * aLength.GetAnimValInSpecifiedUnits() / 100.0f;
-  }
-
-  return aLength.GetAnimValue(const_cast<SVGSVGElement*>(aSelf));
-}
-
-//----------------------------------------------------------------------
-// public helpers:
-
-gfx::Matrix
-SVGSVGElement::GetViewBoxTransform() const
-{
-  float viewportWidth, viewportHeight;
-  if (IsInner()) {
-    SVGSVGElement *ctx = GetCtx();
-    viewportWidth = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
-    viewportHeight = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
-  } else {
-    viewportWidth = mViewportWidth;
-    viewportHeight = mViewportHeight;
-  }
-
-  if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
-    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
-  }
-
-  nsSVGViewBoxRect viewBox =
-    GetViewBoxWithSynthesis(viewportWidth, viewportHeight);
-
-  if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
-    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
-  }
-
-  return SVGContentUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
-                                              viewBox.x, viewBox.y,
-                                              viewBox.width, viewBox.height,
-                                              GetPreserveAspectRatioWithOverride());
-}
-
-void
-SVGSVGElement::UpdateHasChildrenOnlyTransform()
-{
-  bool hasChildrenOnlyTransform =
-    HasViewBoxOrSyntheticViewBox() ||
-    (IsRoot() && (mCurrentTranslate != SVGPoint(0.0f, 0.0f) ||
-                  mCurrentScale != 1.0f));
-  mHasChildrenOnlyTransform = hasChildrenOnlyTransform;
-}
-
-void
-SVGSVGElement::ChildrenOnlyTransformChanged(uint32_t aFlags)
-{
-  // Avoid wasteful calls:
-  MOZ_ASSERT(!(GetPrimaryFrame()->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
-             "Non-display SVG frames don't maintain overflow rects");
-
-  nsChangeHint changeHint;
-
-  bool hadChildrenOnlyTransform = mHasChildrenOnlyTransform;
-
-  UpdateHasChildrenOnlyTransform();
-
-  if (hadChildrenOnlyTransform != mHasChildrenOnlyTransform) {
-    // Reconstruct the frame tree to handle stacking context changes:
-    // XXXjwatt don't do this for root-<svg> or even outer-<svg>?
-    changeHint = nsChangeHint_ReconstructFrame;
-  } else {
-    // We just assume the old and new transforms are different.
-    changeHint = nsChangeHint(nsChangeHint_UpdateOverflow |
-                              nsChangeHint_ChildrenOnlyTransform);
-  }
-
-  // If we're not reconstructing the frame tree, then we only call
-  // PostRestyleEvent if we're not being called under reflow to avoid recursing
-  // to death. See bug 767056 comments 10 and 12. Since our nsSVGOuterSVGFrame
-  // is being reflowed we're going to invalidate and repaint its entire area
-  // anyway (which will include our children).
-  if ((changeHint & nsChangeHint_ReconstructFrame) ||
-      !(aFlags & eDuringReflow)) {
-    nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
-  }
-}
-
+// nsSVGElement
 nsresult
 SVGSVGElement::BindToTree(nsIDocument* aDocument,
                           nsIContent* aParent,
                           nsIContent* aBindingParent,
                           bool aCompileEventHandlers)
 {
   nsSMILAnimationController* smilController = nullptr;
 
@@ -719,17 +509,17 @@ SVGSVGElement::BindToTree(nsIDocument* a
         // time container. However, we need to make sure that we'll get a
         // kick-start if we get promoted to be outermost later on.
         mTimedDocumentRoot = nullptr;
         mStartAnimationOnBindToTree = true;
       }
     }
   }
 
-  nsresult rv = SVGSVGElementBase::BindToTree(aDocument, aParent,
+  nsresult rv = SVGGraphicsElement::BindToTree(aDocument, aParent,
                                               aBindingParent,
                                               aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv,rv);
 
   nsIDocument* doc = GetComposedDoc();
   if (doc) {
     // Setup the style sheet during binding, not element construction,
     // because we could move the root SVG element from the document
@@ -751,17 +541,98 @@ SVGSVGElement::BindToTree(nsIDocument* a
 
 void
 SVGSVGElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   if (mTimedDocumentRoot) {
     mTimedDocumentRoot->SetParent(nullptr);
   }
 
-  SVGSVGElementBase::UnbindFromTree(aDeep, aNullParent);
+  SVGGraphicsElement::UnbindFromTree(aDeep, aNullParent);
+}
+
+nsSVGAnimatedTransformList*
+SVGSVGElement::GetAnimatedTransformList(uint32_t aFlags)
+{
+  if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) {
+    return mSVGView->mTransforms;
+  }
+  return SVGGraphicsElement::GetAnimatedTransformList(aFlags);
+}
+
+nsresult
+SVGSVGElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
+{
+  if (aVisitor.mEvent->mMessage == eSVGLoad) {
+    if (mTimedDocumentRoot) {
+      mTimedDocumentRoot->Begin();
+      // Set 'resample needed' flag, so that if any script calls a DOM method
+      // that requires up-to-date animations before our first sample callback,
+      // we'll force a synchronous sample.
+      AnimationNeedsResample();
+    }
+  }
+  return SVGSVGElementBase::GetEventTargetParent(aVisitor);
+}
+
+bool
+SVGSVGElement::IsEventAttributeName(nsIAtom* aName)
+{
+  /* The events in EventNameType_SVGSVG are for events that are only
+     applicable to outermost 'svg' elements. We don't check if we're an outer
+     'svg' element in case we're not inserted into the document yet, but since
+     the target of the events in question will always be the outermost 'svg'
+     element, this shouldn't cause any real problems.
+  */
+  return nsContentUtils::IsEventAttributeName(aName,
+         (EventNameType_SVGGraphic | EventNameType_SVGSVG));
+}
+
+//----------------------------------------------------------------------
+// public helpers:
+
+int32_t
+SVGSVGElement::GetIntrinsicWidth()
+{
+  if (mLengthAttributes[ATTR_WIDTH].IsPercentage()) {
+    return -1;
+  }
+  // Passing |this| as a SVGViewportElement* invokes the variant of GetAnimValue
+  // that uses the passed argument as the context, but that's fine since we
+  // know the length isn't a percentage so the context won't be used (and we
+  // need to pass the element to be able to resolve em/ex units).
+  float width = mLengthAttributes[ATTR_WIDTH].GetAnimValue(this);
+  return nsSVGUtils::ClampToInt(width);
+}
+
+int32_t
+SVGSVGElement::GetIntrinsicHeight()
+{
+  if (mLengthAttributes[ATTR_HEIGHT].IsPercentage()) {
+    return -1;
+  }
+  // Passing |this| as a SVGViewportElement* invokes the variant of GetAnimValue
+  // that uses the passed argument as the context, but that's fine since we
+  // know the length isn't a percentage so the context won't be used (and we
+  // need to pass the element to be able to resolve em/ex units).
+  float height = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(this);
+  return nsSVGUtils::ClampToInt(height);
+}
+
+void
+SVGSVGElement::FlushImageTransformInvalidation()
+{
+  MOZ_ASSERT(!GetParent(), "Should only be called on root node");
+  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
+             "Should only be called on image documents");
+
+  if (mImageNeedsTransformInvalidation) {
+    InvalidateTransformNotifyFrame();
+    mImageNeedsTransformInvalidation = false;
+  }
 }
 
 //----------------------------------------------------------------------
 // implementation helpers
 
 bool
 SVGSVGElement::WillBeOutermostSVG(nsIContent* aParent,
                                   nsIContent* aBindingParent) const
@@ -795,301 +666,23 @@ SVGSVGElement::InvalidateTransformNotify
 
 bool
 SVGSVGElement::HasPreserveAspectRatio()
 {
   return HasAttr(kNameSpaceID_None, nsGkAtoms::preserveAspectRatio) ||
     mPreserveAspectRatio.IsAnimated();
 }
 
-SVGViewElement*
-SVGSVGElement::GetCurrentViewElement() const
-{
-  if (mCurrentViewID) {
-    //XXXsmaug It is unclear how this should work in case we're in Shadow DOM.
-    nsIDocument* doc = GetUncomposedDoc();
-    if (doc) {
-      Element *element = doc->GetElementById(*mCurrentViewID);
-      if (element && element->IsSVGElement(nsGkAtoms::view)) {
-        return static_cast<SVGViewElement*>(element);
-      }
-    }
-  }
-  return nullptr;
-}
-
-nsSVGViewBoxRect
-SVGSVGElement::GetViewBoxWithSynthesis(
-  float aViewportWidth, float aViewportHeight) const
-{
-  // The logic here should match HasViewBoxRect().
-  SVGViewElement* viewElement = GetCurrentViewElement();
-  if (viewElement && viewElement->mViewBox.HasRect()) {
-    return viewElement->mViewBox.GetAnimValue();
-  }
-  if (mSVGView && mSVGView->mViewBox.HasRect()) {
-    return mSVGView->mViewBox.GetAnimValue();
-  }
-  if (mViewBox.HasRect()) {
-    return mViewBox.GetAnimValue();
-  }
-
-  if (ShouldSynthesizeViewBox()) {
-    // Special case -- fake a viewBox, using height & width attrs.
-    // (Use |this| as context, since if we get here, we're outermost <svg>.)
-    return nsSVGViewBoxRect(0, 0,
-              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
-                                                 mViewportWidth, this),
-              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
-                                                 mViewportHeight, this));
-
-  }
-
-  // No viewBox attribute, so we shouldn't auto-scale. This is equivalent
-  // to having a viewBox that exactly matches our viewport size.
-  return nsSVGViewBoxRect(0, 0, aViewportWidth, aViewportHeight);
-}
-
-SVGPreserveAspectRatio
-SVGSVGElement::GetPreserveAspectRatioWithOverride() const
-{
-  nsIDocument* doc = GetUncomposedDoc();
-  if (doc && doc->IsBeingUsedAsImage()) {
-    const SVGPreserveAspectRatio *pAROverridePtr = GetPreserveAspectRatioProperty();
-    if (pAROverridePtr) {
-      return *pAROverridePtr;
-    }
-  }
-
-  SVGViewElement* viewElement = GetCurrentViewElement();
-
-  // This check is equivalent to "!HasViewBoxRect() && ShouldSynthesizeViewBox()".
-  // We're just holding onto the viewElement that HasViewBoxRect() would look up,
-  // so that we don't have to look it up again later.
-  if (!((viewElement && viewElement->mViewBox.HasRect()) ||
-        (mSVGView && mSVGView->mViewBox.HasRect()) ||
-        mViewBox.HasRect()) &&
-      ShouldSynthesizeViewBox()) {
-    // If we're synthesizing a viewBox, use preserveAspectRatio="none";
-    return SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE, SVG_MEETORSLICE_SLICE);
-  }
-
-  if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
-    return viewElement->mPreserveAspectRatio.GetAnimValue();
-  }
-  if (mSVGView && mSVGView->mPreserveAspectRatio.IsExplicitlySet()) {
-    return mSVGView->mPreserveAspectRatio.GetAnimValue();
-  }
-  return mPreserveAspectRatio.GetAnimValue();
-}
-
-//----------------------------------------------------------------------
-// SVGSVGElement
-
-float
-SVGSVGElement::GetLength(uint8_t aCtxType)
-{
-  float h, w;
-
-  SVGViewElement* viewElement = GetCurrentViewElement();
-  const nsSVGViewBoxRect* viewbox = nullptr;
-
-  // The logic here should match HasViewBoxRect().
-  if (viewElement && viewElement->mViewBox.HasRect()) {
-    viewbox = &viewElement->mViewBox.GetAnimValue();
-  } else if (mSVGView && mSVGView->mViewBox.HasRect()) {
-    viewbox = &mSVGView->mViewBox.GetAnimValue();
-  } else if (mViewBox.HasRect()) {
-    viewbox = &mViewBox.GetAnimValue();
-  }
-
-  if (viewbox) {
-    w = viewbox->width;
-    h = viewbox->height;
-  } else if (IsInner()) {
-    SVGSVGElement *ctx = GetCtx();
-    w = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
-    h = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
-  } else if (ShouldSynthesizeViewBox()) {
-    w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
-                                           mViewportWidth, this);
-    h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
-                                           mViewportHeight, this);
-  } else {
-    w = mViewportWidth;
-    h = mViewportHeight;
-  }
-
-  w = std::max(w, 0.0f);
-  h = std::max(h, 0.0f);
-
-  switch (aCtxType) {
-  case SVGContentUtils::X:
-    return w;
-  case SVGContentUtils::Y:
-    return h;
-  case SVGContentUtils::XY:
-    return float(SVGContentUtils::ComputeNormalizedHypotenuse(w, h));
-  }
-  return 0;
-}
-
-//----------------------------------------------------------------------
-// nsSVGElement methods
-
-/* virtual */ gfxMatrix
-SVGSVGElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
-                                        SVGTransformTypes aWhich) const
-{
-  // 'transform' attribute (or an override from a fragment identifier):
-  gfxMatrix userToParent;
-
-  if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
-    userToParent = GetUserToParentTransform(mAnimateMotionTransform,
-                                            mSVGView && mSVGView->mTransforms
-                                              ? mSVGView->mTransforms
-                                              : mTransforms);
-    if (aWhich == eUserSpaceToParent) {
-      return userToParent * aMatrix;
-    }
-  }
-
-  gfxMatrix childToUser;
-
-  if (IsInner()) {
-    float x, y;
-    const_cast<SVGSVGElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
-    childToUser = ThebesMatrix(GetViewBoxTransform().PostTranslate(x, y));
-  } else if (IsRoot()) {
-    childToUser = ThebesMatrix(GetViewBoxTransform()
-                                 .PostScale(mCurrentScale, mCurrentScale)
-                                 .PostTranslate(mCurrentTranslate.GetX(),
-                                                mCurrentTranslate.GetY()));
-  } else {
-    // outer-<svg>, but inline in some other content:
-    childToUser = ThebesMatrix(GetViewBoxTransform());
-  }
-
-  if (aWhich == eAllTransforms) {
-    return childToUser * userToParent * aMatrix;
-  }
-
-  MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
-
-  // The following may look broken because pre-multiplying our eChildToUserSpace
-  // transform with another matrix without including our eUserSpaceToParent
-  // transform between the two wouldn't make sense.  We don't expect that to
-  // ever happen though.  We get here either when the identity matrix has been
-  // passed because our caller just wants our eChildToUserSpace transform, or
-  // when our eUserSpaceToParent transform has already been multiplied into the
-  // matrix that our caller passes (such as when we're called from PaintSVG).
-  return childToUser * aMatrix;
-}
-
-nsSVGAnimatedTransformList*
-SVGSVGElement::GetAnimatedTransformList(uint32_t aFlags)
-{
-  if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) {
-    return mSVGView->mTransforms;
-  }
-  return SVGSVGElementBase::GetAnimatedTransformList(aFlags);
-}
-
-/* virtual */ bool
-SVGSVGElement::HasValidDimensions() const
-{
-  return !IsInner() ||
-    ((!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
-       mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
-     (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
-       mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0));
-}
-
-nsSVGElement::LengthAttributesInfo
-SVGSVGElement::GetLengthInfo()
-{
-  return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
-                              ArrayLength(sLengthInfo));
-}
-
 nsSVGElement::EnumAttributesInfo
 SVGSVGElement::GetEnumInfo()
 {
   return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
                             ArrayLength(sEnumInfo));
 }
 
-nsSVGViewBox*
-SVGSVGElement::GetViewBox()
-{
-  return &mViewBox;
-}
-
-SVGAnimatedPreserveAspectRatio *
-SVGSVGElement::GetPreserveAspectRatio()
-{
-  return &mPreserveAspectRatio;
-}
-
-bool
-SVGSVGElement::HasViewBoxRect() const
-{
-  SVGViewElement* viewElement = GetCurrentViewElement();
-  if ((viewElement && viewElement->mViewBox.HasRect()) ||
-      (mSVGView && mSVGView->mViewBox.HasRect())) {
-    return true;
-  }
-  return mViewBox.HasRect();
-}
-
-bool
-SVGSVGElement::ShouldSynthesizeViewBox() const
-{
-  MOZ_ASSERT(!HasViewBoxRect(), "Should only be called if we lack a viewBox");
-
-  return IsRoot() && OwnerDoc()->IsBeingUsedAsImage();
-}
-
-bool
-SVGSVGElement::SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR)
-{
-  SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
-  nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
-                            pAROverridePtr,
-                            nsINode::DeleteProperty<SVGPreserveAspectRatio>,
-                            true);
-  MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
-             "Setting override value when it's already set...?");
-
-  if (MOZ_UNLIKELY(NS_FAILED(rv))) {
-    // property-insertion failed (e.g. OOM in property-table code)
-    delete pAROverridePtr;
-    return false;
-  }
-  return true;
-}
-
-const SVGPreserveAspectRatio*
-SVGSVGElement::GetPreserveAspectRatioProperty() const
-{
-  void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
-  if (valPtr) {
-    return static_cast<SVGPreserveAspectRatio*>(valPtr);
-  }
-  return nullptr;
-}
-
-bool
-SVGSVGElement::ClearPreserveAspectRatioProperty()
-{
-  void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
-  delete static_cast<SVGPreserveAspectRatio*>(valPtr);
-  return valPtr;
-}
-
 void
 SVGSVGElement::
   SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR)
 {
 #ifdef DEBUG
   MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
              "should only override preserveAspectRatio in images");
 #endif
@@ -1126,51 +719,118 @@ SVGSVGElement::ClearImageOverridePreserv
     mImageNeedsTransformInvalidation = true;
   }
 
   if (ClearPreserveAspectRatioProperty()) {
     mImageNeedsTransformInvalidation = true;
   }
 }
 
-void
-SVGSVGElement::FlushImageTransformInvalidation()
+bool
+SVGSVGElement::SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR)
 {
-  MOZ_ASSERT(!GetParent(), "Should only be called on root node");
-  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
-             "Should only be called on image documents");
+  SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
+  nsresult rv = SetProperty(nsGkAtoms::overridePreserveAspectRatio,
+                            pAROverridePtr,
+                            nsINode::DeleteProperty<SVGPreserveAspectRatio>,
+                            true);
+  MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
+             "Setting override value when it's already set...?");
+
+  if (MOZ_UNLIKELY(NS_FAILED(rv))) {
+    // property-insertion failed (e.g. OOM in property-table code)
+    delete pAROverridePtr;
+    return false;
+  }
+  return true;
+}
 
-  if (mImageNeedsTransformInvalidation) {
-    InvalidateTransformNotifyFrame();
-    mImageNeedsTransformInvalidation = false;
+const SVGPreserveAspectRatio*
+SVGSVGElement::GetPreserveAspectRatioProperty() const
+{
+  void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
+  if (valPtr) {
+    return static_cast<SVGPreserveAspectRatio*>(valPtr);
   }
+  return nullptr;
+}
+
+bool
+SVGSVGElement::ClearPreserveAspectRatioProperty()
+{
+  void* valPtr = UnsetProperty(nsGkAtoms::overridePreserveAspectRatio);
+  delete static_cast<SVGPreserveAspectRatio*>(valPtr);
+  return valPtr;
 }
 
-int32_t
-SVGSVGElement::GetIntrinsicWidth()
+
+SVGPreserveAspectRatio
+SVGSVGElement::GetPreserveAspectRatioWithOverride() const
 {
-  if (mLengthAttributes[ATTR_WIDTH].IsPercentage()) {
-    return -1;
+  nsIDocument* doc = GetUncomposedDoc();
+  if (doc && doc->IsBeingUsedAsImage()) {
+    const SVGPreserveAspectRatio *pAROverridePtr = GetPreserveAspectRatioProperty();
+    if (pAROverridePtr) {
+      return *pAROverridePtr;
+    }
   }
-  // Passing |this| as a SVGSVGElement* invokes the variant of GetAnimValue
-  // that uses the passed argument as the context, but that's fine since we
-  // know the length isn't a percentage so the context won't be used (and we
-  // need to pass the element to be able to resolve em/ex units).
-  float width = mLengthAttributes[ATTR_WIDTH].GetAnimValue(this);
-  return nsSVGUtils::ClampToInt(width);
+
+  SVGViewElement* viewElement = GetCurrentViewElement();
+
+  // This check is equivalent to "!HasViewBoxRect() && ShouldSynthesizeViewBox()".
+  // We're just holding onto the viewElement that HasViewBoxRect() would look up,
+  // so that we don't have to look it up again later.
+  if (!((viewElement && viewElement->mViewBox.HasRect()) ||
+        (mSVGView && mSVGView->mViewBox.HasRect()) ||
+        mViewBox.HasRect()) &&
+      ShouldSynthesizeViewBox()) {
+    // If we're synthesizing a viewBox, use preserveAspectRatio="none";
+    return SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE, SVG_MEETORSLICE_SLICE);
+  }
+
+  if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
+    return viewElement->mPreserveAspectRatio.GetAnimValue();
+  }
+  if (mSVGView && mSVGView->mPreserveAspectRatio.IsExplicitlySet()) {
+    return mSVGView->mPreserveAspectRatio.GetAnimValue();
+  }
+  return mPreserveAspectRatio.GetAnimValue();
 }
 
-int32_t
-SVGSVGElement::GetIntrinsicHeight()
+SVGViewElement*
+SVGSVGElement::GetCurrentViewElement() const
 {
-  if (mLengthAttributes[ATTR_HEIGHT].IsPercentage()) {
-    return -1;
+  if (mCurrentViewID) {
+    //XXXsmaug It is unclear how this should work in case we're in Shadow DOM.
+    nsIDocument* doc = GetUncomposedDoc();
+    if (doc) {
+      Element *element = doc->GetElementById(*mCurrentViewID);
+      if (element && element->IsSVGElement(nsGkAtoms::view)) {
+        return static_cast<SVGViewElement*>(element);
+      }
+    }
   }
-  // Passing |this| as a SVGSVGElement* invokes the variant of GetAnimValue
-  // that uses the passed argument as the context, but that's fine since we
-  // know the length isn't a percentage so the context won't be used (and we
-  // need to pass the element to be able to resolve em/ex units).
-  float height = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(this);
-  return nsSVGUtils::ClampToInt(height);
+  return nullptr;
+}
+
+const nsSVGViewBox&
+SVGSVGElement::GetViewBoxInternal() const
+{
+  SVGViewElement* viewElement = GetCurrentViewElement();
+
+  if (viewElement && viewElement->mViewBox.HasRect()) {
+    return viewElement->mViewBox;
+  } else if (mSVGView && mSVGView->mViewBox.HasRect()) {
+    return mSVGView->mViewBox;
+  }
+
+  return mViewBox;
+}
+
+nsSVGAnimatedTransformList*
+SVGSVGElement::GetTransformInternal() const
+{
+  return (mSVGView && mSVGView->mTransforms)
+         ? mSVGView->mTransforms: mTransforms;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGSVGElement.h
+++ b/dom/svg/SVGSVGElement.h
@@ -2,60 +2,51 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 mozilla_dom_SVGSVGElement_h
 #define mozilla_dom_SVGSVGElement_h
 
-#include "mozilla/dom/FromParser.h"
-#include "nsAutoPtr.h"
-#include "nsIContentInlines.h"
-#include "nsISVGPoint.h"
-#include "nsSVGEnum.h"
-#include "nsSVGLength2.h"
-#include "SVGGraphicsElement.h"
-#include "SVGImageContext.h"
-#include "nsSVGViewBox.h"
-#include "SVGPreserveAspectRatio.h"
-#include "SVGAnimatedPreserveAspectRatio.h"
-#include "mozilla/Attributes.h"
+#include "SVGViewportElement.h"
 
 nsresult NS_NewSVGSVGElement(nsIContent **aResult,
                              already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                              mozilla::dom::FromParser aFromParser);
 
 class nsSMILTimeContainer;
-class nsSVGOuterSVGFrame;
-class nsSVGInnerSVGFrame;
 
 namespace mozilla {
-class AutoPreserveAspectRatioOverride;
-class AutoSVGTimeSetRestore;
-class DOMSVGAnimatedPreserveAspectRatio;
+class AutoSVGViewHandler;
+class SVGFragmentIdentifier;
+class EventChainPreVisitor;
 class DOMSVGLength;
 class DOMSVGNumber;
-class EventChainPreVisitor;
-class SVGFragmentIdentifier;
-class AutoSVGViewHandler;
 
 namespace dom {
 class SVGAngle;
-class SVGAnimatedRect;
 class SVGMatrix;
-class SVGTransform;
-class SVGViewElement;
 class SVGIRect;
+class SVGSVGElement;
 
-class SVGSVGElement;
+// Stores svgView arguments of SVG fragment identifiers.
+class SVGView {
+public:
+  SVGView();
+
+  nsSVGEnum                             mZoomAndPan;
+  nsSVGViewBox                          mViewBox;
+  SVGAnimatedPreserveAspectRatio        mPreserveAspectRatio;
+  nsAutoPtr<nsSVGAnimatedTransformList> mTransforms;
+};
 
 class DOMSVGTranslatePoint final : public nsISVGPoint {
 public:
-  DOMSVGTranslatePoint(SVGPoint* aPt, SVGSVGElement *aElement)
+  DOMSVGTranslatePoint(SVGPoint* aPt, SVGSVGElement* aElement)
     : nsISVGPoint(aPt, true), mElement(aElement) {}
 
   explicit DOMSVGTranslatePoint(DOMSVGTranslatePoint* aPt)
     : nsISVGPoint(&aPt->mPt, true), mElement(aPt->mElement) {}
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
 
@@ -71,55 +62,27 @@ public:
   virtual nsISupports* GetParentObject() override;
 
   RefPtr<SVGSVGElement> mElement;
 
 private:
   ~DOMSVGTranslatePoint() {}
 };
 
-class svgFloatSize {
-public:
-  svgFloatSize(float aWidth, float aHeight)
-    : width(aWidth)
-    , height(aHeight)
-  {}
-  bool operator!=(const svgFloatSize& rhs) {
-    return width != rhs.width || height != rhs.height;
-  }
-  float width;
-  float height;
-};
-
-// Stores svgView arguments of SVG fragment identifiers.
-class SVGView {
-  friend class mozilla::AutoSVGViewHandler;
-  friend class mozilla::dom::SVGSVGElement;
-public:
-  SVGView();
-
-private:
-  nsSVGEnum                             mZoomAndPan;
-  nsSVGViewBox                          mViewBox;
-  SVGAnimatedPreserveAspectRatio        mPreserveAspectRatio;
-  nsAutoPtr<nsSVGAnimatedTransformList> mTransforms;
-};
-
-typedef SVGGraphicsElement SVGSVGElementBase;
+typedef SVGViewportElement SVGSVGElementBase;
 
 class SVGSVGElement final : public SVGSVGElementBase
 {
   friend class ::nsSVGOuterSVGFrame;
-  friend class ::nsSVGInnerSVGFrame;
-  friend class mozilla::AutoPreserveAspectRatioOverride;
-  friend class mozilla::AutoSVGTimeSetRestore;
-  friend class mozilla::dom::SVGView;
   friend class mozilla::SVGFragmentIdentifier;
   friend class mozilla::AutoSVGViewHandler;
+  friend class mozilla::AutoPreserveAspectRatioOverride;
+  friend class mozilla::dom::SVGView;
 
+protected:
   SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                 FromParser aFromParser);
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   friend nsresult (::NS_NewSVGSVGElement(nsIContent **aResult,
                                          already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                                          mozilla::dom::FromParser aFromParser));
 
@@ -134,134 +97,25 @@ public:
    * For use by zoom controls to allow currentScale, currentTranslate.x and
    * currentTranslate.y to be set by a single operation that dispatches a
    * single SVGZoom event (instead of one SVGZoom and two SVGScroll events).
    *
    * XXX SVGZoomEvent is no more, is this needed?
    */
   void SetCurrentScaleTranslate(float s, float x, float y);
 
-  /**
-   * Retrieve the value of currentScale and currentTranslate.
-   */
-  const SVGPoint& GetCurrentTranslate() { return mCurrentTranslate; }
-  float GetCurrentScale() { return mCurrentScale; }
-
-  /**
-   * Retrieve the value of currentScale, currentTranslate.x or
-   * currentTranslate.y prior to the last change made to any one of them.
-   */
-  const SVGPoint& GetPreviousTranslate() { return mPreviousTranslate; }
-  float GetPreviousScale() { return mPreviousScale; }
-
-  nsSMILTimeContainer* GetTimedDocumentRoot();
-
   // nsIContent interface
-  NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override;
   virtual nsresult GetEventTargetParent(
                      EventChainPreVisitor& aVisitor) override;
-
   virtual bool IsEventAttributeName(nsIAtom* aName) override;
 
-  // nsSVGElement specializations:
-  virtual gfxMatrix PrependLocalTransformsTo(
-    const gfxMatrix &aMatrix,
-    SVGTransformTypes aWhich = eAllTransforms) const override;
-  virtual nsSVGAnimatedTransformList*
-	  GetAnimatedTransformList(uint32_t aFlags = 0) override;
-  virtual bool HasValidDimensions() const override;
-
-  // SVGSVGElement methods:
-  float GetLength(uint8_t mCtxType);
-
-  // public helpers:
-
-  /**
-   * Returns -1 if the width/height is a percentage, else returns the user unit
-   * length clamped to fit in a int32_t.
-   * XXX see bug 1112533 comment 3 - we should fix drawImage so that we can
-   * change these methods to make zero the error flag for percentages.
-   */
-  int32_t GetIntrinsicWidth();
-  int32_t GetIntrinsicHeight();
-
-  /**
-   * Returns true if this element has a base/anim value for its "viewBox"
-   * attribute that defines a viewBox rectangle with finite values, or
-   * if there is a view element overriding this element's viewBox and it
-   * has a valid viewBox.
-   *
-   * Note that this does not check whether we need to synthesize a viewBox,
-   * so you must call ShouldSynthesizeViewBox() if you need to check that too.
-   *
-   * Note also that this method does not pay attention to whether the width or
-   * height values of the viewBox rect are positive!
-   */
-  bool HasViewBoxRect() const;
-
-  /**
-   * Returns true if we should synthesize a viewBox for ourselves (that is, if
-   * we're the root element in an image document, and we're not currently being
-   * painted for an <svg:image> element).
-   *
-   * Only call this method if HasViewBoxRect() returns false.
-   */
-  bool ShouldSynthesizeViewBox() const;
-
-  bool HasViewBoxOrSyntheticViewBox() const {
-    return HasViewBoxRect() || ShouldSynthesizeViewBox();
-  }
-
-  gfx::Matrix GetViewBoxTransform() const;
-
-  bool HasChildrenOnlyTransform() const {
-    return mHasChildrenOnlyTransform;
-  }
-
-  void UpdateHasChildrenOnlyTransform();
-
-  enum ChildrenOnlyTransformChangedFlags {
-    eDuringReflow = 1
-  };
-
-  /**
-   * This method notifies the style system that the overflow rects of our
-   * immediate childrens' frames need to be updated. It is called by our own
-   * frame when changes (e.g. to currentScale) cause our children-only
-   * transform to change.
-   *
-   * The reason we have this method instead of overriding
-   * GetAttributeChangeHint is because we need to act on non-attribute (e.g.
-   * currentScale) changes in addition to attribute (e.g. viewBox) changes.
-   */
-  void ChildrenOnlyTransformChanged(uint32_t aFlags = 0);
-
-  // This services any pending notifications for the transform on on this root
-  // <svg> node needing to be recalculated.  (Only applicable in
-  // SVG-as-an-image documents.)
-  virtual void FlushImageTransformInvalidation();
-
+  // nsINode methods:
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
-  // Returns true IFF our attributes are currently overridden by a <view>
-  // element and that element's ID matches the passed-in string.
-  bool IsOverriddenBy(const nsAString &aViewID) const {
-    return mCurrentViewID && mCurrentViewID->Equals(aViewID);
-  }
-
-  svgFloatSize GetViewportSize() const {
-    return svgFloatSize(mViewportWidth, mViewportHeight);
-  }
-
-  void SetViewportSize(const svgFloatSize& aSize) {
-    mViewportWidth  = aSize.width;
-    mViewportHeight = aSize.height;
-  }
-
   // WebIDL
   already_AddRefed<SVGAnimatedLength> X();
   already_AddRefed<SVGAnimatedLength> Y();
   already_AddRefed<SVGAnimatedLength> Width();
   already_AddRefed<SVGAnimatedLength> Height();
   float PixelUnitToMillimeterX();
   float PixelUnitToMillimeterY();
   float ScreenPixelToMillimeterX();
@@ -285,64 +139,72 @@ public:
   already_AddRefed<DOMSVGLength> CreateSVGLength();
   already_AddRefed<SVGAngle> CreateSVGAngle();
   already_AddRefed<nsISVGPoint> CreateSVGPoint();
   already_AddRefed<SVGMatrix> CreateSVGMatrix();
   already_AddRefed<SVGIRect> CreateSVGRect();
   already_AddRefed<SVGTransform> CreateSVGTransform();
   already_AddRefed<SVGTransform> CreateSVGTransformFromMatrix(SVGMatrix& matrix);
   using nsINode::GetElementById; // This does what we want
-  already_AddRefed<SVGAnimatedRect> ViewBox();
-  already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
   uint16_t ZoomAndPan();
   void SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv);
-  virtual nsSVGViewBox* GetViewBox() override;
 
-private:
   // nsSVGElement overrides
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) override;
   virtual void UnbindFromTree(bool aDeep, bool aNullParent) override;
+  virtual nsSVGAnimatedTransformList*
+    GetAnimatedTransformList(uint32_t aFlags = 0) override;
+
+  // SVGSVGElement methods:
+
+  // Returns true IFF our attributes are currently overridden by a <view>
+  // element and that element's ID matches the passed-in string.
+  bool IsOverriddenBy(const nsAString &aViewID) const {
+    return mCurrentViewID && mCurrentViewID->Equals(aViewID);
+  }
+
+  nsSMILTimeContainer* GetTimedDocumentRoot();
+
+  // public helpers:
+
+  /**
+   * Returns -1 if the width/height is a percentage, else returns the user unit
+   * length clamped to fit in a int32_t.
+   * XXX see bug 1112533 comment 3 - we should fix drawImage so that we can
+   * change these methods to make zero the error flag for percentages.
+   */
+  int32_t GetIntrinsicWidth();
+  int32_t GetIntrinsicHeight();
+
+  // This services any pending notifications for the transform on on this root
+  // <svg> node needing to be recalculated.  (Only applicable in
+  // SVG-as-an-image documents.)
+  virtual void FlushImageTransformInvalidation();
+
+  svgFloatSize GetViewportSize() const {
+    return svgFloatSize(mViewportWidth, mViewportHeight);
+  }
+
+  void SetViewportSize(const svgFloatSize& aSize) {
+    mViewportWidth  = aSize.width;
+    mViewportHeight = aSize.height;
+  }
+
+private:
+  // SVGViewportElement methods:
+
+  virtual SVGViewElement* GetCurrentViewElement() const;
+  virtual SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const override;
 
   // implementation helpers:
 
-  SVGViewElement* GetCurrentViewElement() const;
-
-  // Methods for <image> elements to override my "PreserveAspectRatio" value.
-  // These are private so that only our friends
-  // (AutoPreserveAspectRatioOverride in particular) have access.
-  void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR);
-  void ClearImageOverridePreserveAspectRatio();
-
-  // Set/Clear properties to hold old version of preserveAspectRatio
-  // when it's being overridden by an <image> element that we are inside of.
-  bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR);
-  const SVGPreserveAspectRatio* GetPreserveAspectRatioProperty() const;
-  bool ClearPreserveAspectRatioProperty();
-
-  bool IsRoot() const {
-    NS_ASSERTION((IsInUncomposedDoc() && !GetParent()) ==
-                 (OwnerDoc() && (OwnerDoc()->GetRootElement() == this)),
-                 "Can't determine if we're root");
-    return IsInUncomposedDoc() && !GetParent();
-  }
-
-  /**
-   * Returns true if this is an SVG <svg> element that is the child of
-   * another non-foreignObject SVG element.
-   */
-  bool IsInner() const {
-    const nsIContent *parent = GetFlattenedTreeParent();
-    return parent && parent->IsSVGElement() &&
-           !parent->IsSVGElement(nsGkAtoms::foreignObject);
-  }
-
-  /* 
+  /*
    * While binding to the tree we need to determine if we will be the outermost
    * <svg> element _before_ the children are bound (as they want to know what
    * timed document root to register with) and therefore _before_ our parent is
    * set (both actions are performed by Element::BindToTree) so we
    * can't use GetOwnerSVGElement() as it relies on GetParent(). This code is
    * basically a simplified version of GetOwnerSVGElement that uses the parent
    * parameters passed in instead.
    */
@@ -352,62 +214,43 @@ private:
   // invalidate viewbox -> viewport xform & inform frames
   void InvalidateTransformNotifyFrame();
 
   // Returns true if we have at least one of the following:
   // - a (valid or invalid) value for the preserveAspectRatio attribute
   // - a SMIL-animated value for the preserveAspectRatio attribute
   bool HasPreserveAspectRatio();
 
- /**
-  * Returns the explicit viewBox rect, if specified, or else a synthesized
-  * viewBox, if appropriate, or else a viewBox matching the dimensions of the
-  * SVG viewport.
-  */
-  nsSVGViewBoxRect GetViewBoxWithSynthesis(
-      float aViewportWidth, float aViewportHeight) const;
-  /**
-   * Returns the explicit or default preserveAspectRatio, unless we're
-   * synthesizing a viewBox, in which case it returns the "none" value.
-   */
-  SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const;
+  // Methods for <image> elements to override my "PreserveAspectRatio" value.
+  // These are private so that only our friends
+  // (AutoPreserveAspectRatioOverride in particular) have access.
+  void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR);
+  void ClearImageOverridePreserveAspectRatio();
 
-  virtual LengthAttributesInfo GetLengthInfo() override;
+  // Set/Clear properties to hold old version of preserveAspectRatio
+  // when it's being overridden by an <image> element that we are inside of.
+  bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR);
+  const SVGPreserveAspectRatio* GetPreserveAspectRatioProperty() const;
+  bool ClearPreserveAspectRatioProperty();
 
-  enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT };
-  nsSVGLength2 mLengthAttributes[4];
-  static LengthInfo sLengthInfo[4];
+  virtual SVGPoint GetCurrentTranslate() const override
+  { return mCurrentTranslate; }
+  virtual float GetCurrentScale() const override
+  { return mCurrentScale; }
+
+  virtual const nsSVGViewBox& GetViewBoxInternal() const override;
+  virtual nsSVGAnimatedTransformList* GetTransformInternal() const override;
 
   virtual EnumAttributesInfo GetEnumInfo() override;
 
   enum { ZOOMANDPAN };
   nsSVGEnum mEnumAttributes[1];
   static nsSVGEnumMapping sZoomAndPanMap[];
   static EnumInfo sEnumInfo[1];
 
-  virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override;
-
-  nsSVGViewBox                   mViewBox;
-  SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
-
-  // mCurrentViewID and mSVGView are mutually exclusive; we can have
-  // at most one non-null.
-  nsAutoPtr<nsString>            mCurrentViewID;
-  nsAutoPtr<SVGView>             mSVGView;
-
-  // The size of the rectangular SVG viewport into which we render. This is
-  // not (necessarily) the same as the content area. See:
-  //
-  //   http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
-  //
-  // XXXjwatt Currently only used for outer <svg>, but maybe we could use -1 to
-  // flag this as an inner <svg> to save the overhead of GetCtx calls?
-  // XXXjwatt our frame should probably reset these when it's destroyed.
-  float mViewportWidth, mViewportHeight;
-
   // The time container for animations within this SVG document fragment. Set
   // for all outermost <svg> elements (not nested <svg> elements).
   nsAutoPtr<nsSMILTimeContainer> mTimedDocumentRoot;
 
   // zoom and pan
   // IMPORTANT: see the comment in RecordCurrentScaleTranslate before writing
   // code to change any of these!
   SVGPoint mCurrentTranslate;
@@ -415,33 +258,62 @@ private:
   SVGPoint mPreviousTranslate;
   float    mPreviousScale;
 
   // For outermost <svg> elements created from parsing, animation is started by
   // the onload event in accordance with the SVG spec, but for <svg> elements
   // created by script or promoted from inner <svg> to outermost <svg> we need
   // to manually kick off animation when they are bound to the tree.
   bool     mStartAnimationOnBindToTree;
+
   bool     mImageNeedsTransformInvalidation;
-  bool     mHasChildrenOnlyTransform;
+
+  // mCurrentViewID and mSVGView are mutually exclusive; we can have
+  // at most one non-null.
+  nsAutoPtr<nsString>            mCurrentViewID;
+  nsAutoPtr<SVGView>             mSVGView;
 };
 
 } // namespace dom
 
+class MOZ_RAII AutoSVGTimeSetRestore
+{
+public:
+  AutoSVGTimeSetRestore(dom::SVGSVGElement* aRootElem,
+                        float aFrameTime
+                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    : mRootElem(aRootElem)
+    , mOriginalTime(mRootElem->GetCurrentTime())
+  {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    mRootElem->SetCurrentTime(aFrameTime); // Does nothing if there's no change.
+  }
+
+  ~AutoSVGTimeSetRestore()
+  {
+    mRootElem->SetCurrentTime(mOriginalTime);
+  }
+
+private:
+  const RefPtr<dom::SVGSVGElement> mRootElem;
+  const float mOriginalTime;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 class MOZ_RAII AutoPreserveAspectRatioOverride
 {
 public:
   AutoPreserveAspectRatioOverride(const Maybe<SVGImageContext>& aSVGContext,
                                   dom::SVGSVGElement* aRootElem
                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mRootElem(aRootElem)
     , mDidOverride(false)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    MOZ_ASSERT(mRootElem, "No SVG node to manage?");
+    MOZ_ASSERT(mRootElem, "No SVG/Symbol node to manage?");
 
     if (aSVGContext.isSome() &&
         aSVGContext->GetPreserveAspectRatio().isSome()) {
       // Override preserveAspectRatio in our helper document.
       // XXXdholbert We should technically be overriding the helper doc's clip
       // and overflow properties here, too. See bug 272288 comment 36.
       mRootElem->SetImageOverridePreserveAspectRatio(
                    *aSVGContext->GetPreserveAspectRatio());
@@ -457,35 +329,11 @@ public:
   }
 
 private:
   const RefPtr<dom::SVGSVGElement> mRootElem;
   bool mDidOverride;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class MOZ_RAII AutoSVGTimeSetRestore
-{
-public:
-  AutoSVGTimeSetRestore(dom::SVGSVGElement* aRootElem,
-                        float aFrameTime
-                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-    : mRootElem(aRootElem)
-    , mOriginalTime(mRootElem->GetCurrentTime())
-  {
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    mRootElem->SetCurrentTime(aFrameTime); // Does nothing if there's no change.
-  }
-
-  ~AutoSVGTimeSetRestore()
-  {
-    mRootElem->SetCurrentTime(mOriginalTime);
-  }
-
-private:
-  const RefPtr<dom::SVGSVGElement> mRootElem;
-  const float mOriginalTime;
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 } // namespace mozilla
 
 #endif // SVGSVGElement_h
--- a/dom/svg/SVGSymbolElement.cpp
+++ b/dom/svg/SVGSymbolElement.cpp
@@ -38,72 +38,18 @@ SVGSymbolElement::~SVGSymbolElement()
 }
 
 //----------------------------------------------------------------------
 // nsIDOMNode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSymbolElement)
 
 //----------------------------------------------------------------------
-
-already_AddRefed<SVGAnimatedRect>
-SVGSymbolElement::ViewBox()
-{
-  return mViewBox.ToSVGAnimatedRect(this);
-}
-
-already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
-SVGSymbolElement::PreserveAspectRatio()
-{
-  return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
-}
-
-//----------------------------------------------------------------------
-// nsIContent methods
-
-NS_IMETHODIMP_(bool)
-SVGSymbolElement::IsAttributeMapped(const nsIAtom* name) const
-{
-  static const MappedAttributeEntry* const map[] = {
-    sColorMap,
-    sFEFloodMap,
-    sFillStrokeMap,
-    sFiltersMap,
-    sFontSpecificationMap,
-    sGradientStopMap,
-    sGraphicsMap,
-    sLightingEffectsMap,
-    sMarkersMap,
-    sTextContentElementsMap,
-    sViewportsMap
-   };
-
-  return FindAttributeDependence(name, map) ||
-    SVGSymbolElementBase::IsAttributeMapped(name);
-}
-
-//----------------------------------------------------------------------
 // SVGTests methods
 
 bool
 SVGSymbolElement::IsInChromeDoc() const
 {
   return nsContentUtils::IsChromeDoc(OwnerDoc());
 }
 
-
-//----------------------------------------------------------------------
-// nsSVGElement methods
-
-nsSVGViewBox *
-SVGSymbolElement::GetViewBox()
-{
-  return &mViewBox;
-}
-
-SVGAnimatedPreserveAspectRatio *
-SVGSymbolElement::GetPreserveAspectRatio()
-{
-  return &mPreserveAspectRatio;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGSymbolElement.h
+++ b/dom/svg/SVGSymbolElement.h
@@ -2,61 +2,42 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 mozilla_dom_SVGSymbolElement_h
 #define mozilla_dom_SVGSymbolElement_h
 
-#include "mozilla/dom/SVGTests.h"
-#include "nsSVGElement.h"
-#include "nsSVGViewBox.h"
-#include "SVGAnimatedPreserveAspectRatio.h"
+#include "SVGViewportElement.h"
 
 nsresult NS_NewSVGSymbolElement(nsIContent **aResult,
                                 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
-typedef nsSVGElement SVGSymbolElementBase;
+typedef SVGViewportElement SVGSymbolElementBase;
 
-class SVGSymbolElement final : public SVGSymbolElementBase,
-                               public SVGTests
+class SVGSymbolElement final : public SVGSymbolElementBase
 {
 protected:
   friend nsresult (::NS_NewSVGSymbolElement(nsIContent **aResult,
                                             already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   explicit SVGSymbolElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   ~SVGSymbolElement();
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
   // interfaces:
-
   NS_DECL_ISUPPORTS_INHERITED
 
-  // nsIContent interface
-  NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const override;
-
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
-  // WebIDL
-  already_AddRefed<SVGAnimatedRect> ViewBox();
-  already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
-
   // SVGTests
   bool IsInChromeDoc() const override;
-
-protected:
-  virtual nsSVGViewBox *GetViewBox() override;
-  virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override;
-
-  nsSVGViewBox mViewBox;
-  SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SVGSymbolElement_h
--- a/dom/svg/SVGViewElement.h
+++ b/dom/svg/SVGViewElement.h
@@ -23,23 +23,24 @@ class nsSVGOuterSVGFrame;
 
 nsresult NS_NewSVGViewElement(nsIContent **aResult,
                               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 class SVGFragmentIdentifier;
 
 namespace dom {
-class SVGSVGElement;
+class SVGViewportElement;
 
 class SVGViewElement : public SVGViewElementBase
 {
 protected:
   friend class mozilla::SVGFragmentIdentifier;
   friend class SVGSVGElement;
+  friend class SVGViewportElement;
   friend class ::nsSVGOuterSVGFrame;
   explicit SVGViewElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   friend nsresult (::NS_NewSVGViewElement(nsIContent **aResult,
                                           already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   virtual JSObject* WrapNode(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
 public:
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
new file mode 100644
--- /dev/null
+++ b/dom/svg/SVGViewportElement.cpp
@@ -0,0 +1,374 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 <stdint.h>
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/ContentEvents.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/Likely.h"
+#include "mozilla/dom/SVGMatrix.h"
+#include "mozilla/dom/SVGViewportElement.h"
+#include "mozilla/dom/SVGViewElement.h"
+
+#include "DOMSVGLength.h"
+#include "DOMSVGPoint.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsFrameSelection.h"
+#include "nsError.h"
+#include "nsGkAtoms.h"
+#include "nsIDocument.h"
+#include "nsIFrame.h"
+#include "nsIPresShell.h"
+#include "nsISVGSVGFrame.h" //XXX
+#include "nsLayoutUtils.h"
+#include "nsStyleUtil.h"
+#include "nsSMILTypes.h"
+#include "SVGContentUtils.h"
+
+#include <algorithm>
+#include "prtime.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace dom {
+
+nsSVGElement::LengthInfo SVGViewportElement::sLengthInfo[4] =
+{
+  { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
+  { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
+  { &nsGkAtoms::width, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X },
+  { &nsGkAtoms::height, 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y },
+};
+
+//----------------------------------------------------------------------
+// Implementation
+
+SVGViewportElement::SVGViewportElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+  : SVGGraphicsElement(aNodeInfo),
+    mViewportWidth(0),
+    mViewportHeight(0),
+    mHasChildrenOnlyTransform(false)
+{
+}
+
+SVGViewportElement::~SVGViewportElement()
+{
+}
+
+//----------------------------------------------------------------------
+
+already_AddRefed<SVGAnimatedRect>
+SVGViewportElement::ViewBox()
+{
+  return mViewBox.ToSVGAnimatedRect(this);
+}
+
+already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
+SVGViewportElement::PreserveAspectRatio()
+{
+  return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
+}
+
+//----------------------------------------------------------------------
+// nsIContent methods
+
+NS_IMETHODIMP_(bool)
+SVGViewportElement::IsAttributeMapped(const nsIAtom* name) const
+{
+  // We want to map the 'width' and 'height' attributes into style for
+  // outer-<svg>, except when the attributes aren't set (since their default
+  // values of '100%' can cause unexpected and undesirable behaviour for SVG
+  // inline in HTML). We rely on nsSVGElement::UpdateContentStyleRule() to
+  // prevent mapping of the default values into style (it only maps attributes
+  // that are set). We also rely on a check in nsSVGElement::
+  // UpdateContentStyleRule() to prevent us mapping the attributes when they're
+  // given a <length> value that is not currently recognized by the SVG
+  // specification.
+
+  if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) {
+    return true;
+  }
+
+  static const MappedAttributeEntry* const map[] = {
+    sColorMap,
+    sFEFloodMap,
+    sFillStrokeMap,
+    sFiltersMap,
+    sFontSpecificationMap,
+    sGradientStopMap,
+    sGraphicsMap,
+    sLightingEffectsMap,
+    sMarkersMap,
+    sTextContentElementsMap,
+    sViewportsMap
+  };
+
+  return FindAttributeDependence(name, map) ||
+    SVGGraphicsElement::IsAttributeMapped(name);
+}
+
+//----------------------------------------------------------------------
+// nsSVGElement overrides
+
+// Helper for GetViewBoxTransform on root <svg> node
+// * aLength: internal value for our <svg> width or height attribute.
+// * aViewportLength: length of the corresponding dimension of the viewport.
+// * aSelf: the outermost <svg> node itself.
+// NOTE: aSelf is not an ancestor viewport element, so it can't be used to
+// resolve percentage lengths. (It can only be used to resolve
+// 'em'/'ex'-valued units).
+inline float
+ComputeSynthesizedViewBoxDimension(const nsSVGLength2& aLength,
+                                   float aViewportLength,
+                                   const SVGViewportElement* aSelf)
+{
+  if (aLength.IsPercentage()) {
+    return aViewportLength * aLength.GetAnimValInSpecifiedUnits() / 100.0f;
+  }
+
+  return aLength.GetAnimValue(const_cast<SVGViewportElement*>(aSelf));
+}
+
+//----------------------------------------------------------------------
+// public helpers:
+
+void
+SVGViewportElement::UpdateHasChildrenOnlyTransform()
+{
+  bool hasChildrenOnlyTransform =
+    HasViewBoxOrSyntheticViewBox() ||
+    (IsRoot() && (GetCurrentTranslate() != SVGPoint(0.0f, 0.0f) ||
+                  GetCurrentScale() != 1.0f));
+  mHasChildrenOnlyTransform = hasChildrenOnlyTransform;
+}
+
+void
+SVGViewportElement::ChildrenOnlyTransformChanged(uint32_t aFlags)
+{
+  // Avoid wasteful calls:
+  MOZ_ASSERT(!(GetPrimaryFrame()->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
+             "Non-display SVG frames don't maintain overflow rects");
+
+  nsChangeHint changeHint;
+
+  bool hadChildrenOnlyTransform = mHasChildrenOnlyTransform;
+
+  UpdateHasChildrenOnlyTransform();
+
+  if (hadChildrenOnlyTransform != mHasChildrenOnlyTransform) {
+    // Reconstruct the frame tree to handle stacking context changes:
+    // XXXjwatt don't do this for root-<svg> or even outer-<svg>?
+    changeHint = nsChangeHint_ReconstructFrame;
+  } else {
+    // We just assume the old and new transforms are different.
+    changeHint = nsChangeHint(nsChangeHint_UpdateOverflow |
+                              nsChangeHint_ChildrenOnlyTransform);
+  }
+
+  // If we're not reconstructing the frame tree, then we only call
+  // PostRestyleEvent if we're not being called under reflow to avoid recursing
+  // to death. See bug 767056 comments 10 and 12. Since our nsSVGOuterSVGFrame
+  // is being reflowed we're going to invalidate and repaint its entire area
+  // anyway (which will include our children).
+  if ((changeHint & nsChangeHint_ReconstructFrame) ||
+      !(aFlags & eDuringReflow)) {
+    nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
+  }
+}
+
+gfx::Matrix
+SVGViewportElement::GetViewBoxTransform() const
+{
+  float viewportWidth, viewportHeight;
+  if (IsInner()) {
+    SVGViewportElement *ctx = GetCtx();
+    viewportWidth = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
+    viewportHeight = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
+  } else {
+    viewportWidth = mViewportWidth;
+    viewportHeight = mViewportHeight;
+  }
+
+  if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
+    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
+  }
+
+  nsSVGViewBoxRect viewBox =
+    GetViewBoxWithSynthesis(viewportWidth, viewportHeight);
+
+  if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
+    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
+  }
+
+  return SVGContentUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
+                                              viewBox.x, viewBox.y,
+                                              viewBox.width, viewBox.height,
+                                              GetPreserveAspectRatioWithOverride());
+}
+//----------------------------------------------------------------------
+// SVGViewportElement
+
+float
+SVGViewportElement::GetLength(uint8_t aCtxType)
+{
+  const nsSVGViewBoxRect* viewbox =
+    GetViewBoxInternal().HasRect() ? &GetViewBoxInternal().GetAnimValue()
+                                   : nullptr;
+
+  float h, w;
+  if (viewbox) {
+    w = viewbox->width;
+    h = viewbox->height;
+  } else if (IsInner()) {
+    SVGViewportElement *ctx = GetCtx();
+    w = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
+    h = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
+  } else if (ShouldSynthesizeViewBox()) {
+    w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
+                                           mViewportWidth, this);
+    h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
+                                           mViewportHeight, this);
+  } else {
+    w = mViewportWidth;
+    h = mViewportHeight;
+  }
+
+  w = std::max(w, 0.0f);
+  h = std::max(h, 0.0f);
+
+  switch (aCtxType) {
+  case SVGContentUtils::X:
+    return w;
+  case SVGContentUtils::Y:
+    return h;
+  case SVGContentUtils::XY:
+    return float(SVGContentUtils::ComputeNormalizedHypotenuse(w, h));
+  }
+  return 0;
+}
+
+//----------------------------------------------------------------------
+// nsSVGElement methods
+
+/* virtual */ gfxMatrix
+SVGViewportElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
+                                        SVGTransformTypes aWhich) const
+{
+  // 'transform' attribute (or an override from a fragment identifier):
+  gfxMatrix userToParent;
+
+  if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
+    userToParent = GetUserToParentTransform(mAnimateMotionTransform,
+                                            GetTransformInternal());
+    if (aWhich == eUserSpaceToParent) {
+      return userToParent * aMatrix;
+    }
+  }
+
+  gfxMatrix childToUser;
+
+  if (IsInner()) {
+    float x, y;
+    const_cast<SVGViewportElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
+    childToUser = ThebesMatrix(GetViewBoxTransform().PostTranslate(x, y));
+  } else if (IsRoot()) {
+    SVGPoint translate = GetCurrentTranslate();
+    float scale = GetCurrentScale();
+    childToUser = ThebesMatrix(GetViewBoxTransform()
+                                 .PostScale(scale, scale)
+                                 .PostTranslate(translate.GetX(),
+                                                translate.GetY()));
+  } else {
+    // outer-<svg>, but inline in some other content:
+    childToUser = ThebesMatrix(GetViewBoxTransform());
+  }
+
+  if (aWhich == eAllTransforms) {
+    return childToUser * userToParent * aMatrix;
+  }
+
+  MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
+
+  // The following may look broken because pre-multiplying our eChildToUserSpace
+  // transform with another matrix without including our eUserSpaceToParent
+  // transform between the two wouldn't make sense.  We don't expect that to
+  // ever happen though.  We get here either when the identity matrix has been
+  // passed because our caller just wants our eChildToUserSpace transform, or
+  // when our eUserSpaceToParent transform has already been multiplied into the
+  // matrix that our caller passes (such as when we're called from PaintSVG).
+  return childToUser * aMatrix;
+}
+
+/* virtual */ bool
+SVGViewportElement::HasValidDimensions() const
+{
+  return !IsInner() ||
+    ((!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
+       mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
+     (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
+       mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0));
+}
+
+
+
+nsSVGViewBox*
+SVGViewportElement::GetViewBox()
+{
+  return &mViewBox;
+}
+
+SVGAnimatedPreserveAspectRatio *
+SVGViewportElement::GetPreserveAspectRatio()
+{
+  return &mPreserveAspectRatio;
+}
+
+bool
+SVGViewportElement::ShouldSynthesizeViewBox() const
+{
+  MOZ_ASSERT(!HasViewBoxRect(), "Should only be called if we lack a viewBox");
+
+  return IsRoot() && OwnerDoc()->IsBeingUsedAsImage();
+}
+
+//----------------------------------------------------------------------
+// implementation helpers
+
+nsSVGViewBoxRect
+SVGViewportElement::GetViewBoxWithSynthesis(
+  float aViewportWidth, float aViewportHeight) const
+{
+  if (GetViewBoxInternal().HasRect()) {
+    return GetViewBoxInternal().GetAnimValue();
+  }
+
+  if (ShouldSynthesizeViewBox()) {
+    // Special case -- fake a viewBox, using height & width attrs.
+    // (Use |this| as context, since if we get here, we're outermost <svg>.)
+    return nsSVGViewBoxRect(0, 0,
+              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
+                                                 mViewportWidth, this),
+              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
+                                                 mViewportHeight, this));
+
+  }
+
+  // No viewBox attribute, so we shouldn't auto-scale. This is equivalent
+  // to having a viewBox that exactly matches our viewport size.
+  return nsSVGViewBoxRect(0, 0, aViewportWidth, aViewportHeight);
+}
+
+nsSVGElement::LengthAttributesInfo
+SVGViewportElement::GetLengthInfo()
+{
+  return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
+                              ArrayLength(sLengthInfo));
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/svg/SVGViewportElement.h
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 mozilla_dom_SVGViewportElement_h
+#define mozilla_dom_SVGViewportElement_h
+
+#include "mozilla/dom/FromParser.h"
+#include "nsAutoPtr.h"
+#include "nsIContentInlines.h"
+#include "nsISVGPoint.h"
+#include "nsSVGEnum.h"
+#include "nsSVGLength2.h"
+#include "SVGGraphicsElement.h"
+#include "SVGImageContext.h"
+#include "nsSVGViewBox.h"
+#include "SVGPreserveAspectRatio.h"
+#include "SVGAnimatedPreserveAspectRatio.h"
+#include "mozilla/Attributes.h"
+
+class nsSVGOuterSVGFrame;
+class nsSVGInnerSVGFrame;
+
+namespace mozilla {
+class AutoPreserveAspectRatioOverride;
+class DOMSVGAnimatedPreserveAspectRatio;
+
+namespace dom {
+class SVGAnimatedRect;
+class SVGTransform;
+class SVGViewElement;
+class SVGViewportElement;
+
+class svgFloatSize {
+public:
+  svgFloatSize(float aWidth, float aHeight)
+    : width(aWidth)
+    , height(aHeight)
+  {}
+  bool operator!=(const svgFloatSize& rhs) {
+    return width != rhs.width || height != rhs.height;
+  }
+  float width;
+  float height;
+};
+
+class SVGViewportElement : public SVGGraphicsElement
+{
+  friend class ::nsSVGOuterSVGFrame;
+  friend class ::nsSVGInnerSVGFrame;
+
+protected:
+
+  SVGViewportElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+  ~SVGViewportElement();
+
+public:
+
+  // nsIContent interface
+  NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override;
+
+  // nsSVGElement specializations:
+  virtual gfxMatrix PrependLocalTransformsTo(
+    const gfxMatrix &aMatrix,
+    SVGTransformTypes aWhich = eAllTransforms) const override;
+
+  virtual bool HasValidDimensions() const override;
+
+  // SVGViewportElement methods:
+
+  float GetLength(uint8_t mCtxType);
+
+  // public helpers:
+
+  /**
+   * Returns true if this element has a base/anim value for its "viewBox"
+   * attribute that defines a viewBox rectangle with finite values, or
+   * if there is a view element overriding this element's viewBox and it
+   * has a valid viewBox.
+   *
+   * Note that this does not check whether we need to synthesize a viewBox,
+   * so you must call ShouldSynthesizeViewBox() if you need to chck that too.
+   *
+   * Note also that this method does not pay attention to whether the width or
+   * height values of the viewBox rect are positive!
+   */
+  bool HasViewBoxRect() const {
+    return GetViewBoxInternal().HasRect();
+  }
+
+  /**
+   * Returns true if we should synthesize a viewBox for ourselves (that is, if
+   * we're the root element in an image document, and we're not currently being
+   * painted for an <svg:image> element).
+   *
+   * Only call this method if HasViewBoxRect() returns false.
+   */
+  bool ShouldSynthesizeViewBox() const;
+
+  bool HasViewBoxOrSyntheticViewBox() const {
+    return HasViewBoxRect() || ShouldSynthesizeViewBox();
+  }
+
+  bool HasChildrenOnlyTransform() const {
+    return mHasChildrenOnlyTransform;
+  }
+
+  void UpdateHasChildrenOnlyTransform();
+
+  enum ChildrenOnlyTransformChangedFlags {
+    eDuringReflow = 1
+  };
+
+  /**
+   * This method notifies the style system that the overflow rects of our
+   * immediate childrens' frames need to be updated. It is called by our own
+   * frame when changes (e.g. to currentScale) cause our children-only
+   * transform to change.
+   *
+   * The reason we have this method instead of overriding
+   * GetAttributeChangeHint is because we need to act on non-attribute (e.g.
+   * currentScale) changes in addition to attribute (e.g. viewBox) changes.
+   */
+  void ChildrenOnlyTransformChanged(uint32_t aFlags = 0);
+
+  gfx::Matrix GetViewBoxTransform() const;
+
+  // WebIDL
+  already_AddRefed<SVGAnimatedRect> ViewBox();
+  already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
+  virtual nsSVGViewBox* GetViewBox() override;
+
+protected:
+
+  // implementation helpers:
+
+  bool IsRoot() const {
+    NS_ASSERTION((IsInUncomposedDoc() && !GetParent()) ==
+                 (OwnerDoc() && (OwnerDoc()->GetRootElement() == this)),
+                 "Can't determine if we're root");
+    return IsInUncomposedDoc() && !GetParent();
+  }
+
+  /**
+   * Returns true if either this is an SVG <svg> element that is the child of
+   * another non-foreignObject SVG element, or this is a SVG <symbol> element
+   * this is the root of a use-element shadow tree.
+   */
+  bool IsInner() const {
+    const nsIContent *parent = GetFlattenedTreeParent();
+    return parent && parent->IsSVGElement() &&
+           !parent->IsSVGElement(nsGkAtoms::foreignObject);
+  }
+
+  /**
+   * Returns the explicit or default preserveAspectRatio, unless we're
+   * synthesizing a viewBox, in which case it returns the "none" value.
+   */
+  virtual SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const {
+    return mPreserveAspectRatio.GetAnimValue();
+  }
+
+  /**
+   * Returns the explicit viewBox rect, if specified, or else a synthesized
+   * viewBox, if appropriate, or else a viewBox matching the dimensions of the
+   * SVG viewport.
+   */
+  nsSVGViewBoxRect GetViewBoxWithSynthesis(
+      float aViewportWidth, float aViewportHeight) const;
+
+  /**
+   * Retrieve the value of currentScale and currentTranslate.
+   */
+  virtual SVGPoint GetCurrentTranslate() const
+  { return SVGPoint(0.0f, 0.0f); }
+  virtual float GetCurrentScale() const
+  { return 1.0f; }
+
+  enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT };
+  nsSVGLength2 mLengthAttributes[4];
+  static LengthInfo sLengthInfo[4];
+  virtual LengthAttributesInfo GetLengthInfo() override;
+
+  virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override;
+
+  virtual const nsSVGViewBox& GetViewBoxInternal() const { return mViewBox; }
+  virtual nsSVGAnimatedTransformList* GetTransformInternal() const {
+    return mTransforms;
+  }
+  nsSVGViewBox                   mViewBox;
+  SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
+
+  // The size of the rectangular SVG viewport into which we render. This is
+  // not (necessarily) the same as the content area. See:
+  //
+  //   http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
+  //
+  // XXXjwatt Currently only used for outer <svg>, but maybe we could use -1 to
+  // flag this as an inner <svg> to save the overhead of GetCtx calls?
+  // XXXjwatt our frame should probably reset these when it's destroyed.
+  float mViewportWidth, mViewportHeight;
+
+  bool     mHasChildrenOnlyTransform;
+};
+
+} // namespace dom
+
+} // namespace mozilla
+
+#endif // SVGViewportElement_h
--- a/dom/svg/moz.build
+++ b/dom/svg/moz.build
@@ -97,16 +97,17 @@ EXPORTS.mozilla.dom += [
     'SVGTextPathElement.h',
     'SVGTextPositioningElement.h',
     'SVGTitleElement.h',
     'SVGTransform.h',
     'SVGTransformableElement.h',
     'SVGTSpanElement.h',
     'SVGUseElement.h',
     'SVGViewElement.h',
+    'SVGViewportElement.h',
 ]
 
 UNIFIED_SOURCES += [
     'DOMSVGAnimatedLengthList.cpp',
     'DOMSVGAnimatedNumberList.cpp',
     'DOMSVGLength.cpp',
     'DOMSVGLengthList.cpp',
     'DOMSVGNumber.cpp',
@@ -243,16 +244,17 @@ UNIFIED_SOURCES += [
     'SVGTransformableElement.cpp',
     'SVGTransformList.cpp',
     'SVGTransformListParser.cpp',
     'SVGTransformListSMILType.cpp',
     'SVGTSpanElement.cpp',
     'SVGUseElement.cpp',
     'SVGViewBoxSMILType.cpp',
     'SVGViewElement.cpp',
+    'SVGViewportElement.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/dom',
     '/dom/base',
--- a/layout/svg/nsSVGInnerSVGFrame.cpp
+++ b/layout/svg/nsSVGInnerSVGFrame.cpp
@@ -8,17 +8,17 @@
 
 // Keep others in (case-insensitive) order:
 #include "gfx2DGlue.h"
 #include "gfxContext.h"
 #include "nsIFrame.h"
 #include "nsSVGDisplayableFrame.h"
 #include "nsSVGContainerFrame.h"
 #include "nsSVGIntegrationUtils.h"
-#include "mozilla/dom/SVGSVGElement.h"
+#include "mozilla/dom/SVGViewportElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
 nsIFrame*
 NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
@@ -62,17 +62,17 @@ nsSVGInnerSVGFrame::PaintSVG(gfxContext&
                (mState & NS_FRAME_IS_NONDISPLAY),
                "If display lists are enabled, only painting of non-display "
                "SVG should take this code path");
 
   gfxContextAutoSaveRestore autoSR;
 
   if (StyleDisplay()->IsScrollableOverflow()) {
     float x, y, width, height;
-    static_cast<SVGSVGElement*>(mContent)->
+    static_cast<SVGViewportElement*>(mContent)->
       GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
 
     if (width <= 0 || height <= 0) {
       return;
     }
 
     autoSR.SetContext(&aContext);
     gfxRect clipRect =
@@ -85,17 +85,17 @@ nsSVGInnerSVGFrame::PaintSVG(gfxContext&
 }
 
 void
 nsSVGInnerSVGFrame::ReflowSVG()
 {
   // mRect must be set before FinishAndStoreOverflow is called in order
   // for our overflow areas to be clipped correctly.
   float x, y, width, height;
-  static_cast<SVGSVGElement*>(mContent)->
+  static_cast<SVGViewportElement*>(mContent)->
     GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
   mRect = nsLayoutUtils::RoundGfxRectToAppRect(
                            gfxRect(x, y, width, height),
                            PresContext()->AppUnitsPerCSSPixel());
 
   // If we have a filter, we need to invalidate ourselves because filter
   // output can change even if none of our descendants need repainting.
   if (StyleEffects()->HasFilters()) {
@@ -108,24 +108,24 @@ nsSVGInnerSVGFrame::ReflowSVG()
 void
 nsSVGInnerSVGFrame::NotifySVGChanged(uint32_t aFlags)
 {
   MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
              "Invalidation logic may need adjusting");
 
   if (aFlags & COORD_CONTEXT_CHANGED) {
 
-    SVGSVGElement *svg = static_cast<SVGSVGElement*>(mContent);
+    SVGViewportElement *svg = static_cast<SVGViewportElement*>(mContent);
 
     bool xOrYIsPercentage =
-      svg->mLengthAttributes[SVGSVGElement::ATTR_X].IsPercentage() ||
-      svg->mLengthAttributes[SVGSVGElement::ATTR_Y].IsPercentage();
+      svg->mLengthAttributes[SVGViewportElement::ATTR_X].IsPercentage() ||
+      svg->mLengthAttributes[SVGViewportElement::ATTR_Y].IsPercentage();
     bool widthOrHeightIsPercentage =
-      svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH].IsPercentage() ||
-      svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT].IsPercentage();
+      svg->mLengthAttributes[SVGViewportElement::ATTR_WIDTH].IsPercentage() ||
+      svg->mLengthAttributes[SVGViewportElement::ATTR_HEIGHT].IsPercentage();
 
     if (xOrYIsPercentage || widthOrHeightIsPercentage) {
       // Ancestor changes can't affect how we render from the perspective of
       // any rendering observers that we may have, so we don't need to
       // invalidate them. We also don't need to invalidate ourself, since our
       // changed ancestor will have invalidated its entire area, which includes
       // our area.
       // For perf reasons we call this before calling NotifySVGChanged() below.
@@ -175,17 +175,17 @@ nsSVGInnerSVGFrame::GetBBoxContribution(
   if (aFlags & nsSVGUtils::eForGetClientRects) {
     // XXXjwatt For consistency with the old code this code includes the
     // viewport we establish in the result, but only includes the bounds of our
     // descendants if they are not clipped to that viewport.  However, this is
     // both inconsistent with Chrome and with the specs.  See bug 1350755.
     // Ideally getClientRects/getBoundingClientRect should be consistent with
     // getBBox.
     float x, y, w, h;
-    static_cast<SVGSVGElement*>(mContent)->
+    static_cast<SVGViewportElement*>(mContent)->
       GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
     if (w < 0.0f) w = 0.0f;
     if (h < 0.0f) h = 0.0f;
     Rect viewport(x, y, w, h);
     bbox = aToBBoxUserspace.TransformBounds(viewport);
     if (StyleDisplay()->IsScrollableOverflow()) {
       return bbox;
     }
@@ -204,17 +204,17 @@ nsSVGInnerSVGFrame::GetBBoxContribution(
 nsresult
 nsSVGInnerSVGFrame::AttributeChanged(int32_t  aNameSpaceID,
                                      nsIAtom* aAttribute,
                                      int32_t  aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       !(GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
 
-    SVGSVGElement* content = static_cast<SVGSVGElement*>(mContent);
+    SVGViewportElement* content = static_cast<SVGViewportElement*>(mContent);
 
     if (aAttribute == nsGkAtoms::width ||
         aAttribute == nsGkAtoms::height) {
       nsLayoutUtils::PostRestyleEvent(
         mContent->AsElement(), nsRestyleHint(0),
         nsChangeHint_InvalidateRenderingObservers);
       nsSVGUtils::ScheduleReflowSVG(this);
 
@@ -308,17 +308,17 @@ nsSVGInnerSVGFrame::NotifyViewportOrTran
 
 gfxMatrix
 nsSVGInnerSVGFrame::GetCanvasTM()
 {
   if (!mCanvasTM) {
     NS_ASSERTION(GetParent(), "null parent");
 
     nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
-    SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
+    SVGViewportElement *content = static_cast<SVGViewportElement*>(mContent);
 
     gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
 
     mCanvasTM = new gfxMatrix(tm);
   }
   return *mCanvasTM;
 }