Bug 265894 - Part 3. Implement nsSVGViewportFrame, and let nsSVGInnerFrame inherit from it. draft
authorcku <cku@mozilla.com>
Wed, 14 Jun 2017 21:05:37 +0800
changeset 597163 5f22b22f50f778871e275ead555e70da78063656
parent 597162 77c3ad45e00713769a9d87786c8a1e17044ce96c
child 597164 32c74ff4a986c07e09c8f58a0fb6c9a7c2cfae02
push id64856
push userbmo:cku@mozilla.com
push dateTue, 20 Jun 2017 07:46:45 +0000
bugs265894
milestone56.0a1
Bug 265894 - Part 3. Implement nsSVGViewportFrame, and let nsSVGInnerFrame inherit from it. MozReview-Commit-ID: 9lME1xiNhhY
dom/svg/SVGSymbolElement.cpp
dom/svg/SVGSymbolElement.h
dom/svg/SVGViewportElement.h
layout/svg/moz.build
layout/svg/nsSVGInnerSVGFrame.cpp
layout/svg/nsSVGInnerSVGFrame.h
layout/svg/nsSVGViewportFrame.cpp
layout/svg/nsSVGViewportFrame.h
--- a/dom/svg/SVGSymbolElement.cpp
+++ b/dom/svg/SVGSymbolElement.cpp
@@ -38,18 +38,72 @@ 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
+} // namespace mozilla
\ No newline at end of file
--- a/dom/svg/SVGSymbolElement.h
+++ b/dom/svg/SVGSymbolElement.h
@@ -2,42 +2,61 @@
 /* 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 "SVGViewportElement.h"
+#include "mozilla/dom/SVGTests.h"
+#include "nsSVGElement.h"
+#include "nsSVGViewBox.h"
+#include "SVGAnimatedPreserveAspectRatio.h"
 
 nsresult NS_NewSVGSymbolElement(nsIContent **aResult,
                                 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
 namespace mozilla {
 namespace dom {
 
-typedef SVGViewportElement SVGSymbolElementBase;
+typedef nsSVGElement SVGSymbolElementBase;
 
-class SVGSymbolElement final : public SVGSymbolElementBase
+class SVGSymbolElement final : public SVGSymbolElementBase,
+                               public SVGTests
 {
 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
+#endif // mozilla_dom_SVGSymbolElement_h
\ No newline at end of file
--- a/dom/svg/SVGViewportElement.h
+++ b/dom/svg/SVGViewportElement.h
@@ -16,17 +16,17 @@
 #include "SVGGraphicsElement.h"
 #include "SVGImageContext.h"
 #include "nsSVGViewBox.h"
 #include "SVGPreserveAspectRatio.h"
 #include "SVGAnimatedPreserveAspectRatio.h"
 #include "mozilla/Attributes.h"
 
 class nsSVGOuterSVGFrame;
-class nsSVGInnerSVGFrame;
+class nsSVGViewportFrame;
 
 namespace mozilla {
 class AutoPreserveAspectRatioOverride;
 class DOMSVGAnimatedPreserveAspectRatio;
 
 namespace dom {
 class SVGAnimatedRect;
 class SVGTransform;
@@ -44,17 +44,17 @@ public:
   }
   float width;
   float height;
 };
 
 class SVGViewportElement : public SVGGraphicsElement
 {
   friend class ::nsSVGOuterSVGFrame;
-  friend class ::nsSVGInnerSVGFrame;
+  friend class ::nsSVGViewportFrame;
 
 protected:
 
   SVGViewportElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   ~SVGViewportElement();
 
 public:
 
--- a/layout/svg/moz.build
+++ b/layout/svg/moz.build
@@ -50,16 +50,17 @@ UNIFIED_SOURCES += [
     'nsSVGMarkerFrame.cpp',
     'nsSVGMaskFrame.cpp',
     'nsSVGOuterSVGFrame.cpp',
     'nsSVGPatternFrame.cpp',
     'nsSVGStopFrame.cpp',
     'nsSVGSwitchFrame.cpp',
     'nsSVGUseFrame.cpp',
     'nsSVGUtils.cpp',
+    'nsSVGViewportFrame.cpp',
     'SVGContextPaint.cpp',
     'SVGFEContainerFrame.cpp',
     'SVGFEImageFrame.cpp',
     'SVGFELeafFrame.cpp',
     'SVGFEUnstyledLeafFrame.cpp',
     'SVGGeometryFrame.cpp',
     'SVGImageContext.cpp',
     'SVGTextFrame.cpp',
--- a/layout/svg/nsSVGInnerSVGFrame.cpp
+++ b/layout/svg/nsSVGInnerSVGFrame.cpp
@@ -1,338 +1,36 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Main header first:
 #include "nsSVGInnerSVGFrame.h"
 
-// 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/SVGViewportElement.h"
-
-using namespace mozilla;
-using namespace mozilla::dom;
-using namespace mozilla::gfx;
-using namespace mozilla::image;
-
 nsIFrame*
 NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSVGInnerSVGFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSVGInnerSVGFrame)
 
 //----------------------------------------------------------------------
 // nsIFrame methods
 
 NS_QUERYFRAME_HEAD(nsSVGInnerSVGFrame)
   NS_QUERYFRAME_ENTRY(nsSVGInnerSVGFrame)
   NS_QUERYFRAME_ENTRY(nsISVGSVGFrame)
-NS_QUERYFRAME_TAIL_INHERITING(nsSVGDisplayContainerFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsSVGViewportFrame)
 
 #ifdef DEBUG
 void
 nsSVGInnerSVGFrame::Init(nsIContent*       aContent,
                          nsContainerFrame* aParent,
                          nsIFrame*         aPrevInFlow)
 {
   NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::svg),
                "Content is not an SVG 'svg' element!");
 
-  nsSVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
+  nsSVGViewportFrame::Init(aContent, aParent, aPrevInFlow);
 }
 #endif /* DEBUG */
