Bug 450340. Support SVG mask/clip-path/filter CSS properties applied to non-SVG content. r=longsonr,sr=mats
authorRobert O'Callahan <robert@ocallahan.org>
Thu, 11 Sep 2008 12:24:16 +1200
changeset 19125 9eb7090abff39956da866f5009141755da211c35
parent 19124 c9482d51ef473b881fc0fb5a16546113941e220d
child 19126 fd1e113db2f489a539bad27655f0abcf6f13e76f
push id1954
push userrocallahan@mozilla.com
push dateThu, 11 Sep 2008 00:24:35 +0000
treeherdermozilla-central@9eb7090abff3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr, mats
bugs450340
milestone1.9.1b1pre
Bug 450340. Support SVG mask/clip-path/filter CSS properties applied to non-SVG content. r=longsonr,sr=mats
content/base/src/nsGkAtomList.h
content/svg/content/src/nsSVGFilterElement.h
content/svg/content/src/nsSVGFilters.h
content/svg/content/src/nsSVGLength2.cpp
content/svg/content/src/nsSVGLength2.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSRendering.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsFrame.cpp
layout/reftests/reftest.list
layout/reftests/svg-integration/clipPath-html-01-ref.svg
layout/reftests/svg-integration/clipPath-html-01.xhtml
layout/reftests/svg-integration/clipPath-html-02-ref.svg
layout/reftests/svg-integration/clipPath-html-02.xhtml
layout/reftests/svg-integration/clipPath-html-03-ref.svg
layout/reftests/svg-integration/clipPath-html-03.xhtml
layout/reftests/svg-integration/clipPath-html-04-ref.xhtml
layout/reftests/svg-integration/clipPath-html-04.xhtml
layout/reftests/svg-integration/clipPath-html-05-ref.xhtml
layout/reftests/svg-integration/clipPath-html-05.xhtml
layout/reftests/svg-integration/clipPath-html-06-ref.xhtml
layout/reftests/svg-integration/clipPath-html-06.xhtml
layout/reftests/svg-integration/filter-html-01-ref.svg
layout/reftests/svg-integration/filter-html-01.xhtml
layout/reftests/svg-integration/mask-html-01-ref.svg
layout/reftests/svg-integration/mask-html-01.xhtml
layout/reftests/svg-integration/reftest.list
layout/style/ua.css
layout/svg/base/src/Makefile.in
layout/svg/base/src/nsISVGChildFrame.h
layout/svg/base/src/nsSVGClipPathFrame.cpp
layout/svg/base/src/nsSVGClipPathFrame.h
layout/svg/base/src/nsSVGContainerFrame.cpp
layout/svg/base/src/nsSVGContainerFrame.h
layout/svg/base/src/nsSVGEffects.cpp
layout/svg/base/src/nsSVGFilterFrame.cpp
layout/svg/base/src/nsSVGFilterFrame.h
layout/svg/base/src/nsSVGFilterInstance.cpp
layout/svg/base/src/nsSVGFilterInstance.h
layout/svg/base/src/nsSVGFilterPaintCallback.h
layout/svg/base/src/nsSVGForeignObjectFrame.cpp
layout/svg/base/src/nsSVGForeignObjectFrame.h
layout/svg/base/src/nsSVGGFrame.cpp
layout/svg/base/src/nsSVGGFrame.h
layout/svg/base/src/nsSVGGeometryFrame.cpp
layout/svg/base/src/nsSVGGlyphFrame.cpp
layout/svg/base/src/nsSVGGlyphFrame.h
layout/svg/base/src/nsSVGInnerSVGFrame.cpp
layout/svg/base/src/nsSVGIntegrationUtils.cpp
layout/svg/base/src/nsSVGIntegrationUtils.h
layout/svg/base/src/nsSVGMaskFrame.cpp
layout/svg/base/src/nsSVGMaskFrame.h
layout/svg/base/src/nsSVGPathGeometryFrame.cpp
layout/svg/base/src/nsSVGPathGeometryFrame.h
layout/svg/base/src/nsSVGTSpanFrame.cpp
layout/svg/base/src/nsSVGTSpanFrame.h
layout/svg/base/src/nsSVGTextFrame.cpp
layout/svg/base/src/nsSVGTextFrame.h
layout/svg/base/src/nsSVGUtils.cpp
layout/svg/base/src/nsSVGUtils.h
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1601,24 +1601,26 @@ GK_ATOM(generatedContent, "GeneratedCont
 #ifdef MOZ_MATHML
 GK_ATOM(HTMLReflowMetricsProperty, "HTMLReflowMetricsProperty") // nsHTMLReflowMetrics*
 #endif
 GK_ATOM(IBSplitSpecialPrevSibling, "IBSplitSpecialPrevSibling")// nsIFrame*
 GK_ATOM(IBSplitSpecialSibling, "IBSplitSpecialSibling")    // nsIFrame*
 GK_ATOM(lineCursorProperty, "LineCursorProperty") // nsLineBox*
 GK_ATOM(rowCursorProperty, "RowCursorProperty") // nsTableRowGroupFrame::FrameCursorData*
 GK_ATOM(maxElementWidthProperty, "MaxElementWidthProperty")  // nscoord*
+GK_ATOM(outlineInnerRectProperty, "OutlineInnerRectProperty") // nsRect*
 GK_ATOM(outOfFlowDirtyRectProperty, "OutOfFlowDirtyRectProperty") // nsRect*
 GK_ATOM(overflowAreaProperty, "OverflowArea")              // nsRect*
 GK_ATOM(overflowProperty, "OverflowProperty")              // list of nsIFrame*
 GK_ATOM(overflowContainersProperty, "OverflowContainersProperty")             // nsFrameList*
 GK_ATOM(excessOverflowContainersProperty, "ExcessOverflowContainersProperty") // nsFrameList*
 GK_ATOM(overflowLinesProperty, "OverflowLinesProperty")    // list of nsLineBox*
 GK_ATOM(overflowOutOfFlowsProperty, "OverflowOutOfFlowsProperty")      // nsFrameList*
 GK_ATOM(overflowPlaceholdersProperty, "OverflowPlaceholdersProperty")  // nsFrameList*
+GK_ATOM(preEffectsBBoxProperty, "PreEffectsBBoxProperty") // nsRect*
 GK_ATOM(rowUnpaginatedHeightProperty, "RowUnpaginatedHeightProperty")  // nscoord*
 GK_ATOM(spaceManagerProperty, "SpaceManagerProperty")      // the space manager for a block
 GK_ATOM(tabWidthProperty, "TabWidthProperty")              // nsTArray<TabSetting>* array of tab widths
 GK_ATOM(tableBCProperty, "TableBCProperty")                // table border collapsing info (e.g. damage area, table border widths)
 GK_ATOM(usedMarginProperty, "UsedMarginProperty") // nsMargin*
 GK_ATOM(usedPaddingProperty, "UsedPaddingProperty") // nsMargin*
 GK_ATOM(viewProperty, "ViewProperty")                      
 
--- a/content/svg/content/src/nsSVGFilterElement.h
+++ b/content/svg/content/src/nsSVGFilterElement.h
@@ -49,16 +49,17 @@
 typedef nsSVGGraphicElement nsSVGFilterElementBase;
 
 class nsSVGFilterElement : public nsSVGFilterElementBase,
                            public nsIDOMSVGFilterElement,
                            public nsIDOMSVGURIReference,
                            public nsIDOMSVGUnitTypes
 {
   friend class nsSVGFilterFrame;
+  friend class nsAutoFilterInstance;
 
 protected:
   friend nsresult NS_NewSVGFilterElement(nsIContent **aResult,
                                          nsINodeInfo *aNodeInfo);
   nsSVGFilterElement(nsINodeInfo* aNodeInfo);
 
   // nsISVGValue interface:
   NS_IMETHOD SetValueString(const nsAString &aValue) { return NS_OK; }
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -37,16 +37,17 @@
 #ifndef __NS_SVGFILTERSELEMENT_H__
 #define __NS_SVGFILTERSELEMENT_H__
 
 #include "nsSVGStylableElement.h"
 #include "nsSVGLength2.h"
 #include "nsIFrame.h"
 #include "gfxRect.h"
 #include "gfxImageSurface.h"
+#include "nsIDOMSVGFilters.h"
 
 class nsSVGFilterResource;
 class nsSVGString;
 class nsSVGFilterInstance;
 
 typedef nsSVGStylableElement nsSVGFEBase;
 
 #define NS_SVG_FE_CID \
--- a/content/svg/content/src/nsSVGLength2.cpp
+++ b/content/svg/content/src/nsSVGLength2.cpp
@@ -35,16 +35,18 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsSVGLength2.h"
 #include "prdtoa.h"
 #include "nsTextFormatter.h"
 #include "nsSVGSVGElement.h"
+#include "nsIFrame.h"
+#include "nsSVGIntegrationUtils.h"
 
 NS_IMPL_ADDREF(nsSVGLength2::DOMBaseVal)
 NS_IMPL_RELEASE(nsSVGLength2::DOMBaseVal)
 
 NS_IMPL_ADDREF(nsSVGLength2::DOMAnimVal)
 NS_IMPL_RELEASE(nsSVGLength2::DOMAnimVal)
 
 NS_IMPL_ADDREF(nsSVGLength2::DOMAnimatedLength)
@@ -175,29 +177,60 @@ nsSVGLength2::GetMMPerPixel(nsSVGSVGElem
     NS_ASSERTION(mmPerPx != 0.0f, "invalid mm/pixels");
     mmPerPx = 1e-4f; // some small value
   }
 
   return mmPerPx;
 }
 
 float
+nsSVGLength2::GetMMPerPixel(nsIFrame *aNonSVGFrame) const
+{
+  nsPresContext* presContext = aNonSVGFrame->PresContext();
+  float pixelsPerInch =
+    presContext->AppUnitsToFloatCSSPixels(presContext->AppUnitsPerInch());
+  return 25.4f/pixelsPerInch;
+}
+
+static float
+FixAxisLength(float aLength)
+{
+  if (aLength == 0.0f) {
+    NS_WARNING("zero axis length");
+    return 1e-20f;
+  }
+  return aLength;
+}
+
+float
 nsSVGLength2::GetAxisLength(nsSVGSVGElement *aCtx) const
 {
   if (!aCtx)
     return 1;
 
-  float d = aCtx->GetLength(mCtxType);
+  return FixAxisLength(aCtx->GetLength(mCtxType));
+}
 
-  if (d == 0.0f) {
-    NS_WARNING("zero axis length");
-    d = 1e-20f;
+float
+nsSVGLength2::GetAxisLength(nsIFrame *aNonSVGFrame) const
+{
+  gfxRect rect = nsSVGIntegrationUtils::GetSVGRectForNonSVGFrame(aNonSVGFrame);
+  float length;
+  switch (mCtxType) {
+  case nsSVGUtils::X: length = rect.Width(); break;
+  case nsSVGUtils::Y: length = rect.Height(); break;
+  case nsSVGUtils::XY:
+    length = nsSVGUtils::ComputeNormalizedHypotenuse(rect.Width(), rect.Height());
+    break;
+  default:
+    NS_NOTREACHED("Unknown axis type");
+    length = 1;
+    break;
   }
-
-  return d;
+  return FixAxisLength(length);
 }
 
 float
 nsSVGLength2::GetUnitScaleFactor(nsSVGElement *aSVGElement) const
 {
   switch (mSpecifiedUnitType) {
   case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER:
   case nsIDOMSVGLength::SVG_LENGTHTYPE_PX:
@@ -235,16 +268,49 @@ nsSVGLength2::GetUnitScaleFactor(nsSVGSV
   case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS:
     return 1 / GetExLength(aCtx);
   default:
     NS_NOTREACHED("Unknown unit type");
     return 0;
   }
 }
 
+float
+nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame) const
+{
+  nsIContent* content = aFrame->GetContent();
+  if (content->IsNodeOfType(nsINode::eSVG))
+    return GetUnitScaleFactor(static_cast<nsSVGElement*>(content));
+
+  switch (mSpecifiedUnitType) {
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER:
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_PX:
+    return 1;
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_MM:
+    return GetMMPerPixel(aFrame);
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_CM:
+    return GetMMPerPixel(aFrame) / 10.0f;
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_IN:
+    return GetMMPerPixel(aFrame) / 25.4f;
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_PT:
+    return GetMMPerPixel(aFrame) * POINTS_PER_INCH_FLOAT / 25.4f;
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_PC:
+    return GetMMPerPixel(aFrame) * POINTS_PER_INCH_FLOAT / 24.4f / 12.0f;
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE:
+    return 100.0f / GetAxisLength(aFrame);
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS:
+    return 1 / GetEmLength(aFrame);
+  case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS:
+    return 1 / GetExLength(aFrame);
+  default:
+    NS_NOTREACHED("Unknown unit type");
+    return 0;
+  }
+}
+
 void
 nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue,
                                            nsSVGElement *aSVGElement)
 {
   mBaseVal = aValue;
   aSVGElement->DidChangeLength(mAttrEnum, PR_TRUE);
 }
 
--- a/content/svg/content/src/nsSVGLength2.h
+++ b/content/svg/content/src/nsSVGLength2.h
@@ -38,16 +38,18 @@
 #define __NS_SVGLENGTH2_H__
 
 #include "nsIDOMSVGLength.h"
 #include "nsIDOMSVGAnimatedLength.h"
 #include "nsSVGUtils.h"
 #include "nsSVGElement.h"
 #include "nsDOMError.h"
 
+class nsIFrame;
+
 class nsSVGLength2
 {
 
 public:
   void Init(PRUint8 aCtxType = nsSVGUtils::XY,
             PRUint8 aAttrEnum = 0xff,
             float aValue = 0,
             PRUint8 aUnitType = nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER) {
@@ -59,45 +61,55 @@ public:
   }
 
   nsresult SetBaseValueString(const nsAString& aValue,
                               nsSVGElement *aSVGElement,
                               PRBool aDoSetAttr);
   void GetBaseValueString(nsAString& aValue);
   void GetAnimValueString(nsAString& aValue);
 
-  float GetBaseValue(nsSVGElement* aSVGElement)
+  float GetBaseValue(nsSVGElement* aSVGElement) const
     { return mBaseVal / GetUnitScaleFactor(aSVGElement); }
-  float GetAnimValue(nsSVGElement* aSVGElement)
+  float GetAnimValue(nsSVGElement* aSVGElement) const
     { return mAnimVal / GetUnitScaleFactor(aSVGElement); }
+  float GetAnimValue(nsIFrame* aFrame) const
+    { return mAnimVal / GetUnitScaleFactor(aFrame); }
 
   PRUint8 GetCtxType() const { return mCtxType; }
   PRUint8 GetSpecifiedUnitType() const { return mSpecifiedUnitType; }
   PRBool IsPercentage() const
     { return mSpecifiedUnitType == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE; }
   float GetAnimValInSpecifiedUnits() const { return mAnimVal; }
   float GetBaseValInSpecifiedUnits() const { return mBaseVal; }
 
-  float GetBaseValue(nsSVGSVGElement* aCtx)
+  float GetBaseValue(nsSVGSVGElement* aCtx) const
     { return mBaseVal / GetUnitScaleFactor(aCtx); }
-  float GetAnimValue(nsSVGSVGElement* aCtx)
+  float GetAnimValue(nsSVGSVGElement* aCtx) const
     { return mAnimVal / GetUnitScaleFactor(aCtx); }
   
   nsresult ToDOMAnimatedLength(nsIDOMSVGAnimatedLength **aResult,
                                nsSVGElement* aSVGElement);
 
 private:
   
   float mAnimVal;
   float mBaseVal;
   PRUint8 mSpecifiedUnitType;
   PRUint8 mAttrEnum; // element specified tracking for attribute
   PRUint8 mCtxType; // X, Y or Unspecified
   PRPackedBool mIsAnimated;
   
+  float GetMMPerPixel(nsIFrame *aNonSVGFrame) const;
+  float GetAxisLength(nsIFrame *aNonSVGFrame) const;
+  float GetEmLength(nsIFrame *aFrame) const
+    { return nsSVGUtils::GetFontSize(aFrame); }
+  float GetExLength(nsIFrame *aFrame) const
+    { return nsSVGUtils::GetFontXHeight(aFrame); }
+  float GetUnitScaleFactor(nsIFrame *aFrame) const;
+
   float GetMMPerPixel(nsSVGSVGElement *aCtx) const;
   float GetAxisLength(nsSVGSVGElement *aCtx) const;
   float GetEmLength(nsSVGElement *aSVGElement) const
     { return nsSVGUtils::GetFontSize(aSVGElement); }
   float GetExLength(nsSVGElement *aSVGElement) const
     { return nsSVGUtils::GetFontXHeight(aSVGElement); }
   float GetUnitScaleFactor(nsSVGElement *aSVGElement) const;
   float GetUnitScaleFactor(nsSVGSVGElement *aCtx) const;
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -484,17 +484,17 @@ static nsIFrame* GetSpecialSibling(nsIFr
   aFrame = aFrame->GetFirstContinuation();
 
   void* value = aFrame->GetProperty(nsGkAtoms::IBSplitSpecialSibling);
 
   return static_cast<nsIFrame*>(value);
 }
 
 static nsIFrame*
-GetIBSplitSpecialPrevSibling(nsIFrame* aFrame)
+GetIBSplitSpecialPrevSiblingForAnonymousBlock(nsIFrame* aFrame)
 {
   NS_PRECONDITION(IsFrameSpecial(aFrame) && !IsInlineFrame(aFrame),
                   "Shouldn't call this");
   
   // We only store the "special sibling" annotation with the first
   // frame in the continuation chain. Walk back to find that frame now.  
   return
     static_cast<nsIFrame*>
@@ -603,20 +603,17 @@ FindLastBlock(nsIFrame* aKid)
       lastBlock = aKid;
     }
     aKid = aKid->GetNextSibling();
   }
   return lastBlock;
 }
 
 /*
- * Unlike the special (next) sibling, the special previous sibling
- * property points only from the anonymous block to the original
- * inline that preceded it.  DO NOT CHANGE THAT -- the
- * GetParentStyleContextFrame code depends on it!  It is useful for
+ * The special-prev-sibling is useful for
  * finding the "special parent" of a frame (i.e., a frame from which a
  * good parent style context can be obtained), one looks at the
  * special previous sibling annotation of the real parent of the frame
  * (if the real parent has NS_FRAME_IS_SPECIAL).
  */
 inline void
 MarkIBSpecialPrevSibling(nsIFrame *aAnonymousFrame,
                          nsIFrame *aSpecialParent)
