Bug 769742 - Account for nsSVGOuterSVGFrames' border/padding offset by giving nsSVGOuterSVGFrame an anonymous child to wrap its real children. r=roc.
authorJonathan Watt <jwatt@jwatt.org>
Mon, 09 Jul 2012 02:04:56 +0100
changeset 98674 6e0f0a9228ce3cb663e03912b2c495be61792186
parent 98673 43993f45c3286c30fd5b71d1a448f474aaf0ca5a
child 98675 29a65f75102139125b242a8b3c9b46bd895bb6ff
push id907
push usertim.taubert@gmx.de
push dateTue, 10 Jul 2012 09:05:39 +0000
treeherderfx-team@8bba6a37776e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs769742
milestone16.0a1
Bug 769742 - Account for nsSVGOuterSVGFrames' border/padding offset by giving nsSVGOuterSVGFrame an anonymous child to wrap its real children. r=roc.
content/base/src/nsGkAtomList.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/generic/nsFrameIdList.h
layout/style/nsCSSAnonBoxList.h
layout/svg/base/src/nsSVGOuterSVGFrame.cpp
layout/svg/base/src/nsSVGOuterSVGFrame.h
layout/svg/base/src/nsSVGUtils.cpp
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1745,16 +1745,17 @@ GK_ATOM(svgGFrame, "SVGGFrame")
 GK_ATOM(svgGlyphFrame, "SVGGlyphFrame")
 GK_ATOM(svgGradientFrame, "SVGGradientFrame")
 GK_ATOM(svgImageFrame, "SVGImageFrame")
 GK_ATOM(svgInnerSVGFrame, "SVGInnerSVGFrame")
 GK_ATOM(svgLinearGradientFrame, "SVGLinearGradientFrame")
 GK_ATOM(svgMarkerFrame, "SVGMarkerFrame")
 GK_ATOM(svgMaskFrame, "SVGMaskFrame")
 GK_ATOM(svgOuterSVGFrame, "SVGOuterSVGFrame")
+GK_ATOM(svgOuterSVGAnonChildFrame, "SVGOuterSVGAnonChildFrame")
 GK_ATOM(svgPathGeometryFrame, "SVGPathGeometryFrame")
 GK_ATOM(svgPatternFrame, "SVGPatternFrame")
 GK_ATOM(svgRadialGradientFrame, "SVGRadialGradientFrame")
 GK_ATOM(svgStopFrame, "SVGStopFrame")
 GK_ATOM(svgSwitchFrame, "SVGSwitchFrame")
 GK_ATOM(svgTextFrame, "SVGTextFrame")
 GK_ATOM(svgTextPathFrame, "SVGTextPathFrame")
 GK_ATOM(svgTSpanFrame, "SVGTSpanFrame")
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -134,16 +134,18 @@ nsIFrame*
 NS_NewHTMLVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
 #endif
 
 #include "nsSVGTextContainerFrame.h"
 
 nsIFrame*
 NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
+NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
+nsIFrame*
 NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
 NS_NewSVGGenericContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 nsIFrame*