-
-//----------------------------------------------------------------------
-// nsSVGDisplayableFrame methods
-
-void
-nsSVGInnerSVGFrame::PaintSVG(gfxContext& aContext,
-                             const gfxMatrix& aTransform,
-                             imgDrawingParams& aImgParams,
-                             const nsIntRect *aDirtyRect)
-{
-  NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
-               (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<SVGViewportElement*>(mContent)->
-      GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
-
-    if (width <= 0 || height <= 0) {
-      return;
-    }
-
-    autoSR.SetContext(&aContext);
-    gfxRect clipRect =
-      nsSVGUtils::GetClipRectForFrame(this, x, y, width, height);
-    nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
-  }
-
-  nsSVGDisplayContainerFrame::PaintSVG(aContext, aTransform, aImgParams,
-                                       aDirtyRect);
-}
-
-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<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()) {
-    InvalidateFrame();
-  }
-
-  nsSVGDisplayContainerFrame::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) {
-
-    SVGViewportElement *svg = static_cast<SVGViewportElement*>(mContent);
-
-    bool xOrYIsPercentage =
-      svg->mLengthAttributes[SVGViewportElement::ATTR_X].IsPercentage() ||
-      svg->mLengthAttributes[SVGViewportElement::ATTR_Y].IsPercentage();
-    bool widthOrHeightIsPercentage =
-      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.
-      nsSVGUtils::ScheduleReflowSVG(this);
-    }
-
-    // Coordinate context changes affect mCanvasTM if we have a
-    // percentage 'x' or 'y', or if we have a percentage 'width' or 'height' AND
-    // a 'viewBox'.
-
-    if (!(aFlags & TRANSFORM_CHANGED) &&
-        (xOrYIsPercentage ||
-         (widthOrHeightIsPercentage && svg->HasViewBoxRect()))) {
-      aFlags |= TRANSFORM_CHANGED;
-    }
-
-    if (svg->HasViewBoxRect() || !widthOrHeightIsPercentage) {
-      // Remove COORD_CONTEXT_CHANGED, since we establish the coordinate
-      // context for our descendants and this notification won't change its
-      // dimensions:
-      aFlags &= ~COORD_CONTEXT_CHANGED;
-
-      if (!aFlags) {
-        return; // No notification flags left
-      }
-    }
-  }
-
-  if (aFlags & TRANSFORM_CHANGED) {
-    // make sure our cached transform matrix gets (lazily) updated
-    mCanvasTM = nullptr;
-  }
-
-  nsSVGDisplayContainerFrame::NotifySVGChanged(aFlags);
-}
-
-SVGBBox
-nsSVGInnerSVGFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
-                                        uint32_t aFlags)
-{
-  // XXXjwatt It seems like authors would want the result to be clipped by the
-  // viewport we establish if IsScrollableOverflow() is true.  We should
-  // consider doing that.  See bug 1350755.
-
-  SVGBBox bbox;
-
-  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<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;
-    }
-    // Else we're not clipping to our viewport so we fall through and include
-    // the bounds of our children.
-  }
-
-  SVGBBox descendantsBbox =
-    nsSVGDisplayContainerFrame::GetBBoxContribution(aToBBoxUserspace, aFlags);
-
-  bbox.UnionEdges(descendantsBbox);
-
-  return bbox;
-}
-
-nsresult
-nsSVGInnerSVGFrame::AttributeChanged(int32_t  aNameSpaceID,
-                                     nsIAtom* aAttribute,
-                                     int32_t  aModType)
-{
-  if (aNameSpaceID == kNameSpaceID_None &&
-      !(GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
-
-    SVGViewportElement* content = static_cast<SVGViewportElement*>(mContent);
-
-    if (aAttribute == nsGkAtoms::width ||
-        aAttribute == nsGkAtoms::height) {
-      nsLayoutUtils::PostRestyleEvent(
-        mContent->AsElement(), nsRestyleHint(0),
-        nsChangeHint_InvalidateRenderingObservers);
-      nsSVGUtils::ScheduleReflowSVG(this);
-
-      if (content->HasViewBoxOrSyntheticViewBox()) {
-        // make sure our cached transform matrix gets (lazily) updated
-        mCanvasTM = nullptr;
-        content->ChildrenOnlyTransformChanged();
-        nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
-      } else {
-        uint32_t flags = COORD_CONTEXT_CHANGED;
-        if (mCanvasTM && mCanvasTM->IsSingular()) {
-          mCanvasTM = nullptr;
-          flags |= TRANSFORM_CHANGED;
-        }
-        nsSVGUtils::NotifyChildrenOfSVGChange(this, flags);
-      }
-
-    } else if (aAttribute == nsGkAtoms::transform ||
-               aAttribute == nsGkAtoms::preserveAspectRatio ||
-               aAttribute == nsGkAtoms::viewBox ||
-               aAttribute == nsGkAtoms::x ||
-               aAttribute == nsGkAtoms::y) {
-      // make sure our cached transform matrix gets (lazily) updated
-      mCanvasTM = nullptr;
-
-      nsSVGUtils::NotifyChildrenOfSVGChange(
-          this, aAttribute == nsGkAtoms::viewBox ?
-                  TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
-
-      // We don't invalidate for transform changes (the layers code does that).
-      // Also note that SVGTransformableElement::GetAttributeChangeHint will
-      // return nsChangeHint_UpdateOverflow for "transform" attribute changes
-      // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
-
-      if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
-        nsLayoutUtils::PostRestyleEvent(
-          mContent->AsElement(), nsRestyleHint(0),
-          nsChangeHint_InvalidateRenderingObservers);
-        nsSVGUtils::ScheduleReflowSVG(this);
-      } else if (aAttribute == nsGkAtoms::viewBox ||
-                 (aAttribute == nsGkAtoms::preserveAspectRatio &&
-                  content->HasViewBoxOrSyntheticViewBox())) {
-        content->ChildrenOnlyTransformChanged();
-        // SchedulePaint sets a global state flag so we only need to call it once
-        // (on ourself is fine), not once on each child (despite bug 828240).
-        SchedulePaint();
-      }
-    }
-  }
-
-  return NS_OK;
-}
-
-nsIFrame*
-nsSVGInnerSVGFrame::GetFrameForPoint(const gfxPoint& aPoint)
-{
-  NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
-               (mState & NS_FRAME_IS_NONDISPLAY),
-               "If display lists are enabled, only hit-testing of non-display "
-               "SVG should take this code path");
-
-  if (StyleDisplay()->IsScrollableOverflow()) {
-    Rect clip;
-    static_cast<nsSVGElement*>(mContent)->
-      GetAnimatedLengthValues(&clip.x, &clip.y,
-                              &clip.width, &clip.height, nullptr);
-    if (!clip.Contains(ToPoint(aPoint))) {
-      return nullptr;
-    }
-  }
-
-  return nsSVGDisplayContainerFrame::GetFrameForPoint(aPoint);
-}
-
-//----------------------------------------------------------------------
-// nsISVGSVGFrame methods:
-
-void
-nsSVGInnerSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags)
-{
-  // The dimensions of inner-<svg> frames are purely defined by their "width"
-  // and "height" attributes, and transform changes can only occur as a result
-  // of changes to their "width", "height", "viewBox" or "preserveAspectRatio"
-  // attributes. Changes to all of these attributes are handled in
-  // AttributeChanged(), so we should never be called.
-  NS_ERROR("Not called for nsSVGInnerSVGFrame");
-}
-
-//----------------------------------------------------------------------
-// nsSVGContainerFrame methods:
-
-gfxMatrix
-nsSVGInnerSVGFrame::GetCanvasTM()
-{
-  if (!mCanvasTM) {
-    NS_ASSERTION(GetParent(), "null parent");
-
-    nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
-    SVGViewportElement *content = static_cast<SVGViewportElement*>(mContent);
-
-    gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
-
-    mCanvasTM = new gfxMatrix(tm);
-  }
-  return *mCanvasTM;
-}
-
-bool
-nsSVGInnerSVGFrame::HasChildrenOnlyTransform(gfx::Matrix *aTransform) const
-{
-  SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
-
-  if (content->HasViewBoxOrSyntheticViewBox()) {
-    // XXX Maybe return false if the transform is the identity transform?
-    if (aTransform) {
-      *aTransform = content->GetViewBoxTransform();
-    }
-    return true;
-  }
-  return false;
-}
--- a/layout/svg/nsSVGInnerSVGFrame.h
+++ b/layout/svg/nsSVGInnerSVGFrame.h
@@ -1,32 +1,26 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __NS_SVGINNERSVGFRAME_H__
 #define __NS_SVGINNERSVGFRAME_H__
 