@@ -7832,17 +7829,18 @@ nsCSSFrameConstructor::AppendFrames(nsFr
     do {
       NS_ASSERTION(IsFrameSpecial(parentFrame) && !IsInlineFrame(parentFrame),
                    "Shouldn't be in this code");
       nsIFrame* inlineSibling = GetSpecialSibling(parentFrame);
       PRBool isPositioned = PR_FALSE;
       nsIContent* content = nsnull;
       nsStyleContext* styleContext = nsnull;
       if (!inlineSibling) {
-        nsIFrame* firstInline = GetIBSplitSpecialPrevSibling(parentFrame);
+        nsIFrame* firstInline =
+          GetIBSplitSpecialPrevSiblingForAnonymousBlock(parentFrame);
         NS_ASSERTION(firstInline, "How did that happen?");
 
         content = firstInline->GetContent();
         styleContext = firstInline->GetStyleContext();
         isPositioned = (styleContext->GetStyleDisplay()->mPosition ==
                         NS_STYLE_POSITION_RELATIVE);
       }
 
@@ -12450,18 +12448,21 @@ nsCSSFrameConstructor::ConstructInline(n
 
   // Mark the frames as special (note: marking for inlineFrame is handled by
   // MoveFramesToEndOfIBSplit). That way if any of the append/insert/remove
   // methods try to fiddle with the children, the containing block will be
   // reframed instead.
   SetFrameIsSpecial(aNewFrame, blockFrame);
   SetFrameIsSpecial(blockFrame, inlineFrame);
   MarkIBSpecialPrevSibling(blockFrame, aNewFrame);
-
-#ifdef DEBUG
+  if (inlineFrame) {
+    MarkIBSpecialPrevSibling(inlineFrame, blockFrame);
+  }
+
+  #ifdef DEBUG
   if (gNoisyInlineConstruction) {
     nsIFrameDebug*  frameDebug;
 
     printf("nsCSSFrameConstructor::ConstructInline:\n");
     if (NS_SUCCEEDED(CallQueryInterface(aNewFrame, &frameDebug))) {
       printf("  ==> leading inline frame:\n");
       frameDebug->List(stdout, 2);
     }
@@ -12720,18 +12721,18 @@ nsCSSFrameConstructor::WipeContainingBlo
         return PR_FALSE;
       }
 
       // Walk up until we get a float containing block that's not part of an
       // {ib} split, since otherwise we might have to ship floats out of it
       // too.
       nsIFrame* floatContainer = aFrame;
       do {
-        floatContainer =
-          GetFloatContainingBlock(GetIBSplitSpecialPrevSibling(floatContainer));
+        floatContainer = GetFloatContainingBlock(
+          GetIBSplitSpecialPrevSiblingForAnonymousBlock(floatContainer));
         if (!floatContainer) {
           break;
         }
         if (!IsFrameSpecial(floatContainer)) {
           return PR_FALSE;
         }
       } while (1);
     }
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -637,16 +637,26 @@ nsCSSRendering::PaintBorder(nsPresContex
                          bgColor->mBackgroundColor);
   br.DrawBorders();
 
   ctx->Restore();
 
   SN();
 }
 
+static nsRect
+GetOutlineInnerRect(nsIFrame* aFrame)
+{
+  nsRect* savedOutlineInnerRect = static_cast<nsRect*>
+    (aFrame->GetProperty(nsGkAtoms::outlineInnerRectProperty));
+  if (savedOutlineInnerRect)
+    return *savedOutlineInnerRect;
+  return aFrame->GetOverflowRect();
+}
+
 void
 nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
                              nsIRenderingContext& aRenderingContext,
                              nsIFrame* aForFrame,
                              const nsRect& aDirtyRect,
                              const nsRect& aBorderArea,
                              const nsStyleBorder& aBorderStyle,
                              const nsStyleOutline& aOutlineStyle,
@@ -666,76 +676,61 @@ nsCSSRendering::PaintOutline(nsPresConte
   }
 
   const nsStyleBackground* bgColor = nsCSSRendering::FindNonTransparentBackground
     (aStyleContext, PR_FALSE);
 
   // get the radius for our outline
   GetBorderRadiusTwips(aOutlineStyle.mOutlineRadius, aBorderArea.width, twipsRadii);
 
-  nscoord offset;
-  aOutlineStyle.GetOutlineOffset(offset);
-
   // When the outline property is set on :-moz-anonymous-block or
   // :-moz-anonyomus-positioned-block pseudo-elements, it inherited that
   // outline from the inline that was broken because it contained a
   // block.  In that case, we don't want a really wide outline if the
   // block inside the inline is narrow, so union the actual contents of
   // the anonymous blocks.
   nsIFrame *frameForArea = aForFrame;
   do {
     nsIAtom *pseudoType = frameForArea->GetStyleContext()->GetPseudoType();
     if (pseudoType != nsCSSAnonBoxes::mozAnonymousBlock &&
         pseudoType != nsCSSAnonBoxes::mozAnonymousPositionedBlock)
       break;
     // If we're done, we really want it and all its later siblings.
     frameForArea = frameForArea->GetFirstChild(nsnull);
     NS_ASSERTION(frameForArea, "anonymous block with no children?");
   } while (frameForArea);
-  nsRect overflowArea;
+  nsRect innerRect; // relative to aBorderArea.TopLeft()
   if (frameForArea == aForFrame) {
-    overflowArea = aForFrame->GetOverflowRect();
+    innerRect = GetOutlineInnerRect(aForFrame);
   } else {
     for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
       // The outline has already been included in aForFrame's overflow
       // area, but not in those of its descendants, so we have to
       // include it.  Otherwise we'll end up drawing the outline inside
       // the border.
-      nsRect r(frameForArea->GetOverflowRect() +
+      nsRect r(GetOutlineInnerRect(frameForArea) +
                frameForArea->GetOffsetTo(aForFrame));
-      nscoord delta = PR_MAX(offset + width, 0);
-      r.Inflate(delta, delta);
-      overflowArea.UnionRect(overflowArea, r);
+      innerRect.UnionRect(innerRect, r);
     }
   }
 
-  nsRect outerRect(overflowArea + aBorderArea.TopLeft());
-  nsRect innerRect(outerRect);
-  if (width + offset >= 0) {
-    // the overflow area is exactly the outside edge of the outline
-    innerRect.Deflate(width, width);
-  } else {
-    // the overflow area is exactly the rectangle containing the frame and its
-    // children; we can compute the outline directly
-    innerRect.Deflate(-offset, -offset);
-    if (innerRect.width < 0 || innerRect.height < 0) {
-      return; // Protect against negative outline sizes
-    }
-    outerRect = innerRect;
-    outerRect.Inflate(width, width);
-  }
-
+  innerRect += aBorderArea.TopLeft();
+  nscoord offset;
+  aOutlineStyle.GetOutlineOffset(offset);
+  innerRect.Inflate(offset, offset);
   // If the dirty rect is completely inside the border area (e.g., only the
   // content is being painted), then we can skip out now
   // XXX this isn't exactly true for rounded borders, where the inside curves may
   // encroach into the content area.  A safer calculation would be to
   // shorten insideRect by the radius one each side before performing this test.
-  if (innerRect.Contains(aDirtyRect)) {
+  if (innerRect.Contains(aDirtyRect))
     return;
-  }
+
+  nsRect outerRect = innerRect;
+  outerRect.Inflate(width, width);
 
   // Get our conversion values
   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
 
   // get the outer rectangles
   gfxRect oRect(RectToGfxRect(outerRect, twipsPerPixel));
 
   // convert the radii
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -46,16 +46,19 @@
 
 #include "nsCSSRendering.h"
 #include "nsISelectionController.h"
 #include "nsIPresShell.h"
 #include "nsRegion.h"
 #include "nsFrameManager.h"
 #include "gfxContext.h"
 #include "nsStyleStructInlines.h"
+#ifdef MOZ_SVG
+#include "nsSVGIntegrationUtils.h"
+#endif
 
 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
     PRBool aIsForEvents, PRBool aBuildCaret)
     : mReferenceFrame(aReferenceFrame),
       mMovingFrame(nsnull),
       mIgnoreScrollFrame(nsnull),
       mCurrentTableItem(nsnull),
       mBuildCaret(aBuildCaret),
@@ -262,16 +265,25 @@ nsDisplayList::FlattenTo(nsTArray<nsDisp
       item->GetList()->FlattenTo(aElements);
       item->~nsDisplayItem();
     } else {
       aElements->AppendElement(item);
     }
   }
 }
 
+nsRect
+nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const {
+  nsRect bounds;
+  for (nsDisplayItem* i = GetBottom(); i != nsnull; i = i->GetAbove()) {
+    bounds.UnionRect(bounds, i->GetBounds(aBuilder));
+  }
+  return bounds;
+}
+
 void
 nsDisplayList::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
                                   nsRegion* aVisibleRegion) {
   nsAutoTArray<nsDisplayItem*, 512> elements;
   FlattenTo(&elements);
 
   for (PRInt32 i = elements.Length() - 1; i >= 0; --i) {
     nsDisplayItem* item = elements[i];
@@ -666,21 +678,17 @@ nsDisplayWrapList::~nsDisplayWrapList() 
 nsIFrame*
 nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
                            HitTestState* aState) {
   return mList.HitTest(aBuilder, aPt, aState);
 }
 
 nsRect
 nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder) {
-  nsRect bounds;
-  for (nsDisplayItem* i = mList.GetBottom(); i != nsnull; i = i->GetAbove()) {
-    bounds.UnionRect(bounds, i->GetBounds(aBuilder));
-  }
-  return bounds;
+  return mList.GetBounds(aBuilder);
 }
 
 PRBool
 nsDisplayWrapList::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
                                       nsRegion* aVisibleRegion) {
   mList.OptimizeVisibility(aBuilder, aVisibleRegion);
   // If none of the items are visible, they will all have been deleted
   return mList.GetTop() != nsnull;
@@ -923,8 +931,77 @@ PRBool nsDisplayClip::TryMerge(nsDisplay
   return PR_TRUE;
 }
 
 nsDisplayWrapList* nsDisplayClip::WrapWithClone(nsDisplayListBuilder* aBuilder,
                                                 nsDisplayItem* aItem) {
   return new (aBuilder)
     nsDisplayClip(aItem->GetUnderlyingFrame(), mClippingFrame, aItem, mClip);
 }
+
+#ifdef MOZ_SVG
+nsDisplaySVGEffects::nsDisplaySVGEffects(nsIFrame* aFrame, nsDisplayList* aList)
+    : nsDisplayWrapList(aFrame, aList), mEffectsFrame(aFrame),
+      mBounds(aFrame->GetOverflowRect())
+{
+  MOZ_COUNT_CTOR(nsDisplaySVGEffects);
+}
+
+#ifdef NS_BUILD_REFCNT_LOGGING
+nsDisplaySVGEffects::~nsDisplaySVGEffects()
+{
+  MOZ_COUNT_DTOR(nsDisplaySVGEffects);
+}
+#endif
+
+PRBool nsDisplaySVGEffects::IsOpaque(nsDisplayListBuilder* aBuilder)
+{
+  return PR_FALSE;
+}
+
+nsIFrame*
+nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
+                             HitTestState* aState)
+{
+  if (!nsSVGIntegrationUtils::HitTestFrameForEffects(mEffectsFrame,
+          aPt - aBuilder->ToReferenceFrame(mEffectsFrame)))
+    return nsnull;
+  return mList.HitTest(aBuilder, aPt, aState);
+}
+
+void nsDisplaySVGEffects::Paint(nsDisplayListBuilder* aBuilder,
+                                nsIRenderingContext* aCtx, const nsRect& aDirtyRect)
+{
+  nsSVGIntegrationUtils::PaintFramesWithEffects(aCtx,
+          mEffectsFrame, aDirtyRect, aBuilder, &mList);
+}
+
+PRBool nsDisplaySVGEffects::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
+                                               nsRegion* aVisibleRegion) {
+  nsRegion vis;
+  vis.And(*aVisibleRegion, GetBounds(aBuilder));
+  nsPoint offset = aBuilder->ToReferenceFrame(mEffectsFrame);
+  nsRect dirtyRect = nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mEffectsFrame,
+       vis.GetBounds() - offset) + offset;
+
+  // Our children may be translucent so we should not allow them to subtract
+  // area from aVisibleRegion.
+  nsRegion childrenVisibleRegion(dirtyRect);
+  nsDisplayWrapList::OptimizeVisibility(aBuilder, &childrenVisibleRegion);
+  return !vis.IsEmpty();
+}
+
+PRBool nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem)
+{
+  if (aItem->GetType() != TYPE_SVG_EFFECTS)
+    return PR_FALSE;
+  // items for the same content element should be merged into a single
+  // compositing group
+  // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects
+  if (aItem->GetUnderlyingFrame()->GetContent() != mFrame->GetContent())
+    return PR_FALSE;
+  nsDisplaySVGEffects* other = static_cast<nsDisplaySVGEffects*>(aItem);
+  mList.AppendToBottom(&other->mList);
+  mBounds.UnionRect(mBounds,
+    other->mBounds + other->mEffectsFrame->GetOffsetTo(mEffectsFrame));
+  return PR_TRUE;
+}
+#endif
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -387,16 +387,19 @@ public:
    * It's useful to be able to dynamically check the type of certain items.
    * For items whose type never gets checked, TYPE_GENERIC will suffice.
    */
   enum Type {
     TYPE_GENERIC,
     TYPE_OUTLINE,
     TYPE_CLIP,
     TYPE_OPACITY,
+#ifdef MOZ_SVG
+    TYPE_SVG_EFFECTS,
+#endif
     TYPE_WRAPLIST
   };
 
   struct HitTestState {
     ~HitTestState() {
       NS_ASSERTION(mItemBuffer.Length() == 0,
                    "mItemBuffer should have been cleared");
     }
@@ -690,16 +693,20 @@ public:
    * corresponds to the origin of the reference frame. For best results,
    * aCtx's current transform should make (0,0) pixel-aligned. The
    * rectangle in aDirtyRect is painted, which *must* be contained in the
    * dirty rect used to construct the display list.
    */
   void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
              const nsRect& aDirtyRect) const;
   /**
+   * Get the bounds. Takes the union of the bounds of all children.
+   */
+  nsRect GetBounds(nsDisplayListBuilder* aBuilder) const;
+  /**
    * Find the topmost display item that returns a non-null frame, and return
    * the frame.
    */
   nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
                     nsDisplayItem::HitTestState* aState) const;
 
 private:
   // This class is only used on stack, so we don't have to worry about leaking
@@ -1247,9 +1254,44 @@ private:
   // The frame that is responsible for the clipping. This may be different
   // from mFrame because mFrame represents the content that is being
   // clipped, and for example may be used to obtain the z-index of the
   // content.
   nsIFrame* mClippingFrame;
   nsRect    mClip;
 };
 
+#ifdef MOZ_SVG
+/**
+ * A display item to paint a stacking context with effects
+ * set by the stacking context root frame's style.
+ */
+class nsDisplaySVGEffects : public nsDisplayWrapList {
+public:
+  nsDisplaySVGEffects(nsIFrame* aFrame, nsDisplayList* aList);
+#ifdef NS_BUILD_REFCNT_LOGGING
+  virtual ~nsDisplaySVGEffects();
+#endif
+  
+  virtual Type GetType() { return TYPE_SVG_EFFECTS; }
+  virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder);
+  virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
+                            HitTestState* aState);
+  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
+    return mBounds + aBuilder->ToReferenceFrame(mEffectsFrame);
+  }
+  virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
+     const nsRect& aDirtyRect);
+  virtual PRBool OptimizeVisibility(nsDisplayListBuilder* aBuilder,
+                                    nsRegion* aVisibleRegion);  
+  virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
+  NS_DISPLAY_DECL_NAME("SVGEffects")
+
+  nsIFrame* GetEffectsFrame() { return mEffectsFrame; }
+
+private:
+  nsIFrame* mEffectsFrame;
+  // relative to mEffectsFrame
+  nsRect    mBounds;
+};
+#endif
+
 #endif /*NSDISPLAYLIST_H_*/
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -74,16 +74,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsIBaseWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIWidget.h"
 
 #ifdef MOZ_SVG
 #include "nsSVGUtils.h"
+#include "nsSVGIntegrationUtils.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsSVGOuterSVGFrame.h"
 #endif
 
 /**
  * A namespace class for static layout utilities.
  */
 
@@ -1032,17 +1033,29 @@ AccumulateItemInRegion(nsRegion* aRegion
 static void
 AddItemsToRegion(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
                  const nsRect& aRect, const nsRect& aClipRect, nsPoint aDelta,
                  nsRegion* aRegion)
 {
   for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
     nsDisplayList* sublist = item->GetList();
     if (sublist) {
-      if (item->GetType() == nsDisplayItem::TYPE_CLIP) {
+      nsDisplayItem::Type type = item->GetType();
+#ifdef MOZ_SVG
+      if (type == nsDisplayItem::TYPE_SVG_EFFECTS) {
+        nsDisplaySVGEffects* effectsItem = static_cast<nsDisplaySVGEffects*>(item);
+        if (!aBuilder->IsMovingFrame(effectsItem->GetEffectsFrame())) {
+          // Invalidate the whole thing
+          nsRect r;
+          r.IntersectRect(aClipRect, effectsItem->GetBounds(aBuilder));
+          aRegion->Or(*aRegion, r);
+        }
+      } else
+#endif
+      if (type == nsDisplayItem::TYPE_CLIP) {
         nsDisplayClip* clipItem = static_cast<nsDisplayClip*>(item);
         nsRect clip = aClipRect;
         // If the clipping frame is moving, then it isn't clipping any
         // non-moving content (see ApplyAbsPosClipping), so we don't need
         // to do anything special, but we should not restrict aClipRect.
         if (!aBuilder->IsMovingFrame(clipItem->GetClippingFrame())) {
           clip.IntersectRect(clip, clipItem->GetClipRect());
 
@@ -1236,55 +1249,73 @@ nsLayoutUtils::BinarySearchForPosition(n
     if (BinarySearchForPosition(aRendContext, aText, aBaseWidth, aBaseInx, inx, aEndInx, aCursorPos, aIndex, aTextWidth)) {
       return PR_TRUE;
     }
   }
   return PR_FALSE;
 }
 
 static void
-AddRectsForFrame(nsIFrame* aFrame, nsIFrame* aRelativeTo,
-                 nsLayoutUtils::RectCallback* aCallback)
+AddBoxesForFrame(nsIFrame* aFrame,
+                 nsLayoutUtils::BoxCallback* aCallback)
 {
   nsIAtom* pseudoType = aFrame->GetStyleContext()->GetPseudoType();
 
   if (pseudoType == nsCSSAnonBoxes::tableOuter) {
-    AddRectsForFrame(aFrame->GetFirstChild(nsnull), aRelativeTo,
-                     aCallback);
+    AddBoxesForFrame(aFrame->GetFirstChild(nsnull), aCallback);
     nsIFrame* kid = aFrame->GetFirstChild(nsGkAtoms::captionList);
     if (kid) {
-      AddRectsForFrame(kid, aRelativeTo, aCallback);
+      AddBoxesForFrame(kid, aCallback);
     }
   } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock ||
              pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock ||
              pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock ||
              pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) {
     for (nsIFrame* kid = aFrame->GetFirstChild(nsnull); kid; kid = kid->GetNextSibling()) {
-      AddRectsForFrame(kid, aRelativeTo, aCallback);
+      AddBoxesForFrame(kid, aCallback);
     }
   } else {
+    aCallback->AddBox(aFrame);
+  }
+}
+
+void
+nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback)
+{
+  while (aFrame) {
+    AddBoxesForFrame(aFrame, aCallback);
+    aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame);
+  }
+}
+
+struct BoxToBorderRect : public nsLayoutUtils::BoxCallback {
+  nsIFrame*                    mRelativeTo;
+  nsLayoutUtils::RectCallback* mCallback;
+
+  BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback)
+    : mCallback(aCallback), mRelativeTo(aRelativeTo) {}
+
+  virtual void AddBox(nsIFrame* aFrame) {
 #ifdef MOZ_SVG
     nsRect r;
     nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r);
     if (outer) {
-      aCallback->AddRect(r + outer->GetOffsetTo(aRelativeTo));
+      mCallback->AddRect(r + outer->GetOffsetTo(mRelativeTo));
     } else
 #endif
-      aCallback->AddRect(nsRect(aFrame->GetOffsetTo(aRelativeTo), aFrame->GetSize()));
+      mCallback->AddRect(nsRect(aFrame->GetOffsetTo(mRelativeTo), aFrame->GetSize()));
   }
-}
+};
 
 void
 nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo,
                                  RectCallback* aCallback)
 {
-  while (aFrame) {
-    AddRectsForFrame(aFrame, aRelativeTo, aCallback);
-    aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame);
-  }
+  BoxToBorderRect converter(aRelativeTo, aCallback);
+  GetAllInFlowBoxes(aFrame, &converter);
 }
 
 struct RectAccumulator : public nsLayoutUtils::RectCallback {
   nsRect       mResultRect;
   nsRect       mFirstRect;
   PRPackedBool mSeenFirstRect;
 
   RectAccumulator() : mSeenFirstRect(PR_FALSE) {}
@@ -1470,16 +1501,33 @@ nsLayoutUtils::GetNextContinuationOrSpec
 
     void* value = aFrame->GetProperty(nsGkAtoms::IBSplitSpecialSibling);
     return static_cast<nsIFrame*>(value);
   }
 
   return nsnull;
 }
 
+nsIFrame*
+nsLayoutUtils::GetFirstContinuationOrSpecialSibling(nsIFrame *aFrame)
+{
+  nsIFrame *result = aFrame->GetFirstContinuation();
+  if (result->GetStateBits() & NS_FRAME_IS_SPECIAL) {
+    while (PR_TRUE) {
+      nsIFrame *f = static_cast<nsIFrame*>
+        (result->GetProperty(nsGkAtoms::IBSplitSpecialPrevSibling));
+      if (!f)
+        break;
+      result = f;
+    }
+  }
+
+  return result;
+}
+
 PRBool
 nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame)
 {
   if (!aFrame)
     return PR_FALSE;
 
   nsIFrame* rootScrollFrame =
     aFrame->PresContext()->PresShell()->GetRootScrollFrame();
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -481,16 +481,29 @@ public:
                           PRInt32    aBaseWidth,
                           PRInt32    aBaseInx,
                           PRInt32    aStartInx, 
                           PRInt32    aEndInx, 
                           PRInt32    aCursorPos, 
                           PRInt32&   aIndex,
                           PRInt32&   aTextWidth);
 
+  class BoxCallback {
+  public:
+    virtual void AddBox(nsIFrame* aFrame) = 0;
+  };
+  /**
+   * Collect all CSS boxes associated with aFrame and its
+   * continuations, "drilling down" through outer table frames and
+   * some anonymous blocks since they're not real CSS boxes.
+   * If aFrame is null, no boxes are returned.
+   * SVG frames return a single box, themselves.
+   */
+  static void GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback);
+
   class RectCallback {
   public:
     virtual void AddRect(const nsRect& aRect) = 0;
   };
   /**
    * Collect all CSS border-boxes associated with aFrame and its
    * continuations, "drilling down" through outer table frames and
    * some anonymous blocks since they're not real CSS boxes.
@@ -575,16 +588,23 @@ public:
 
   /**
    * Get a frame's next-in-flow, or, if it doesn't have one, its special sibling.
    */
   static nsIFrame*
   GetNextContinuationOrSpecialSibling(nsIFrame *aFrame);
 
   /**
+   * Get the first frame in the continuation-plus-special-sibling chain
+   * containing aFrame.
+   */
+  static nsIFrame*
+  GetFirstContinuationOrSpecialSibling(nsIFrame *aFrame);
+  
+  /**
    * Check whether aFrame is a part of the scrollbar or scrollcorner of
    * the root content.
    * @param aFrame the checking frame
    * @return if TRUE, the frame is a part of the scrollbar or scrollcorner of
    *         the root content.
    */
   static PRBool IsViewportScrollbarFrame(nsIFrame* aFrame);
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -115,16 +115,20 @@
 #include "nsUnicharUtils.h"
 #include "nsLayoutErrors.h"
 #include "nsContentErrors.h"
 #include "nsHTMLContainerFrame.h"
 #include "nsBoxLayoutState.h"
 #include "nsBlockFrame.h"
 #include "nsDisplayList.h"
 
+#ifdef MOZ_SVG
+#include "nsSVGIntegrationUtils.h"
+#endif
+
 #include "gfxContext.h"
 
 static NS_DEFINE_CID(kLookAndFeelCID,  NS_LOOKANDFEEL_CID);
 static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID);
 
 // Struct containing cached metrics for box-wrapped frames.
 struct nsBoxLayoutMetrics
 {
@@ -1176,17 +1180,25 @@ nsIFrame::BuildDisplayListForStackingCon
   const nsStyleDisplay* disp = GetStyleDisplay();
   PRBool applyAbsPosClipping =
       ApplyAbsPosClipping(aBuilder, disp, this, &absPosClip);
   nsRect dirtyRect = aDirtyRect;
   if (applyAbsPosClipping) {
     dirtyRect.IntersectRect(dirtyRect,
                             absPosClip - aBuilder->ToReferenceFrame(this));
   }
-      
+
+#ifdef MOZ_SVG
+  PRBool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
+  if (usingSVGEffects) {
+    dirtyRect =
+      nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
+  }
+#endif
+
   nsDisplayListCollection set;
   nsresult rv;
   {    
     nsDisplayListBuilder::AutoIsRootSetter rootSetter(aBuilder, PR_TRUE);
     rv = BuildDisplayList(aBuilder, dirtyRect, set);
   }
   NS_ENSURE_SUCCESS(rv, rv);
     
@@ -1257,22 +1269,27 @@ nsIFrame::BuildDisplayListForStackingCon
     nsAbsPosClipWrapper wrapper(this, absPosClip);
     nsDisplayItem* item = wrapper.WrapList(aBuilder, this, &resultList);
     if (!item)
       return NS_ERROR_OUT_OF_MEMORY;
     // resultList was emptied
     resultList.AppendToTop(item);
   }
  