@@ -2402,35 +2404,40 @@ nsCSSFrameConstructor::ConstructDocEleme
                         contentFrame);
     *aNewFrame = contentFrame;
     processChildren = true;
   }
   else
 #endif
   if (aDocElement->IsSVG()) {
     if (aDocElement->Tag() == nsGkAtoms::svg) {
-      contentFrame = NS_NewSVGOuterSVGFrame(mPresShell, styleContext);
-      if (NS_UNLIKELY(!contentFrame)) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-      InitAndRestoreFrame(state, aDocElement,
-                          state.GetGeometricParent(display,
-                                                   mDocElementContainingBlock),
-                          nsnull, contentFrame);
-
-      // AddChild takes care of transforming the frame tree for fixed-pos
-      // or abs-pos situations
+      // We're going to call the right function ourselves, so no need to give a
+      // function to this FrameConstructionData.
+
+      // XXXbz on the other hand, if we converted this whole function to
+      // FrameConstructionData/Item, then we'd need the right function
+      // here... but would probably be able to get away with less code in this
+      // function in general.
+      // Use a null PendingBinding, since our binding is not in fact pending.
+      static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nsnull);
+      nsRefPtr<nsStyleContext> extraRef(styleContext);
+      FrameConstructionItem item(&rootSVGData, aDocElement,
+                                 aDocElement->Tag(), kNameSpaceID_SVG,
+                                 nsnull, extraRef.forget(), true);
+
       nsFrameItems frameItems;
-      rv = state.AddChild(contentFrame, frameItems, aDocElement,
-                          styleContext, mDocElementContainingBlock);
-      if (NS_FAILED(rv) || frameItems.IsEmpty()) {
+      rv = ConstructOuterSVG(state, item, mDocElementContainingBlock,
+                             styleContext->GetStyleDisplay(),
+                             frameItems, &contentFrame);
+      if (NS_FAILED(rv))
         return rv;
-      }
+      if (!contentFrame || frameItems.IsEmpty())
+        return NS_ERROR_FAILURE;
       *aNewFrame = frameItems.FirstChild();
-      processChildren = true;
+      NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
     } else {
       return NS_ERROR_FAILURE;
     }
   } else {
     bool docElemIsTable = (display->mDisplay == NS_STYLE_DISPLAY_TABLE);
     if (docElemIsTable) {
       // We're going to call the right function ourselves, so no need to give a
       // function to this FrameConstructionData.
@@ -2493,18 +2500,19 @@ nsCSSFrameConstructor::ConstructDocEleme
   if (!isChild) {
     mRootElementStyleFrame = mRootElementFrame;
   }
 
   if (processChildren) {
     // Still need to process the child content
     nsFrameItems childItems;
 
-    NS_ASSERTION(!nsLayoutUtils::GetAsBlock(contentFrame),
-                 "Only XUL and SVG frames should reach here");
+    NS_ASSERTION(!nsLayoutUtils::GetAsBlock(contentFrame) &&
+                 !contentFrame->IsFrameOfType(nsIFrame::eSVG),
+                 "Only XUL frames should reach here");
     // Use a null PendingBinding, since our binding is not in fact pending.
     ProcessChildren(state, aDocElement, styleContext, contentFrame, true,
                     childItems, false, nsnull);
 
     // Set the initial child lists
     contentFrame->SetInitialChildList(kPrincipalList, childItems);
   }
 
@@ -4697,16 +4705,92 @@ nsCSSFrameConstructor::FindMathMLData(El
     SIMPLE_MATHML_CREATE(menclose_, NS_NewMathMLmencloseFrame),
     SIMPLE_MATHML_CREATE(semantics_, NS_NewMathMLsemanticsFrame)
   };
 
   return FindDataByTag(aTag, aElement, aStyleContext, sMathMLData,
                        ArrayLength(sMathMLData));
 }
 
+
+// Construct an nsSVGOuterSVGFrame, the anonymous child that wraps its real
+// children, and its descendant frames.
+nsresult
+nsCSSFrameConstructor::ConstructOuterSVG(nsFrameConstructorState& aState,
+                                         FrameConstructionItem&   aItem,
+                                         nsIFrame*                aParentFrame,
+                                         const nsStyleDisplay*    aDisplay,
+                                         nsFrameItems&            aFrameItems,
+                                         nsIFrame**               aNewFrame)
+{
+  nsIContent* const content = aItem.mContent;
+  nsStyleContext* const styleContext = aItem.mStyleContext;
+
+  nsresult rv = NS_OK;
+
+  // Create the nsSVGOuterSVGFrame:
+  nsIFrame* newFrame = NS_NewSVGOuterSVGFrame(mPresShell, styleContext);
+
+  nsIFrame* geometricParent =
+    aState.GetGeometricParent(styleContext->GetStyleDisplay(),
+                              aParentFrame);
+
+  InitAndRestoreFrame(aState, content, geometricParent, nsnull, newFrame);
+
+  // Create the pseudo SC for the anonymous wrapper child as a child of the SC:
+  nsRefPtr<nsStyleContext> scForAnon;
+  scForAnon = mPresShell->StyleSet()->
+    ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozSVGOuterSVGAnonChild,
+                             styleContext);
+
+  // Create the anonymous inner wrapper frame
+  nsIFrame* innerFrame = NS_NewSVGOuterSVGAnonChildFrame(mPresShell, scForAnon);
+
+  if (!innerFrame) {
+    newFrame->Destroy();
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  InitAndRestoreFrame(aState, content, newFrame, nsnull, innerFrame);
+
+  // Put the newly created frames into the right child list
+  SetInitialSingleChild(newFrame, innerFrame);
+
+  rv = aState.AddChild(newFrame, aFrameItems, content, styleContext,
+                       aParentFrame);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (!mRootElementFrame) {
+    // The frame we're constructing will be the root element frame.
+    // Set mRootElementFrame before processing children.
+    mRootElementFrame = newFrame;
+  }
+
+  nsFrameItems childItems;
+
+  // Process children
+  if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
+    rv = ConstructFramesFromItemList(aState, aItem.mChildItems,
+                                     innerFrame, childItems);
+  } else {
+    rv = ProcessChildren(aState, content, styleContext, innerFrame,
+                         true, childItems, false, aItem.mPendingBinding);
+  }
+  // XXXbz what about cleaning up?
+  if (NS_FAILED(rv)) return rv;
+
+  // Set the inner wrapper frame's initial primary list
+  innerFrame->SetInitialChildList(kPrincipalList, childItems);
+
+  *aNewFrame = newFrame;
+  return rv;
+}
+
 // Only outer <svg> elements can be floated or positioned.  All other SVG
 // should be in-flow.
 #define SIMPLE_SVG_FCDATA(_func)                                        \
   FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW |                             \
               FCDATA_SKIP_ABSPOS_PUSH |                                 \
               FCDATA_DISALLOW_GENERATED_CONTENT,  _func)
 #define SIMPLE_SVG_CREATE(_tag, _func)            \
   { &nsGkAtoms::_tag, SIMPLE_SVG_FCDATA(_func) }