-#include "mozilla/Attributes.h"
-#include "nsAutoPtr.h"
-#include "nsSVGContainerFrame.h"
-#include "nsISVGSVGFrame.h"
-
-class gfxContext;
+#include "nsSVGViewportFrame.h"
 
 class nsSVGInnerSVGFrame final
-  : public nsSVGDisplayContainerFrame
-  , public nsISVGSVGFrame
+  : public nsSVGViewportFrame
 {
   friend nsIFrame*
   NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 protected:
   explicit nsSVGInnerSVGFrame(nsStyleContext* aContext)
-    : nsSVGDisplayContainerFrame(aContext, kClassID)
+    : nsSVGViewportFrame(aContext, kClassID)
   {
   }
 
 public:
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS(nsSVGInnerSVGFrame)
 
 #ifdef DEBUG
@@ -36,39 +30,12 @@ public:
 #endif
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGInnerSVG"), aResult);
   }
 #endif
-
-  virtual nsresult  AttributeChanged(int32_t         aNameSpaceID,
-                                     nsIAtom*        aAttribute,
-                                     int32_t         aModType) override;
-
-  // nsSVGDisplayableFrame interface:
-  virtual void PaintSVG(gfxContext& aContext,
-                        const gfxMatrix& aTransform,
-                        imgDrawingParams& aImgParams,
-                        const nsIntRect* aDirtyRect = nullptr) override;
-  virtual void ReflowSVG() override;
-  virtual void NotifySVGChanged(uint32_t aFlags) override;
-  SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
-                              uint32_t aFlags) override;
-  virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
-
-  // nsSVGContainerFrame methods:
-  virtual gfxMatrix GetCanvasTM() override;
-
-  virtual bool HasChildrenOnlyTransform(Matrix *aTransform) const override;
-
-  // nsISVGSVGFrame interface:
-  virtual void NotifyViewportOrTransformChanged(uint32_t aFlags) override;
-
-protected:
-
-  nsAutoPtr<gfxMatrix> mCanvasTM;
 };
 
 #endif
 