-  if (disp->mOpacity == 1.0f) {
-    aList->AppendToTop(&resultList);
+#ifdef MOZ_SVG
+  if (usingSVGEffects) {
+    rv = aList->AppendNewToTop(new (aBuilder) nsDisplaySVGEffects(this, &resultList));
+  } else
+#endif
+  if (disp->mOpacity < 1.0f) {
+    rv = aList->AppendNewToTop(new (aBuilder) nsDisplayOpacity(this, &resultList));
   } else {
-    rv = aList->AppendNewToTop(new (aBuilder) nsDisplayOpacity(this, &resultList));
-  }
-  
+    aList->AppendToTop(&resultList);
+  }
+
   return rv;
 }
 
 class nsDisplaySummary : public nsDisplayItem
 {
 public:
   nsDisplaySummary(nsIFrame* aFrame) : nsDisplayItem(aFrame) {
     MOZ_COUNT_CTOR(nsDisplaySummary);
@@ -1401,17 +1418,21 @@ nsIFrame::BuildDisplayListForChild(nsDis
   
   const nsStyleDisplay* ourDisp = GetStyleDisplay();
   // REVIEW: Taken from nsBoxFrame::Paint
   // Don't paint our children if the theme object is a leaf.
   if (IsThemed(ourDisp) &&
       !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance))
     return NS_OK;
 
-  PRBool isComposited = disp->mOpacity != 1.0f;
+  PRBool isComposited = disp->mOpacity != 1.0f
+#ifdef MOZ_SVG
+    || nsSVGIntegrationUtils::UsingEffectsForFrame(aChild)
+#endif
+    ;
   PRBool isPositioned = disp->IsPositioned();
   if (isComposited || isPositioned || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
     // If you change this, also change IsPseudoStackingContextFromStyle()
     pseudoStackingContext = PR_TRUE;
   }
   
   nsRect overflowClip;
   PRBool applyOverflowClip =
@@ -3586,16 +3607,25 @@ nsIFrame::Invalidate(const nsRect& aDama
   
   InvalidateInternal(aDamageRect, 0, 0, nsnull, aImmediate);
 }
 
 void
 nsIFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY,
                              nsIFrame* aForChild, PRBool aImmediate)
 {
+#ifdef MOZ_SVG
+  if (nsSVGIntegrationUtils::UsingEffectsForFrame(this)) {
+    nsRect r = nsSVGIntegrationUtils::GetInvalidAreaForChangedSource(this,
+            aDamageRect + nsPoint(aX, aY));
+    GetParent()->InvalidateInternal(r, mRect.x, mRect.y, this, aImmediate);
+    return;
+  }
+#endif
+
   GetParent()->
     InvalidateInternal(aDamageRect, aX + mRect.x, aY + mRect.y, this, aImmediate);
 }
 
 void
 nsIFrame::InvalidateRectDifference(const nsRect& aR1, const nsRect& aR2)
 {
   nsRect sizeHStrip, sizeVStrip;
@@ -3615,37 +3645,100 @@ nsIFrame::InvalidateRoot(const nsRect& a
                          nscoord aX, nscoord aY, PRBool aImmediate)
 {
   PRUint32 flags = aImmediate ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_NO_SYNC;
   nsIView* view = GetView();
   NS_ASSERTION(view, "This can only be called on frames with views");
   view->GetViewManager()->UpdateView(view, aDamageRect + nsPoint(aX, aY), flags);
 }
 
-static nsRect ComputeOutlineRect(const nsIFrame* aFrame, PRBool* aAnyOutline,
-                                 const nsRect& aOverflowRect) {
+static void
+DestroyRectFunc(void*    aFrame,
+                nsIAtom* aPropertyName,
+                void*    aPropertyValue,
+                void*    aDtorData)
+{
+  delete static_cast<nsRect*>(aPropertyValue);
+}
+
+static void
+SetRectProperty(nsIFrame* aFrame, nsIAtom* aProp, const nsRect& aRect)
+{
+  nsRect* r = new nsRect(aRect);
+  if (!r)
+    return;
+  aFrame->SetProperty(aProp, r, DestroyRectFunc);
+}
+
+static nsRect
+ComputeOutlineAndEffectsRect(nsIFrame* aFrame, PRBool* aAnyOutlineOrEffects,
+                             const nsRect& aOverflowRect,
+                             PRBool aStoreRectProperties) {
+  nsRect r = aOverflowRect;
+  *aAnyOutlineOrEffects = PR_FALSE;
+
+  // box-shadow
+  nsCSSShadowArray* boxShadows = aFrame->GetStyleBorder()->mBoxShadow;
+  if (boxShadows) {
+    nsRect shadows;
+    for (PRUint32 i = 0; i < boxShadows->Length(); ++i) {
+      nsRect tmpRect = r;
+      nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
+      nscoord xOffset = shadow->mXOffset.GetCoordValue();
+      nscoord yOffset = shadow->mYOffset.GetCoordValue();
+      nscoord outsetRadius = shadow->mRadius.GetCoordValue() +
+                             shadow->mSpread.GetCoordValue();
+
+      tmpRect.MoveBy(nsPoint(xOffset, yOffset));
+      tmpRect.Inflate(outsetRadius, outsetRadius);
+
+      shadows.UnionRect(shadows, tmpRect);
+    }
+    r.UnionRect(r, shadows);
+  }
+
   const nsStyleOutline* outline = aFrame->GetStyleOutline();
   PRUint8 outlineStyle = outline->GetOutlineStyle();
-  nsRect r = aOverflowRect;
-  *aAnyOutline = PR_FALSE;
   if (outlineStyle != NS_STYLE_BORDER_STYLE_NONE) {
     nscoord width;
 #ifdef DEBUG
     PRBool result = 
 #endif
       outline->GetOutlineWidth(width);
     NS_ASSERTION(result, "GetOutlineWidth had no cached outline width");
     if (width > 0) {
+      if (aStoreRectProperties) {
+        SetRectProperty(aFrame, nsGkAtoms::outlineInnerRectProperty, r);
+      }
+
       nscoord offset;
       outline->GetOutlineOffset(offset);
       nscoord inflateBy = PR_MAX(width + offset, 0);
       r.Inflate(inflateBy, inflateBy);
-      *aAnyOutline = PR_TRUE;
+      *aAnyOutlineOrEffects = PR_TRUE;
     }
   }
+  
+  // Note that we don't remove the outlineInnerRect if a frame loses outline
+  // style. That would require an extra property lookup for every frame,
+  // or a new frame state bit to track whether a property had been stored,
+  // or something like that. It's not worth doing that here. At most it's
+  // only one heap-allocated rect per frame and it will be cleaned up when
+  // the frame dies.
+
+#ifdef MOZ_SVG
+  if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
+    *aAnyOutlineOrEffects = PR_TRUE;
+    if (aStoreRectProperties) {
+      SetRectProperty(aFrame, nsGkAtoms::preEffectsBBoxProperty, r);
+    }
+    r = nsSVGIntegrationUtils::ComputeFrameEffectsRect(aFrame, r);
+  }
+#endif
+
   return r;
 }
 
 nsPoint
 nsIFrame::GetRelativeOffset(const nsStyleDisplay* aDisplay) const
 {
   if (!aDisplay || NS_STYLE_POSITION_RELATIVE == aDisplay->mPosition) {
     nsPoint *offsets = static_cast<nsPoint*>
@@ -3694,20 +3787,20 @@ nsIFrame::CheckInvalidateSizeChange(cons
   // between the old and new areas needs to be painted. We are
   // assuming that the difference between the old and new areas will
   // be invalidated by some other means. That also means invalidating
   // the old frame area is the same as invalidating the new frame area
   // (since in either case the UNION of old and new areas will be
   // invalidated)
 
   // Invalidate the entire old frame+outline if the frame has an outline
-  PRBool anyOutline;
-  nsRect r = ComputeOutlineRect(this, &anyOutline,
-                                aNewDesiredSize.mOverflowArea);
-  if (anyOutline) {
+  PRBool anyOutlineOrEffects;
+  nsRect r = ComputeOutlineAndEffectsRect(this, &anyOutlineOrEffects,
+                                          aOldOverflowRect, PR_FALSE);
+  if (anyOutlineOrEffects) {
     r.UnionRect(aOldOverflowRect, r);
     Invalidate(r);
     return;
   }
 
   // Invalidate the old frame borders if the frame has borders. Those borders
   // may be moving.
   const nsStyleBorder* border = GetStyleBorder();
@@ -5256,26 +5349,16 @@ nsFrame::ChildIsDirty(nsIFrame* aChild)
 #ifdef ACCESSIBILITY
 NS_IMETHODIMP
 nsFrame::GetAccessible(nsIAccessible** aAccessible)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 #endif
 
-// Destructor function for the overflow area property
-static void
-DestroyRectFunc(void*    aFrame,
-                nsIAtom* aPropertyName,
-                void*    aPropertyValue,
-                void*    aDtorData)
-{
-  delete static_cast<nsRect*>(aPropertyValue);
-}
-
 /** Create or retrieve the previously stored overflow area, if the frame does 
  * not overflow and no creation is required return nsnull.
  * @param aCreateIfNecessary  create a new nsRect for the overflow area
  * @return pointer to the overflow area rectangle 
  */
 nsRect*
 nsIFrame::GetOverflowAreaProperty(PRBool aCreateIfNecessary) 
 {
@@ -5309,39 +5392,21 @@ IsInlineFrame(nsIFrame *aFrame)
   return type == nsGkAtoms::inlineFrame ||
          type == nsGkAtoms::positionedInlineFrame;
 }
 
 nsRect
 nsIFrame::GetAdditionalOverflow(const nsRect& aOverflowArea,
                                 const nsSize& aNewSize)
 {
-  nsRect overflowRect;
-
   // outline
-  PRBool hasOutline;
-  overflowRect = ComputeOutlineRect(this, &hasOutline, aOverflowArea);
-
-  // box-shadow
-  nsCSSShadowArray* boxShadows = GetStyleBorder()->mBoxShadow;
-  if (boxShadows) {
-    for (PRUint32 i = 0; i < boxShadows->Length(); ++i) {
-      nsRect tmpRect(nsPoint(0, 0), aNewSize);
-      nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
-      nscoord xOffset = shadow->mXOffset.GetCoordValue();
-      nscoord yOffset = shadow->mYOffset.GetCoordValue();
-      nscoord outsetRadius = shadow->mRadius.GetCoordValue() +
-                             shadow->mSpread.GetCoordValue();
-
-      tmpRect.MoveBy(nsPoint(xOffset, yOffset));
-      tmpRect.Inflate(outsetRadius, outsetRadius);
-
-      overflowRect.UnionRect(overflowRect, tmpRect);
-    }
-  }
+  PRBool hasOutlineOrEffects;
+  nsRect overflowRect =
+    ComputeOutlineAndEffectsRect(this, &hasOutlineOrEffects,
+                                 aOverflowArea, PR_TRUE);
 
   // Absolute position clipping
   PRBool hasAbsPosClip;
   nsRect absPosClipRect;
   hasAbsPosClip = GetAbsPosClipRect(GetStyleDisplay(), &absPosClipRect, aNewSize);
   if (hasAbsPosClip) {
     overflowRect.IntersectRect(overflowRect, absPosClipRect);
   }
@@ -5432,28 +5497,35 @@ nsFrame::GetParentStyleContextFrame(nsPr
 
 
 /**
  * This function takes a "special" frame and _if_ that frame is the
  * anonymous block crated by an ib split it returns the split inline
  * as aSpecialSibling.  This is needed because the split inline's
  * style context is the parent of the anonymous block's srtyle context.
  *
- * If aFrame is not the anonymous block, aSpecialSibling is not
- * touched.
+ * If aFrame is not the anonymous block, aSpecialSibling is set to null.
  */
 static nsresult
-GetIBSpecialSibling(nsPresContext* aPresContext,
-                    nsIFrame* aFrame,
-                    nsIFrame** aSpecialSibling)
+GetIBSpecialSiblingForAnonymousBlock(nsPresContext* aPresContext,
+                                     nsIFrame* aFrame,
+                                     nsIFrame** aSpecialSibling)
 {
   NS_PRECONDITION(aFrame, "Must have a non-null frame!");
   NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL,
                "GetIBSpecialSibling should not be called on a non-special frame");
-  
+
+  nsIAtom* type = aFrame->GetStyleContext()->GetPseudoType();
+  if (type != nsCSSAnonBoxes::mozAnonymousBlock &&
+      type != nsCSSAnonBoxes::mozAnonymousPositionedBlock) {
+    // it's not the anonymous block
+    *aSpecialSibling = nsnull;
+    return NS_OK;
+  }
+
   // Find the first-in-flow of the frame.  (Ugh.  This ends up
   // being O(N^2) when it is called O(N) times.)
   aFrame = aFrame->GetFirstInFlow();
 
   /*
    * Now look up the nsGkAtoms::IBSplitSpecialPrevSibling
    * property, which is only set on the anonymous block frames we're
    * interested in.
@@ -5527,20 +5599,21 @@ nsFrame::CorrectStyleParentFrame(nsIFram
   // Otherwise, walk up out of all anon boxes.  For placeholder frames, walk out
   // of all pseudo-elements as well.  Otherwise ReParentStyleContext could cause
   // style data to be out of sync with the frame tree.
   nsIFrame* parent = aProspectiveParent;
   do {
     if (parent->GetStateBits() & NS_FRAME_IS_SPECIAL) {
       nsIFrame* sibling;
       nsresult rv =
-        GetIBSpecialSibling(parent->PresContext(), parent, &sibling);
+        GetIBSpecialSiblingForAnonymousBlock(parent->PresContext(), parent, &sibling);
       if (NS_FAILED(rv)) {
-        // If GetIBSpecialSibling fails, then what?  we used to return what is
-        // now |aProspectiveParent|, but maybe |parent| would make more sense?
+        // If GetIBSpecialSiblingForAnonymousBlock fails, then what?
+        // we used to return what is now |aProspectiveParent|, but maybe
+        // |parent| would make more sense?
         NS_NOTREACHED("Shouldn't get here");
         return aProspectiveParent;
       }
 
       if (sibling) {
         // |parent| was the block in an {ib} split; use the inline as
         // |the style parent.
         parent = sibling;
@@ -5589,20 +5662,21 @@ nsFrame::DoGetParentStyleContextFrame(ns
     return NS_OK;
   }
   
   if (!(mState & NS_FRAME_OUT_OF_FLOW)) {
     /*
      * If this frame is the anonymous block created when an inline
      * with a block inside it got split, then the parent style context
      * is on the first of the three special frames.  We can get to it
-     * using GetIBSpecialSibling
+     * using GetIBSpecialSiblingForAnonymousBlock
      */
     if (mState & NS_FRAME_IS_SPECIAL) {
-      nsresult rv = GetIBSpecialSibling(aPresContext, this, aProviderFrame);
+      nsresult rv =
+        GetIBSpecialSiblingForAnonymousBlock(aPresContext, this, aProviderFrame);
       if (NS_FAILED(rv)) {
         NS_NOTREACHED("Shouldn't get here");
         *aProviderFrame = nsnull;
         return rv;
       }
 
       if (*aProviderFrame) {
         return NS_OK;
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -79,16 +79,19 @@ include pixel-rounding/reftest.list
 
 # printing
 include printing/reftest.list
 include pagination/reftest.list
 
 # svg/
 include svg/reftest.list
 
+# svg-integration/
+include svg-integration/reftest.list
+
 # table-background/
 include table-background/reftest.list
 
 # table-dom/
 include table-dom/reftest.list
 
 # table-width/
 include table-width/reftest.list
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-01-ref.svg
@@ -0,0 +1,8 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+ <rect x="250" y="0" width="250" height="500" fill="lime"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-01.xhtml
@@ -0,0 +1,17 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<body style="margin:0">
+  <div style="position:absolute; top:0; left:0; clip-path: url(#c1); width:500px; height:500px; background:lime;"></div>
+
+  <svg:svg height="0">
+    <svg:clipPath id="c1" clipPathUnits="objectBoundingBox">
+      <svg:rect x="0.5" y="0" width="0.5" height="1"/>
+    </svg:clipPath>
+  </svg:svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-02-ref.svg
@@ -0,0 +1,9 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <rect x="0" y="0" width="500" height="200" fill="lime"/>
+  <rect x="0" y="200" width="500" height="120" fill="blue"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-02.xhtml
@@ -0,0 +1,20 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<body style="margin:0">
+  <div style="clip-path: url(#c1); width:500px; height:200px; background:lime;">
+    <div style="height:200px;"/>
+    <div style="height:200px; background:blue;"/>
+  </div>
+
+  <svg:svg height="0">
+    <svg:clipPath id="c1" clipPathUnits="objectBoundingBox">
+      <svg:rect x="0" y="0" width="1" height="0.8"/>
+    </svg:clipPath>
+  </svg:svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-03-ref.svg
@@ -0,0 +1,10 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <rect x="0" y="0" width="100" height="150" fill="lime"/>
+  <rect x="100" y="0" width="100" height="200" fill="lime"/>
+  <rect x="100" y="200" width="100" height="100" fill="blue"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-03.xhtml
@@ -0,0 +1,21 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<body style="margin:0">
+  <div style="clip-path: url(#c1); width:500px; height:200px; background:lime;">
+    <div style="height:200px;"/>
+    <div style="height:200px; background:blue;"/>
+  </div>
+
+  <svg:svg height="0">
+    <svg:clipPath id="c1" clipPathUnits="userSpaceOnuse">
+      <svg:rect x="0" y="0" width="100" height="150"/>
+      <svg:rect x="100" y="0" width="100" height="300"/>
+    </svg:clipPath>
+  </svg:svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-04-ref.xhtml
@@ -0,0 +1,14 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<body style="margin:0; line-height:100px;">
+  <span style="display:inline-block; width:200px;"
+  /><span style="background:lime;"><span style="display:inline-block; width:50px;"/></span><br/>
+  <span style="display:inline-block; width:50px;"
+  /><span style="background:lime;"><span style="display:inline-block; width:50px;"/></span>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-04.xhtml
@@ -0,0 +1,24 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<head>
+<style>
+.unit { display:inline-block; width:100px; height:1px; }
+</style>
+</head>
+<body style="margin:0; width:350px; line-height:100px;">
+  <span class="unit"/><span class="unit"
+  /><span style="clip-path:url(#c1); background:lime;"><span class="unit"/><span class="unit"
+  /></span>
+
+  <svg:svg height="0">
+    <svg:clipPath id="c1" clipPathUnits="userSpaceOnuse">
+      <svg:rect x="50" y="0" width="200" height="200"/>
+    </svg:clipPath>
+  </svg:svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-05-ref.xhtml
@@ -0,0 +1,14 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<body style="margin:0; line-height:100px;">
+  <span style="display:inline-block; width:200px;"
+  /><span style="background:lime;"><span style="display:inline-block; width:70px;"/></span><br/>
+  <span style="display:inline-block; width:30px;"
+  /><span style="background:lime;"><span style="display:inline-block; width:70px;"/></span>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-05.xhtml
@@ -0,0 +1,24 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<head>
+<style>
+.unit { display:inline-block; width:100px; height:1px; }
+</style>
+</head>
+<body style="margin:0; width:350px; line-height:100px;">
+  <span class="unit"/><span class="unit"
+  /><span style="clip-path:url(#c1); background:lime;"><span class="unit"/><span class="unit"
+  /></span>
+
+  <svg:svg height="0">
+    <svg:clipPath id="c1" clipPathUnits="objectBoundingBox">
+      <svg:rect x="0.1" y="0" width="0.8" height="1"/>
+    </svg:clipPath>
+  </svg:svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-06-ref.xhtml
@@ -0,0 +1,20 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<head>
+<style>
+.unit { display:inline-block; width:50px; height:10px; }
+</style>
+</head>
+<body style="margin:0">
+  <span>
+    <span class="unit"></span>
+    <div style="height:200px;"/>
+    <span class="unit" style="background:lime;"></span>
+  </span>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/clipPath-html-06.xhtml
@@ -0,0 +1,26 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<head>
+<style>
+.unit { display:inline-block; width:100px; height:10px; }
+</style>
+</head>
+<body style="margin:0">
+  <span style="clip-path: url(#c1);">
+    <span class="unit" style="background:lime;"></span>
+    <div style="height:200px; width:100px;"/>
+    <span class="unit" style="background:lime;"></span>
+  </span>
+
+  <svg:svg height="0">
+    <svg:clipPath id="c1" clipPathUnits="objectBoundingBox">
+      <svg:rect x="0" y="0.5" width="0.5" height="0.5"/>
+    </svg:clipPath>
+  </svg:svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/filter-html-01-ref.svg
@@ -0,0 +1,14 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <filter id="f1">
+    <feFlood flood-color="black" result="black"/>
+    <feGaussianBlur in="SourceAlpha" stdDeviation="10"/>
+    <feComposite in="black" operator="in"/>
+    <feComposite in="SourceGraphic"/>
+  </filter>
+  <rect x="50" y="50" width="200" height="200" fill="lime" filter="url(#f1)"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/filter-html-01.xhtml
@@ -0,0 +1,20 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<body style="margin:0">
+  <div style="background:lime; width:200px; height:200px; margin:50px; filter:url(#f1)"/>
+
+  <svg:svg height="0">
+    <svg:filter id="f1">
+      <svg:feFlood flood-color="black" result="black"/>
+      <svg:feGaussianBlur in="SourceAlpha" stdDeviation="10"/>
+      <svg:feComposite in="black" operator="in"/>
+      <svg:feComposite in="SourceGraphic"/>
+    </svg:filter>
+  </svg:svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/mask-html-01-ref.svg
@@ -0,0 +1,13 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+  <linearGradient id="g" gradientUnits="objectBoundingBox" x2="0" y2="1">
+    <stop stop-color="lime" offset="0"/>
+    <stop stop-color="lime" stop-opacity="0" offset="1"/>
+  </linearGradient>
+  <circle cx="125" cy="125" r="125" id="circle" fill="lime"/>
+  <rect x="250" y="0" width="250" height="500" fill="url(#g)"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/mask-html-01.xhtml
@@ -0,0 +1,22 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:svg="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<body style="margin:0">
+  <div style="mask: url(#m1); width:500px; height:500px; background:lime;"></div>
+
+  <svg:svg height="0">
+    <svg:mask id="m1" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
+      <svg:linearGradient id="g" gradientUnits="objectBoundingBox" x2="0" y2="1">
+        <svg:stop stop-color="white" offset="0"/>
+        <svg:stop stop-color="white" stop-opacity="0" offset="1"/>
+      </svg:linearGradient>
+      <svg:circle cx="0.25" cy="0.25" r="0.25" id="circle" fill="white"/>
+      <svg:rect x="0.5" y="0" width="0.5" height="1" fill="url(#g)"/>
+    </svg:mask>
+  </svg:svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg-integration/reftest.list
@@ -0,0 +1,9 @@
+== clipPath-html-01.xhtml clipPath-html-01-ref.svg
+== clipPath-html-02.xhtml clipPath-html-02-ref.svg
+== clipPath-html-03.xhtml clipPath-html-03-ref.svg
+== clipPath-html-04.xhtml clipPath-html-04-ref.xhtml
+== clipPath-html-05.xhtml clipPath-html-05-ref.xhtml
+== clipPath-html-06.xhtml clipPath-html-06-ref.xhtml
+== filter-html-01.xhtml filter-html-01-ref.svg
+== mask-html-01.xhtml mask-html-01-ref.svg
+
--- a/layout/style/ua.css
+++ b/layout/style/ua.css
@@ -123,16 +123,19 @@
   display: block !important;
   position: static !important;
 }
 
 *|*::-moz-anonymous-block, *|*::-moz-anonymous-positioned-block {
   /* we currently inherit from the inline that is split */
   outline: inherit;
   -moz-outline-offset: inherit;
+  clip-path: inherit;
+  filter: inherit;
+  mask: inherit;
 }
 
 *|*::-moz-xul-anonymous-block {
   display: block ! important;
   position: static ! important;
   float: none ! important;
 }
 
--- a/layout/svg/base/src/Makefile.in
+++ b/layout/svg/base/src/Makefile.in
@@ -75,16 +75,17 @@ CPPSRCS		= \
 		nsSVGForeignObjectFrame.cpp \
 		nsSVGGFrame.cpp \
 		nsSVGGenericContainerFrame.cpp \
 		nsSVGGeometryFrame.cpp \
 		nsSVGGlyphFrame.cpp \
 		nsSVGGradientFrame.cpp \
 		nsSVGImageFrame.cpp \
 		nsSVGInnerSVGFrame.cpp \
+		nsSVGIntegrationUtils.cpp \
 		nsSVGLeafFrame.cpp \
 		nsSVGMarkerFrame.cpp \
 		nsSVGMaskFrame.cpp \
 		nsSVGOuterSVGFrame.cpp \
 		nsSVGPaintServerFrame.cpp \
 		nsSVGPathGeometryFrame.cpp \
 		nsSVGPatternFrame.cpp \
 		nsSVGStopFrame.cpp \
@@ -98,16 +99,17 @@ CPPSRCS		= \
 		$(NULL)
 
 include $(topsrcdir)/config/config.mk
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 EXPORTS = \
+	nsSVGIntegrationUtils.h \
 	nsSVGUtils.h \
 	nsSVGFilterInstance.h \
 	nsSVGForeignObjectFrame.h \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	= \
--- a/layout/svg/base/src/nsISVGChildFrame.h
+++ b/layout/svg/base/src/nsISVGChildFrame.h
@@ -46,18 +46,18 @@
 
 class gfxContext;
 class nsPresContext;
 class nsIDOMSVGRect;
 class nsIDOMSVGMatrix;
 class nsSVGRenderState;
 
 #define NS_ISVGCHILDFRAME_IID \
-{ 0xe4ecddbf, 0xde7c, 0x4cd9, \
- { 0x92, 0x4a, 0xfa, 0x81, 0xba, 0x83, 0x26, 0x69 } }
+{ 0x8b80b2a0, 0x2e1f, 0x4775, \
+  { 0xab, 0x47, 0xbe, 0xeb, 0x4b, 0x81, 0x63, 0x6d } }
 
 class nsISVGChildFrame : public nsISupports {
 public:
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISVGCHILDFRAME_IID)
 
   // Paint this frame - aDirtyRect is the area being redrawn, in frame
   // offset pixel coordinates
@@ -93,16 +93,17 @@ public:
   };
   virtual void NotifySVGChanged(PRUint32 aFlags)=0;
   NS_IMETHOD NotifyRedrawSuspended()=0;
   NS_IMETHOD NotifyRedrawUnsuspended()=0;
 
   // Set whether we should stop multiplying matrices when building up
   // the current transformation matrix at this frame.
   NS_IMETHOD SetMatrixPropagation(PRBool aPropagate)=0;
+  virtual PRBool GetMatrixPropagation()=0;
 
   // Set the current transformation matrix to a particular matrix.
   // Value is only used if matrix propagation is prevented
   // (SetMatrixPropagation()).  nsnull aCTM means identity transform.
   NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM)=0;
   virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM()=0;
 
   // XXX move this function into interface nsISVGLocatableMetrics
--- a/layout/svg/base/src/nsSVGClipPathFrame.cpp
+++ b/layout/svg/base/src/nsSVGClipPathFrame.cpp
@@ -56,17 +56,17 @@ NS_NewSVGClipPathFrame(nsIPresShell* aPr
     return nsnull;
   }
 
   return new (aPresShell) nsSVGClipPathFrame(aContext);
 }
 
 nsresult
 nsSVGClipPathFrame::ClipPaint(nsSVGRenderState* aContext,
-                              nsISVGChildFrame* aParent,
+                              nsIFrame* aParent,
                               nsIDOMSVGMatrix *aMatrix)
 {
   // If the flag is set when we get here, it means this clipPath frame
   // has already been used painting the current clip, and the document
   // has a clip reference loop.
   if (mInUse) {
     NS_WARNING("Clip loop detected!");
     return NS_OK;
@@ -98,17 +98,17 @@ nsSVGClipPathFrame::ClipPaint(nsSVGRende
     aContext->GetGfxContext()->Clip();
     aContext->GetGfxContext()->NewPath();
   }
 
   return NS_OK;
 }
 
 PRBool
-nsSVGClipPathFrame::ClipHitTest(nsISVGChildFrame* aParent,
+nsSVGClipPathFrame::ClipHitTest(nsIFrame* aParent,
                                 nsIDOMSVGMatrix *aMatrix,
                                 const nsPoint &aPoint)
 {
   // If the flag is set when we get here, it means this clipPath frame
   // has already been used in hit testing against the current clip,
   // and the document has a clip reference loop.
   if (mInUse) {
     NS_WARNING("Clip loop detected!");
--- a/layout/svg/base/src/nsSVGClipPathFrame.h
+++ b/layout/svg/base/src/nsSVGClipPathFrame.h
@@ -49,20 +49,20 @@ protected:
   nsSVGClipPathFrame(nsStyleContext* aContext) :
     nsSVGClipPathFrameBase(aContext),
     mClipParentMatrix(nsnull),
     mInUse(PR_FALSE) {}
 
 public:
   // nsSVGClipPathFrame methods:
   nsresult ClipPaint(nsSVGRenderState* aContext,
-                     nsISVGChildFrame* aParent,
+                     nsIFrame* aParent,
                      nsIDOMSVGMatrix *aMatrix);
 
-  PRBool ClipHitTest(nsISVGChildFrame* aParent,
+  PRBool ClipHitTest(nsIFrame* aParent,
                      nsIDOMSVGMatrix *aMatrix,
                      const nsPoint &aPoint);
 
   // Check if this clipPath is made up of more than one geometry object.
   // If so, the clipping API in cairo isn't enough and we need to use
   // mask based clipping.
   PRBool IsTrivial();
 
@@ -95,17 +95,17 @@ public:
     }
     ~AutoClipPathReferencer() {
       mFrame->mInUse = PR_FALSE;
     }
   private:
     nsSVGClipPathFrame *mFrame;
   };
 
-  nsISVGChildFrame *mClipParent;
+  nsIFrame *mClipParent;
   nsCOMPtr<nsIDOMSVGMatrix> mClipParentMatrix;
 
   // nsSVGContainerFrame methods:
   virtual already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
 
   // recursion prevention flag
   PRPackedBool mInUse;
 };
--- a/layout/svg/base/src/nsSVGContainerFrame.cpp
+++ b/layout/svg/base/src/nsSVGContainerFrame.cpp
@@ -83,26 +83,27 @@ nsSVGContainerFrame::RemoveFrame(nsIAtom
   return mFrames.DestroyFrame(aOldFrame) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsSVGContainerFrame::Init(nsIContent* aContent,
                           nsIFrame* aParent,
                           nsIFrame* aPrevInFlow)
 {
-  AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD);
+  AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_PROPAGATE_TRANSFORM);
   nsresult rv = nsSVGContainerFrameBase::Init(aContent, aParent, aPrevInFlow);
   return rv;
 }
 
 NS_IMETHODIMP
 nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
                                  nsIFrame* aParent,
                                  nsIFrame* aPrevInFlow)
 {
+  AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
   if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
     AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
   }
   nsresult rv = nsSVGContainerFrameBase::Init(aContent, aParent, aPrevInFlow);
   return rv;
 }
 
 NS_IMETHODIMP
@@ -272,8 +273,25 @@ nsSVGDisplayContainerFrame::NotifyRedraw
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSVGDisplayContainerFrame::GetBBox(nsIDOMSVGRect **_retval)
 {
   return nsSVGUtils::GetBBox(&mFrames, _retval);
 }
+
+NS_IMETHODIMP
+nsSVGDisplayContainerFrame::SetMatrixPropagation(PRBool aPropagate)
+{
+  if (aPropagate) {
+    AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
+  } else {
+    RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
+  }
+  return NS_OK;
+}
+
+PRBool
+nsSVGDisplayContainerFrame::GetMatrixPropagation()
+{
+  return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
+}
--- a/layout/svg/base/src/nsSVGContainerFrame.h
+++ b/layout/svg/base/src/nsSVGContainerFrame.h
@@ -104,17 +104,18 @@ public:
   NS_IMETHOD PaintSVG(nsSVGRenderState* aContext, nsIntRect *aDirtyRect);
   NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
   NS_IMETHOD_(nsRect) GetCoveredRegion();
   NS_IMETHOD UpdateCoveredRegion();
   NS_IMETHOD InitialUpdate();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   NS_IMETHOD NotifyRedrawSuspended();
   NS_IMETHOD NotifyRedrawUnsuspended();
-  NS_IMETHOD SetMatrixPropagation(PRBool aPropagate) { return NS_ERROR_FAILURE; }
+  NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
+  virtual PRBool GetMatrixPropagation();
   NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { return NS_ERROR_FAILURE; }
   virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM() { return nsnull; }
   NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);
   NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_TRUE; }
   NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_FALSE; }
 };
 
 #endif
--- a/layout/svg/base/src/nsSVGEffects.cpp
+++ b/layout/svg/base/src/nsSVGEffects.cpp
@@ -122,17 +122,17 @@ nsSVGFilterProperty::nsSVGFilterProperty
   UpdateRect();
 }
 
 void
 nsSVGFilterProperty::UpdateRect()
 {
   nsSVGFilterFrame *filter = GetFilterFrame(nsnull);
   if (filter) {
-    mFilterRect = filter->GetInvalidationRegion(mFrame, mFrame->GetRect());
+    mFilterRect = filter->GetFilterBBox(mFrame, nsnull);
   } else {
     mFilterRect = nsRect();
   }
 }
 
 void
 nsSVGFilterProperty::DoUpdate()
 {
--- a/layout/svg/base/src/nsSVGFilterFrame.cpp
+++ b/layout/svg/base/src/nsSVGFilterFrame.cpp
@@ -37,21 +37,23 @@
 #include "nsSVGFilterFrame.h"
 #include "nsIDocument.h"
 #include "nsISVGValueUtils.h"
 #include "nsSVGMatrix.h"
 #include "nsSVGOuterSVGFrame.h"
 #include "nsGkAtoms.h"
 #include "nsSVGUtils.h"
 #include "nsSVGFilterElement.h"
-#include "nsSVGFilterInstance.h"
 #include "nsSVGFilters.h"
 #include "gfxASurface.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
+#include "nsSVGFilterPaintCallback.h"
+#include "nsSVGRect.h"
+#include "nsSVGFilterInstance.h"
 
 nsIFrame*
 NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext)
 {
   nsCOMPtr<nsIDOMSVGFilterElement> filter = do_QueryInterface(aContent);
   if (!filter) {
     NS_ERROR("Can't create frame! Content is not an SVG filter");
     return nsnull;
@@ -73,212 +75,233 @@ MapDeviceRectToFilterSpace(const gfxMatr
     nsIntRect intRect;
     if (NS_SUCCEEDED(nsSVGUtils::GfxRectToIntRect(r, &intRect))) {
       rect = intRect;
     }
   }
   return rect;
 }
 
-nsresult
-nsSVGFilterFrame::CreateInstance(nsISVGChildFrame *aTarget,
-                                 const nsIntRect *aDirtyOutputRect,
-                                 const nsIntRect *aDirtyInputRect,
-                                 nsSVGFilterInstance **aInstance)
-{
-  *aInstance = nsnull;
+class NS_STACK_CLASS nsAutoFilterInstance {
+public:
+  nsAutoFilterInstance(nsIFrame *aTarget,
+                       nsSVGFilterFrame *aFilterFrame,
+                       nsSVGFilterPaintCallback *aPaint,
+                       const nsIntRect *aDirtyOutputRect,
+                       const nsIntRect *aDirtyInputRect,
+                       const nsIntRect *aOverrideSourceBBox);
+  ~nsAutoFilterInstance();
 
-  nsIFrame *frame;
-  CallQueryInterface(aTarget, &frame);
-
-  nsCOMPtr<nsIDOMSVGMatrix> ctm = nsSVGUtils::GetCanvasTM(frame);
-
-  nsSVGElement *target = static_cast<nsSVGElement*>(frame->GetContent());
+  // If this returns null, then draw nothing. Either the filter draws
+  // nothing or it is "in error".
+  nsSVGFilterInstance* get() { return mInstance; }
 
-  aTarget->SetMatrixPropagation(PR_FALSE);
-  aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
-                            nsISVGChildFrame::TRANSFORM_CHANGED);
-
-  nsSVGFilterElement *filter = static_cast<nsSVGFilterElement*>(mContent);
+private:
+  nsAutoPtr<nsSVGFilterInstance> mInstance;
+  // Store mTarget separately even though mInstance has it, because if
+  // mInstance creation fails we still need to be able to clean up
+  nsISVGChildFrame*              mTarget;
+};
 
-  float x, y, width, height;
-  nsCOMPtr<nsIDOMSVGRect> bbox;
-  aTarget->GetBBox(getter_AddRefs(bbox));
+nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
+                                           nsSVGFilterFrame *aFilterFrame,
+                                           nsSVGFilterPaintCallback *aPaint,
+                                           const nsIntRect *aDirtyOutputRect,
+                                           const nsIntRect *aDirtyInputRect,
+                                           const nsIntRect *aOverrideSourceBBox)
+{
+  nsCOMPtr<nsIDOMSVGMatrix> ctm = nsSVGUtils::GetCanvasTM(aTarget);
 
-  nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight;
-  tmpX = &filter->mLengthAttributes[nsSVGFilterElement::X];
-  tmpY = &filter->mLengthAttributes[nsSVGFilterElement::Y];
-  tmpWidth = &filter->mLengthAttributes[nsSVGFilterElement::WIDTH];
-  tmpHeight = &filter->mLengthAttributes[nsSVGFilterElement::HEIGHT];
+  CallQueryInterface(aTarget, &mTarget);
+  if (mTarget) {
+    mTarget->SetMatrixPropagation(PR_FALSE);
+    mTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
+                              nsISVGChildFrame::TRANSFORM_CHANGED);
+  }
+
+  nsSVGFilterElement *filter = static_cast<nsSVGFilterElement*>(
+          aFilterFrame->GetContent());
 
   PRUint16 units =
     filter->mEnumAttributes[nsSVGFilterElement::FILTERUNITS].GetAnimValue();