@@ -4784,18 +4868,17 @@ nsCSSFrameConstructor::FindSVGData(Eleme
   if (aTag == nsGkAtoms::svg && !parentIsSVG) {
     // We need outer <svg> elements to have an nsSVGOuterSVGFrame regardless
     // of whether they fail conditional processing attributes, since various
     // SVG frames assume that one exists.  We handle the non-rendering
     // of failing outer <svg> element contents like <switch> statements,
     // and do the PassesConditionalProcessingTests call in
     // nsSVGOuterSVGFrame::Init.
     static const FrameConstructionData sOuterSVGData =
-      FCDATA_DECL(FCDATA_SKIP_ABSPOS_PUSH | FCDATA_DISALLOW_GENERATED_CONTENT,
-                  NS_NewSVGOuterSVGFrame);
+      FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructOuterSVG);
     return &sOuterSVGData;
   }
   
   nsCOMPtr<DOMSVGTests> tests(do_QueryInterface(aElement));
   if (tests && !tests->PassesConditionalProcessingTests()) {
     // Elements with failing conditional processing attributes never get
     // rendered.  Note that this is not where we select which frame in a
     // <switch> to render!  That happens in nsSVGSwitchFrame::PaintSVG.
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1346,16 +1346,28 @@ private:
   // we know for sure that the content is not something that should get a frame
   // constructed by tag.
   static const FrameConstructionData*
     FindXULDisplayData(const nsStyleDisplay* aDisplay,
                        Element* aElement,
                        nsStyleContext* aStyleContext);
 
 // SVG - rods
+  /**
+   * Construct an nsSVGOuterSVGFrame, the anonymous child that wraps its real
+   * children, and its descendant frames.  This is the FrameConstructionData
+   * callback used for the job.
+   */
+  nsresult ConstructOuterSVG(nsFrameConstructorState& aState,
+                             FrameConstructionItem&   aItem,
+                             nsIFrame*                aParentFrame,
+                             const nsStyleDisplay*    aDisplay,
+                             nsFrameItems&            aFrameItems,
+                             nsIFrame**               aNewFrame);
+
   static const FrameConstructionData* FindSVGData(Element* aElement,
                                                   nsIAtom* aTag,
                                                   PRInt32 aNameSpaceID,
                                                   nsIFrame* aParentFrame,
                                                   nsStyleContext* aStyleContext);
 
   /* Not static because it does PropagateScrollToViewport.  If this
      changes, make this static */
--- a/layout/generic/nsFrameIdList.h
+++ b/layout/generic/nsFrameIdList.h
@@ -142,16 +142,17 @@ FRAME_ID(nsSVGGFrame)
 FRAME_ID(nsSVGGlyphFrame)
 FRAME_ID(nsSVGGradientFrame)
 FRAME_ID(nsSVGImageFrame)
 FRAME_ID(nsSVGInnerSVGFrame)
 FRAME_ID(nsSVGLinearGradientFrame)
 FRAME_ID(nsSVGMarkerFrame)
 FRAME_ID(nsSVGMaskFrame)
 FRAME_ID(nsSVGOuterSVGFrame)
+FRAME_ID(nsSVGOuterSVGAnonChildFrame)
 FRAME_ID(nsSVGPaintServerFrame)
 FRAME_ID(nsSVGPathGeometryFrame)
 FRAME_ID(nsSVGPatternFrame)
 FRAME_ID(nsSVGRadialGradientFrame)
 FRAME_ID(nsSVGStopFrame)
 FRAME_ID(nsSVGSwitchFrame)
 FRAME_ID(nsSVGTextContainerFrame)
 FRAME_ID(nsSVGTextFrame)
--- a/layout/style/nsCSSAnonBoxList.h
+++ b/layout/style/nsCSSAnonBoxList.h
@@ -76,9 +76,10 @@ CSS_ANON_BOX(moztreeline, ":-moz-tree-li
 CSS_ANON_BOX(moztreetwisty, ":-moz-tree-twisty")
 CSS_ANON_BOX(moztreeimage, ":-moz-tree-image")
 CSS_ANON_BOX(moztreecelltext, ":-moz-tree-cell-text")
 CSS_ANON_BOX(moztreecheckbox, ":-moz-tree-checkbox")
 CSS_ANON_BOX(moztreeprogressmeter, ":-moz-tree-progressmeter")
 CSS_ANON_BOX(moztreedropfeedback, ":-moz-tree-drop-feedback")
 #endif
 
+CSS_ANON_BOX(mozSVGOuterSVGAnonChild, ":-moz-svg-outer-svg-anon-child")
 CSS_ANON_BOX(mozSVGForeignContent, ":-moz-svg-foreign-content")
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp
@@ -386,39 +386,42 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext
     changeBits |= FULL_ZOOM_CHANGED;
     mFullZoom = PresContext()->GetFullZoom();
   }
   mViewportInitialized = true;
   if (changeBits) {
     NotifyViewportOrTransformChanged(changeBits);
   }
 
-  // Now that we've marked the necessary children as dirty, call
-  // UpdateBounds() on them:
+  nsSVGOuterSVGAnonChildFrame *anonKid =
+    static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild());
 