new file mode 100644
--- /dev/null
+++ b/layout/svg/nsSVGViewportFrame.cpp
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Main header first:
+#include "nsSVGViewportFrame.h"
+
+// 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/SVGViewportElement.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::gfx;
+using namespace mozilla::image;
+
+//----------------------------------------------------------------------
+// nsSVGDisplayableFrame methods
+
+void
+nsSVGViewportFrame::PaintSVG(gfxContext& aContext,
+                             const gfxMatrix& aTransform,
+                             imgDrawingParams& aImgParams,
+                             const nsIntRect *aDirtyRect)
+{
+  NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
+               (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<SVGViewportElement*>(mContent)->
+      GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
+
+    if (width <= 0 || height <= 0) {
+      return;
+    }
+
+    autoSR.SetContext(&aContext);
+    gfxRect clipRect =
+      nsSVGUtils::GetClipRectForFrame(this, x, y, width, height);
+    nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
+  }
+
+  nsSVGDisplayContainerFrame::PaintSVG(aContext, aTransform, aImgParams,
+                                       aDirtyRect);
+}
+
+void
+nsSVGViewportFrame::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<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()) {
+    InvalidateFrame();
+  }
+
+  nsSVGDisplayContainerFrame::ReflowSVG();
+}
+
+void
+nsSVGViewportFrame::NotifySVGChanged(uint32_t aFlags)
+{
+  MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
+             "Invalidation logic may need adjusting");
+
+  if (aFlags & COORD_CONTEXT_CHANGED) {
+
+    SVGViewportElement *svg = static_cast<SVGViewportElement*>(mContent);
+
+    bool xOrYIsPercentage =
+      svg->mLengthAttributes[SVGViewportElement::ATTR_X].IsPercentage() ||
+      svg->mLengthAttributes[SVGViewportElement::ATTR_Y].IsPercentage();
+    bool widthOrHeightIsPercentage =
+      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.
+      nsSVGUtils::ScheduleReflowSVG(this);
+    }
+
+    // Coordinate context changes affect mCanvasTM if we have a
+    // percentage 'x' or 'y', or if we have a percentage 'width' or 'height' AND
+    // a 'viewBox'.
+
+    if (!(aFlags & TRANSFORM_CHANGED) &&
+        (xOrYIsPercentage ||
+         (widthOrHeightIsPercentage && svg->HasViewBoxRect()))) {
+      aFlags |= TRANSFORM_CHANGED;
+    }
+
+    if (svg->HasViewBoxRect() || !widthOrHeightIsPercentage) {
+      // Remove COORD_CONTEXT_CHANGED, since we establish the coordinate
+      // context for our descendants and this notification won't change its
+      // dimensions:
+      aFlags &= ~COORD_CONTEXT_CHANGED;
+
+      if (!aFlags) {
+        return; // No notification flags left
+      }
+    }
+  }
+
+  if (aFlags & TRANSFORM_CHANGED) {
+    // make sure our cached transform matrix gets (lazily) updated
+    mCanvasTM = nullptr;
+  }
+
+  nsSVGDisplayContainerFrame::NotifySVGChanged(aFlags);
+}
+
+SVGBBox
+nsSVGViewportFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
+                                        uint32_t aFlags)
+{
+  // XXXjwatt It seems like authors would want the result to be clipped by the
+  // viewport we establish if IsScrollableOverflow() is true.  We should
+  // consider doing that.  See bug 1350755.
+
+  SVGBBox bbox;
+
+  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<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;
+    }
+    // Else we're not clipping to our viewport so we fall through and include
+    // the bounds of our children.
+  }
+
+  SVGBBox descendantsBbox =
+    nsSVGDisplayContainerFrame::GetBBoxContribution(aToBBoxUserspace, aFlags);
+
+  bbox.UnionEdges(descendantsBbox);
+
+  return bbox;
+}
+
+nsresult
+nsSVGViewportFrame::AttributeChanged(int32_t  aNameSpaceID,
+                                     nsIAtom* aAttribute,
+                                     int32_t  aModType)
+{
+  if (aNameSpaceID == kNameSpaceID_None &&
+      !(GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
+
+    SVGViewportElement* content = static_cast<SVGViewportElement*>(mContent);
+
+    if (aAttribute == nsGkAtoms::width ||
+        aAttribute == nsGkAtoms::height) {
+      nsLayoutUtils::PostRestyleEvent(
+        mContent->AsElement(), nsRestyleHint(0),
+        nsChangeHint_InvalidateRenderingObservers);
+      nsSVGUtils::ScheduleReflowSVG(this);
+
+      if (content->HasViewBoxOrSyntheticViewBox()) {
+        // make sure our cached transform matrix gets (lazily) updated
+        mCanvasTM = nullptr;
+        content->ChildrenOnlyTransformChanged();
+        nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
+      } else {
+        uint32_t flags = COORD_CONTEXT_CHANGED;
+        if (mCanvasTM && mCanvasTM->IsSingular()) {
+          mCanvasTM = nullptr;
+          flags |= TRANSFORM_CHANGED;
+        }
+        nsSVGUtils::NotifyChildrenOfSVGChange(this, flags);
+      }
+
+    } else if (aAttribute == nsGkAtoms::transform ||
+               aAttribute == nsGkAtoms::preserveAspectRatio ||
+               aAttribute == nsGkAtoms::viewBox ||
+               aAttribute == nsGkAtoms::x ||
+               aAttribute == nsGkAtoms::y) {
+      // make sure our cached transform matrix gets (lazily) updated
+      mCanvasTM = nullptr;
+
+      nsSVGUtils::NotifyChildrenOfSVGChange(
+          this, aAttribute == nsGkAtoms::viewBox ?
+                  TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
+
+      // We don't invalidate for transform changes (the layers code does that).
+      // Also note that SVGTransformableElement::GetAttributeChangeHint will
+      // return nsChangeHint_UpdateOverflow for "transform" attribute changes
+      // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
+
+      if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
+        nsLayoutUtils::PostRestyleEvent(
+          mContent->AsElement(), nsRestyleHint(0),
+          nsChangeHint_InvalidateRenderingObservers);
+        nsSVGUtils::ScheduleReflowSVG(this);
+      } else if (aAttribute == nsGkAtoms::viewBox ||
+                 (aAttribute == nsGkAtoms::preserveAspectRatio &&
+                  content->HasViewBoxOrSyntheticViewBox())) {
+        content->ChildrenOnlyTransformChanged();
+        // SchedulePaint sets a global state flag so we only need to call it once
+        // (on ourself is fine), not once on each child (despite bug 828240).
+        SchedulePaint();
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+nsIFrame*
+nsSVGViewportFrame::GetFrameForPoint(const gfxPoint& aPoint)
+{
+  NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
+               (mState & NS_FRAME_IS_NONDISPLAY),
+               "If display lists are enabled, only hit-testing of non-display "
+               "SVG should take this code path");
+
+  if (StyleDisplay()->IsScrollableOverflow()) {
+    Rect clip;
+    static_cast<nsSVGElement*>(mContent)->
+      GetAnimatedLengthValues(&clip.x, &clip.y,
+                              &clip.width, &clip.height, nullptr);
+    if (!clip.Contains(ToPoint(aPoint))) {
+      return nullptr;
+    }
+  }
+
+  return nsSVGDisplayContainerFrame::GetFrameForPoint(aPoint);
+}
+
+//----------------------------------------------------------------------
+// nsISVGSVGFrame methods:
+
+void
+nsSVGViewportFrame::NotifyViewportOrTransformChanged(uint32_t aFlags)
+{
+  // The dimensions of inner-<svg> frames are purely defined by their "width"
+  // and "height" attributes, and transform changes can only occur as a result
+  // of changes to their "width", "height", "viewBox" or "preserveAspectRatio"
+  // attributes. Changes to all of these attributes are handled in
+  // AttributeChanged(), so we should never be called.
+  NS_ERROR("Not called for nsSVGViewportFrame");
+}
+
+//----------------------------------------------------------------------
+// nsSVGContainerFrame methods:
+
+gfxMatrix
+nsSVGViewportFrame::GetCanvasTM()
+{
+  if (!mCanvasTM) {
+    NS_ASSERTION(GetParent(), "null parent");
+
+    nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
+    SVGViewportElement *content = static_cast<SVGViewportElement*>(mContent);
+
+    gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
+
+    mCanvasTM = new gfxMatrix(tm);
+  }
+  return *mCanvasTM;
+}
+
+bool
+nsSVGViewportFrame::HasChildrenOnlyTransform(gfx::Matrix *aTransform) const
+{
+  SVGViewportElement *content = static_cast<SVGViewportElement*>(mContent);
+
+  if (content->HasViewBoxOrSyntheticViewBox()) {
+    // XXX Maybe return false if the transform is the identity transform?
+    if (aTransform) {
+      *aTransform = content->GetViewBoxTransform();
+    }
+    return true;
+  }
+  return false;
+}
new file mode 100644
--- /dev/null
+++ b/layout/svg/nsSVGViewportFrame.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __NS_SVGVIEWPORTFRAME_H__
+#define __NS_SVGVIEWPORTFRAME_H__
+
+#include "mozilla/Attributes.h"
+#include "nsAutoPtr.h"
+#include "nsSVGContainerFrame.h"
+#include "nsISVGSVGFrame.h"
+
+class gfxContext;
+/**
+ * Superclass for inner SVG frames and symbol frames.
+ */
+class nsSVGViewportFrame
+  : public nsSVGDisplayContainerFrame
+  , public nsISVGSVGFrame
+{
+protected:
+  nsSVGViewportFrame(nsStyleContext* aContext, nsIFrame::ClassID aID)
+    : nsSVGDisplayContainerFrame(aContext, aID)
+  {
+  }
+public:
+
+  virtual nsresult  AttributeChanged(int32_t         aNameSpaceID,
+                                     nsIAtom*        aAttribute,
+                                     int32_t         aModType) override;
+
+  // nsSVGDisplayableFrame interface:
+  virtual void PaintSVG(gfxContext& aContext,
+                        const gfxMatrix& aTransform,
+                        imgDrawingParams& aImgParams,
+                        const nsIntRect* aDirtyRect = nullptr) override;
+  virtual void ReflowSVG() override;
+  virtual void NotifySVGChanged(uint32_t aFlags) override;
+  SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
+                              uint32_t aFlags) override;
+  virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
+
+  // nsSVGContainerFrame methods:
+  virtual gfxMatrix GetCanvasTM() override;
+
+  virtual bool HasChildrenOnlyTransform(Matrix *aTransform) const override;
+
+  // nsISVGSVGFrame interface:
+  virtual void NotifyViewportOrTransformChanged(uint32_t aFlags) override;
+
+protected:
+
+  nsAutoPtr<gfxMatrix> mCanvasTM;
+};
+
+#endif // __NS_SVGVIEWPORTFRAME_H__
+