-
-  // Compute filter effects region as per spec
-  if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
-    if (!bbox)
-      return NS_OK;
+  nsCOMPtr<nsIDOMSVGRect> bbox;
+  if (aOverrideSourceBBox) {
+    NS_NewSVGRect(getter_AddRefs(bbox),
+                  aOverrideSourceBBox->x, aOverrideSourceBBox->y,
+                  aOverrideSourceBBox->width, aOverrideSourceBBox->height);
+  } else {
+    bbox = nsSVGUtils::GetBBox(aTarget);
+  }
+  if (!bbox && units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
+    return;
 
-    bbox->GetX(&x);
-    x += nsSVGUtils::ObjectSpace(bbox, tmpX);
-    bbox->GetY(&y);
-    y += nsSVGUtils::ObjectSpace(bbox, tmpY);
-    width = nsSVGUtils::ObjectSpace(bbox, tmpWidth);
-    height = nsSVGUtils::ObjectSpace(bbox, tmpHeight);
-  } else {
-    x = nsSVGUtils::UserSpace(target, tmpX);
-    y = nsSVGUtils::UserSpace(target, tmpY);
-    width = nsSVGUtils::UserSpace(target, tmpWidth);
-    height = nsSVGUtils::UserSpace(target, tmpHeight);
-  }
-  
+  gfxRect filterArea = nsSVGUtils::GetRelativeRect(units,
+    &filter->mLengthAttributes[nsSVGFilterElement::X], bbox, aTarget);
+  filterArea.RoundOut();
+
   PRBool resultOverflows;
   gfxIntSize filterRes;
 
   // Compute size of filter buffer
-  if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::filterRes)) {
+  if (filter->HasAttr(kNameSpaceID_None, nsGkAtoms::filterRes)) {
     PRInt32 filterResX, filterResY;
     filter->GetAnimatedIntegerValues(&filterResX, &filterResY, nsnull);
 
     filterRes =
       nsSVGUtils::ConvertToSurfaceSize(gfxSize(filterResX, filterResY),
                                        &resultOverflows);
   } else {
     float scale = nsSVGUtils::MaxExpansion(ctm);
 #ifdef DEBUG_tor
     fprintf(stderr, "scale: %f\n", scale);
 #endif
 
     filterRes =
-      nsSVGUtils::ConvertToSurfaceSize(gfxSize(width, height) * scale,
+      nsSVGUtils::ConvertToSurfaceSize(filterArea.size * scale,
                                        &resultOverflows);
   }
 
   // 0 disables rendering, < 0 is error
   if (filterRes.width <= 0 || filterRes.height <= 0)
-    return NS_OK;
-
-#ifdef DEBUG_tor
-  fprintf(stderr, "filter bbox: %f,%f  %fx%f\n", x, y, width, height);
-  fprintf(stderr, "filterRes: %u %u\n", filterRes.width, filterRes.height);
-#endif
+    return;
 
   // 'fini' is the matrix we will finally use to transform filter space
   // to surface space for drawing
   nsCOMPtr<nsIDOMSVGMatrix> scale, fini;
   NS_NewSVGMatrix(getter_AddRefs(scale),
-                  width / filterRes.width, 0.0f,
-                  0.0f, height / filterRes.height,
-                  x, y);
+                  filterArea.Width() / filterRes.width, 0.0f,
+                  0.0f, filterArea.Height() / filterRes.height,
+                  filterArea.X(), filterArea.Y());
   ctm->Multiply(scale, getter_AddRefs(fini));
   
   gfxMatrix finiM = nsSVGUtils::ConvertSVGMatrixToThebes(fini);
   // fini is always invertible.
   finiM.Invert();
 
   nsIntRect dirtyOutputRect =
     MapDeviceRectToFilterSpace(finiM, filterRes, aDirtyOutputRect);
   nsIntRect dirtyInputRect =
     MapDeviceRectToFilterSpace(finiM, filterRes, aDirtyInputRect);
 
   // Setup instance data
   PRUint16 primitiveUnits =
     filter->mEnumAttributes[nsSVGFilterElement::PRIMITIVEUNITS].GetAnimValue();
-  *aInstance = new nsSVGFilterInstance(aTarget, mContent, bbox,
-                                       gfxRect(x, y, width, height),
-                                       nsIntSize(filterRes.width, filterRes.height),
-                                       fini,
-                                       dirtyOutputRect, dirtyInputRect,
-                                       primitiveUnits);
-  return *aInstance ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+  mInstance = new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterArea,
+                                      nsIntSize(filterRes.width, filterRes.height),
+                                      fini,
+                                      dirtyOutputRect, dirtyInputRect,
+                                      primitiveUnits);
 }
 