-  mCallingUpdateBounds = true;
+  if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
+    // Now that we've marked the necessary children as dirty, call
+    // UpdateBounds() on them:
+
+    mCallingUpdateBounds = true;
 
-  if (!(mState & NS_STATE_SVG_NONDISPLAY_CHILD)) {
-    nsIFrame* kid = mFrames.FirstChild();
-    while (kid) {
-      nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
-      if (SVGFrame && !(kid->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
-        SVGFrame->UpdateBounds(); 
-      }
-      kid = kid->GetNextSibling();
-    }
+    // Update the mRects and visual overflow rects of all our descendants,
+    // including our anonymous wrapper kid:
+    anonKid->UpdateBounds();
+    NS_ABORT_IF_FALSE(!anonKid->GetNextSibling(),
+      "We should have one anonymous child frame wrapping our real children");
+
+    mCallingUpdateBounds = false;
   }
 
-  mCallingUpdateBounds = false;
-
   // Make sure we scroll if we're too big:
   // XXX Use the bounding box of our descendants? (See bug 353460 comment 14.)
   aDesiredSize.SetOverflowAreasToDesiredBounds();
   FinishAndStoreOverflow(&aDesiredSize);
 
+  // Set our anonymous kid's offset from our border box:
+  anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft());
+
   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                   ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d",
                   aDesiredSize.width, aDesiredSize.height));
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -466,19 +469,22 @@ nsDisplayOuterSVG::HitTest(nsDisplayList
   nsRect rectAtOrigin = aRect - ToReferenceFrame();
   nsRect thisRect(nsPoint(0,0), outerSVGFrame->GetSize());
   if (!thisRect.Intersects(rectAtOrigin))
     return;
 
   nsPoint rectCenter(rectAtOrigin.x + rectAtOrigin.width / 2,
                      rectAtOrigin.y + rectAtOrigin.height / 2);
 
+  nsSVGOuterSVGAnonChildFrame *anonKid =
+    static_cast<nsSVGOuterSVGAnonChildFrame*>(
+      outerSVGFrame->GetFirstPrincipalChild());
   nsIFrame* frame = nsSVGUtils::HitTestChildren(
-    outerSVGFrame, rectCenter + outerSVGFrame->GetPosition() -
-                   outerSVGFrame->GetContentRect().TopLeft());
+    anonKid, rectCenter + outerSVGFrame->GetPosition() -
+               outerSVGFrame->GetContentRect().TopLeft());
   if (frame) {
     aOutFrames->AppendElement(frame);
   }
 }
 
 void
 nsDisplayOuterSVG::Paint(nsDisplayListBuilder* aBuilder,
                          nsRenderingContext* aContext)