-static void
-RestoreTargetState(nsISVGChildFrame *aTarget)
+nsAutoFilterInstance::~nsAutoFilterInstance()
 {
-  aTarget->SetOverrideCTM(nsnull);
-  aTarget->SetMatrixPropagation(PR_TRUE);
-  aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
+  if (!mTarget)
+    return;
+
+  mTarget->SetOverrideCTM(nsnull);
+  mTarget->SetMatrixPropagation(PR_TRUE);
+  mTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
                             nsISVGChildFrame::TRANSFORM_CHANGED);
 }
 
 nsresult
 nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
-                              nsISVGChildFrame *aTarget,
+                              nsIFrame *aTarget,
+                              nsSVGFilterPaintCallback *aPaintCallback,
                               const nsIntRect *aDirtyRect)
 {
-  nsAutoPtr<nsSVGFilterInstance> instance;
-  nsresult rv = CreateInstance(aTarget, aDirtyRect, nsnull, getter_Transfers(instance));
-
-  if (NS_SUCCEEDED(rv) && instance) {
-    // Transformation from user space to filter space
-    nsCOMPtr<nsIDOMSVGMatrix> filterTransform =
-      instance->GetUserSpaceToFilterSpaceTransform();
-    aTarget->SetOverrideCTM(filterTransform);
-    aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
-                              nsISVGChildFrame::TRANSFORM_CHANGED);
+  nsAutoFilterInstance instance(aTarget, this, aPaintCallback,
+    aDirtyRect, nsnull, nsnull);
+  if (!instance.get())
+    return NS_OK;
 
-    nsRefPtr<gfxASurface> result;
-    rv = instance->Render(getter_AddRefs(result));
-    if (NS_SUCCEEDED(rv) && result) {
-      nsSVGUtils::CompositeSurfaceMatrix(aContext->GetGfxContext(),
-        result, instance->GetFilterSpaceToDeviceSpaceTransform(), 1.0);
-    }
-  }
-
-  RestoreTargetState(aTarget);
-
-  if (NS_FAILED(rv)) {
-    aTarget->PaintSVG(aContext, nsnull);
+  nsRefPtr<gfxASurface> result;
+  nsresult rv = instance.get()->Render(getter_AddRefs(result));
+  if (NS_SUCCEEDED(rv) && result) {
+    nsSVGUtils::CompositeSurfaceMatrix(aContext->GetGfxContext(),
+      result, instance.get()->GetFilterSpaceToDeviceSpaceTransform(), 1.0);
   }
   return rv;
 }
 
-nsRect
-nsSVGFilterFrame::GetInvalidationRegion(nsIFrame *aTarget, const nsRect& aRect)
+static nsresult
+TransformFilterSpaceToDeviceSpace(nsSVGFilterInstance *aInstance, nsIntRect *aRect)
 {
-  nsISVGChildFrame *svg;
-  CallQueryInterface(aTarget, &svg);
+  gfxMatrix m = nsSVGUtils::ConvertSVGMatrixToThebes(
+    aInstance->GetFilterSpaceToDeviceSpaceTransform());
+  gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height);
+  r = m.TransformBounds(r);
+  r.RoundOut();
+  nsIntRect deviceRect;
+  nsresult rv = nsSVGUtils::GfxRectToIntRect(r, &deviceRect);
+  if (NS_FAILED(rv))
+    return rv;
+  *aRect = deviceRect;
+  return NS_OK;
+}
 
-  nscoord p2a = aTarget->PresContext()->AppUnitsPerDevPixel();
-  nsRect result = aRect;
-  nsIntRect rect = aRect;
-  rect.ScaleRoundOut(1.0/p2a);
+nsIntRect
+nsSVGFilterFrame::GetInvalidationBBox(nsIFrame *aTarget, const nsIntRect& aRect)
+{
+  nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, &aRect, nsnull);
+  if (!instance.get())
+    return nsIntRect();
 
-  nsAutoPtr<nsSVGFilterInstance> instance;
-  nsresult rv = CreateInstance(svg, nsnull, &rect, getter_Transfers(instance));
+  // We've passed in the source's dirty area so the instance knows about it.
+  // Now we can ask the instance to compute the area of the filter output
+  // that's dirty.
+  nsIntRect dirtyRect;
+  nsresult rv = instance.get()->ComputeOutputDirtyRect(&dirtyRect);
   if (NS_SUCCEEDED(rv)) {
-    if (!instance) {
-      // The filter draws nothing, so nothing is dirty.
-      result = nsRect();
-    } else {
-      // We've passed in the source's dirty area so the instance knows about it.
-      // Now we can ask the instance to compute the area of the filter output
-      // that's dirty.
-      nsIntRect filterSpaceDirtyRect;
-      rv = instance->ComputeOutputDirtyRect(&filterSpaceDirtyRect);
-      if (NS_SUCCEEDED(rv)) {
-        gfxMatrix m = nsSVGUtils::ConvertSVGMatrixToThebes(
-          instance->GetFilterSpaceToDeviceSpaceTransform());
-        gfxRect r(filterSpaceDirtyRect.x, filterSpaceDirtyRect.y,
-                  filterSpaceDirtyRect.width, filterSpaceDirtyRect.height);
-        r = m.TransformBounds(r);
-        r.RoundOut();
-        nsIntRect deviceRect;
-        rv = nsSVGUtils::GfxRectToIntRect(r, &deviceRect);
-        if (NS_SUCCEEDED(rv)) {
-          deviceRect.ScaleRoundOut(p2a);
-          result = deviceRect;
-        }
-      }
-    }
+    rv = TransformFilterSpaceToDeviceSpace(instance.get(), &dirtyRect);
+    if (NS_SUCCEEDED(rv))
+      return dirtyRect;
   }
 
-  RestoreTargetState(svg);
-
-  return result;
+  return nsIntRect();
 }
 
+nsIntRect
+nsSVGFilterFrame::GetSourceForInvalidArea(nsIFrame *aTarget, const nsIntRect& aRect)
+{
+  nsAutoFilterInstance instance(aTarget, this, nsnull, &aRect, nsnull, nsnull);
+  if (!instance.get())
+    return nsIntRect();
+
+  // Now we can ask the instance to compute the area of the source
+  // that's needed.
+  nsIntRect neededRect;
+  nsresult rv = instance.get()->ComputeSourceNeededRect(&neededRect);
+  if (NS_SUCCEEDED(rv)) {
+    rv = TransformFilterSpaceToDeviceSpace(instance.get(), &neededRect);
+    if (NS_SUCCEEDED(rv))
+      return neededRect;
+  }
+
+  return nsIntRect();
+}
+
+nsIntRect
+nsSVGFilterFrame::GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox)
+{
+  nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, nsnull, aSourceBBox);
+  if (!instance.get())
+    return nsIntRect();
+
+  // We've passed in the source's bounding box so the instance knows about
+  // it. Now we can ask the instance to compute the bounding box of
+  // the filter output.
+  nsIntRect bbox;
+  nsresult rv = instance.get()->ComputeOutputBBox(&bbox);
+  if (NS_SUCCEEDED(rv)) {
+    rv = TransformFilterSpaceToDeviceSpace(instance.get(), &bbox);
+    if (NS_SUCCEEDED(rv))
+      return bbox;
+  }
+  
+  return nsIntRect();
+}
+  
 nsIAtom *
 nsSVGFilterFrame::GetType() const
 {
   return nsGkAtoms::svgFilterFrame;
 }
--- a/layout/svg/base/src/nsSVGFilterFrame.h
+++ b/layout/svg/base/src/nsSVGFilterFrame.h
@@ -35,46 +35,56 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NS_SVGFILTERFRAME_H__
 #define __NS_SVGFILTERFRAME_H__
 
 #include "nsRect.h"
 #include "nsSVGContainerFrame.h"
 
-class nsSVGFilterInstance;
+class nsSVGRenderState;
+class nsSVGFilterPaintCallback;
 
 typedef nsSVGContainerFrame nsSVGFilterFrameBase;
 class nsSVGFilterFrame : public nsSVGFilterFrameBase
 {
   friend nsIFrame*
   NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
 protected:
   nsSVGFilterFrame(nsStyleContext* aContext) : nsSVGFilterFrameBase(aContext) {}
 
-public:    
+public:
   nsresult FilterPaint(nsSVGRenderState *aContext,
-                       nsISVGChildFrame *aTarget,
+                       nsIFrame *aTarget, nsSVGFilterPaintCallback *aPaintCallback,
                        const nsIntRect* aDirtyRect);
 
-  // Returns invalidation region for filter (can be bigger than the
-  // referencing geometry to filter region sizing) in app units
-  // relative to the origin of the outer svg.
-  // aRect is the area that would be invalidated. Normally you'd just pass
-  // aTarget->GetRect() here.
-  nsRect GetInvalidationRegion(nsIFrame *aTarget, const nsRect& aRect);
+  /**
+   * Returns the area that could change when the given rect of the source changes.
+   * The rectangles are relative to the origin of the outer svg, if aTarget is SVG,
+   * relative to aTarget itself otherwise, in device pixels.
+   */
+  nsIntRect GetInvalidationBBox(nsIFrame *aTarget, const nsIntRect& aRect);
+
+  /**
+   * Returns the area in device pixels that is needed from the source when
+   * the given area needs to be repainted.
+   * The rectangles are relative to the origin of the outer svg, if aTarget is SVG,
+   * relative to aTarget itself otherwise, in device pixels.
+   */
+  nsIntRect GetSourceForInvalidArea(nsIFrame *aTarget, const nsIntRect& aRect);
+
+  /**
+   * Returns the bounding box of the post-filter area of aTarget.
+   * The rectangles are relative to the origin of the outer svg, if aTarget is SVG,
+   * relative to aTarget itself otherwise, in device pixels.
+   * @param aSourceBBox overrides the normal bbox for the source, if non-null
+   */
+  nsIntRect GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox);
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::svgFilterFrame
    */
   virtual nsIAtom* GetType() const;
-
-private:
-  // implementation helpers
-  nsresult CreateInstance(nsISVGChildFrame *aTarget,
-                          const nsIntRect *aDirtyOutputRect,
-                          const nsIntRect *aDirtyInputRect,
-                          nsSVGFilterInstance **aInstance);
 };
 
 #endif
--- a/layout/svg/base/src/nsSVGFilterInstance.cpp
+++ b/layout/svg/base/src/nsSVGFilterInstance.cpp
@@ -33,30 +33,34 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsSVGFilterInstance.h"
 #include "nsSVGUtils.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsSVGMatrix.h"
+#include "gfxPlatform.h"
+#include "nsSVGFilterPaintCallback.h"
+#include "nsSVGFilterElement.h"
 
 static double Square(double aX)
 {
   return aX*aX;
 }
 
 float
 nsSVGFilterInstance::GetPrimitiveLength(nsSVGLength2 *aLength) const
 {
   float value;
-  if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
+  if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
     value = nsSVGUtils::ObjectSpace(mTargetBBox, aLength);
-  else
-    value = nsSVGUtils::UserSpace(TargetElement(), aLength);
+  } else {
+    value = nsSVGUtils::UserSpace(mTargetFrame, aLength);
+  }
 
   switch (aLength->GetCtxType()) {
   case nsSVGUtils::X:
     return value * mFilterSpaceSize.width / mFilterRect.Width();
   case nsSVGUtils::Y:
     return value * mFilterSpaceSize.height / mFilterRect.Height();
   case nsSVGUtils::XY:
   default:
@@ -118,39 +122,19 @@ nsSVGFilterInstance::ComputeFilterPrimit
           defaultFilterSubregion.Union(
               aPrimitive->mInputs[i]->mImage.mFilterPrimitiveSubregion);
     }
   } else {
     defaultFilterSubregion =
       gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height);
   }
 
-  nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight;
-  tmpX = &fE->mLengthAttributes[nsSVGFE::X];
-  tmpY = &fE->mLengthAttributes[nsSVGFE::Y];
-  tmpWidth = &fE->mLengthAttributes[nsSVGFE::WIDTH];
-  tmpHeight = &fE->mLengthAttributes[nsSVGFE::HEIGHT];
-
-  float x, y, width, height;
-  if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
-    mTargetBBox->GetX(&x);
-    x     += nsSVGUtils::ObjectSpace(mTargetBBox, tmpX);
-    mTargetBBox->GetY(&y);
-    y     += nsSVGUtils::ObjectSpace(mTargetBBox, tmpY);
-    width  = nsSVGUtils::ObjectSpace(mTargetBBox, tmpWidth);
-    height = nsSVGUtils::ObjectSpace(mTargetBBox, tmpHeight);
-  } else {
-    nsSVGElement* targetElement = TargetElement();
-    x      = nsSVGUtils::UserSpace(targetElement, tmpX);
-    y      = nsSVGUtils::UserSpace(targetElement, tmpY);
-    width  = nsSVGUtils::UserSpace(targetElement, tmpWidth);
-    height = nsSVGUtils::UserSpace(targetElement, tmpHeight);
-  }
-
-  gfxRect region = UserSpaceToFilterSpace(gfxRect(x, y, width, height));
+  gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits,
+    &fE->mLengthAttributes[nsSVGFE::X], mTargetBBox, mTargetFrame);
+  gfxRect region = UserSpaceToFilterSpace(feArea);
 
   if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::x))
     region.pos.x = defaultFilterSubregion.X();
   if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::y))
     region.pos.y = defaultFilterSubregion.Y();
   if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::width))
     region.size.width = defaultFilterSubregion.Width();
   if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::height))
@@ -335,29 +319,62 @@ nsSVGFilterInstance::ComputeUnionOfAllNe
     r.UnionRect(r, mPrimitives[i].mResultNeededBox);
   }
   return r;
 }
 
 nsresult
 nsSVGFilterInstance::BuildSourceImages()
 {
-  if (mSourceColorAlpha.mResultNeededBox.IsEmpty() &&
-      mSourceAlpha.mResultNeededBox.IsEmpty())
+  nsIntRect neededRect;
+  neededRect.UnionRect(mSourceColorAlpha.mResultNeededBox,
+                       mSourceAlpha.mResultNeededBox);
+  if (neededRect.IsEmpty())
     return NS_OK;
 
   nsRefPtr<gfxImageSurface> sourceColorAlpha = CreateImage();
   if (!sourceColorAlpha)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  nsSVGRenderState tmpState(sourceColorAlpha);
-  nsresult rv = mTargetFrame->PaintSVG(&tmpState, nsnull);
-  if (NS_FAILED(rv))
-    return rv;
+  {
+    // Paint to an offscreen surface first, then copy it to an image
+    // surface. This can be faster especially when the stuff we're painting
+    // contains native themes.
+    nsRefPtr<gfxASurface> offscreen =
+      gfxPlatform::GetPlatform()->CreateOffscreenSurface(
+              gfxIntSize(mSurfaceRect.width, mSurfaceRect.height),
+              gfxASurface::ImageFormatARGB32);
+    if (!offscreen || offscreen->CairoStatus())
+      return NS_ERROR_OUT_OF_MEMORY;
+    offscreen->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y));
+  
+    nsSVGRenderState tmpState(offscreen);
+    nsCOMPtr<nsIDOMSVGMatrix> userSpaceToFilterSpaceTransform
+      = GetUserSpaceToFilterSpaceTransform();
+    if (!userSpaceToFilterSpaceTransform)
+      return NS_ERROR_OUT_OF_MEMORY;
 
+    gfxMatrix m =
+      nsSVGUtils::ConvertSVGMatrixToThebes(userSpaceToFilterSpaceTransform);
+    gfxRect r(neededRect.x, neededRect.y, neededRect.width, neededRect.height);
+    m.Invert();
+    r = m.TransformBounds(r);
+    r.RoundOut();
+    nsIntRect dirty;
+    nsresult rv = nsSVGUtils::GfxRectToIntRect(r, &dirty);
+    if (NS_FAILED(rv))
+      return rv;
+    mPaintCallback->Paint(&tmpState, mTargetFrame, &dirty,
+                          userSpaceToFilterSpaceTransform);
+
+    gfxContext copyContext(sourceColorAlpha);
+    copyContext.SetSource(offscreen);
+    copyContext.Paint();
+  }
+  
   if (!mSourceColorAlpha.mResultNeededBox.IsEmpty()) {
     NS_ASSERTION(mSourceColorAlpha.mImageUsers > 0, "Some user must have needed this");
     mSourceColorAlpha.mImage.mImage = sourceColorAlpha;
     // color model is PREMULTIPLIED SRGB by default.
   }
 
   if (!mSourceAlpha.mResultNeededBox.IsEmpty()) {
     NS_ASSERTION(mSourceAlpha.mImageUsers > 0, "Some user must have needed this");
@@ -523,8 +540,55 @@ nsSVGFilterInstance::ComputeOutputDirtyR
   mSourceColorAlpha.mResultChangeBox = mDirtyInputRect;
   mSourceAlpha.mResultChangeBox = mDirtyInputRect;
   ComputeResultChangeBoxes();
 
   PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
   *aDirty = result->mResultChangeBox;
   return NS_OK;
 }
+
+nsresult
+nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty)
+{
+  nsresult rv = BuildSources();
+  if (NS_FAILED(rv))
+    return rv;
+
+  rv = BuildPrimitives();
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (mPrimitives.IsEmpty()) {
+    // Nothing should be rendered, so nothing is needed.
+    return NS_OK;
+  }
+
+  ComputeResultBoundingBoxes();
+  ComputeNeededBoxes();
+  aDirty->UnionRect(mSourceColorAlpha.mResultNeededBox,
+                    mSourceAlpha.mResultNeededBox);
+  return NS_OK;
+}
+
+nsresult
+nsSVGFilterInstance::ComputeOutputBBox(nsIntRect* aDirty)
+{
+  nsresult rv = BuildSources();
+  if (NS_FAILED(rv))
+    return rv;
+
+  rv = BuildPrimitives();
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (mPrimitives.IsEmpty()) {
+    // Nothing should be rendered.
+    *aDirty = nsIntRect();
+    return NS_OK;
+  }
+
+  ComputeResultBoundingBoxes();
+
+  PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1];
+  *aDirty = result->mResultBoundingBox;
+  return NS_OK;
+}
--- a/layout/svg/base/src/nsSVGFilterInstance.h
+++ b/layout/svg/base/src/nsSVGFilterInstance.h
@@ -46,39 +46,43 @@
 #include "nsSVGFilters.h"
 #include "nsISVGChildFrame.h"
 #include "nsSVGString.h"
 
 #include "gfxImageSurface.h"
 
 class nsSVGLength2;
 class nsSVGElement;
+class nsSVGFilterElement;
+class nsSVGFilterPaintCallback;
 
 /**
  * This class performs all filter processing.
  * 
  * We build a graph of the filter image data flow, essentially
  * converting the filter graph to SSA. This lets us easily propagate
  * analysis data (such as bounding-boxes) over the filter primitive graph.
  */
 class NS_STACK_CLASS nsSVGFilterInstance
 {
 public:
   float GetPrimitiveLength(nsSVGLength2 *aLength) const;
 
-  nsSVGFilterInstance(nsISVGChildFrame *aTargetFrame,
-                      nsIContent* aFilterElement,
+  nsSVGFilterInstance(nsIFrame *aTargetFrame,
+                      nsSVGFilterPaintCallback *aPaintCallback,
+                      nsSVGFilterElement *aFilterElement,
                       nsIDOMSVGRect *aTargetBBox,
                       const gfxRect& aFilterRect,
                       const nsIntSize& aFilterSpaceSize,
                       nsIDOMSVGMatrix *aFilterSpaceToDeviceSpaceTransform,
                       const nsIntRect& aDirtyOutputRect,
                       const nsIntRect& aDirtyInputRect,
                       PRUint16 aPrimitiveUnits) :
     mTargetFrame(aTargetFrame),
+    mPaintCallback(aPaintCallback),
     mFilterElement(aFilterElement),
     mTargetBBox(aTargetBBox),
     mFilterSpaceToDeviceSpaceTransform(aFilterSpaceToDeviceSpaceTransform),
     mFilterRect(aFilterRect),
     mFilterSpaceSize(aFilterSpaceSize),
     mDirtyOutputRect(aDirtyOutputRect),
     mDirtyInputRect(aDirtyInputRect),
     mSurfaceRect(nsIntPoint(0, 0), aFilterSpaceSize),
@@ -95,16 +99,18 @@ public:
   PRUint32 GetFilterResY() const { return mFilterSpaceSize.height; }
   
   const nsIntRect& GetSurfaceRect() const { return mSurfaceRect; }
   PRInt32 GetSurfaceWidth() const { return mSurfaceRect.width; }
   PRInt32 GetSurfaceHeight() const { return mSurfaceRect.height; }
   
   nsresult Render(gfxASurface** aOutput);
   nsresult ComputeOutputDirtyRect(nsIntRect* aDirty);
+  nsresult ComputeSourceNeededRect(nsIntRect* aDirty);
+  nsresult ComputeOutputBBox(nsIntRect* aBBox);
 
   already_AddRefed<nsIDOMSVGMatrix> GetUserSpaceToFilterSpaceTransform() const;
   nsIDOMSVGMatrix* GetFilterSpaceToDeviceSpaceTransform() const {
     return mFilterSpaceToDeviceSpaceTransform.get();
   }
 
 private:
   typedef nsSVGFE::Image Image;
@@ -169,25 +175,20 @@ private:
 
   gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const;
   void ClipToFilterSpace(nsIntRect* aRect) const
   {
     nsIntRect filterSpace(nsIntPoint(0, 0), mFilterSpaceSize);
     aRect->IntersectRect(*aRect, filterSpace);
   }
   void ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfx) const;
-  nsSVGElement* TargetElement() const
-  {
-    nsIFrame* f;
-    CallQueryInterface(mTargetFrame, &f);
-    return static_cast<nsSVGElement*>(f->GetContent());
-  }
 
-  nsISVGChildFrame*       mTargetFrame;
-  nsIContent*             mFilterElement;
+  nsIFrame*               mTargetFrame;
+  nsSVGFilterPaintCallback* mPaintCallback;
+  nsSVGFilterElement*     mFilterElement;
   nsCOMPtr<nsIDOMSVGRect> mTargetBBox;
   nsCOMPtr<nsIDOMSVGMatrix> mFilterSpaceToDeviceSpaceTransform;
   gfxRect                 mFilterRect;
   nsIntSize               mFilterSpaceSize;
   nsIntRect               mDirtyOutputRect;
   nsIntRect               mDirtyInputRect;
   nsIntRect               mSurfaceRect;
   PRUint16                mPrimitiveUnits;
new file mode 100644
--- /dev/null
+++ b/layout/svg/base/src/nsSVGFilterPaintCallback.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __NS_SVGFILTERPAINTCALLBACK_H__
+#define __NS_SVGFILTERPAINTCALLBACK_H__
+
+#include "nsRect.h"
+
+class nsIFrame;
+class nsIDOMSVGMatrix;
+class nsSVGRenderState;
+
+class nsSVGFilterPaintCallback {
+public:
+  /**
+   * Paint the frame contents. aTransform should be applied to aContext
+   * (either via SetOverrideCTM or by applying the transform to aContext
+   * directly).
+   * SVG frames will have had matrix propagation set to false already.
+   * frames have to do their own thing.
+   * The caller will do a Save()/Restore() as necessary so feel free
+   * to mess with context state.
+   * @param aDirtyRect the dirty rect *in user space pixels*
+   * @param aTransform the user-space-to-filter-space transform to apply.
+   * May be null if the identity matrix is requested.
+   */
+  virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget,
+                     const nsIntRect *aDirtyRect, nsIDOMSVGMatrix *aTransform) = 0;
+};
+
+#endif
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
@@ -92,16 +92,17 @@ NS_INTERFACE_MAP_END_INHERITING(nsSVGFor
 // nsIFrame methods
 
 NS_IMETHODIMP
 nsSVGForeignObjectFrame::Init(nsIContent* aContent,
                               nsIFrame*   aParent,
                               nsIFrame*   aPrevInFlow)
 {
   nsresult rv = nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow);
+  AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
   if (NS_SUCCEEDED(rv)) {
     nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this);
   }
   return rv;
 }
 
 void nsSVGForeignObjectFrame::Destroy()
 {
@@ -445,20 +446,30 @@ nsSVGForeignObjectFrame::NotifyRedrawUns
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSVGForeignObjectFrame::SetMatrixPropagation(PRBool aPropagate)
 {
-  mPropagateTransform = aPropagate;
+  if (aPropagate) {
+    AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
+  } else {
+    RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
+  }
   return NS_OK;
 }
 
+PRBool
+nsSVGForeignObjectFrame::GetMatrixPropagation()
+{
+  return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
+}
+
 NS_IMETHODIMP
 nsSVGForeignObjectFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
 {
   mOverrideCTM = aCTM;
   return NS_OK;
 }
 
 already_AddRefed<nsIDOMSVGMatrix>
@@ -509,17 +520,17 @@ nsSVGForeignObjectFrame::GetTMIncludingO
   nsIDOMSVGMatrix* matrix;
   ctm->Translate(x, y, &matrix);
   return matrix;
 }
 
 already_AddRefed<nsIDOMSVGMatrix>
 nsSVGForeignObjectFrame::GetCanvasTM()
 {
-  if (!mPropagateTransform) {
+  if (!GetMatrixPropagation()) {
     nsIDOMSVGMatrix *retval;
     if (mOverrideCTM) {
       retval = mOverrideCTM;
       NS_ADDREF(retval);
     } else {
       NS_NewSVGMatrix(&retval);
     }
     return retval;
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.h
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h
@@ -109,16 +109,17 @@ public:
   NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
   NS_IMETHOD_(nsRect) GetCoveredRegion();
   NS_IMETHOD UpdateCoveredRegion();
   NS_IMETHOD InitialUpdate();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   NS_IMETHOD NotifyRedrawSuspended();
   NS_IMETHOD NotifyRedrawUnsuspended();
   NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
+  virtual PRBool GetMatrixPropagation();
   NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
   virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
   NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);
   NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_TRUE; }
   NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_FALSE; }
 
   // foreignobject public methods
   /**
--- a/layout/svg/base/src/nsSVGGFrame.cpp
+++ b/layout/svg/base/src/nsSVGGFrame.cpp
@@ -76,23 +76,16 @@ nsSVGGFrame::NotifySVGChanged(PRUint32 a
     // make sure our cached transform matrix gets (lazily) updated
     mCanvasTM = nsnull;
   }
 
   nsSVGGFrameBase::NotifySVGChanged(aFlags);
 }
 
 NS_IMETHODIMP
-nsSVGGFrame::SetMatrixPropagation(PRBool aPropagate)
-{
-  mPropagateTransform = aPropagate;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsSVGGFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
 {
   mOverrideCTM = aCTM;
   return NS_OK;
 }
 
 already_AddRefed<nsIDOMSVGMatrix>
 nsSVGGFrame::GetOverrideCTM()
@@ -100,17 +93,17 @@ nsSVGGFrame::GetOverrideCTM()
   nsIDOMSVGMatrix *matrix = mOverrideCTM.get();
   NS_IF_ADDREF(matrix);
   return matrix;
 }
 
 already_AddRefed<nsIDOMSVGMatrix>
 nsSVGGFrame::GetCanvasTM()
 {
-  if (!mPropagateTransform) {
+  if (!GetMatrixPropagation()) {
     nsIDOMSVGMatrix *retval;
     if (mOverrideCTM) {
       retval = mOverrideCTM;
       NS_ADDREF(retval);
     } else {
       NS_NewSVGMatrix(&retval);
     }
     return retval;
--- a/layout/svg/base/src/nsSVGGFrame.h
+++ b/layout/svg/base/src/nsSVGGFrame.h
@@ -68,17 +68,16 @@ public:
 
   // nsIFrame interface:
   NS_IMETHOD AttributeChanged(PRInt32         aNameSpaceID,
                               nsIAtom*        aAttribute,
                               PRInt32         aModType);
 
   // nsISVGChildFrame interface:
   virtual void NotifySVGChanged(PRUint32 aFlags);
-  NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
   NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
   virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
 
   // nsSVGContainerFrame methods:
   virtual already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
 
   nsCOMPtr<nsIDOMSVGMatrix> mCanvasTM;
   nsCOMPtr<nsIDOMSVGMatrix> mOverrideCTM;
--- a/layout/svg/base/src/nsSVGGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGGeometryFrame.cpp
@@ -60,17 +60,18 @@ nsSVGGeometryFrame::Destroy()
   nsSVGGeometryFrameBase::Destroy();
 }
 
 NS_IMETHODIMP
 nsSVGGeometryFrame::Init(nsIContent* aContent,
                          nsIFrame* aParent,
                          nsIFrame* aPrevInFlow)
 {
-  AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
+  AddStateBits((aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) |
+               NS_STATE_SVG_PROPAGATE_TRANSFORM);
   nsresult rv = nsSVGGeometryFrameBase::Init(aContent, aParent, aPrevInFlow);
   return rv;
 }
 
 NS_IMETHODIMP
 nsSVGGeometryFrame::DidSetStyleContext()
 {
   // One of the styles that might have been changed are the urls that
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -1279,16 +1279,33 @@ nsSVGGlyphFrame::EnsureTextRun(float *aD
       return PR_FALSE;
   }
 
   *aDrawScale = float(size/textRunSize);
   *aMetricsScale = (*aDrawScale)/GetTextRunUnitsFactor();
   return PR_TRUE;
 }
 
+NS_IMETHODIMP
+nsSVGGlyphFrame::SetMatrixPropagation(PRBool aPropagate)
+{
+  if (aPropagate) {
+    AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
+  } else {
+    RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
+  }
+  return NS_OK;
+}
+
+PRBool
+nsSVGGlyphFrame::GetMatrixPropagation()
+{
+  return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
+}
+
 //----------------------------------------------------------------------
 // helper class
 
 CharacterIterator::CharacterIterator(nsSVGGlyphFrame *aSource,
         PRBool aForceGlobalTransform)
   : mSource(aSource), mCurrentAdvance(0), mCurrentChar(-1),
     mInError(PR_FALSE)
 {
--- a/layout/svg/base/src/nsSVGGlyphFrame.h
+++ b/layout/svg/base/src/nsSVGGlyphFrame.h
@@ -120,17 +120,18 @@ public:
   NS_IMETHOD UpdateCoveredRegion();
   NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);
 
   NS_IMETHOD_(nsRect) GetCoveredRegion();
   NS_IMETHOD InitialUpdate();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   NS_IMETHOD NotifyRedrawSuspended();
   NS_IMETHOD NotifyRedrawUnsuspended();
-  NS_IMETHOD SetMatrixPropagation(PRBool aPropagate) { return NS_OK; }
+  NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
+  virtual PRBool GetMatrixPropagation();
   NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { return NS_ERROR_FAILURE; }
   virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM() { return nsnull; }
   NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_FALSE; }
   NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_TRUE; }
 
   // nsSVGGeometryFrame methods
   NS_IMETHOD GetCanvasTM(nsIDOMSVGMatrix * *aCTM);
 
--- a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp
+++ b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp
@@ -80,17 +80,16 @@ public:
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGInnerSVG"), aResult);
   }
 #endif
 
   // nsISVGChildFrame interface:
   NS_IMETHOD PaintSVG(nsSVGRenderState *aContext, nsIntRect *aDirtyRect);
   virtual void NotifySVGChanged(PRUint32 aFlags);
-  NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
   NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
   virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
   NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
 
   // nsSVGContainerFrame methods:
   virtual already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
 
   // nsISVGValueObserver
@@ -224,23 +223,16 @@ nsSVGInnerSVGFrame::NotifySVGChanged(PRU
     // make sure our cached transform matrix gets (lazily) updated
     mCanvasTM = nsnull;
   }
 
   nsSVGInnerSVGFrameBase::NotifySVGChanged(aFlags);
 }
 
 NS_IMETHODIMP
-nsSVGInnerSVGFrame::SetMatrixPropagation(PRBool aPropagate)
-{
-  mPropagateTransform = aPropagate;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsSVGInnerSVGFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
 {
   mOverrideCTM = aCTM;
   return NS_OK;
 }
 
 already_AddRefed<nsIDOMSVGMatrix>
 nsSVGInnerSVGFrame::GetOverrideCTM()
new file mode 100644
--- /dev/null
+++ b/layout/svg/base/src/nsSVGIntegrationUtils.cpp
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   rocallahan@mozilla.com
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsSVGIntegrationUtils.h"
+
+#include "nsSVGUtils.h"
+#include "nsSVGEffects.h"
+#include "nsRegion.h"
+#include "nsLayoutUtils.h"
+#include "nsDisplayList.h"
+#include "nsSVGMatrix.h"
+#include "nsSVGFilterPaintCallback.h"
+
+// ----------------------------------------------------------------------
+
+PRBool
+nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
+{
+  const nsStyleSVGReset *style = aFrame->GetStyleSVGReset();
+  return style->mFilter || style->mClipPath || style->mMask;
+}
+
+// Get the union the frame border-box rects over all continuations,
+// relative to aFirst. This defines "user space" for non-SVG frames.
+static nsRect GetNonSVGUserSpace(nsIFrame* aFirst)
+{
+  NS_ASSERTION(!aFirst->GetPrevContinuation(), "Not first continuation");
+  return nsLayoutUtils::GetAllInFlowRectsUnion(aFirst, aFirst);
+}
+
+static nsRect
+GetPreEffectsOverflowRect(nsIFrame* aFrame)
+{
+  nsRect* r = static_cast<nsRect*>(aFrame->GetProperty(nsGkAtoms::preEffectsBBoxProperty));
+  if (r)
+    return *r;
+  return aFrame->GetOverflowRect();
+}
+
+struct BBoxCollector : public nsLayoutUtils::BoxCallback {
+  nsIFrame*     mReferenceFrame;
+  nsIFrame*     mCurrentFrame;
+  const nsRect& mCurrentFrameOverflowArea;
+  nsRect        mResult;
+
+  BBoxCollector(nsIFrame* aReferenceFrame, nsIFrame* aCurrentFrame,
+                const nsRect& aCurrentFrameOverflowArea)
+    : mReferenceFrame(aReferenceFrame), mCurrentFrame(aCurrentFrame),
+      mCurrentFrameOverflowArea(aCurrentFrameOverflowArea) {}
+
+  virtual void AddBox(nsIFrame* aFrame) {
+    nsRect overflow = aFrame == mCurrentFrame ? mCurrentFrameOverflowArea
+        : GetPreEffectsOverflowRect(aFrame);
+    mResult.UnionRect(mResult, overflow + aFrame->GetOffsetTo(mReferenceFrame));
+  }
+};
+
+static nsRect
+GetSVGBBox(nsIFrame* aNonSVGFrame, nsIFrame* aCurrentFrame,
+           const nsRect& aCurrentOverflow, const nsRect& aUserSpaceRect)
+{
+  NS_ASSERTION(!aNonSVGFrame->GetPrevContinuation(),
+               "Need first continuation here");
+  // Compute union of all overflow areas relative to 'first'.
+  BBoxCollector collector(aNonSVGFrame, aCurrentFrame, aCurrentOverflow);
+  nsLayoutUtils::GetAllInFlowBoxes(aNonSVGFrame, &collector);
+  // Get it into "user space" for non-SVG frames
+  return collector.mResult - aUserSpaceRect.TopLeft();
+}
+
+nsRect
+nsSVGIntegrationUtils::ComputeFrameEffectsRect(nsIFrame* aFrame,
+                                               const nsRect& aOverflowRect)
+{
+  PRBool isOK;
+  nsIFrame* firstFrame =
+    nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
+  nsSVGEffects::EffectProperties effectProperties =
+    nsSVGEffects::GetEffectProperties(firstFrame);
+  nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
+    effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
+  if (!filterFrame)
+    return aOverflowRect;
+
+  // XXX this isn't really right. We can't compute the correct filter
+  // bbox until all aFrame's continuations have been reflowed.
+  // but then it's too late to set the overflow areas for the earlier frames.
+  nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
+  nsRect r = GetSVGBBox(firstFrame, aFrame, aOverflowRect, userSpaceRect);
+  // r is relative to user space
+  PRUint32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
+  r.ScaleRoundOutInverse(appUnitsPerDevPixel);
+  r = filterFrame->GetFilterBBox(firstFrame, &r);
+  r.ScaleRoundOut(appUnitsPerDevPixel);
+  // Make it relative to aFrame again
+  return r + userSpaceRect.TopLeft() - aFrame->GetOffsetTo(firstFrame);
+}
+
+nsRect
+nsSVGIntegrationUtils::GetInvalidAreaForChangedSource(nsIFrame* aFrame,
+                                                      const nsRect& aInvalidRect)
+{
+  // Don't bother calling GetEffectProperties; the filter property should
+  // already have been set up during reflow/ComputeFrameEffectsRect
+  nsIFrame* firstFrame =
+    nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
+  nsSVGFilterFrame* filterFrame = nsSVGEffects::GetFilterFrame(firstFrame);
+  if (!filterFrame)
+    return aInvalidRect;
+
+  PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
+  nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
+  nsPoint offset = aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft();
+  nsRect r = aInvalidRect + offset;
+  r.ScaleRoundOutInverse(appUnitsPerDevPixel);
+  r = filterFrame->GetInvalidationBBox(firstFrame, r);
+  r.ScaleRoundOut(appUnitsPerDevPixel);
+  return r - offset;
+}
+
+nsRect
+nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
+                                                       const nsRect& aDamageRect)
+{
+  // Don't bother calling GetEffectProperties; the filter property should
+  // already have been set up during reflow/ComputeFrameEffectsRect
+  nsIFrame* firstFrame =
+    nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
+  nsSVGFilterFrame* filterFrame =
+    nsSVGEffects::GetFilterFrame(firstFrame);
+  if (!filterFrame)
+    return aDamageRect;
+  
+  PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
+  nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
+  nsPoint offset = aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft();
+  nsRect r = aDamageRect + offset;
+  r.ScaleRoundOutInverse(appUnitsPerDevPixel);
+  r = filterFrame->GetSourceForInvalidArea(firstFrame, r);
+  r.ScaleRoundOut(appUnitsPerDevPixel);
+  return r - offset;
+}
+
+PRBool
+nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
+{
+  nsIFrame* firstFrame =
+    nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
+  nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
+  // get point relative to userSpaceRect
+  nsPoint pt = aPt + aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft();
+  return nsSVGUtils::HitTestClip(firstFrame, pt);
+}
+
+class RegularFramePaintCallback : public nsSVGFilterPaintCallback
+{
+public:
+  RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
+                            nsDisplayList* aInnerList,
+                            const nsPoint& aOffset)
+    : mBuilder(aBuilder), mInnerList(aInnerList), mOffset(aOffset) {}
+
+  virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget,
+                     const nsIntRect* aDirtyRect, nsIDOMSVGMatrix *aTransform)
+  {
+    nsIRenderingContext* ctx = aContext->GetRenderingContext(aTarget);
+    gfxContext* gfxCtx = aContext->GetGfxContext();
+
+    if (aTransform) {
+      // Transform by aTransform first
+      gfxMatrix m = nsSVGUtils::ConvertSVGMatrixToThebes(aTransform);
+      gfxCtx->Multiply(m);
+    }
+
+    // We're expected to paint with 1 unit equal to 1 CSS pixel. But
+    // mInnerList->Paint expects 1 unit to equal 1 device pixel. So
+    // adjust.
+    gfxFloat scale =
+      nsPresContext::AppUnitsToFloatCSSPixels(aTarget->PresContext()->AppUnitsPerDevPixel());
+    gfxCtx->Scale(scale, scale);
+
+    nsIRenderingContext::AutoPushTranslation push(ctx, -mOffset.x, -mOffset.y);
+    nsRect dirty;
+    if (aDirtyRect) {
+      dirty = *aDirtyRect;
+      dirty.ScaleRoundOut(nsIDeviceContext::AppUnitsPerCSSPixel());
+      dirty += mOffset;
+    } else {
+      dirty = mInnerList->GetBounds(mBuilder);
+    }
+    mInnerList->Paint(mBuilder, ctx, dirty);
+  }
+
+private:
+  nsDisplayListBuilder* mBuilder;
+  nsDisplayList* mInnerList;
+  nsPoint mOffset;
+};
+
+void
+nsSVGIntegrationUtils::PaintFramesWithEffects(nsIRenderingContext* aCtx,
+                                              nsIFrame* aEffectsFrame,
+                                              const nsRect& aDirtyRect,
+                                              nsDisplayListBuilder* aBuilder,
+                                              nsDisplayList* aInnerList)
+{
+#ifdef DEBUG
+  nsISVGChildFrame *svgChildFrame;
+  CallQueryInterface(aEffectsFrame, &svgChildFrame);
+#endif
+  NS_ASSERTION(!svgChildFrame, "Should never be called on an SVG frame");
+
+  float opacity = aEffectsFrame->GetStyleDisplay()->mOpacity;
+  if (opacity == 0.0f)
+    return;
+
+  /* Properties are added lazily and may have been removed by a restyle,
+     so make sure all applicable ones are set again. */
+  nsIFrame* firstFrame =
+    nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aEffectsFrame);
+  nsSVGEffects::EffectProperties effectProperties =
+    nsSVGEffects::GetEffectProperties(firstFrame);
+
+  /* SVG defines the following rendering model:
+   *
+   *  1. Render geometry
+   *  2. Apply filter
+   *  3. Apply clipping, masking, group opacity
+   *
+   * We follow this, but perform a couple of optimizations:
+   *
+   * + Use cairo's clipPath when representable natively (single object
+   *   clip region).
+   *
+   * + Merge opacity and masking if both used together.
+   */
+
+  PRBool isOK = PR_TRUE;
+  nsSVGClipPathFrame *clipPathFrame = effectProperties.mClipPath ?
+    effectProperties.mClipPath->GetClipPathFrame(&isOK) : nsnull;
+  nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
+    effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
+  nsSVGMaskFrame *maskFrame = effectProperties.mMask ?
+    effectProperties.mMask->GetMaskFrame(&isOK) : nsnull;
+
+  PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE;
+
+  if (!isOK) {
+    // Some resource is missing. We shouldn't paint anything.
+    return;
+  }
+
+  gfxContext* gfx = aCtx->ThebesContext();
+  gfxMatrix savedCTM = gfx->CurrentMatrix();
+  nsSVGRenderState svgContext(aCtx);
+
+  nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame) + aBuilder->ToReferenceFrame(firstFrame);
+  PRInt32 appUnitsPerDevPixel = aEffectsFrame->PresContext()->AppUnitsPerDevPixel();
+  userSpaceRect.ScaleRoundPreservingCentersInverse(appUnitsPerDevPixel);
+  userSpaceRect.ScaleRoundOut(appUnitsPerDevPixel);
+  aCtx->Translate(userSpaceRect.x, userSpaceRect.y);
+
+  nsCOMPtr<nsIDOMSVGMatrix> matrix = GetInitialMatrix(aEffectsFrame);
+
+  PRBool complexEffects = PR_FALSE;
+  /* Check if we need to do additional operations on this child's
+   * rendering, which necessitates rendering into another surface. */
+  if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
+    complexEffects = PR_TRUE;
+    gfx->Save();
+    gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
+  }
+
+  /* If this frame has only a trivial clipPath, set up cairo's clipping now so
+   * we can just do normal painting and get it clipped appropriately.
+   */
+  if (clipPathFrame && isTrivialClip) {
+    gfx->Save();
+    clipPathFrame->ClipPaint(&svgContext, aEffectsFrame, matrix);
+  }
+
+  /* Paint the child */
+  if (filterFrame) {
+    RegularFramePaintCallback paint(aBuilder, aInnerList, userSpaceRect.TopLeft());
+    nsRect r = aDirtyRect - userSpaceRect.TopLeft();
+    r.ScaleRoundOutInverse(appUnitsPerDevPixel);
+    filterFrame->FilterPaint(&svgContext, aEffectsFrame, &paint, &r);
+  } else {
+    gfx->SetMatrix(savedCTM);
+    aInnerList->Paint(aBuilder, aCtx, aDirtyRect);
+    aCtx->Translate(userSpaceRect.x, userSpaceRect.y);
+  }
+
+  if (clipPathFrame && isTrivialClip) {
+    gfx->Restore();
+  }
+
+  /* No more effects, we're done. */
+  if (!complexEffects) {
+    gfx->SetMatrix(savedCTM);
+    return;
+  }
+
+  gfx->PopGroupToSource();
+
+  nsRefPtr<gfxPattern> maskSurface =
+    maskFrame ? maskFrame->ComputeMaskAlpha(&svgContext, aEffectsFrame,
+                                            matrix, opacity) : nsnull;
+
+  nsRefPtr<gfxPattern> clipMaskSurface;
+  if (clipPathFrame && !isTrivialClip) {
+    gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
+
+    nsresult rv = clipPathFrame->ClipPaint(&svgContext, aEffectsFrame, matrix);
+    clipMaskSurface = gfx->PopGroup();
+
+    if (NS_SUCCEEDED(rv) && clipMaskSurface) {
+      // Still more set after clipping, so clip to another surface
+      if (maskSurface || opacity != 1.0f) {
+        gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
+        gfx->Mask(clipMaskSurface);
+        gfx->PopGroupToSource();
+      } else {
+        gfx->Mask(clipMaskSurface);
+      }
+    }
+  }
+
+  if (maskSurface) {
+    gfx->Mask(maskSurface);
+  } else if (opacity != 1.0f) {
+    gfx->Paint(opacity);
+  }
+
+  gfx->Restore();
+  gfx->SetMatrix(savedCTM);
+}
+
+already_AddRefed<nsIDOMSVGMatrix>
+nsSVGIntegrationUtils::GetInitialMatrix(nsIFrame* aNonSVGFrame)
+{
+  NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
+               "SVG frames should not get here");
+  PRInt32 appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
+  nsCOMPtr<nsIDOMSVGMatrix> matrix;
+  float devPxPerCSSPx =
+    1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
+  NS_NewSVGMatrix(getter_AddRefs(matrix),
+                  devPxPerCSSPx, 0.0f,
+                  0.0f, devPxPerCSSPx);
+  return matrix.forget();
+}
+
+gfxRect
+nsSVGIntegrationUtils::GetSVGRectForNonSVGFrame(nsIFrame* aNonSVGFrame)
+{
+  NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
+               "SVG frames should not get here");
+  nsIFrame* firstFrame =
+    nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame);
+  nsRect r = GetNonSVGUserSpace(firstFrame);
+  nsPresContext* presContext = firstFrame->PresContext();
+  return gfxRect(0, 0, presContext->AppUnitsToFloatCSSPixels(r.width),
+                       presContext->AppUnitsToFloatCSSPixels(r.height));
+}
+
+gfxRect
+nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame)
+{
+  NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
+               "SVG frames should not get here");
+  nsIFrame* firstFrame =
+    nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame);
+  nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame);
+  nsRect r = GetSVGBBox(firstFrame, nsnull, nsRect(), userSpaceRect);
+  gfxRect result(r.x, r.y, r.width, r.height);
+  nsPresContext* presContext = aNonSVGFrame->PresContext();
+  result.ScaleInverse(presContext->AppUnitsPerCSSPixel());
+  return result;
+}
new file mode 100644
--- /dev/null
+++ b/layout/svg/base/src/nsSVGIntegrationUtils.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla SVG project.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   rocallahan@mozilla.com
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef NSSVGINTEGRATIONUTILS_H_
+#define NSSVGINTEGRATIONUTILS_H_
+
+#include "nsPoint.h"
+#include "nsRect.h"
+#include "gfxRect.h"
+
+class nsIFrame;
+class nsDisplayListBuilder;
+class nsDisplayList;
+class nsIRenderingContext;
+class nsIDOMSVGMatrix;
+
+/***** Integration of SVG effects with regular frame painting *****/
+
+class nsSVGIntegrationUtils
+{
+public:
+  static PRBool
+  UsingEffectsForFrame(const nsIFrame* aFrame);
+
+  /**
+   * Adjust overflow rect for effects.
+   * XXX this is a problem. We really need to compute the effects rect for
+   * a whole chain of frames for a given element at once. but we have no
+   * way to do this effectively with Gecko's current reflow architecture.
+   * See http://groups.google.com/group/mozilla.dev.tech.layout/msg/6b179066f3051f65
+   */
+  static nsRect
+  ComputeFrameEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect);
+  /**
+   * Adjust the frame's invalidation area to cover effects
+   */
+  static nsRect
+  GetInvalidAreaForChangedSource(nsIFrame* aFrame, const nsRect& aInvalidRect);
+  /**
+   * Figure out which area of the source is needed given an area to
+   * repaint
+   */
+  static nsRect
+  GetRequiredSourceForInvalidArea(nsIFrame* aFrame, const nsRect& aDamageRect);
+  /**
+   * Returns true if the given point is not clipped out by effects.
+   * @param aPt in appunits relative to aFrame
+   */
+  static PRBool
+  HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt);
+
+  /**
+   * Paint non-SVG frame with SVG effects.
+   * @param aOffset the offset in appunits where aFrame should be positioned
+   * in aCtx's coordinate system
+   */
+  static void
+  PaintFramesWithEffects(nsIRenderingContext* aCtx,
+                         nsIFrame* aEffectsFrame, const nsRect& aDirtyRect,
+                         nsDisplayListBuilder* aBuilder,
+                         nsDisplayList* aInnerList);
+
+  static already_AddRefed<nsIDOMSVGMatrix>
+  GetInitialMatrix(nsIFrame* aNonSVGFrame);
+  /**
+   * Returns aNonSVGFrame's rect in CSS pixel units. This is the union
+   * of all its continuations' rectangles. The top-left is always 0,0
+   * since "user space" origin for non-SVG frames is the top-left of the
+   * union of all the continuations' rectangles.
+   */
+  static gfxRect
+  GetSVGRectForNonSVGFrame(nsIFrame* aNonSVGFrame);
+  /**
+   * Returns aNonSVGFrame's bounding box in CSS units. This is the union
+   * of all its continuations' overflow areas, relative to the top-left
+   * of all the continuations' rectangles.
+   */
+  static gfxRect
+  GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame);
+};
+
+#endif /*NSSVGINTEGRATIONUTILS_H_*/
--- a/layout/svg/base/src/nsSVGMaskFrame.cpp
+++ b/layout/svg/base/src/nsSVGMaskFrame.cpp
@@ -56,17 +56,17 @@ NS_NewSVGMaskFrame(nsIPresShell* aPresSh
     return nsnull;
   }
 
   return new (aPresShell) nsSVGMaskFrame(aContext);
 }
 
 already_AddRefed<gfxPattern>
 nsSVGMaskFrame::ComputeMaskAlpha(nsSVGRenderState *aContext,
-                                 nsISVGChildFrame* aParent,
+                                 nsIFrame* aParent,
                                  nsIDOMSVGMatrix* aMatrix,
                                  float aOpacity)
 {
   // If the flag is set when we get here, it means this mask frame
   // has already been used painting the current mask, and the document
   // has a mask reference loop.
   if (mInUse) {
     NS_WARNING("Mask loop detected!");
@@ -74,77 +74,33 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsSVGRe
   }
   AutoMaskReferencer maskRef(this);
 
   gfxContext *gfx = aContext->GetGfxContext();
 
   gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
 
   {
-    nsIFrame *frame;
-    CallQueryInterface(aParent, &frame);
-    nsSVGElement *parent = static_cast<nsSVGElement*>(frame->GetContent());
-
-    float x, y, width, height;
-
     nsSVGMaskElement *mask = static_cast<nsSVGMaskElement*>(mContent);
 
-    nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight;
-    tmpX = &mask->mLengthAttributes[nsSVGMaskElement::X];
-    tmpY = &mask->mLengthAttributes[nsSVGMaskElement::Y];
-    tmpWidth = &mask->mLengthAttributes[nsSVGMaskElement::WIDTH];
-    tmpHeight = &mask->mLengthAttributes[nsSVGMaskElement::HEIGHT];
-
     PRUint16 units =
       mask->mEnumAttributes[nsSVGMaskElement::MASKUNITS].GetAnimValue();
-
+    nsCOMPtr<nsIDOMSVGRect> bbox;
     if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
-
-      aParent->SetMatrixPropagation(PR_FALSE);
-      aParent->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
-                                nsISVGChildFrame::TRANSFORM_CHANGED);
-
-      nsCOMPtr<nsIDOMSVGRect> bbox;
-      aParent->GetBBox(getter_AddRefs(bbox));
-
-      aParent->SetMatrixPropagation(PR_TRUE);
-      aParent->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
-                                nsISVGChildFrame::TRANSFORM_CHANGED);
-
+      bbox = nsSVGUtils::GetBBox(aParent);
       if (!bbox)
         return nsnull;