@@ -541,18 +547,18 @@ nsSVGOuterSVGFrame::AttributeChanged(PRI
       !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     if (aAttribute == nsGkAtoms::viewBox ||
         aAttribute == nsGkAtoms::preserveAspectRatio ||
         aAttribute == nsGkAtoms::transform) {
 
       // make sure our cached transform matrix gets (lazily) updated
       mCanvasTM = nsnull;
 
-      nsSVGUtils::NotifyChildrenOfSVGChange(
-          this, aAttribute == nsGkAtoms::viewBox ?
+      nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(),
+                aAttribute == nsGkAtoms::viewBox ?
                   TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
 
       static_cast<nsSVGSVGElement*>(mContent)->ChildrenOnlyTransformChanged();
 
     } else if (aAttribute == nsGkAtoms::width ||
                aAttribute == nsGkAtoms::height) {
 
       // Don't call ChildrenOnlyTransformChanged() here, since we call it
@@ -671,17 +677,48 @@ nsSVGOuterSVGFrame::NotifyViewportOrTran
     mCanvasTM = nsnull;
 
     if (haveNonFulLZoomTransformChange &&
         !(mState & NS_STATE_SVG_NONDISPLAY_CHILD)) {
       content->ChildrenOnlyTransformChanged();
     }
   }
 
-  nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags);
+  nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(), aFlags);
+}
+
+//----------------------------------------------------------------------
+// nsISVGChildFrame methods:
+
+NS_IMETHODIMP
+nsSVGOuterSVGFrame::PaintSVG(nsRenderingContext* aContext,
+                             const nsIntRect *aDirtyRect)
+{
+  NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
+                 nsGkAtoms::svgOuterSVGAnonChildFrame &&
+               !GetFirstPrincipalChild()->GetNextSibling(),
+               "We should have a single, anonymous, child");
+  nsSVGOuterSVGAnonChildFrame *anonKid =
+    static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild());
+  return anonKid->PaintSVG(aContext, aDirtyRect);
+}
+
+SVGBBox
+nsSVGOuterSVGFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
+                                        PRUint32 aFlags)
+{
+  NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
+                 nsGkAtoms::svgOuterSVGAnonChildFrame &&
+               !GetFirstPrincipalChild()->GetNextSibling(),
+               "We should have a single, anonymous, child");
+  // We must defer to our child so that we don't include our
+  // content->PrependLocalTransformsTo() transforms.
+  nsSVGOuterSVGAnonChildFrame *anonKid =
+    static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild());
+  return anonKid->GetBBoxContribution(aToBBoxUserspace, aFlags);
 }
 
 //----------------------------------------------------------------------
 // nsSVGContainerFrame methods:
 
 gfxMatrix
 nsSVGOuterSVGFrame::GetCanvasTM(PRUint32 aFor)
 {
@@ -700,34 +737,16 @@ nsSVGOuterSVGFrame::GetCanvasTM(PRUint32
 
     gfxMatrix tm = content->PrependLocalTransformsTo(
                      gfxMatrix().Scale(devPxPerCSSPx, devPxPerCSSPx));
     mCanvasTM = new gfxMatrix(tm);
   }
   return *mCanvasTM;
 }
 