-
-#ifdef DEBUG_tor
-      bbox->GetX(&x);
-      bbox->GetY(&y);
-      bbox->GetWidth(&width);
-      bbox->GetHeight(&height);
-
-      fprintf(stderr, "mask bbox: %f,%f %fx%f\n", x, y, width, height);
-#endif
-
-      bbox->GetX(&x);
-      x += nsSVGUtils::ObjectSpace(bbox, tmpX);
-      bbox->GetY(&y);
-      y += nsSVGUtils::ObjectSpace(bbox, tmpY);
-      width = nsSVGUtils::ObjectSpace(bbox, tmpWidth);
-      height = nsSVGUtils::ObjectSpace(bbox, tmpHeight);
-    } else {
-      x = nsSVGUtils::UserSpace(parent, tmpX);
-      y = nsSVGUtils::UserSpace(parent, tmpY);
-      width = nsSVGUtils::UserSpace(parent, tmpWidth);
-      height = nsSVGUtils::UserSpace(parent, tmpHeight);
     }
 
-#ifdef DEBUG_tor
-    fprintf(stderr, "mask clip: %f,%f %fx%f\n", x, y, width, height);
-#endif
+    gfxRect maskArea = nsSVGUtils::GetRelativeRect(units,
+      &mask->mLengthAttributes[nsSVGMaskElement::X], bbox, aParent);
 
     gfx->Save();
-    nsSVGUtils::SetClipRect(gfx, aMatrix, x, y, width, height);
+    nsSVGUtils::SetClipRect(gfx, aMatrix, maskArea.X(), maskArea.Y(),
+                            maskArea.Width(), maskArea.Height());
   }
 
   mMaskParent = aParent;
   mMaskParentMatrix = aMatrix;
 
   for (nsIFrame* kid = mFrames.FirstChild(); kid;
        kid = kid->GetNextSibling()) {
     nsSVGUtils::PaintChildWithEffects(aContext, nsnull, kid);
--- a/layout/svg/base/src/nsSVGMaskFrame.h
+++ b/layout/svg/base/src/nsSVGMaskFrame.h
@@ -52,17 +52,17 @@ protected:
   nsSVGMaskFrame(nsStyleContext* aContext) :
     nsSVGMaskFrameBase(aContext),
     mMaskParentMatrix(nsnull),
     mInUse(PR_FALSE) {}
 
 public:
   // nsSVGMaskFrame method:
   already_AddRefed<gfxPattern> ComputeMaskAlpha(nsSVGRenderState *aContext,
-                                                nsISVGChildFrame* aParent,
+                                                nsIFrame* aParent,
                                                 nsIDOMSVGMatrix* aMatrix,
                                                 float aOpacity = 1.0f);
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::svgMaskFrame
    */
@@ -90,17 +90,17 @@ private:
     }
     ~AutoMaskReferencer() {
       mFrame->mInUse = PR_FALSE;
     }
   private:
     nsSVGMaskFrame *mFrame;
   };
 
-  nsISVGChildFrame *mMaskParent;
+  nsIFrame *mMaskParent;
   nsCOMPtr<nsIDOMSVGMatrix> mMaskParentMatrix;
   // recursion prevention flag
   PRPackedBool mInUse;
 
   // nsSVGContainerFrame methods:
   virtual already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
 };
 
--- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
@@ -494,20 +494,30 @@ nsSVGPathGeometryFrame::NotifyRedrawUnsu
     nsSVGUtils::UpdateGraphic(this);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSVGPathGeometryFrame::SetMatrixPropagation(PRBool aPropagate)
 {
-  mPropagateTransform = aPropagate;
+  if (aPropagate) {
+    AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
+  } else {
+    RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
+  }
   return NS_OK;
 }
 
+PRBool
+nsSVGPathGeometryFrame::GetMatrixPropagation()
+{
+  return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
+}
+
 NS_IMETHODIMP
 nsSVGPathGeometryFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
 {
   mOverrideCTM = aCTM;
   return NS_OK;
 }
 
 already_AddRefed<nsIDOMSVGMatrix>
@@ -533,17 +543,17 @@ nsSVGPathGeometryFrame::GetBBox(nsIDOMSV
 // nsSVGGeometryFrame methods:
 
 /* readonly attribute nsIDOMSVGMatrix canvasTM; */
 NS_IMETHODIMP
 nsSVGPathGeometryFrame::GetCanvasTM(nsIDOMSVGMatrix * *aCTM)
 {
   *aCTM = nsnull;
 
-  if (!mPropagateTransform) {
+  if (!GetMatrixPropagation()) {
     if (mOverrideCTM) {
       *aCTM = mOverrideCTM;
       NS_ADDREF(*aCTM);
       return NS_OK;
     }
     return NS_NewSVGMatrix(aCTM);
   }
 
--- a/layout/svg/base/src/nsSVGPathGeometryFrame.h
+++ b/layout/svg/base/src/nsSVGPathGeometryFrame.h
@@ -60,18 +60,17 @@ typedef nsSVGGeometryFrame nsSVGPathGeom
 class nsSVGPathGeometryFrame : public nsSVGPathGeometryFrameBase,
                                public nsISVGChildFrame
 {
   friend nsIFrame*
   NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsIContent* aContent,
                              nsStyleContext* aContext);
 protected:
   nsSVGPathGeometryFrame(nsStyleContext* aContext) :
-    nsSVGPathGeometryFrameBase(aContext),
-    mPropagateTransform(PR_TRUE) {}
+    nsSVGPathGeometryFrameBase(aContext) {}
 
 public:
   // nsISupports interface:
   NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
 private:
   NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
   NS_IMETHOD_(nsrefcnt) Release() { return 1; }
 
@@ -107,16 +106,17 @@ protected:
   NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
   NS_IMETHOD_(nsRect) GetCoveredRegion();
   NS_IMETHOD UpdateCoveredRegion();
   NS_IMETHOD InitialUpdate();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   NS_IMETHOD NotifyRedrawSuspended();
   NS_IMETHOD NotifyRedrawUnsuspended();
   NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
+  virtual PRBool GetMatrixPropagation();
   NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
   virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
   NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);
   NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_FALSE; }
   NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_TRUE; }
 
 protected:
   virtual PRUint16 GetHittestMask();
@@ -138,12 +138,11 @@ private:
   }
 
   nsSVGMarkerProperty *GetMarkerProperty();
   void UpdateMarkerProperty();
 
   void RemovePathProperties();
 
   nsCOMPtr<nsIDOMSVGMatrix> mOverrideCTM;
-  PRPackedBool mPropagateTransform;
 };
 
 #endif // __NS_SVGPATHGEOMETRYFRAME_H__
--- a/layout/svg/base/src/nsSVGTSpanFrame.cpp
+++ b/layout/svg/base/src/nsSVGTSpanFrame.cpp
@@ -98,23 +98,16 @@ nsSVGTSpanFrame::AttributeChanged(PRInt3
 
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 // nsISVGChildFrame methods
 
 NS_IMETHODIMP
-nsSVGTSpanFrame::SetMatrixPropagation(PRBool aPropagate)
-{
-  mPropagateTransform = aPropagate;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsSVGTSpanFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
 {
   mOverrideCTM = aCTM;
   return NS_OK;
 }
 
 already_AddRefed<nsIDOMSVGMatrix>
 nsSVGTSpanFrame::GetOverrideCTM()
--- a/layout/svg/base/src/nsSVGTSpanFrame.h
+++ b/layout/svg/base/src/nsSVGTSpanFrame.h
@@ -76,17 +76,16 @@ public:
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGTSpan"), aResult);
   }
 #endif
   // nsISVGChildFrame interface:
-  NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
   NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
   virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
 
   // nsSVGContainerFrame methods:
   virtual already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
   
   // nsISVGGlyphFragmentNode interface:
   NS_IMETHOD_(PRUint32) GetNumberOfChars();
--- a/layout/svg/base/src/nsSVGTextFrame.cpp
+++ b/layout/svg/base/src/nsSVGTextFrame.cpp
@@ -208,23 +208,16 @@ NS_IMETHODIMP
 nsSVGTextFrame::NotifyRedrawUnsuspended()
 {
   mMetricsState = unsuspended;
   UpdateGlyphPositioning(PR_FALSE);
   return nsSVGTextFrameBase::NotifyRedrawUnsuspended();
 }
 
 NS_IMETHODIMP
-nsSVGTextFrame::SetMatrixPropagation(PRBool aPropagate)
-{
-  mPropagateTransform = aPropagate;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsSVGTextFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM)
 {
   mOverrideCTM = aCTM;
   return NS_OK;
 }
 
 already_AddRefed<nsIDOMSVGMatrix>
 nsSVGTextFrame::GetOverrideCTM()
@@ -277,17 +270,17 @@ nsSVGTextFrame::GetBBox(nsIDOMSVGRect **
 }
 
 //----------------------------------------------------------------------
 // nsSVGContainerFrame methods:
 
 already_AddRefed<nsIDOMSVGMatrix>
 nsSVGTextFrame::GetCanvasTM()
 {
-  if (!mPropagateTransform) {
+  if (!GetMatrixPropagation()) {
     nsIDOMSVGMatrix *retval;
     if (mOverrideCTM) {
       retval = mOverrideCTM;
       NS_ADDREF(retval);
     } else {
       NS_NewSVGMatrix(&retval);
     }
     return retval;
--- a/layout/svg/base/src/nsSVGTextFrame.h
+++ b/layout/svg/base/src/nsSVGTextFrame.h
@@ -46,17 +46,16 @@ typedef nsSVGTextContainerFrame nsSVGTex
 class nsSVGTextFrame : public nsSVGTextFrameBase
 {
   friend nsIFrame*
   NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
 protected:
   nsSVGTextFrame(nsStyleContext* aContext)
     : nsSVGTextFrameBase(aContext),
       mMetricsState(unsuspended),
-      mPropagateTransform(PR_TRUE),
       mPositioningDirty(PR_TRUE) {}
 
 public:
   // nsIFrame:
   NS_IMETHOD  AttributeChanged(PRInt32         aNameSpaceID,
                                nsIAtom*        aAttribute,
                                PRInt32         aModType);
 
@@ -70,17 +69,16 @@ public:
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGText"), aResult);
   }
 #endif
 
   // nsISVGChildFrame interface:
-  NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
   NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM);
   virtual already_AddRefed<nsIDOMSVGMatrix> GetOverrideCTM();
   virtual void NotifySVGChanged(PRUint32 aFlags);
   NS_IMETHOD NotifyRedrawSuspended();
   NS_IMETHOD NotifyRedrawUnsuspended();
   // Override these four to ensure that UpdateGlyphPositioning is called
   // to bring glyph positions up to date
   NS_IMETHOD PaintSVG(nsSVGRenderState* aContext, nsIntRect *aDirtyRect);
@@ -114,13 +112,12 @@ private:
   void UpdateGlyphPositioning(PRBool aForceGlobalTransform);
 
   nsCOMPtr<nsIDOMSVGMatrix> mCanvasTM;
   nsCOMPtr<nsIDOMSVGMatrix> mOverrideCTM;
 
   enum UpdateState { unsuspended, suspended };
   UpdateState mMetricsState;
 
-  PRPackedBool mPropagateTransform;
   PRPackedBool mPositioningDirty;
 };
 
 #endif
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -78,16 +78,18 @@
 #include "gfxRect.h"
 #include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsIFontMetrics.h"
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsSVGRect.h"
 #include "nsSVGEffects.h"
+#include "nsSVGIntegrationUtils.h"
+#include "nsSVGFilterPaintCallback.h"
 
 gfxASurface *nsSVGUtils::mThebesComputationalSurface = nsnull;
 
 // c = n / 255
 // (c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1 / 2.4) - 0.055) * 255 + 0.5
 static const PRUint8 glinearRGBTosRGBMap[256] = {
   0,  13,  22,  28,  34,  38,  42,  46,
  50,  53,  56,  59,  61,  64,  66,  69,
@@ -212,41 +214,53 @@ float
 nsSVGUtils::GetFontSize(nsIContent *aContent)
 {
   nsIFrame* frame = GetFrameForContent(aContent);
   if (!frame) {
     NS_WARNING("no frame in GetFontSize()");
     return 1.0f;
   }
 
-  return nsPresContext::AppUnitsToFloatCSSPixels(frame->GetStyleFont()->mSize) /
-         frame->PresContext()->TextZoom();
+  return GetFontSize(frame);
+}
+
+float
+nsSVGUtils::GetFontSize(nsIFrame *aFrame)
+{
+  return nsPresContext::AppUnitsToFloatCSSPixels(aFrame->GetStyleFont()->mSize) /
+         aFrame->PresContext()->TextZoom();
 }
 
 float
 nsSVGUtils::GetFontXHeight(nsIContent *aContent)
 {
   nsIFrame* frame = GetFrameForContent(aContent);
   if (!frame) {
     NS_WARNING("no frame in GetFontXHeight()");
     return 1.0f;
   }
 
+  return GetFontXHeight(frame);
+}
+  
+float
+nsSVGUtils::GetFontXHeight(nsIFrame *aFrame)
+{
   nsCOMPtr<nsIFontMetrics> fontMetrics;
-  nsLayoutUtils::GetFontMetricsForFrame(frame, getter_AddRefs(fontMetrics));
+  nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fontMetrics));
 
   if (!fontMetrics) {
     NS_WARNING("no FontMetrics in GetFontXHeight()");
     return 1.0f;
   }
 
   nscoord xHeight;
   fontMetrics->GetXHeight(xHeight);
   return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) /
-         frame->PresContext()->TextZoom();
+         aFrame->PresContext()->TextZoom();
 }
 
 void
 nsSVGUtils::UnPremultiplyImageDataAlpha(PRUint8 *data, 
                                         PRInt32 stride,
                                         const nsIntRect &rect)
 {
   for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
@@ -575,17 +589,17 @@ nsSVGUtils::FindFilterInvalidation(nsIFr
   while (aFrame) {
     if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
       break;
 
     nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
     if (property) {
       nsSVGFilterFrame *filter = property->GetFilterFrame(nsnull);
       if (filter) {
-        rect = filter->GetInvalidationRegion(aFrame, rect);
+        rect = filter->GetInvalidationBBox(aFrame, rect);
       }
     }
     aFrame = aFrame->GetParent();
   }
 
   return rect;
 }
 
@@ -650,17 +664,17 @@ nsSVGUtils::NotifyAncestorsOfFilterRegio
 
 double
 nsSVGUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight)
 {
   return sqrt((aWidth*aWidth + aHeight*aHeight)/2);
 }
 
 float
-nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, nsSVGLength2 *aLength)
+nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, const nsSVGLength2 *aLength)
 {
   float fraction, axis;
 
   switch (aLength->GetCtxType()) {
   case X:
     aRect->GetWidth(&axis);
     break;
   case Y:
@@ -680,21 +694,27 @@ nsSVGUtils::ObjectSpace(nsIDOMSVGRect *a
   } else
     fraction = aLength->GetAnimValue(static_cast<nsSVGSVGElement*>
                                                 (nsnull));
 
   return fraction * axis;
 }
 
 float
-nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, nsSVGLength2 *aLength)
+nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
 {
   return aLength->GetAnimValue(aSVGElement);
 }
 
+float
+nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
+{
+  return aLength->GetAnimValue(aNonSVGContext);
+}
+
 void
 nsSVGUtils::TransformPoint(nsIDOMSVGMatrix *matrix, 
                            float *x, float *y)
 {
   nsCOMPtr<nsIDOMSVGPoint> point;
   NS_NewSVGPoint(getter_AddRefs(point), *x, *y);
   if (!point)
     return;
@@ -846,16 +866,19 @@ nsSVGUtils::GetViewBoxTransform(float aV
 }
 
 
 // This is ugly and roc will want to kill me...
 
 already_AddRefed<nsIDOMSVGMatrix>
 nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
 {
+  if (!aFrame->IsFrameOfType(nsIFrame::eSVG))
+    return nsSVGIntegrationUtils::GetInitialMatrix(aFrame);
+
   if (!aFrame->IsLeaf()) {
     // foreignObject is the one non-leaf svg frame that isn't a SVGContainer
     if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
       nsSVGForeignObjectFrame *foreignFrame =
         static_cast<nsSVGForeignObjectFrame*>(aFrame);
       return foreignFrame->GetCanvasTM();
     }
     nsSVGContainerFrame *containerFrame = static_cast<nsSVGContainerFrame*>
@@ -912,16 +935,36 @@ nsSVGUtils::RemoveObserver(nsISupports *
   CallQueryInterface(aObserver, &observer);
   CallQueryInterface(aTarget, &v);
   if (observer && v)
     v->RemoveObserver(observer);
 }
 
 // ************************************************************
 
+class SVGPaintCallback : public nsSVGFilterPaintCallback
+{
+public:
+  virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget,
+                     const nsIntRect* aDirtyRect, nsIDOMSVGMatrix *aTransform)
+  {
+    nsISVGChildFrame *svgChildFrame;
+    CallQueryInterface(aTarget, &svgChildFrame);
+    NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
+
+    if (aTransform) {
+      svgChildFrame->SetOverrideCTM(aTransform);
+      svgChildFrame->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
+                                      nsISVGChildFrame::TRANSFORM_CHANGED);
+    }
+
+    svgChildFrame->PaintSVG(aContext, const_cast<nsIntRect*>(aDirtyRect));
+  }
+};
+
 void
 nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext,
                                   nsIntRect *aDirtyRect,
                                   nsIFrame *aFrame)
 {
   nsISVGChildFrame *svgChildFrame;
   CallQueryInterface(aFrame, &svgChildFrame);
 
@@ -933,24 +976,31 @@ nsSVGUtils::PaintChildWithEffects(nsSVGR
     return;
 
   /* Properties are added lazily and may have been removed by a restyle,
      so make sure all applicable ones are set again. */
 
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(aFrame);
 
-  /* Check if we need to draw anything */
+  PRBool isOK = PR_TRUE;
+  nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
+    effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
+
+  /* Check if we need to draw anything. HasValidCoveredRect only returns
+   * true for path geometry and glyphs, so basically we're traversing
+   * all containers and we can only skip leaves here.
+   */
   if (aDirtyRect && svgChildFrame->HasValidCoveredRect()) {
-    nsRect rect = *aDirtyRect;
-    rect.ScaleRoundOut(aFrame->PresContext()->AppUnitsPerDevPixel());
-    if (effectProperties.mFilter) {
-      if (!rect.Intersects(FindFilterInvalidation(aFrame, aFrame->GetRect())))
+    if (filterFrame) {
+      if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull)))
         return;
     } else {
+      nsRect rect = *aDirtyRect;
+      rect.ScaleRoundOut(aFrame->PresContext()->AppUnitsPerDevPixel());
       if (!rect.Intersects(aFrame->GetRect()))
         return;
     }
   }
 
   /* SVG defines the following rendering model:
    *
    *  1. Render geometry
@@ -966,21 +1016,18 @@ nsSVGUtils::PaintChildWithEffects(nsSVGR
    */
 
   if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
     opacity = 1.0f;
 
   gfxContext *gfx = aContext->GetGfxContext();
   PRBool complexEffects = PR_FALSE;
 
-  PRBool isOK = PR_TRUE;
   nsSVGClipPathFrame *clipPathFrame = effectProperties.mClipPath ?
     effectProperties.mClipPath->GetClipPathFrame(&isOK) : nsnull;
-  nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
-    effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
   nsSVGMaskFrame *maskFrame = effectProperties.mMask ?
     effectProperties.mMask->GetMaskFrame(&isOK) : nsnull;
 
   PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE;
 
   if (!isOK) {
     // Some resource is missing. We shouldn't paint anything.
     return;
@@ -997,45 +1044,46 @@ nsSVGUtils::PaintChildWithEffects(nsSVGR
     gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
   }
 
   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
    * we can just do normal painting and get it clipped appropriately.
    */
   if (clipPathFrame && isTrivialClip) {
     gfx->Save();
-    clipPathFrame->ClipPaint(aContext, svgChildFrame, matrix);
+    clipPathFrame->ClipPaint(aContext, aFrame, matrix);
   }
 
   /* Paint the child */
   if (filterFrame) {
-    filterFrame->FilterPaint(aContext, svgChildFrame, aDirtyRect);
+    SVGPaintCallback paintCallback;
+    filterFrame->FilterPaint(aContext, aFrame, &paintCallback, aDirtyRect);
   } else {
     svgChildFrame->PaintSVG(aContext, aDirtyRect);
   }
 
   if (clipPathFrame && isTrivialClip) {
     gfx->Restore();
   }
 
   /* No more effects, we're done. */
   if (!complexEffects)
     return;
 
   gfx->PopGroupToSource();
 
   nsRefPtr<gfxPattern> maskSurface =
-    maskFrame ? maskFrame->ComputeMaskAlpha(aContext, svgChildFrame,
+    maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame,
                                             matrix, opacity) : nsnull;
 
   nsRefPtr<gfxPattern> clipMaskSurface;
   if (clipPathFrame && !isTrivialClip) {
     gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
 
-    nsresult rv = clipPathFrame->ClipPaint(aContext, svgChildFrame, matrix);
+    nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix);
     clipMaskSurface = gfx->PopGroup();
 
     if (NS_SUCCEEDED(rv) && clipMaskSurface) {
       // Still more set after clipping, so clip to another surface
       if (maskSurface || opacity != 1.0f) {
         gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
         gfx->Mask(clipMaskSurface);
         gfx->PopGroupToSource();
@@ -1072,21 +1120,18 @@ nsSVGUtils::HitTestClip(nsIFrame *aFrame
 
   nsSVGClipPathFrame *clipPathFrame = props.mClipPath->GetClipPathFrame(nsnull);
   if (!clipPathFrame) {
     // clipPath is not a valid resource, so nothing gets painted, so
     // hit-testing must fail.
     return PR_FALSE;
   }
 
-  nsISVGChildFrame* SVGFrame;
-  CallQueryInterface(aFrame, &SVGFrame);
-
   nsCOMPtr<nsIDOMSVGMatrix> matrix = GetCanvasTM(aFrame);
-  return clipPathFrame->ClipHitTest(SVGFrame, matrix, aPoint);
+  return clipPathFrame->ClipHitTest(aFrame, matrix, aPoint);
 }
 
 nsIFrame *
 nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
 {
   // XXX: The frame's children are linked in a singly-linked list in document
   // order. If we were to hit test the children in this order we would need to
   // hit test *every* SVG frame, since even if we get a hit, later SVG frames
@@ -1322,16 +1367,68 @@ nsresult
 nsSVGUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut)
 {
   *aOut = nsIntRect(PRInt32(aIn.X()), PRInt32(aIn.Y()),
                     PRInt32(aIn.Width()), PRInt32(aIn.Height()));
   return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height) == aIn
     ? NS_OK : NS_ERROR_FAILURE;
 }
 
+already_AddRefed<nsIDOMSVGRect>
+nsSVGUtils::GetBBox(nsIFrame *aFrame)
+{
+  nsISVGChildFrame *svg;
+  CallQueryInterface(aFrame, &svg);
+  if (!svg) {
+    nsIDOMSVGRect *rect = nsnull;
+    gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
+    NS_NewSVGRect(&rect, r);
+    return rect;
+  }
+
+  PRBool needToDisablePropagation = svg->GetMatrixPropagation();
+  if (needToDisablePropagation) {
+    svg->SetMatrixPropagation(PR_FALSE);
+    svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
+                          nsISVGChildFrame::TRANSFORM_CHANGED);
+  }
+  
+  nsCOMPtr<nsIDOMSVGRect> bbox;
+  svg->GetBBox(getter_AddRefs(bbox));
+  
+  if (needToDisablePropagation) {
+    svg->SetMatrixPropagation(PR_TRUE);
+    svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
+                          nsISVGChildFrame::TRANSFORM_CHANGED);
+  }
+
+  return bbox.forget();
+}
+
+gfxRect
+nsSVGUtils::GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH,
+                            nsIDOMSVGRect *aBBox, nsIFrame *aFrame)
+{
+  float x, y, width, height;
+  if (aUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+    aBBox->GetX(&x);
+    x += ObjectSpace(aBBox, &aXYWH[0]);
+    aBBox->GetY(&y);
+    y += ObjectSpace(aBBox, &aXYWH[1]);
+    width = ObjectSpace(aBBox, &aXYWH[2]);
+    height = ObjectSpace(aBBox, &aXYWH[3]);
+  } else {
+    x = nsSVGUtils::UserSpace(aFrame, &aXYWH[0]);
+    y = nsSVGUtils::UserSpace(aFrame, &aXYWH[1]);
+    width = nsSVGUtils::UserSpace(aFrame, &aXYWH[2]);
+    height = nsSVGUtils::UserSpace(aFrame, &aXYWH[3]);
+  }
+  return gfxRect(x, y, width, height);
+}
+
 PRBool
 nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
 {
   if (!aFrame->GetStyleSVGReset()->mFilter) {
     nsIAtom *type = aFrame->GetType();
     if (type == nsGkAtoms::svgImageFrame)
       return PR_TRUE;
     if (type == nsGkAtoms::svgPathGeometryFrame) {
@@ -1358,43 +1455,55 @@ nsSVGUtils::MaxExpansion(nsIDOMSVGMatrix
   float g = (a * a + b * b - c * c - d * d) / 2;
   float h = a * c + b * d;
   return sqrt(f + sqrt(g * g + h * h));
 }
 
 already_AddRefed<nsIDOMSVGMatrix>
 nsSVGUtils::AdjustMatrixForUnits(nsIDOMSVGMatrix *aMatrix,
                                  nsSVGEnum *aUnits,
-                                 nsISVGChildFrame *aFrame)
+                                 nsIFrame *aFrame)
 {
   nsCOMPtr<nsIDOMSVGMatrix> fini = aMatrix;
 
   if (aFrame &&
       aUnits->GetAnimValue() == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
-    nsCOMPtr<nsIDOMSVGRect> rect;
-    nsresult rv = aFrame->GetBBox(getter_AddRefs(rect));
-
-    if (NS_SUCCEEDED(rv)) {
-      float minx, miny, width, height;
-      rect->GetX(&minx);
-      rect->GetY(&miny);
-      rect->GetWidth(&width);
-      rect->GetHeight(&height);
+    float minx, miny, width, height;
 
-      // Correct for scaling in outersvg CTM
-      nsIFrame *frame;
-      CallQueryInterface(aFrame, &frame);
-      nsPresContext *presCtx = frame->PresContext();
-      float cssPxPerDevPx =
-        presCtx->AppUnitsToFloatCSSPixels(presCtx->AppUnitsPerDevPixel());
-      minx *= cssPxPerDevPx;
-      miny *= cssPxPerDevPx;
-      width *= cssPxPerDevPx;
-      height *= cssPxPerDevPx;
+    PRBool gotRect = PR_FALSE;
+    if (aFrame->IsFrameOfType(nsIFrame::eSVG)) {
+      nsISVGChildFrame *svgFrame;
+      CallQueryInterface(aFrame, &svgFrame);
+      nsCOMPtr<nsIDOMSVGRect> rect;
+      svgFrame->GetBBox(getter_AddRefs(rect));
+      if (rect) {
+        gotRect = PR_TRUE;
+        rect->GetX(&minx);
+        rect->GetY(&miny);
+        rect->GetWidth(&width);
+        rect->GetHeight(&height);
+        // Correct for scaling in outersvg CTM
+        nsPresContext *presCtx = aFrame->PresContext();
+        float scaleInv =
+          presCtx->AppUnitsToGfxUnits(presCtx->AppUnitsPerCSSPixel());
+        minx /= scaleInv;
+        miny /= scaleInv;
+        width /= scaleInv;
+        height /= scaleInv;
+      }
+    } else {
+      gotRect = PR_TRUE;
+      gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
+      minx = r.X();
+      miny = r.Y();
+      width = r.Width();
+      height = r.Height();
+    }
 
+    if (gotRect) {
       nsCOMPtr<nsIDOMSVGMatrix> tmp;
       aMatrix->Translate(minx, miny, getter_AddRefs(tmp));
       tmp->ScaleNonUniform(width, height, getter_AddRefs(fini));
     }
   }
 
   nsIDOMSVGMatrix* retval = fini.get();
   NS_IF_ADDREF(retval);
@@ -1439,12 +1548,14 @@ nsSVGRenderState::nsSVGRenderState(gfxAS
 }
 
 nsIRenderingContext*
 nsSVGRenderState::GetRenderingContext(nsIFrame *aFrame)
 {
   if (!mRenderingContext) {
     nsIDeviceContext* devCtx = aFrame->PresContext()->DeviceContext();
     devCtx->CreateRenderingContextInstance(*getter_AddRefs(mRenderingContext));
+    if (!mRenderingContext)
+      return nsnull;
     mRenderingContext->Init(devCtx, mGfxContext);
   }
   return mRenderingContext;
 }
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -95,16 +95,18 @@ class nsISVGChildFrame;
 /* Do we have a paint server for stroke with a valid URL? */
 #define NS_STATE_SVG_STROKE_PSERVER   0x01000000
 /* Do we have any paint servers with valid URLs? */
 #define NS_STATE_SVG_PSERVER_MASK     0x01800000
 
 /* are we the child of a non-display container? */
 #define NS_STATE_SVG_NONDISPLAY_CHILD 0x02000000
 
+#define NS_STATE_SVG_PROPAGATE_TRANSFORM 0x04000000
+
 /**
  * Byte offsets of channels in a native packed gfxColor or cairo image surface.
  */
 #ifdef IS_BIG_ENDIAN
 #define GFX_ARGB32_OFFSET_A 0
 #define GFX_ARGB32_OFFSET_R 1
 #define GFX_ARGB32_OFFSET_G 2
 #define GFX_ARGB32_OFFSET_B 3
@@ -187,21 +189,22 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsISVGFilt
 
 class nsSVGUtils
 {
 public:
   /*
    * Get a font-size (em) of an nsIContent
    */
   static float GetFontSize(nsIContent *aContent);
-
+  static float GetFontSize(nsIFrame *aFrame);
   /*
    * Get an x-height of of an nsIContent
    */
   static float GetFontXHeight(nsIContent *aContent);
+  static float GetFontXHeight(nsIFrame *aFrame);
 
   /*
    * Converts image data from premultipled to unpremultiplied alpha
    */
   static void UnPremultiplyImageDataAlpha(PRUint8 *data, 
                                           PRInt32 stride, 
                                           const nsIntRect &rect);
   /*
@@ -294,23 +297,29 @@ public:
    * Computes sqrt((aWidth^2 + aHeight^2)/2);
    */
   static double ComputeNormalizedHypotenuse(double aWidth, double aHeight);
 
   /* Computes the input length in terms of object space coordinates.
      Input: rect - bounding box
             length - length to be converted
   */
-  static float ObjectSpace(nsIDOMSVGRect *aRect, nsSVGLength2 *aLength);
+  static float ObjectSpace(nsIDOMSVGRect *aRect, const nsSVGLength2 *aLength);
 
   /* Computes the input length in terms of user space coordinates.
      Input: content - object to be used for determining user space
+     Input: length - length to be converted
+  */
+  static float UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength);
+
+  /* Computes the input length in terms of user space coordinates.
+     Input: aFrame - object to be used for determining user space
             length - length to be converted
   */
-  static float UserSpace(nsSVGElement *aSVGElement, nsSVGLength2 *aLength);
+  static float UserSpace(nsIFrame *aFrame, const nsSVGLength2 *aLength);
 
   /* Tranforms point by the matrix.  In/out: x,y */
   static void
   TransformPoint(nsIDOMSVGMatrix *matrix,
                  float *x, float *y);
 
   /* Returns the angle halfway between the two specified angles */
   static float
@@ -332,18 +341,18 @@ public:
   
   static already_AddRefed<nsIDOMSVGMatrix>
   GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
                       float aViewboxX, float aViewboxY,
                       float aViewboxWidth, float aViewboxHeight,
                       nsIDOMSVGAnimatedPreserveAspectRatio *aPreserveAspectRatio,
                       PRBool aIgnoreAlign = PR_FALSE);
 
-  /* Paint frame with SVG effects - aDirtyRect is the area being
-   * redrawn, in frame offset pixel coordinates */
+  /* Paint SVG frame with SVG effects - aDirtyRect is the area being
+   * redrawn, in device pixel coordinates relative to the outer svg */
   static void
   PaintChildWithEffects(nsSVGRenderState *aContext,
                         nsIntRect *aDirtyRect,
                         nsIFrame *aFrame);
 
   /**
    * Called by nsCSSFrameConstructor when style changes require the
    * effect properties on aFrame to be updated
@@ -366,17 +375,18 @@ public:
   AddObserver(nsISupports *aObserver, nsISupports *aTarget);
 
   /* Remove observation of an nsISVGValue from an nsISVGValueObserver */
   static void
   RemoveObserver(nsISupports *aObserver, nsISupports *aTarget);
 
   /*
    * Returns the CanvasTM of the indicated frame, whether it's a
-   * child or container SVG frame.
+   * child SVG frame, container SVG frame, or a regular frame.
+   * For regular frames, we just return an identity matrix.
    */
   static already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM(nsIFrame *aFrame);
 
   /*
    * Tells child frames that something that might affect them has changed
    */
   static void
   NotifyChildrenOfSVGChange(nsIFrame *aFrame, PRUint32 aFlags);
@@ -464,17 +474,36 @@ public:
   /* Calculate the maximum expansion of a matrix */
   static float
   MaxExpansion(nsIDOMSVGMatrix *aMatrix);
 
   /* Take a CTM and adjust for object bounding box coordinates, if needed */
   static already_AddRefed<nsIDOMSVGMatrix>
   AdjustMatrixForUnits(nsIDOMSVGMatrix *aMatrix,
                        nsSVGEnum *aUnits,
-                       nsISVGChildFrame *aFrame);
+                       nsIFrame *aFrame);
+
+  /**
+   * Get bounding-box for aFrame. Matrix propagation is disabled so the
+   * bounding box is computed in terms of aFrame's own user space.
+   */
+  static already_AddRefed<nsIDOMSVGRect>
+  GetBBox(nsIFrame *aFrame);
+  /**
+   * Compute a rectangle in userSpaceOnUse or objectBoundingBoxUnits.
+   * @param aXYWH pointer to 4 consecutive nsSVGLength2 objects containing
+   * the x, y, width and height values in that order
+   * @param aBBox the bounding box of the object the rect is relative to;
+   * may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
+   * @param aFrame the object in which to interpret user-space units;
+   * may be null if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
+   */
+  static gfxRect
+  GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH, nsIDOMSVGRect *aBBox,
+                  nsIFrame *aFrame);
 
 #ifdef DEBUG
   static void
   WritePPM(const char *fname, gfxImageSurface *aSurface);
 #endif
 
 private:
   /* Computational (nil) surfaces */