-bool
-nsSVGOuterSVGFrame::HasChildrenOnlyTransform(gfxMatrix *aTransform) const
-{
-  nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
-
-  bool hasTransform = content->HasChildrenOnlyTransform();
-
-  if (hasTransform && aTransform) {
-    // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
-    gfxMatrix identity;
-    *aTransform =
-      content->PrependLocalTransformsTo(identity,
-                                        nsSVGElement::eChildToUserSpace);
-  }
-
-  return hasTransform;
-}
-
 //----------------------------------------------------------------------
 // Implementation helpers
 
 bool
 nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(nsIFrame **aEmbeddingFrame)
 {
   if (!mContent->GetParent()) {
     // Our content is the document element
@@ -772,8 +791,60 @@ nsSVGOuterSVGFrame::IsRootOfImage()
 
 bool
 nsSVGOuterSVGFrame::VerticalScrollbarNotNeeded() const
 {
   nsSVGLength2 &height = static_cast<nsSVGSVGElement*>(mContent)->
                            mLengthAttributes[nsSVGSVGElement::HEIGHT];
   return height.IsPercentage() && height.GetBaseValInSpecifiedUnits() <= 100;
 }
+
+
+//----------------------------------------------------------------------
+// Implementation of nsSVGOuterSVGAnonChildFrame
+
+nsIFrame*
+NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell,
+                                nsStyleContext* aContext)
+{
+  return new (aPresShell) nsSVGOuterSVGAnonChildFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame)
+
+#ifdef DEBUG
+NS_IMETHODIMP
+nsSVGOuterSVGAnonChildFrame::Init(nsIContent* aContent,
+                                  nsIFrame* aParent,
+                                  nsIFrame* aPrevInFlow)
+{
+  NS_ABORT_IF_FALSE(aParent->GetType() == nsGkAtoms::svgOuterSVGFrame,
+                    "Unexpected parent");
+  return nsSVGOuterSVGAnonChildFrameBase::Init(aContent, aParent, aPrevInFlow);
+}
+#endif
+
+nsIAtom *
+nsSVGOuterSVGAnonChildFrame::GetType() const
+{
+  return nsGkAtoms::svgOuterSVGAnonChildFrame;
+}
+
+bool
+nsSVGOuterSVGAnonChildFrame::HasChildrenOnlyTransform(gfxMatrix *aTransform) const
+{
+  // We must claim our nsSVGOuterSVGFrame's children-only transforms as our own
+  // so that the children we are used to wrap are transformed properly.
+
+  nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
+
+  bool hasTransform = content->HasChildrenOnlyTransform();
+
+  if (hasTransform && aTransform) {
+    // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
+    gfxMatrix identity;
+    *aTransform =
+      content->PrependLocalTransformsTo(identity,
+                                        nsSVGElement::eChildToUserSpace);
+  }
+
+  return hasTransform;
+}
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.h
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.h
@@ -71,30 +71,52 @@ public:
     return MakeFrameName(NS_LITERAL_STRING("SVGOuterSVG"), aResult);
   }
 #endif
 
   NS_IMETHOD  AttributeChanged(PRInt32         aNameSpaceID,
                                nsIAtom*        aAttribute,
                                PRInt32         aModType);
 
+  virtual nsIFrame* GetContentInsertionFrame() {
+    // Any children must be added to our single anonymous inner frame kid.
+    NS_ABORT_IF_FALSE(GetFirstPrincipalChild() &&
+                      GetFirstPrincipalChild()->GetType() ==
+                        nsGkAtoms::svgOuterSVGAnonChildFrame,
+                      "Where is our anonymous child?");
+    return GetFirstPrincipalChild()->GetContentInsertionFrame();
+  }
+
   virtual bool IsSVGTransformed(gfxMatrix *aOwnTransform,
                                 gfxMatrix *aFromParentTransform) const {
     // Outer-<svg> can transform its children with viewBox, currentScale and
     // currentTranslate, but it itself is not transformed by SVG transforms.
     return false;
   }
 
   // nsISVGSVGFrame interface:
   virtual void NotifyViewportOrTransformChanged(PRUint32 aFlags);
 
+  // nsISVGChildFrame methods:
+  NS_IMETHOD PaintSVG(nsRenderingContext* aContext,
+                      const nsIntRect *aDirtyRect);
+
+  virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
+                                      PRUint32 aFlags);
+
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM(PRUint32 aFor);
 
-  virtual bool HasChildrenOnlyTransform(gfxMatrix *aTransform) const;
+  virtual bool HasChildrenOnlyTransform(gfxMatrix *aTransform) const {
+    // Our anonymous wrapper child must claim our children-only transforms as
+    // its own so that our real children (the frames it wraps) are transformed
+    // by them, and we must pretend we don't have any children-only transforms
+    // so that our anonymous child is _not_ transformed by them.
+    return false;
+  }
 
   /**
    * Return true only if the height is unspecified (defaulting to 100%) or else
    * the height is explicitly set to a percentage value no greater than 100%.
    */
   bool VerticalScrollbarNotNeeded() const;
 
   bool IsCallingUpdateBounds() const {
@@ -119,9 +141,86 @@ protected:
   nsAutoPtr<gfxMatrix> mCanvasTM;
 
   float mFullZoom;
 
   bool mViewportInitialized;
   bool mIsRootContent;
 };
 
+////////////////////////////////////////////////////////////////////////
+// nsSVGOuterSVGAnonChildFrame class
+
+typedef nsSVGDisplayContainerFrame nsSVGOuterSVGAnonChildFrameBase;
+
+/**
+ * nsSVGOuterSVGFrames have a single direct child that is an instance of this
+ * class, and which is used to wrap their real child frames. Such anonymous
+ * wrapper frames created from this class exist because SVG frames need their
+ * GetPosition() offset to be their offset relative to "user space" (in app
+ * units) so that they can play nicely with nsDisplayTransform. This is fine
+ * for all SVG frames except for direct children of an nsSVGOuterSVGFrame,
+ * since an nsSVGOuterSVGFrame can have CSS border and padding (unlike other
+ * SVG frames). The direct children can't include the offsets due to any such
+ * border/padding in their mRects since that would break nsDisplayTransform,
+ * but not including these offsets would break other parts of the Mozilla code
+ * that assume a frame's mRect contains its border-box-to-parent-border-box
+ * offset, in particular nsIFrame::GetOffsetTo and the functions that depend on
+ * it. Wrapping an nsSVGOuterSVGFrame's children in an instance of this class
+ * with its GetPosition() set to its nsSVGOuterSVGFrame's border/padding offset
+ * keeps both nsDisplayTransform and nsIFrame::GetOffsetTo happy.
+ *
+ * The reason that this class inherit from nsSVGDisplayContainerFrame rather
+ * than simply from nsContainerFrame is so that we can avoid having special
+ * handling for these inner wrappers in multiple parts of the SVG code. For
+ * example, the implementations of IsSVGTransformed and GetCanvasTM assume
+ * nsSVGContainerFrame instances all the way up to the nsSVGOuterSVGFrame.
+ */
+class nsSVGOuterSVGAnonChildFrame
+  : public nsSVGOuterSVGAnonChildFrameBase
+{
+  friend nsIFrame*
+  NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell,
+                                  nsStyleContext* aContext);
+
+  nsSVGOuterSVGAnonChildFrame(nsStyleContext* aContext)
+    : nsSVGOuterSVGAnonChildFrameBase(aContext)
+  {}
+
+public:
+  NS_DECL_FRAMEARENA_HELPERS
+
+#ifdef DEBUG
+  NS_IMETHOD Init(nsIContent* aContent,
+                  nsIFrame* aParent,
+                  nsIFrame* aPrevInFlow);
+
+  NS_IMETHOD GetFrameName(nsAString& aResult) const {
+    return MakeFrameName(NS_LITERAL_STRING("SVGOuterSVGAnonChild"), aResult);
+  }
 #endif
+
+  /**
+   * Get the "type" of the frame
+   *
+   * @see nsGkAtoms::svgOuterSVGAnonChildFrame
+   */
+  virtual nsIAtom* GetType() const;
+
+  virtual bool IsSVGTransformed(gfxMatrix *aOwnTransform,
+                                gfxMatrix *aFromParentTransform) const {
+    // Outer-<svg> can transform its children with viewBox, currentScale and
+    // currentTranslate, but it itself is not transformed by _SVG_ transforms.
+    return false;
+  }
+
+  // nsSVGContainerFrame methods:
+  virtual gfxMatrix GetCanvasTM(PRUint32 aFor) {
+    // GetCanvasTM returns the transform from an SVG frame to the frame's
+    // nsSVGOuterSVGFrame's content box, so we do not include any x/y offset
+    // set on us for any CSS border or padding on our nsSVGOuterSVGFrame.
+    return static_cast<nsSVGOuterSVGFrame*>(mParent)->GetCanvasTM(aFor);
+  }
+
+  virtual bool HasChildrenOnlyTransform(gfxMatrix *aTransform) const;
+};
+
+#endif
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -698,17 +698,16 @@ nsSVGUtils::InvalidateBounds(nsIFrame *a
   if (!aFrame) {
     // We seem to be able to get here, even though SVG frames are never created
     // without an ancestor nsSVGOuterSVGFrame. See bug 767996.
     return;
   }
 
   NS_ASSERTION(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG,
                "SVG frames must always have an nsSVGOuterSVGFrame ancestor!");
-  invalidArea.MoveBy(aFrame->GetContentRect().TopLeft() - aFrame->GetPosition());
 
   static_cast<nsSVGOuterSVGFrame*>(aFrame)->InvalidateWithFlags(invalidArea,
                                                                 aFlags);
 }
 
 void
 nsSVGUtils::ScheduleBoundsUpdate(nsIFrame *aFrame)
 {