Bug 455984. Rework gradient and pattern code to use nsReferencedElement and nsSVGRenderingObservers, so they observe changes to the ID-element-map properly and propagate invalidations correctly (and with simpler code too). r=longsonr,sr=mats
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 30 Sep 2008 21:47:20 +1300
changeset 19908 1c681814281d68baac8110aedb6f83fc48614ed4
parent 19907 7d2bb64668b0f83ef2b7ade4b45f85a3fe100829
child 19909 ecf324f616d254621b2551d258b1599af58c05ee
push id2537
push userrocallahan@mozilla.com
push dateTue, 30 Sep 2008 08:47:35 +0000
treeherdermozilla-central@1c681814281d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr, mats
bugs455984
milestone1.9.1b1pre
Bug 455984. Rework gradient and pattern code to use nsReferencedElement and nsSVGRenderingObservers, so they observe changes to the ID-element-map properly and propagate invalidations correctly (and with simpler code too). r=longsonr,sr=mats
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/reftests/svg/dynamic-clipPath-01.svg
layout/reftests/svg/dynamic-filter-contents-01-ref.svg
layout/reftests/svg/dynamic-filter-contents-01.svg
layout/reftests/svg/dynamic-gradient-contents-01.svg
layout/reftests/svg/dynamic-mask-contents-01.svg
layout/reftests/svg/dynamic-pattern-01.svg
layout/reftests/svg/dynamic-pattern-02.svg
layout/reftests/svg/dynamic-pattern-contents-01.svg
layout/reftests/svg/dynamic-pattern-contents-02.svg
layout/reftests/svg/reftest.list
layout/style/nsStyleContext.cpp
layout/style/nsStyleStruct.cpp
layout/svg/base/src/Makefile.in
layout/svg/base/src/nsSVGClipPathFrame.h
layout/svg/base/src/nsSVGContainerFrame.h
layout/svg/base/src/nsSVGEffects.cpp
layout/svg/base/src/nsSVGEffects.h
layout/svg/base/src/nsSVGForeignObjectFrame.cpp
layout/svg/base/src/nsSVGGeometryFrame.cpp
layout/svg/base/src/nsSVGGeometryFrame.h
layout/svg/base/src/nsSVGGlyphFrame.cpp
layout/svg/base/src/nsSVGGradientFrame.cpp
layout/svg/base/src/nsSVGGradientFrame.h
layout/svg/base/src/nsSVGIntegrationUtils.cpp
layout/svg/base/src/nsSVGLeafFrame.cpp
layout/svg/base/src/nsSVGPaintServerFrame.cpp
layout/svg/base/src/nsSVGPaintServerFrame.h
layout/svg/base/src/nsSVGPathGeometryFrame.cpp
layout/svg/base/src/nsSVGPatternFrame.cpp
layout/svg/base/src/nsSVGPatternFrame.h
layout/svg/base/src/nsSVGStopFrame.cpp
layout/svg/base/src/nsSVGUtils.cpp
layout/svg/base/src/nsSVGUtils.h
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -143,17 +143,17 @@
 #include "nsIXBLService.h"
 
 #undef NOISY_FIRST_LETTER
 
 #ifdef MOZ_MATHML
 #include "nsMathMLParts.h"
 #endif
 #ifdef MOZ_SVG
-#include "nsSVGUtils.h"
+#include "nsSVGEffects.h"
 #endif
 
 nsIFrame*
 NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 #if defined(MOZ_MEDIA)
 nsIFrame*
 NS_NewHTMLVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
@@ -9821,17 +9821,17 @@ nsCSSFrameConstructor::ProcessRestyledFr
     }
 
     if (hint & nsChangeHint_ReconstructFrame) {
       RecreateFramesForContent(content);
     } else {
       NS_ASSERTION(frame, "This shouldn't happen");
 #ifdef MOZ_SVG
       if (hint & nsChangeHint_UpdateEffects) {
-        nsSVGUtils::UpdateEffects(frame);
+        nsSVGEffects::UpdateEffects(frame);
       }
 #endif
       if (hint & nsChangeHint_ReflowFrame) {
         StyleChangeReflow(frame);
       }
       if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView)) {
         ApplyRenderingChangeToTree(mPresShell->GetPresContext(), frame, hint);
       }
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -97,21 +97,20 @@ template<class E> class nsCOMArray;
 class nsWeakFrame;
 class nsIScrollableFrame;
 class gfxASurface;
 class gfxContext;
 
 typedef short SelectionType;
 typedef PRUint32 nsFrameState;
 
-
-// 134e504f-4fd1-4590-9f5d-899afee63d0f
+// 5c103bc2-788e-4bbe-b82e-635bea34e78f
 #define NS_IPRESSHELL_IID \
-{ 0x134e504f, 0x4fd1, 0x4590, \
-  { 0x9f, 0x5d, 0x89, 0x9a, 0xfe, 0xe6, 0x3d, 0x0f } }
+{ 0x5c103bc2, 0x788e, 0x4bbe, \
+  { 0xb8, 0x2e, 0x63, 0x5b, 0xea, 0x34, 0xe7, 0x8f } }
 
 // Constants for ScrollContentIntoView() function
 #define NS_PRESSHELL_SCROLL_TOP      0
 #define NS_PRESSHELL_SCROLL_BOTTOM   100
 #define NS_PRESSHELL_SCROLL_LEFT     0
 #define NS_PRESSHELL_SCROLL_RIGHT    100
 #define NS_PRESSHELL_SCROLL_CENTER   50
 #define NS_PRESSHELL_SCROLL_ANYWHERE -1
@@ -160,16 +159,18 @@ public:
   /**
    * All callers are responsible for calling |Destroy| after calling
    * |EndObservingDocument|.  It needs to be separate only because form
    * controls incorrectly store their data in the frames rather than the
    * content model and printing calls |EndObservingDocument| multiple
    * times to make form controls behave nicely when printed.
    */
   NS_IMETHOD Destroy() = 0;
+  
+  PRBool IsDestroying() { return mIsDestroying; }
 
   // All frames owned by the shell are allocated from an arena.  They are also recycled
   // using free lists (separate free lists being maintained for each size_t).
   // Methods for recycling frames.
   virtual void* AllocateFrame(size_t aSize) = 0;
   virtual void  FreeFrame(size_t aSize, void* aFreeChunk) = 0;
 
   /**
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2624,17 +2624,16 @@ nsLayoutUtils::CalculateContentBottom(ns
           contentBottom = PR_MAX(contentBottom,
                                  CalculateContentBottom(child) + offset);
         }
       }
 
       childList = aFrame->GetAdditionalChildListName(nextListID);
       nextListID++;
     } while (childList);
-
   }
 
   return contentBottom;
 }
 
 /* static */ nsIFrame*
 nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame)
 {
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -117,16 +117,17 @@
 #include "nsContentErrors.h"
 #include "nsHTMLContainerFrame.h"
 #include "nsBoxLayoutState.h"
 #include "nsBlockFrame.h"
 #include "nsDisplayList.h"
 
 #ifdef MOZ_SVG
 #include "nsSVGIntegrationUtils.h"
+#include "nsSVGEffects.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.
@@ -425,29 +426,30 @@ nsFrame::Init(nsIContent*      aContent,
 
   if (aPrevInFlow) {
     // Make sure the general flags bits are the same
     nsFrameState state = aPrevInFlow->GetStateBits();
 
     // Make bits that are currently off (see constructor) the same:
     mState |= state & (NS_FRAME_SELECTED_CONTENT |
                        NS_FRAME_INDEPENDENT_SELECTION |
-                       NS_FRAME_IS_SPECIAL);
+                       NS_FRAME_IS_SPECIAL |
+                       NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
   }
   if (mParent) {
     nsFrameState state = mParent->GetStateBits();
 
     // Make bits that are currently off (see constructor) the same:
     mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
                        NS_FRAME_GENERATED_CONTENT);
   }
   if (GetStyleDisplay()->HasTransform()) {
     // The frame gets reconstructed if we toggle the -moz-transform
     // property, so we can set this bit here and then ignore it.
-    mState |= NS_FRAME_MAY_BE_TRANSFORMED;
+    mState |= NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS;
   }
   
   DidSetStyleContext();
 
   if (IsBoxWrapped())
     InitBoxMetrics(PR_FALSE);
 
   return NS_OK;
@@ -490,16 +492,20 @@ nsFrame::RemoveFrame(nsIAtom*        aLi
 {
   NS_PRECONDITION(PR_FALSE, "not a container");
   return NS_ERROR_UNEXPECTED;
 }
 
 void
 nsFrame::Destroy()
 {
+#ifdef MOZ_SVG
+  nsSVGEffects::InvalidateDirectRenderingObservers(this);
+#endif
+
   // Get the view pointer now before the frame properties disappear
   // when we call NotifyDestroyingFrame()
   nsIView* view = GetView();
   nsPresContext* presContext = PresContext();
 
   nsIPresShell *shell = presContext->GetPresShell();
   NS_ASSERTION(!(mState & NS_FRAME_OUT_OF_FLOW) ||
                !shell->FrameManager()->GetPlaceholderFrameFor(this),
@@ -670,17 +676,17 @@ nsIFrame::GetPaddingRect() const
   nsRect r(mRect);
   r.Deflate(b);
   return r;
 }
 
 PRBool
 nsIFrame::IsTransformed() const
 {
-  return (mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
+  return (mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
     GetStyleDisplay()->HasTransform();
 }
 
 nsRect
 nsIFrame::GetContentRect() const
 {
   nsMargin bp(GetUsedBorderAndPadding());
   ApplySkipSides(bp);
@@ -1193,17 +1199,18 @@ nsIFrame::BuildDisplayListForStackingCon
   const nsStyleDisplay* disp = GetStyleDisplay();
   PRBool applyAbsPosClipping =
       ApplyAbsPosClipping(aBuilder, disp, this, &absPosClip);
   nsRect dirtyRect = aDirtyRect;
 
   /* If we're being transformed, we need to invert the matrix transform so that we don't 
    * grab points in the wrong coordinate system!
    */
-  if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && disp->HasTransform())
+  if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
+      disp->HasTransform())
     dirtyRect = nsDisplayTransform::UntransformRect(dirtyRect, this, nsPoint(0, 0));
   
   if (applyAbsPosClipping) {
     dirtyRect.IntersectRect(dirtyRect,
                             absPosClip - aBuilder->ToReferenceFrame(this));
   }
 
 #ifdef MOZ_SVG
@@ -1313,17 +1320,18 @@ nsIFrame::BuildDisplayListForStackingCon
       return NS_ERROR_OUT_OF_MEMORY;
 
     resultList.AppendToTop(opacityList);
   }
 
   /* If we're going to apply a transformation, wrap everything in an
    * nsDisplayTransform.
    */
-  if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && disp->HasTransform()) {
+  if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
+      disp->HasTransform()) {
     nsDisplayTransform* transform = new (aBuilder) nsDisplayTransform(this, &resultList);
     if (!transform)  
       return NS_ERROR_OUT_OF_MEMORY;
 
     resultList.AppendToTop(transform);
   }
 
   aList->AppendToTop(&resultList);
@@ -1463,17 +1471,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
   // Don't paint our children if the theme object is a leaf.
   if (IsThemed(ourDisp) &&
       !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance))
     return NS_OK;
 
   // Child is composited if it's transformed, partially transparent, or has
   // SVG effects.
   PRBool isComposited = disp->mOpacity != 1.0f ||
-    ((aChild->mState & NS_FRAME_MAY_BE_TRANSFORMED) && 
+    ((aChild->mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) && 
      aChild->GetStyleDisplay()->HasTransform())
 #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()
@@ -3679,17 +3687,17 @@ nsIFrame::InvalidateInternalAfterResize(
    * coordinate space for the frame, and sometimes its in the transformed
    * coordinate space.  If we get it wrong, we'll display incorrectly.  Until I
    * find a better fix for this problem, we'll invalidate the union of the two
    * rectangles (original rectangle and transformed rectangle).  At least one of
    * these will be correct.
    *
    * See bug #452496 for more details.
    */
-  if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
+  if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
       GetStyleDisplay()->HasTransform()) {
     nsRect newDamageRect;
     newDamageRect.UnionRect(nsDisplayTransform::TransformRect
                             (aDamageRect, this, nsPoint(-aX, -aY)), aDamageRect);
     GetParent()->
       InvalidateInternal(newDamageRect, aX + mRect.x, aY + mRect.y, this,
                          aFlags);
   }
@@ -3757,17 +3765,17 @@ nsIFrame::GetTransformMatrix(nsIFrame **
    * we have to check to see if we have a parent.  If not, we'll set the
    * outparam to null (indicating that there's nothing left) and will hand back
    * the identity matrix.
    */
   if (!*aOutAncestor)
     return gfxMatrix();
   
   /* Keep iterating while the frame can't possibly be transformed. */
-  while (!((*aOutAncestor)->mState & NS_FRAME_MAY_BE_TRANSFORMED)) {
+  while (!(*aOutAncestor)->IsTransformed()) {
     /* If no parent, stop iterating.  Otherwise, update the ancestor. */
     nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
     if (!parent)
       break;
 
     *aOutAncestor = parent;
   }
 
@@ -3927,17 +3935,17 @@ nsRect
 nsIFrame::GetOverflowRectRelativeToParent() const
 {
   return GetOverflowRect() + mRect.TopLeft();
 }
   
 nsRect
 nsIFrame::GetOverflowRectRelativeToSelf() const
 {
-  if (!(mState & NS_FRAME_MAY_BE_TRANSFORMED) ||
+  if (!(mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) ||
       !GetStyleDisplay()->HasTransform())
     return GetOverflowRect();
   return *static_cast<nsRect*>
     (GetProperty(nsGkAtoms::preEffectsBBoxProperty));
 }
   
 void
 nsFrame::CheckInvalidateSizeChange(nsHTMLReflowMetrics& aNewDesiredSize)
@@ -5627,17 +5635,17 @@ nsIFrame::FinishAndStoreOverflow(nsRect*
       disp->mOverflowX == NS_STYLE_OVERFLOW_CLIP) {
     *aOverflowArea = nsRect(nsPoint(0, 0), aNewSize);
     geometricOverflow = PR_FALSE;
   }
 
   *aOverflowArea = GetAdditionalOverflow(*aOverflowArea, aNewSize);
 
   /* If we're transformed, transform the overflow rect by the current transformation. */
-  if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && 
+  if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) && 
       GetStyleDisplay()->HasTransform()) {
     // Save overflow area before the transform
     SetRectProperty(this, nsGkAtoms::preTransformBBoxProperty, *aOverflowArea);
 
     /* Since our size might not actually have been computed yet, we need to make sure that we use the
      * correct dimensions by overriding the stored bounding rectangle with the value the caller has
      * ensured us we'll use.
      */
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -100,20 +100,20 @@ struct nsPeekOffsetStruct;
 struct nsPoint;
 struct nsRect;
 struct nsSize;
 struct nsMargin;
 
 typedef class nsIFrame nsIBox;
 
 // IID for the nsIFrame interface
-// 626a1563-1bae-4a6e-8d2c-2dc2c13048dd
+// 3459e7bb-2b22-4eb3-b60d-27d9f851b919
 #define NS_IFRAME_IID \
-  { 0x626a1563, 0x1bae, 0x4a6e, \
-    { 0x8d, 0x2c, 0x2d, 0xc2, 0xc1, 0x30, 0x48, 0xdd } }
+  { 0x3459e7bb, 0x2b22, 0x4eb3, \
+    { 0xb6, 0x0d, 0x27, 0xd9, 0xf8, 0x51, 0xb9, 0x19 } }
 
 /**
  * Indication of how the frame can be split. This is used when doing runaround
  * of floats, and when pulling up child frames from a next-in-flow.
  *
  * The choices are splittable, not splittable at all, and splittable in
  * a non-rectangular fashion. This last type only applies to block-level
  * elements, and indicates whether splitting can be used when doing runaround.
@@ -231,17 +231,19 @@ enum {
   // which means that it is part of the mangled frame hierarchy that
   // results when an inline has been split because of a nested block.
   NS_FRAME_IS_SPECIAL =                         0x00008000,
 
   // If this bit is set, the frame may have a transform that it applies
   // to its coordinate system (e.g. CSS transform, SVG foreignObject).
   // This is used primarily in GetTransformMatrix to optimize for the
   // common case.
-  NS_FRAME_MAY_BE_TRANSFORMED =                 0x00010000,
+  // ALSO, if this bit is set, the frame's first-continuation may
+  // have an associated nsSVGRenderingObserverList.
+  NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS = 0x00010000,
 
 #ifdef IBMBIDI
   // If this bit is set, the frame itself is a bidi continuation,
   // or is incomplete (its next sibling is a bidi continuation)
   NS_FRAME_IS_BIDI =                            0x00020000,
 #endif
 
   // If this bit is set the frame has descendant with a view
@@ -1577,32 +1579,33 @@ public:
 
   /**
    * Bit-flags to pass to IsFrameOfType()
    */
   enum {
     eMathML =                           1 << 0,
     eSVG =                              1 << 1,
     eSVGForeignObject =                 1 << 2,
-    eBidiInlineContainer =              1 << 3,
+    eSVGContainer =                     1 << 3,
+    eBidiInlineContainer =              1 << 4,
     // the frame is for a replaced element, such as an image
-    eReplaced =                         1 << 4,
+    eReplaced =                         1 << 5,
     // Frame that contains a block but looks like a replaced element
     // from the outside
-    eReplacedContainsBlock =            1 << 5,
+    eReplacedContainsBlock =            1 << 6,
     // A frame that participates in inline reflow, i.e., one that
     // requires nsHTMLReflowState::mLineLayout.
-    eLineParticipant =                  1 << 6,
-    eXULBox =                           1 << 7,
-    eCanContainOverflowContainers =     1 << 8,
-    eBlockFrame =                       1 << 9,
+    eLineParticipant =                  1 << 7,
+    eXULBox =                           1 << 8,
+    eCanContainOverflowContainers =     1 << 9,
+    eBlockFrame =                       1 << 10,
     // If this bit is set, the frame doesn't allow ignorable whitespace as
     // children. For example, the whitespace between <table>\n<tr>\n<td>
     // will be excluded during the construction of children. 
-    eExcludesIgnorableWhitespace =      1 << 10,
+    eExcludesIgnorableWhitespace =      1 << 11,
 
     // These are to allow nsFrame::Init to assert that IsFrameOfType
     // implementations all call the base class method.  They are only
     // meaningful in DEBUG builds.
     eDEBUGAllFrames =                   1 << 30,
     eDEBUGNoFrames =                    1 << 31
   };
 
--- a/layout/reftests/svg/dynamic-clipPath-01.svg
+++ b/layout/reftests/svg/dynamic-clipPath-01.svg
@@ -1,15 +1,15 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/licenses/publicdomain/
 -->
 <svg xmlns="http://www.w3.org/2000/svg" version="1.1"
      class="reftest-wait"
-     onload="setTimeout(doTest,10)"
+     onload="setTimeout(doTest,500)"
      xmlns:xlink="http://www.w3.org/1999/xlink">
   <title>Testing that dynamic changes to the element for a given ID are reflected in clip-path</title>
   <defs>
     <svg id="d">
       <rect width="100%" height="50%" fill="lime"/>
       <rect y="50%" width="100%" height="50%" fill="red"/>
     </svg>
   </defs>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-filter-contents-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">
+  <rect width="100" height="100" fill="lime"/>
+  <rect width="100" height="100" fill="red" opacity="0.5"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-filter-contents-01.svg
@@ -0,0 +1,26 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg class="reftest-wait" xmlns="http://www.w3.org/2000/svg" onload="setTimeout(doTest,500)">
+  <style>
+  .foo feComposite { color-interpolation-filters:sRGB; }
+  </style>
+
+  <defs id="d">
+    <filter id="f" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox" 
+            x="0" y="0" width="1" height="1">
+      <feFlood flood-color="#ff0000" flood-opacity="0.5" result="flood"/>
+      <feComposite width="1" height="1" in="flood" operator="over" in2="SourceGraphic"/>
+    </filter>
+  </defs>
+
+  <rect width="100" height="100" fill="lime" filter="url(#f)"/>
+
+  <script>
+  function doTest() {
+    document.getElementById("d").setAttribute("class", "foo");
+    document.documentElement.removeAttribute("class");
+  }
+  </script>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-gradient-contents-01.svg
@@ -0,0 +1,27 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="reftest-wait"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     onload="setTimeout(doTest, 500)">
+  <title>Testing that dynamic changes to the element for a given ID are reflected in patterns</title>
+  <style>
+    .foo stop { stop-color:lime; }
+  </style>
+  <defs id="d">
+    <linearGradient id="g" gradientUnits="objectBoundingBox" x2="0" y2="1">
+      <stop stop-color="red" offset="0"/>
+      <stop stop-color="red" offset="1"/>
+    </linearGradient>
+  </defs>
+ 
+  <rect id="u1" width="100%" height="100%" fill="url(#g)"/>
+
+  <script>
+  function doTest() {
+    document.getElementById("g").setAttribute("class", "foo");
+    document.documentElement.removeAttribute("class");
+  }
+  </script>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-mask-contents-01.svg
@@ -0,0 +1,24 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" onload="setTimeout(doTest,500)" class="reftest-wait">
+  <style>
+  .foo .maskrect { fill:white; }
+  </style>
+
+  <defs id="d">
+    <mask id="m1" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
+      <rect class="maskrect" width="1" height="1"/>
+    </mask>
+  </defs>
+
+  <rect id="rect" width="100%" height="100%" fill="lime" mask="url(#m1)"/>
+
+  <script>
+  function doTest() {
+    document.getElementById("d").setAttribute("class", "foo");
+    document.documentElement.removeAttribute("class");
+  }
+  </script>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-pattern-01.svg
@@ -0,0 +1,110 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="reftest-wait"
+     xmlns:xlink="http://www.w3.org/1999/xlink" onload="setTimeout(doTest,500)">
+  <title>Testing that dynamic changes to the element for a given ID are reflected in patterns</title>
+
+  <rect id="u1" x="10%" width="11%" height="100%" fill="url(#r1)"/>
+  <script>
+    // force frame construction; test that parsing "r1" after frame construction
+    // is still bound to "u1" eventually
+    var rect = document.getElementById("u1").getBoundingClientRect();
+  </script>
+  <pattern id="r1" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="lime"/>
+  </pattern>
+
+  <pattern id="x" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="lime"/>
+  </pattern>
+  <rect id="u2" x="20%" width="11%" height="100%" fill="url(#r2)"/>
+
+  <pattern id="r3" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="red"/>
+  </pattern>
+  <pattern id="r3" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="lime"/>
+  </pattern>
+  <rect id="u3" x="30%" width="11%" height="100%" fill="url(#r3)"/>
+
+  <pattern id="r4" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="red"/>
+  </pattern>
+  <pattern id="r4" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="lime"/>
+  </pattern>
+  <rect id="u4" x="40%" width="11%" height="100%" fill="url(#r4)"/>
+
+  <pattern id="r5" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="red"/>
+  </pattern>
+  <rect id="u5" x="50%" width="11%" height="100%" fill="url(#r5)"/>
+
+  <pattern id="r6" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="red"/>
+  </pattern>
+  <pattern id="r6-2" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="lime"/>
+  </pattern>
+  <rect id="u6" x="60%" width="11%" height="100%" fill="url(#r6)"/>
+
+  <pattern id="r7" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="lime"/>
+  </pattern>
+  <pattern id="r7-2" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="red"/>
+  </pattern>
+  <rect id="u7" x="70%" width="11%" height="100%" fill="url(#r7)"/>
+
+  <pattern id="r8-2" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="lime"/>
+  </pattern>
+  <pattern id="r8" width="100%" height="100%" patternUnits="userSpaceOnUse">
+      <rect width="100%" height="100%" fill="red"/>
+  </pattern>
+  <rect id="u8" x="80%" width="11%" height="100%" fill="url(#r8)"/>
+
+  <rect width="11%" height="100%" fill="lime"/>
+  <rect x="90%" width="11%" height="100%" fill="lime"/>
+
+  <script>
+  function doTest() {
+    // check that changing an id to "r2" lets u2 find it
+    var r2 = document.getElementById("x");
+    r2.setAttribute("id", "r2");
+
+    var rect = document.getElementById("u3").getBoundingClientRect();
+    // check that removing the bad r3 lets u3 find the good one
+    var r3 = document.getElementById("r3");
+    r3.parentNode.removeChild(r3);
+
+    // check that renaming the bad r4 lets u4 find the good one
+    var r4 = document.getElementById("r4");
+    r4.removeAttribute("id");
+
+    // check that changing u5's reference works
+    var u5 = document.getElementById("u5");
+    u5.setAttribute("fill", "url(#r1)");
+
+    // check that inserting a good element before the bad r6 works
+    var r6 = document.getElementById("r6-2");
+    r6.parentNode.removeChild(r6);
+    r6.setAttribute("id", "r6");
+    document.documentElement.insertBefore(r6, document.documentElement.firstChild);
+
+    // check that inserting a bad element after a good one doesn't break anything
+    var r7 = document.getElementById("r7-2");
+    r7.parentNode.removeChild(r7);
+    r7.setAttribute("id", "r7");
+    document.documentElement.appendChild(r7);
+
+    // check that renaming a good element to r8 works
+    var r8 = document.getElementById("r8-2");
+    r8.setAttribute("id", "r8");
+
+    document.documentElement.removeAttribute("class");
+  }
+  </script>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-pattern-02.svg
@@ -0,0 +1,26 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="reftest-wait"
+     xmlns:xlink="http://www.w3.org/1999/xlink" onload="setTimeout(doTest,500)">
+  <title>Testing that href changes are live</title>
+  <defs>
+    <pattern id="r1" width="100%" height="100%" patternUnits="userSpaceOnUse">
+    </pattern>
+  </defs>
+  <defs>
+    <pattern id="r2">
+      <rect width="100%" height="100%" fill="lime"/>
+    </pattern>
+  </defs>
+ 
+  <rect id="u1" width="100%" height="100%" fill="url(#r1)"/>
+
+  <script>
+  function doTest() {
+    document.getElementById("r1").setAttributeNS("http://www.w3.org/1999/xlink", "href", "#r2");
+    document.documentElement.removeAttribute("class");
+  }
+  </script>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-pattern-contents-01.svg
@@ -0,0 +1,29 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="reftest-wait"
+     xmlns:xlink="http://www.w3.org/1999/xlink" onload="setTimeout(doTest,500)">
+  <title>Testing that style changes are reflected in patterns</title>
+  <style>
+    .foo { fill:lime; }
+  </style>
+  <defs>
+    <pattern id="r1" width="100%" height="100%" patternUnits="userSpaceOnUse" xlink:href="#r2">
+    </pattern>
+  </defs>
+  <defs id="d">
+    <pattern id="r2">
+      <rect width="100%" height="100%"/>
+    </pattern>
+  </defs>
+ 
+  <rect id="u1" width="100%" height="100%" fill="url(#r1)"/>
+
+  <script>
+  function doTest() {
+    document.getElementById("d").setAttribute("class", "foo");
+    document.documentElement.removeAttribute("class");
+  }
+  </script>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-pattern-contents-02.svg
@@ -0,0 +1,26 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/licenses/publicdomain/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="reftest-wait"
+     xmlns:xlink="http://www.w3.org/1999/xlink" onload="setTimeout(doTest,500)">
+  <title>Testing that ID-map changes are tracked by pattern inheritance</title>
+  <defs>
+    <pattern id="r1" width="100%" height="100%" patternUnits="userSpaceOnUse" xlink:href="#r2">
+    </pattern>
+  </defs>
+  <defs>
+    <pattern id="r0">
+      <rect width="100%" height="100%" fill="lime"/>
+    </pattern>
+  </defs>
+ 
+  <rect id="u1" width="100%" height="100%" fill="url(#r1)"/>
+
+  <script>
+  function doTest() {
+    document.getElementById("r0").setAttribute("id", "r2");
+    document.documentElement.removeAttribute("class");
+  }
+  </script>
+</svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -14,17 +14,24 @@ include moz-only/reftest.list
 == clip-surface-clone-01.svg clip-surface-clone-01-ref.svg
 == conditions-01.svg pass.svg
 == currentColor-01.svg pass.svg
 == currentColor-02.svg pass.svg
 == currentColor-03.svg pass.svg
 == dynamic-conditions-01.svg pass.svg
 == dynamic-clipPath-01.svg pass.svg
 == dynamic-feFlood-01.svg pass.svg
+== dynamic-filter-contents-01.svg dynamic-filter-contents-01-ref.svg
+== dynamic-gradient-contents-01.svg pass.svg
 == dynamic-link-style-01.svg pass.svg
+== dynamic-mask-contents-01.svg pass.svg
+== dynamic-pattern-01.svg pass.svg
+== dynamic-pattern-02.svg pass.svg
+== dynamic-pattern-contents-01.svg pass.svg
+== dynamic-pattern-contents-02.svg pass.svg
 == dynamic-rect-01.svg dynamic-rect-01-ref.svg
 == dynamic-rect-02.svg dynamic-rect-02-ref.svg
 == dynamic-rect-03.svg dynamic-rect-03-ref.svg
 == dynamic-rect-04.xhtml pass.svg
 == dynamic-reflow-01.svg dynamic-reflow-01-ref.svg
 == dynamic-switch-01.svg pass.svg
 == dynamic-text-01.svg dynamic-text-01-ref.svg
 == dynamic-text-02.svg dynamic-text-02-ref.svg
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -445,16 +445,17 @@ nsStyleContext::CalcStyleDifference(nsSt
   DO_STRUCT_DIFFERENCE(List);
   // If the quotes implementation is ever going to change we might not need
   // a framechange here and a reflow should be sufficient.  See bug 35768.
   DO_STRUCT_DIFFERENCE(Quotes);
 
 #ifdef MOZ_SVG
   maxHint = nsChangeHint(NS_STYLE_HINT_REFLOW | nsChangeHint_UpdateEffects);
   DO_STRUCT_DIFFERENCE(SVGReset);
+  DO_STRUCT_DIFFERENCE(SVG);
 #endif
 
   // At this point, we know that the worst kind of damage we could do is
   // a reflow.
   maxHint = NS_STYLE_HINT_REFLOW;
       
   // The following structs cause (as their maximal difference) a reflow
   // to occur.  REFLOW Structs: Font, Margin, Padding, Border, List,
@@ -470,19 +471,16 @@ nsStyleContext::CalcStyleDifference(nsSt
   // At this point, we know that the worst kind of damage we could do is
   // a re-render (i.e., a VISUAL change).
   maxHint = NS_STYLE_HINT_VISUAL;
 
   // The following structs cause (as their maximal difference) a
   // re-render to occur.  VISUAL Structs: Color, Background
   DO_STRUCT_DIFFERENCE(Color);
   DO_STRUCT_DIFFERENCE(Background);
-#ifdef MOZ_SVG
-  DO_STRUCT_DIFFERENCE(SVG);
-#endif
 
 #undef DO_STRUCT_DIFFERENCE
 
   return hint;
 }
 
 void
 nsStyleContext::Mark()
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -798,58 +798,89 @@ nsStyleSVG::nsStyleSVG(const nsStyleSVG&
   mPointerEvents = aSource.mPointerEvents;
   mShapeRendering = aSource.mShapeRendering;
   mStrokeLinecap = aSource.mStrokeLinecap;
   mStrokeLinejoin = aSource.mStrokeLinejoin;
   mTextAnchor = aSource.mTextAnchor;
   mTextRendering = aSource.mTextRendering;
 }
 
+static PRBool PaintURIChanged(const nsStyleSVGPaint& aPaint1,
+                              const nsStyleSVGPaint& aPaint2)
+{
+  if (aPaint1.mType != aPaint2.mType) {
+    return aPaint1.mType == eStyleSVGPaintType_Server ||
+           aPaint2.mType == eStyleSVGPaintType_Server;
+  }
+  return aPaint1.mType == eStyleSVGPaintType_Server &&
+    !EqualURIs(aPaint1.mPaint.mPaintServer, aPaint2.mPaint.mPaintServer);
+}
+
 nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const
 {
-  if ( mFill                  != aOther.mFill                  ||
-       mStroke                != aOther.mStroke                ||
+  nsChangeHint hint = nsChangeHint(0);
+
+  if (mTextRendering != aOther.mTextRendering) {
+    NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
+    // May be needed for non-svg frames
+    NS_UpdateHint(hint, nsChangeHint_ReflowFrame);
+  }
 
-       !EqualURIs(mMarkerEnd, aOther.mMarkerEnd)               ||
+  if (mFill != aOther.mFill ||
+      mStroke != aOther.mStroke) {
+    NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
+    if (PaintURIChanged(mFill, aOther.mFill) ||
+        PaintURIChanged(mStroke, aOther.mStroke)) {
+      NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
+    }
+    // Nothing more to do, below we can only set "repaint"
+    return hint;
+  }
+
+  if ( !EqualURIs(mMarkerEnd, aOther.mMarkerEnd)               ||
        !EqualURIs(mMarkerMid, aOther.mMarkerMid)               ||
        !EqualURIs(mMarkerStart, aOther.mMarkerStart)           ||
 
        mStrokeDashoffset      != aOther.mStrokeDashoffset      ||
        mStrokeWidth           != aOther.mStrokeWidth           ||
 
        mFillOpacity           != aOther.mFillOpacity           ||
        mStrokeMiterlimit      != aOther.mStrokeMiterlimit      ||
        mStrokeOpacity         != aOther.mStrokeOpacity         ||
 
        mClipRule              != aOther.mClipRule              ||
        mColorInterpolation    != aOther.mColorInterpolation    ||
        mColorInterpolationFilters != aOther.mColorInterpolationFilters ||
        mFillRule              != aOther.mFillRule              ||
-       mPointerEvents         != aOther.mPointerEvents         ||
        mShapeRendering        != aOther.mShapeRendering        ||
        mStrokeDasharrayLength != aOther.mStrokeDasharrayLength ||
        mStrokeLinecap         != aOther.mStrokeLinecap         ||
        mStrokeLinejoin        != aOther.mStrokeLinejoin        ||
-       mTextAnchor            != aOther.mTextAnchor            ||
-       mTextRendering         != aOther.mTextRendering)
-    return NS_STYLE_HINT_VISUAL;
+       mTextAnchor            != aOther.mTextAnchor) {
+    NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
+    return hint;
+  }
 
   // length of stroke dasharrays are the same (tested above) - check entries
   for (PRUint32 i=0; i<mStrokeDasharrayLength; i++)
-    if (mStrokeDasharray[i] != aOther.mStrokeDasharray[i])
-      return NS_STYLE_HINT_VISUAL;
+    if (mStrokeDasharray[i] != aOther.mStrokeDasharray[i]) {
+      NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
+      return hint;
+    }
 
-  return NS_STYLE_HINT_NONE;
+  return hint;
 }
 
 #ifdef DEBUG
 /* static */
 nsChangeHint nsStyleSVG::MaxDifference()
 {
-  return NS_STYLE_HINT_VISUAL;
+  return NS_CombineHint(NS_CombineHint(nsChangeHint_UpdateEffects,
+                                       nsChangeHint_ReflowFrame),
+                                       nsChangeHint_RepaintFrame);
 }
 #endif
 
 // --------------------
 // nsStyleSVGReset
 //
 nsStyleSVGReset::nsStyleSVGReset() 
 {
@@ -886,24 +917,22 @@ nsChangeHint nsStyleSVGReset::CalcDiffer
   nsChangeHint hint = nsChangeHint(0);
 
   if (!EqualURIs(mClipPath, aOther.mClipPath) ||
       !EqualURIs(mFilter, aOther.mFilter)     ||
       !EqualURIs(mMask, aOther.mMask)) {
     NS_UpdateHint(hint, nsChangeHint_UpdateEffects);
     NS_UpdateHint(hint, nsChangeHint_ReflowFrame);
     NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
-  }
-
-  if (mStopColor             != aOther.mStopColor     ||
-      mFloodColor            != aOther.mFloodColor    ||
-      mLightingColor         != aOther.mLightingColor ||
-      mStopOpacity           != aOther.mStopOpacity   ||
-      mFloodOpacity          != aOther.mFloodOpacity  ||
-      mDominantBaseline != aOther.mDominantBaseline)
+  } else if (mStopColor        != aOther.mStopColor     ||
+             mFloodColor       != aOther.mFloodColor    ||
+             mLightingColor    != aOther.mLightingColor ||
+             mStopOpacity      != aOther.mStopOpacity   ||
+             mFloodOpacity     != aOther.mFloodOpacity  ||
+             mDominantBaseline != aOther.mDominantBaseline)
     NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
 
   return hint;
 }
 
 #ifdef DEBUG
 /* static */
 nsChangeHint nsStyleSVGReset::MaxDifference()
--- a/layout/svg/base/src/Makefile.in
+++ b/layout/svg/base/src/Makefile.in
@@ -99,20 +99,21 @@ 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 = \
+	nsSVGEffects.h \
+	nsSVGFilterInstance.h \
+	nsSVGForeignObjectFrame.h \
 	nsSVGIntegrationUtils.h \
 	nsSVGUtils.h \
-	nsSVGFilterInstance.h \
-	nsSVGForeignObjectFrame.h \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	= \
 		-I$(srcdir)/../../../base \
 		-I$(srcdir)/../../../generic \
 		-I$(srcdir)/../../../style \
--- a/layout/svg/base/src/nsSVGClipPathFrame.h
+++ b/layout/svg/base/src/nsSVGClipPathFrame.h
@@ -97,17 +97,16 @@ public:
       mFrame->mInUse = PR_FALSE;
     }
   private:
     nsSVGClipPathFrame *mFrame;
   };
 
   nsIFrame *mClipParent;
   nsCOMPtr<nsIDOMSVGMatrix> mClipParentMatrix;
+  // recursion prevention flag
+  PRPackedBool mInUse;
 
   // nsSVGContainerFrame methods:
   virtual already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
-
-  // recursion prevention flag
-  PRPackedBool mInUse;
 };
 
 #endif
--- a/layout/svg/base/src/nsSVGContainerFrame.h
+++ b/layout/svg/base/src/nsSVGContainerFrame.h
@@ -66,17 +66,18 @@ public:
   NS_IMETHOD RemoveFrame(nsIAtom*        aListName,
                          nsIFrame*       aOldFrame);
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
 
   virtual PRBool IsFrameOfType(PRUint32 aFlags) const
   {
-    return nsSVGContainerFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
+    return nsSVGContainerFrameBase::IsFrameOfType(
+            aFlags & ~(nsIFrame::eSVG | nsIFrame::eSVGContainer));
   }
 };
 
 class nsSVGDisplayContainerFrame : public nsSVGContainerFrame,
                                    public nsISVGChildFrame
 {
 protected:
   nsSVGDisplayContainerFrame(nsStyleContext* aContext) :
--- a/layout/svg/base/src/nsSVGEffects.cpp
+++ b/layout/svg/base/src/nsSVGEffects.cpp
@@ -33,240 +33,397 @@
  * 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 "nsSVGEffects.h"
 #include "nsISupportsImpl.h"
 #include "nsSVGOuterSVGFrame.h"
+#include "nsSVGFilterFrame.h"
+#include "nsSVGClipPathFrame.h"
+#include "nsSVGMaskFrame.h"
 
-NS_IMPL_ISUPPORTS1(nsSVGPropertyBase, nsIMutationObserver)
+NS_IMPL_ISUPPORTS1(nsSVGRenderingObserver, nsIMutationObserver)
 
-nsSVGPropertyBase::nsSVGPropertyBase(nsIURI *aURI,
-                                     nsIFrame *aFrame)
-  : mElement(this), mFrame(aFrame)
+nsSVGRenderingObserver::nsSVGRenderingObserver(nsIURI *aURI,
+                                               nsIFrame *aFrame)
+  : mElement(this), mFrame(aFrame),
+    mFramePresShell(aFrame->PresContext()->PresShell()),
+    mReferencedFrame(nsnull),
+    mReferencedFramePresShell(nsnull)
 {
   // Start watching the target element
   mElement.Reset(aFrame->GetContent(), aURI);
   if (mElement.get()) {
     mElement.get()->AddMutationObserver(this);
   }
 }
 
-nsSVGPropertyBase::~nsSVGPropertyBase()
+nsSVGRenderingObserver::~nsSVGRenderingObserver()
 {
   if (mElement.get()) {
     mElement.get()->RemoveMutationObserver(this);
   }
+  if (mReferencedFrame && !mReferencedFramePresShell->IsDestroying()) {
+    nsSVGEffects::RemoveRenderingObserver(mReferencedFrame, this);
+  }
 }
 
 nsIFrame*
-nsSVGPropertyBase::GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK)
+nsSVGRenderingObserver::GetReferencedFrame()
 {
+  if (mReferencedFrame && !mReferencedFramePresShell->IsDestroying()) {
+    NS_ASSERTION(mElement.get() &&
+                 static_cast<nsGenericElement*>(mElement.get())->GetPrimaryFrame() == mReferencedFrame,
+                 "Cached frame is incorrect!");
+    return mReferencedFrame;
+  }
+
   if (mElement.get()) {
     nsIFrame *frame =
       static_cast<nsGenericElement*>(mElement.get())->GetPrimaryFrame();
-    if (frame && frame->GetType() == aFrameType)
+    if (frame) {
+      mReferencedFrame = frame;
+      mReferencedFramePresShell = mReferencedFrame->PresContext()->PresShell();
+      nsSVGEffects::AddRenderingObserver(mReferencedFrame, this);
       return frame;
+    }
   }
+  return nsnull;
+}
+
+nsIFrame*
+nsSVGRenderingObserver::GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK)
+{
+  nsIFrame* frame = GetReferencedFrame();
+  if (frame && frame->GetType() == aFrameType)
+    return frame;
   if (aOK) {
     *aOK = PR_FALSE;
   }
   return nsnull;
 }
 
 void
-nsSVGPropertyBase::AttributeChanged(nsIDocument *aDocument,
-                                    nsIContent *aContent,
-                                    PRInt32 aNameSpaceID,
-                                    nsIAtom *aAttribute,
-                                    PRInt32 aModType,
-                                    PRUint32 aStateMask)
+nsSVGRenderingObserver::DoUpdate()
 {
+  if (mFramePresShell->IsDestroying()) {
+    // mFrame is no longer valid. Bail out.
+    mFrame = nsnull;
+    return;
+  }
+  if (mReferencedFrame) {
+    nsSVGEffects::RemoveRenderingObserver(mReferencedFrame, this);
+    mReferencedFrame = nsnull;
+    mReferencedFramePresShell = nsnull;
+  }
+  if (mFrame && mFrame->IsFrameOfType(nsIFrame::eSVG)) {
+    // Changes should propagate out to things that might be observing
+    // the referencing frame or its ancestors.
+    nsSVGEffects::InvalidateRenderingObservers(mFrame);
+  }
+}
+
+void
+nsSVGRenderingObserver::InvalidateViaReferencedFrame()
+{
+  // Clear mReferencedFrame since the referenced frame has already
+  // dropped its reference back to us
+  mReferencedFrame = nsnull;
+  mReferencedFramePresShell = nsnull;
   DoUpdate();
 }
 
 void
-nsSVGPropertyBase::ContentAppended(nsIDocument *aDocument,
-                                   nsIContent *aContainer,
-                                   PRInt32 aNewIndexInContainer)
+nsSVGRenderingObserver::AttributeChanged(nsIDocument *aDocument,
+                                         nsIContent *aContent,
+                                         PRInt32 aNameSpaceID,
+                                         nsIAtom *aAttribute,
+                                         PRInt32 aModType,
+                                         PRUint32 aStateMask)
 {
   DoUpdate();
 }
 
 void
-nsSVGPropertyBase::ContentInserted(nsIDocument *aDocument,
-                                   nsIContent *aContainer,
-                                   nsIContent *aChild,
-                                   PRInt32 aIndexInContainer)
+nsSVGRenderingObserver::ContentAppended(nsIDocument *aDocument,
+                                        nsIContent *aContainer,
+                                        PRInt32 aNewIndexInContainer)
+{
+  DoUpdate();
+}
+
+void
+nsSVGRenderingObserver::ContentInserted(nsIDocument *aDocument,
+                                        nsIContent *aContainer,
+                                        nsIContent *aChild,
+                                        PRInt32 aIndexInContainer)
 {
   DoUpdate();
 }
 
 void
-nsSVGPropertyBase::ContentRemoved(nsIDocument *aDocument,
-                                  nsIContent *aContainer,
-                                  nsIContent *aChild,
-                                  PRInt32 aIndexInContainer)
+nsSVGRenderingObserver::ContentRemoved(nsIDocument *aDocument,
+                                       nsIContent *aContainer,
+                                       nsIContent *aChild,
+                                       PRInt32 aIndexInContainer)
 {
   DoUpdate();
 }
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsSVGFilterProperty,
-                             nsSVGPropertyBase,
+                             nsSVGRenderingObserver,
                              nsISVGFilterProperty)
 
 nsSVGFilterProperty::nsSVGFilterProperty(nsIURI *aURI,
                                          nsIFrame *aFilteredFrame)
-  : nsSVGPropertyBase(aURI, aFilteredFrame)
+  : nsSVGRenderingObserver(aURI, aFilteredFrame)
 {
   UpdateRect();
 }
 
+nsSVGFilterFrame *
+nsSVGFilterProperty::GetFilterFrame() {
+  return static_cast<nsSVGFilterFrame *>
+    (GetReferencedFrame(nsGkAtoms::svgFilterFrame, nsnull));
+}
+
 void
 nsSVGFilterProperty::UpdateRect()
 {
-  nsSVGFilterFrame *filter = GetFilterFrame(nsnull);
+  nsSVGFilterFrame *filter = GetFilterFrame();
   if (filter) {
     mFilterRect = filter->GetFilterBBox(mFrame, nsnull);
     mFilterRect.ScaleRoundOut(filter->PresContext()->AppUnitsPerDevPixel());
   } else {
     mFilterRect = nsRect();
   }
 }
 
-void
-nsSVGFilterProperty::DoUpdate()
+static void
+InvalidateAllContinuations(nsIFrame* aFrame)
 {
-  nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
-  if (outerSVGFrame) {
-    outerSVGFrame->Invalidate(mFilterRect);
-    UpdateRect();
-    outerSVGFrame->Invalidate(mFilterRect);
+  for (nsIFrame* f = aFrame; f; f = f->GetNextContinuation()) {
+    f->InvalidateOverflowRect();
   }
 }
 
 void
-nsSVGFilterProperty::ParentChainChanged(nsIContent *aContent)
+nsSVGFilterProperty::DoUpdate()
 {
-  if (aContent->IsInDoc())
+  nsSVGRenderingObserver::DoUpdate();
+  if (!mFrame)
     return;
 
-  nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
-  if (outerSVGFrame) {
-    outerSVGFrame->InvalidateCoveredRegion(mFrame);
-  }
-
-  mFrame->DeleteProperty(nsGkAtoms::filter);
-}
-
-void
-nsSVGClipPathProperty::DoUpdate()
-{
-  nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
-  if (outerSVGFrame) {
-    outerSVGFrame->InvalidateCoveredRegion(mFrame);
+  if (mFrame->IsFrameOfType(nsIFrame::eSVG)) {
+    nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
+    if (outerSVGFrame) {
+      outerSVGFrame->Invalidate(mFilterRect);
+      UpdateRect();
+      outerSVGFrame->Invalidate(mFilterRect);
+    }
+  } else {
+    InvalidateAllContinuations(mFrame);
+    // Reflow so that changes in the filter overflow area get picked up
+    mFramePresShell->FrameNeedsReflow(
+         mFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
   }
 }
 
 void
-nsSVGClipPathProperty::ParentChainChanged(nsIContent *aContent)
+nsSVGPaintingProperty::DoUpdate()
 {
-  if (aContent->IsInDoc())
+  nsSVGRenderingObserver::DoUpdate();
+  if (!mFrame)
     return;
 
-  nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
-  if (outerSVGFrame) {
-    outerSVGFrame->InvalidateCoveredRegion(mFrame);
-  }
-
-  mFrame->DeleteProperty(nsGkAtoms::clipPath);
-}
-
-void
-nsSVGMaskProperty::DoUpdate()
-{
-  nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
-  if (outerSVGFrame) {
-    outerSVGFrame->InvalidateCoveredRegion(mFrame);
+  if (mFrame->IsFrameOfType(nsIFrame::eSVG)) {
+    nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
+    if (outerSVGFrame) {
+      outerSVGFrame->InvalidateCoveredRegion(mFrame);
+    }
+  } else {
+    InvalidateAllContinuations(mFrame);
   }
 }
 
-void
-nsSVGMaskProperty::ParentChainChanged(nsIContent *aContent)
-{
-  if (aContent->IsInDoc())
-    return;
-
-  nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame);
-  if (outerSVGFrame) {
-    outerSVGFrame->InvalidateCoveredRegion(mFrame);
-  }
-
-  mFrame->DeleteProperty(nsGkAtoms::mask);
-}
-
-static nsSVGPropertyBase *
+static nsSVGRenderingObserver *
 CreateFilterProperty(nsIURI *aURI, nsIFrame *aFrame)
 { return new nsSVGFilterProperty(aURI, aFrame); }
 
-static nsSVGPropertyBase *
-CreateMaskProperty(nsIURI *aURI, nsIFrame *aFrame)
-{ return new nsSVGMaskProperty(aURI, aFrame); }
+static nsSVGRenderingObserver *
+CreatePaintingProperty(nsIURI *aURI, nsIFrame *aFrame)
+{ return new nsSVGPaintingProperty(aURI, aFrame); }
 
-static nsSVGPropertyBase *
-CreateClipPathProperty(nsIURI *aURI, nsIFrame *aFrame)
-{ return new nsSVGClipPathProperty(aURI, aFrame); }
-
-static nsSVGPropertyBase *
+static nsSVGRenderingObserver *
 GetEffectProperty(nsIURI *aURI, nsIFrame *aFrame, nsIAtom *aProp,
-                  nsSVGPropertyBase * (* aCreate)(nsIURI *, nsIFrame *))
+                  nsSVGRenderingObserver * (* aCreate)(nsIURI *, nsIFrame *))
 {
   if (!aURI)
     return nsnull;
-  nsSVGPropertyBase *prop = static_cast<nsSVGPropertyBase*>(aFrame->GetProperty(aProp));
+  nsSVGRenderingObserver *prop =
+    static_cast<nsSVGRenderingObserver*>(aFrame->GetProperty(aProp));
   if (prop)
     return prop;
   prop = aCreate(aURI, aFrame);
   if (!prop)
     return nsnull;
   NS_ADDREF(prop);
   aFrame->SetProperty(aProp,
                       static_cast<nsISupports*>(prop),
                       nsPropertyTable::SupportsDtorFunc);
   return prop;
 }
 
+nsSVGPaintingProperty *
+nsSVGEffects::GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, nsIAtom *aProp)
+{
+  return static_cast<nsSVGPaintingProperty*>(
+          GetEffectProperty(aURI, aFrame, aProp, CreatePaintingProperty));
+}
+
 nsSVGEffects::EffectProperties
 nsSVGEffects::GetEffectProperties(nsIFrame *aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
   EffectProperties result;
   const nsStyleSVGReset *style = aFrame->GetStyleSVGReset();
   result.mFilter = static_cast<nsSVGFilterProperty*>
     (GetEffectProperty(style->mFilter, aFrame, nsGkAtoms::filter, CreateFilterProperty));
-  result.mClipPath = static_cast<nsSVGClipPathProperty*>
-    (GetEffectProperty(style->mClipPath, aFrame, nsGkAtoms::clipPath, CreateClipPathProperty));
-  result.mMask = static_cast<nsSVGMaskProperty*>
-    (GetEffectProperty(style->mMask, aFrame, nsGkAtoms::mask, CreateMaskProperty));
+  result.mClipPath = GetPaintingProperty(style->mClipPath, aFrame, nsGkAtoms::clipPath);
+  result.mMask = GetPaintingProperty(style->mMask, aFrame, nsGkAtoms::mask);
   return result;
 }
 
+nsSVGClipPathFrame *
+nsSVGEffects::EffectProperties::GetClipPathFrame(PRBool *aOK) {
+  if (!mClipPath)
+    return nsnull;
+  return static_cast<nsSVGClipPathFrame *>
+    (mClipPath->GetReferencedFrame(nsGkAtoms::svgClipPathFrame, aOK));
+}
+
+nsSVGMaskFrame *
+nsSVGEffects::EffectProperties::GetMaskFrame(PRBool *aOK) {
+  if (!mMask)
+    return nsnull;
+  return static_cast<nsSVGMaskFrame *>
+    (mMask->GetReferencedFrame(nsGkAtoms::svgMaskFrame, aOK));
+}
+
 void
 nsSVGEffects::UpdateEffects(nsIFrame *aFrame)
 {
   aFrame->DeleteProperty(nsGkAtoms::filter);
   aFrame->DeleteProperty(nsGkAtoms::mask);
   aFrame->DeleteProperty(nsGkAtoms::clipPath);
+
+  aFrame->DeleteProperty(nsGkAtoms::stroke);
+  aFrame->DeleteProperty(nsGkAtoms::fill);
 }
 
 nsSVGFilterProperty *
 nsSVGEffects::GetFilterProperty(nsIFrame *aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
   if (!aFrame->GetStyleSVGReset()->mFilter)
     return nsnull;
 
   return static_cast<nsSVGFilterProperty *>(aFrame->GetProperty(nsGkAtoms::filter));
 }
+
+static PLDHashOperator PR_CALLBACK
+GatherEnumerator(nsVoidPtrHashKey* aEntry, void* aArg)
+{
+  nsTArray<nsSVGRenderingObserver*>* array =
+    static_cast<nsTArray<nsSVGRenderingObserver*>*>(aArg);
+  array->AppendElement(static_cast<nsSVGRenderingObserver*>(
+          const_cast<void*>(aEntry->GetKey())));
+  return PL_DHASH_REMOVE;
+}
+
+void
+nsSVGRenderingObserverList::InvalidateAll()
+{
+  if (mObservers.Count() == 0)
+    return;
+
+  nsAutoTArray<nsSVGRenderingObserver*,10> observers;
+  mObservers.EnumerateEntries(GatherEnumerator, &observers);
+  for (PRUint32 i = 0; i < observers.Length(); ++i) {
+    observers[i]->InvalidateViaReferencedFrame();
+  }
+}
+
+static nsSVGRenderingObserverList *
+GetObserverList(nsIFrame *aFrame)
+{
+  if (!(aFrame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS))
+    return nsnull;
+  return static_cast<nsSVGRenderingObserverList*>(aFrame->GetProperty(nsGkAtoms::observer));
+}
+
+void
+nsSVGEffects::AddRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver)
+{
+  NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
+
+  nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
+  if (!observerList) {
+    observerList = new nsSVGRenderingObserverList();
+    if (!observerList)
+      return;
+    for (nsIFrame* f = aFrame; f; f = f->GetNextContinuation()) {
+      f->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
+    }
+    aFrame->SetProperty(nsGkAtoms::observer, observerList);
+  }
+  observerList->Add(aObserver);
+}
+
+void
+nsSVGEffects::RemoveRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver)
+{
+  NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
+
+  nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
+  if (observerList) {
+    observerList->Remove(aObserver);
+    // Don't remove the property even if the observer list is empty.
+    // This might not be a good time to modify the frame property
+    // hashtables.
+  }
+}
+
+void
+nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame)
+{
+  NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
+
+  nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
+  if (observerList) {
+    observerList->InvalidateAll();
+    return;
+  }
+
+  // Check ancestor SVG containers. The root frame cannot be of type
+  // eSVGContainer so we don't have to check f for null here.
+  for (nsIFrame *f = aFrame->GetParent();
+       f->IsFrameOfType(nsIFrame::eSVGContainer); f = f->GetParent()) {
+    observerList = GetObserverList(f);
+    if (observerList) {
+      observerList->InvalidateAll();
+      return;
+    }
+  }
+}
+
+void
+nsSVGEffects::InvalidateDirectRenderingObservers(nsIFrame *aFrame)
+{
+  nsSVGRenderingObserverList *observerList = GetObserverList(aFrame);
+  if (observerList) {
+    observerList->InvalidateAll();
+  }
+}
--- a/layout/svg/base/src/nsSVGEffects.h
+++ b/layout/svg/base/src/nsSVGEffects.h
@@ -38,41 +38,62 @@
 #ifndef NSSVGEFFECTS_H_
 #define NSSVGEFFECTS_H_
 
 #include "nsIContent.h"
 #include "nsIFrame.h"
 #include "nsReferencedElement.h"
 #include "nsStubMutationObserver.h"
 #include "nsSVGUtils.h"
-#include "nsSVGFilterFrame.h"
-#include "nsSVGClipPathFrame.h"
-#include "nsSVGMaskFrame.h"
+#include "nsTHashtable.h"
+
+class nsSVGClipPathFrame;
+class nsSVGFilterFrame;
+class nsSVGMaskFrame;
 
-class nsSVGPropertyBase : public nsStubMutationObserver {
+/*
+ * SVG elements reference supporting resources by element ID. We need to
+ * track when those resources change and when the DOM changes in ways
+ * that affect which element is referenced by a given ID (e.g., when
+ * element IDs change). The code here is responsible for that.
+ * 
+ * When a frame references a supporting resource, we create a property
+ * object derived from nsSVGRenderingObserver to manage the relationship. The
+ * property object is attached to the referencing frame.
+ */
+class nsSVGRenderingObserver : public nsStubMutationObserver {
 public:
-  nsSVGPropertyBase(nsIURI* aURI, nsIFrame *aFrame);
-  virtual ~nsSVGPropertyBase();
+  nsSVGRenderingObserver(nsIURI* aURI, nsIFrame *aFrame);
+  virtual ~nsSVGRenderingObserver();
 
   // nsISupports
   NS_DECL_ISUPPORTS
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
+  void InvalidateViaReferencedFrame();
+
+  nsIFrame* GetReferencedFrame();
+  /**
+   * @param aOK this is only for the convenience of callers. We set *aOK to false
+   * if this function returns null.
+   */
+  nsIFrame* GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK);
+
 protected:
-  // This is called when the referenced filter/mask/clipPath changes
-  virtual void DoUpdate() = 0;
+  // This is called when the referenced resource changes.
+  virtual void DoUpdate();
 
   class SourceReference : public nsReferencedElement {
   public:
-    SourceReference(nsSVGPropertyBase* aContainer) : mContainer(aContainer) {}
+    SourceReference(nsSVGRenderingObserver* aContainer) : mContainer(aContainer) {}
   protected:
     virtual void ContentChanged(nsIContent* aFrom, nsIContent* aTo) {
       if (aFrom) {
         aFrom->RemoveMutationObserver(mContainer);
       }
       nsReferencedElement::ContentChanged(aFrom, aTo);
       if (aTo) {
         aTo->AddMutationObserver(mContainer);
@@ -80,118 +101,174 @@ protected:
       mContainer->DoUpdate();
     }
     /**
      * Override IsPersistent because we want to keep tracking the element
      * for the ID even when it changes.
      */
     virtual PRBool IsPersistent() { return PR_TRUE; }
   private:
-    nsSVGPropertyBase* mContainer;
+    nsSVGRenderingObserver* mContainer;
   };
   
-  /**
-   * @param aOK this is only for the convenience of callers. We set *aOK to false
-   * if this function returns null.
-   */
-  nsIFrame* GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK);
-
   SourceReference mElement;
-  nsIFrame *mFrame;
+  // The frame that this property is attached to
+   nsIFrame *mFrame;
+  // When a presshell is torn down, we don't delete the properties for
+  // each frame until after the frames are destroyed. So here we remember
+  // the presshell for the frames we care about and, before we use the frame,
+  // we test the presshell to see if it's destroying itself. If it is,
+  // then the frame pointer is not valid and we know the frame has gone away.
+  nsIPresShell *mFramePresShell;
+  nsIFrame *mReferencedFrame;
+  nsIPresShell *mReferencedFramePresShell;
 };
 
 class nsSVGFilterProperty :
-  public nsSVGPropertyBase, public nsISVGFilterProperty {
+  public nsSVGRenderingObserver, public nsISVGFilterProperty {
 public:
   nsSVGFilterProperty(nsIURI *aURI, nsIFrame *aFilteredFrame);
 
-  nsSVGFilterFrame *GetFilterFrame(PRBool *aOK) {
-    return static_cast<nsSVGFilterFrame *>
-      (GetReferencedFrame(nsGkAtoms::svgFilterFrame, aOK));
-  }
+  /**
+   * @return the filter frame, or null if there is no filter frame
+   */
+  nsSVGFilterFrame *GetFilterFrame();
   void UpdateRect();
 
   // nsISupports
   NS_DECL_ISUPPORTS
 
-  // nsIMutationObserver
-  NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
-
   // nsISVGFilterProperty
   virtual void Invalidate() { DoUpdate(); }
 
 private:
-  // nsSVGPropertyBase
+  // nsSVGRenderingObserver
   virtual void DoUpdate();
   
   // Tracks a bounding box for the filtered mFrame. We need this so that
   // if the filter changes we know how much to redraw. We only need this
   // for SVG frames since regular frames have an overflow area
   // that includes the filtered area.
   nsRect mFilterRect;
 };
 
-class nsSVGClipPathProperty : public nsSVGPropertyBase {
+class nsSVGPaintingProperty : public nsSVGRenderingObserver {
 public:
-  nsSVGClipPathProperty(nsIURI *aURI, nsIFrame *aClippedFrame)
-    : nsSVGPropertyBase(aURI, aClippedFrame) {}
+  nsSVGPaintingProperty(nsIURI *aURI, nsIFrame *aClippedFrame)
+    : nsSVGRenderingObserver(aURI, aClippedFrame) {}
 
-  nsSVGClipPathFrame *GetClipPathFrame(PRBool *aOK) {
-    return static_cast<nsSVGClipPathFrame *>
-      (GetReferencedFrame(nsGkAtoms::svgClipPathFrame, aOK));
-  }
-
-  // nsIMutationObserver
-  NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
-
-private:
+protected:
   virtual void DoUpdate();
 };
 
-class nsSVGMaskProperty : public nsSVGPropertyBase {
+/**
+ * A manager for one-shot nsSVGRenderingObserver tracking.
+ * nsSVGRenderingObservers can be added or removed. They are not strongly
+ * referenced so an observer must be removed before it before it dies.
+ * When InvalidateAll is called, all outstanding references get
+ * InvalidateViaReferencedFrame()
+ * called on them and the list is cleared. The intent is that
+ * the observer will force repainting of whatever part of the document
+ * is needed, and then at paint time the observer will be re-added.
+ * 
+ * InvalidateAll must be called before this object is destroyed, i.e.
+ * before the referenced frame is destroyed. This should normally happen
+ * via nsSVGContainerFrame::RemoveFrame, since only frames in the frame
+ * tree should be referenced.
+ */
+class nsSVGRenderingObserverList {
 public:
-  nsSVGMaskProperty(nsIURI *aURI, nsIFrame *aMaskedFrame)
-    : nsSVGPropertyBase(aURI, aMaskedFrame) {}
+  nsSVGRenderingObserverList() { mObservers.Init(5); }
+  ~nsSVGRenderingObserverList() { InvalidateAll(); }
 
-  nsSVGMaskFrame *GetMaskFrame(PRBool *aOK) {
-    return static_cast<nsSVGMaskFrame *>
-      (GetReferencedFrame(nsGkAtoms::svgMaskFrame, aOK));
-  }
-
-  // nsIMutationObserver
-  NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
+  void Add(nsSVGRenderingObserver* aObserver)
+  { mObservers.PutEntry(aObserver); }
+  void Remove(nsSVGRenderingObserver* aObserver)
+  { mObservers.RemoveEntry(aObserver); }
+  void InvalidateAll();
 
 private:
-  virtual void DoUpdate();
+  nsTHashtable<nsVoidPtrHashKey> mObservers;
 };
 
 class nsSVGEffects {
 public:
   struct EffectProperties {
     nsSVGFilterProperty*   mFilter;
-    nsSVGMaskProperty*     mMask;
-    nsSVGClipPathProperty* mClipPath;
+    nsSVGPaintingProperty* mMask;
+    nsSVGPaintingProperty* mClipPath;
+
+    /**
+     * @return the clip-path frame, or null if there is no clip-path frame
+     * @param aOK if a clip-path was specified but the designated element
+     * does not exist or is an element of the wrong type, *aOK is set
+     * to false. Otherwise *aOK is untouched.
+     */
+    nsSVGClipPathFrame *GetClipPathFrame(PRBool *aOK);
+    /**
+     * @return the mask frame, or null if there is no mask frame
+     * @param aOK if a mask was specified but the designated element
+     * does not exist or is an element of the wrong type, *aOK is set
+     * to false. Otherwise *aOK is untouched.
+     */
+    nsSVGMaskFrame *GetMaskFrame(PRBool *aOK);
+    /**
+     * @return the filter frame, or null if there is no filter frame
+     * @param aOK if a filter was specified but the designated element
+     * does not exist or is an element of the wrong type, *aOK is set
+     * to false. Otherwise *aOK is untouched.
+     */
+    nsSVGFilterFrame *GetFilterFrame(PRBool *aOK) {
+      if (!mFilter)
+        return nsnull;
+      nsSVGFilterFrame *filter = mFilter->GetFilterFrame();
+      if (!filter) {
+        *aOK = PR_FALSE;
+      }
+      return filter;
+    }
   };
 
   /**
    * @param aFrame should be the first continuation
    */
   static EffectProperties GetEffectProperties(nsIFrame *aFrame);
-
   /**
    * Called by nsCSSFrameConstructor when style changes require the
    * effect properties on aFrame to be updated
    */
   static void UpdateEffects(nsIFrame *aFrame);
-
   /**
    * @param aFrame should be the first continuation
    */
   static nsSVGFilterProperty *GetFilterProperty(nsIFrame *aFrame);
-  
   static nsSVGFilterFrame *GetFilterFrame(nsIFrame *aFrame) {
     nsSVGFilterProperty *prop = GetFilterProperty(aFrame);
-    PRBool ok;
-    return prop ? prop->GetFilterFrame(&ok) : nsnull;
+    return prop ? prop->GetFilterFrame() : nsnull;
   }
+
+  /**
+   * @param aFrame must be a first-continuation.
+   */
+  static void AddRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver);
+  /**
+   * @param aFrame must be a first-continuation.
+   */
+  static void RemoveRenderingObserver(nsIFrame *aFrame, nsSVGRenderingObserver *aObserver);
+  /**
+   * This can be called on any first-continuation frame. We walk up to
+   * the nearest observable frame and invalidate its observers.
+   */
+  static void InvalidateRenderingObservers(nsIFrame *aFrame);
+  /**
+   * This can be called on any frame. Direct observers
+   * of this frame are all invalidated.
+   */
+  static void InvalidateDirectRenderingObservers(nsIFrame *aFrame);
+
+  /**
+   * Get an nsSVGPaintingProperty for the frame, creating a fresh one if necessary
+   */
+  static nsSVGPaintingProperty *
+  GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, nsIAtom *aProp);
 };
 
 #endif /*NSSVGEFFECTS_H_*/
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
@@ -73,17 +73,18 @@ NS_NewSVGForeignObjectFrame(nsIPresShell
   return new (aPresShell) nsSVGForeignObjectFrame(aContext);
 }
 
 nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext)
   : nsSVGForeignObjectFrameBase(aContext),
     mPropagateTransform(PR_TRUE),
     mInReflow(PR_FALSE)
 {
-  AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED);
+  AddStateBits(NS_FRAME_REFLOW_ROOT |
+               NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
 }
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_INTERFACE_MAP_BEGIN(nsSVGForeignObjectFrame)
   NS_INTERFACE_MAP_ENTRY(nsISVGChildFrame)
 NS_INTERFACE_MAP_END_INHERITING(nsSVGForeignObjectFrameBase)
--- a/layout/svg/base/src/nsSVGGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGGeometryFrame.cpp
@@ -35,158 +35,56 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsPresContext.h"
 #include "nsSVGUtils.h"
 #include "nsSVGGeometryFrame.h"
 #include "nsSVGPaintServerFrame.h"
 #include "nsContentUtils.h"
 #include "gfxContext.h"
-
-//----------------------------------------------------------------------
-// nsISupports methods
-
-NS_INTERFACE_MAP_BEGIN(nsSVGGeometryFrame)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
-NS_INTERFACE_MAP_END_INHERITING(nsSVGGeometryFrameBase)
+#include "nsSVGEffects.h"
 
 //----------------------------------------------------------------------
 // nsIFrame methods
 
-void
-nsSVGGeometryFrame::Destroy()
-{
-  // Remove the properties before the frame goes away, since we need it for QI
-  RemovePaintServerProperties();
-  nsSVGGeometryFrameBase::Destroy();
-}
-
 NS_IMETHODIMP
 nsSVGGeometryFrame::Init(nsIContent* aContent,
                          nsIFrame* aParent,
                          nsIFrame* aPrevInFlow)
 {
   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
-  // point to gradients, etc.  Drop our cached values to those
-  RemovePaintServerProperties();
-
-  return NS_OK;
-}
-
-//----------------------------------------------------------------------
-// nsISVGValueObserver methods:
-
-NS_IMETHODIMP
-nsSVGGeometryFrame::WillModifySVGObservable(nsISVGValue* observable,
-					   nsISVGValue::modificationType aModType)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSVGGeometryFrame::DidModifySVGObservable(nsISVGValue* observable,
-					   nsISVGValue::modificationType aModType)
-{
-  if (!(GetStateBits() & NS_STATE_SVG_PSERVER_MASK))
-    return NS_OK;
-
-  nsIFrame *frame;
-  CallQueryInterface(observable, &frame);
-
-  if (!frame)
-    return NS_OK;
-
-  PRBool refresh = PR_FALSE;
-
-  if (GetStateBits() & NS_STATE_SVG_FILL_PSERVER) {
-    nsIFrame *ps = static_cast<nsIFrame*>(GetProperty(nsGkAtoms::fill));
-    if (frame == ps) {
-      if (aModType == nsISVGValue::mod_die) {
-        DeleteProperty(nsGkAtoms::fill);
-        RemoveStateBits(NS_STATE_SVG_FILL_PSERVER);
-      }
-      refresh = PR_TRUE;
-    }
-  }
-
-  if (GetStateBits() & NS_STATE_SVG_STROKE_PSERVER) {
-    nsIFrame *ps = static_cast<nsIFrame*>(GetProperty(nsGkAtoms::stroke));
-    if (frame == ps) {
-      if (aModType == nsISVGValue::mod_die) {
-        DeleteProperty(nsGkAtoms::stroke);
-        RemoveStateBits(NS_STATE_SVG_STROKE_PSERVER);
-      }
-      refresh = PR_TRUE;
-    }
-  }
-
-  if (refresh) {
-    nsISVGChildFrame* svgFrame = nsnull;
-    CallQueryInterface(this, &svgFrame);
-    if (svgFrame) {
-      nsSVGUtils::UpdateGraphic(svgFrame);
-    }
-  }
-
-  return NS_OK;
-}
-
-
 //----------------------------------------------------------------------
 
-void
-nsSVGGeometryFrame::RemovePaintServerProperties()
-{
-  DeleteProperty(nsGkAtoms::fill);
-  DeleteProperty(nsGkAtoms::stroke);
-  RemoveStateBits(NS_STATE_SVG_PSERVER_MASK);
-}
-
 nsSVGPaintServerFrame *
-nsSVGGeometryFrame::GetPaintServer(const nsStyleSVGPaint *aPaint)
+nsSVGGeometryFrame::GetPaintServer(const nsStyleSVGPaint *aPaint,
+                                   nsIAtom *aType)
 {
   if (aPaint->mType != eStyleSVGPaintType_Server)
     return nsnull;
 
-  nsIURI *uri;
-  uri = aPaint->mPaint.mPaintServer;
-  if (!uri)
+  nsSVGPaintingProperty *property =
+    nsSVGEffects::GetPaintingProperty(aPaint->mPaint.mPaintServer, this, aType);
+  if (!property)
     return nsnull;
-
-  nsIFrame *result;
-  if (NS_FAILED(nsSVGUtils::GetReferencedFrame(&result, uri, mContent,
-                                               PresContext()->PresShell())))
+  nsIFrame *result = property->GetReferencedFrame();
+  if (!result)
     return nsnull;
 
   nsIAtom *type = result->GetType();
   if (type != nsGkAtoms::svgLinearGradientFrame &&
       type != nsGkAtoms::svgRadialGradientFrame &&
       type != nsGkAtoms::svgPatternFrame)
     return nsnull;
 
-  // Loop check for pattern
-  if (type == nsGkAtoms::svgPatternFrame &&
-      nsContentUtils::ContentIsDescendantOf(mContent, result->GetContent()))
-    return nsnull;
-
-  nsSVGPaintServerFrame *server =
-    static_cast<nsSVGPaintServerFrame*>(result);
-
-  server->AddObserver(this);
-  return server;
+  return static_cast<nsSVGPaintServerFrame*>(result);
 }
 
 float
 nsSVGGeometryFrame::GetStrokeWidth()
 {
   nsSVGElement *ctx = static_cast<nsSVGElement*>
                                  (GetType() == nsGkAtoms::svgGlyphFrame ?
                                      mContent->GetParent() : mContent);
@@ -250,70 +148,16 @@ nsSVGGeometryFrame::GetStrokeDashoffset(
 }
 
 PRUint16
 nsSVGGeometryFrame::GetClipRule()
 {
   return GetStyleSVG()->mClipRule;
 }
 
-static void
-PServerPropertyDtor(void *aObject, nsIAtom *aPropertyName,
-                    void *aPropertyValue, void *aData)
-{
-  nsIFrame *ps = static_cast<nsIFrame*>(aPropertyValue);
-  nsSVGUtils::RemoveObserver(static_cast<nsIFrame*>(aObject), ps);
-}
-
-PRBool
-nsSVGGeometryFrame::HasStroke()
-{
-  if (!(GetStateBits() & NS_STATE_SVG_STROKE_PSERVER)) {
-    nsIFrame *ps = GetPaintServer(&GetStyleSVG()->mStroke);
-    if (ps) {
-      SetProperty(nsGkAtoms::stroke, ps, PServerPropertyDtor);
-      AddStateBits(NS_STATE_SVG_STROKE_PSERVER);
-    }
-  }
-
-  // cairo will stop rendering if stroke-width is less than or equal to zero
-  if (GetStrokeWidth() <= 0)
-    return PR_FALSE;
-
-  // Check for eStyleSVGPaintType_Server as the NS_STATE_SVG_STROKE_PSERVER
-  // state bit is only set if we have a valid URL. If we don't, we still have
-  // to stroke although we will be using the fallback colour
-  if (GetStyleSVG()->mStroke.mType == eStyleSVGPaintType_Color ||
-      GetStyleSVG()->mStroke.mType == eStyleSVGPaintType_Server)
-    return PR_TRUE;
-
-  return PR_FALSE;
-}
-
-PRBool
-nsSVGGeometryFrame::HasFill()
-{
-  if (!(GetStateBits() & NS_STATE_SVG_FILL_PSERVER)) {
-    nsIFrame *ps = GetPaintServer(&GetStyleSVG()->mFill);
-    if (ps) {
-      SetProperty(nsGkAtoms::fill, ps, PServerPropertyDtor);
-      AddStateBits(NS_STATE_SVG_FILL_PSERVER);
-    }
-  }
-
-  // Check for eStyleSVGPaintType_Server as the NS_STATE_SVG_FILL_PSERVER
-  // state bit is only set if we have a valid URL. If we don't, we still have
-  // to fill although we will be using the fallback colour
-  if (GetStyleSVG()->mFill.mType == eStyleSVGPaintType_Color ||
-      GetStyleSVG()->mFill.mType == eStyleSVGPaintType_Server)
-    return PR_TRUE;
-
-  return PR_FALSE;
-}
-
 PRBool
 nsSVGGeometryFrame::IsClipChild()
 {
   nsIContent *node = mContent;
 
   do {
     // Return false if we find a non-svg ancestor. Non-SVG elements are not
     // allowed inside an SVG clipPath element.
@@ -352,103 +196,109 @@ nsSVGGeometryFrame::SetupCairoFill(gfxCo
 {
   if (GetStyleSVG()->mFillRule == NS_STYLE_FILL_RULE_EVENODD)
     aContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
   else
     aContext->SetFillRule(gfxContext::FILL_RULE_WINDING);
 
   float opacity = MaybeOptimizeOpacity(GetStyleSVG()->mFillOpacity);
 
-  if (GetStateBits() & NS_STATE_SVG_FILL_PSERVER) {
-    nsSVGPaintServerFrame *ps = static_cast<nsSVGPaintServerFrame*>
-                                           (GetProperty(nsGkAtoms::fill));
-    if (ps->SetupPaintServer(aContext, this, opacity))
-      return PR_TRUE;
+  nsSVGPaintServerFrame *ps =
+    GetPaintServer(&GetStyleSVG()->mFill, nsGkAtoms::fill);
+  if (ps && ps->SetupPaintServer(aContext, this, opacity))
+    return PR_TRUE;
 
-    // On failure, use the fallback colour in case we have an
-    // objectBoundingBox where the width or height of the object is zero.
-    // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
-  }
-
+  // On failure, use the fallback colour in case we have an
+  // objectBoundingBox where the width or height of the object is zero.
+  // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
   if (GetStyleSVG()->mFill.mType == eStyleSVGPaintType_Server) {
     SetupCairoColor(aContext,
                     GetStyleSVG()->mFill.mFallbackColor,
                     opacity);
   } else
     SetupCairoColor(aContext,
                     GetStyleSVG()->mFill.mPaint.mColor,
                     opacity);
 
   return PR_TRUE;
 }
 
-void
+PRBool
 nsSVGGeometryFrame::SetupCairoStrokeGeometry(gfxContext *aContext)
 {
-  aContext->SetLineWidth(GetStrokeWidth());
+  const nsStyleSVG* style = GetStyleSVG();
+  if (style->mStroke.mType == eStyleSVGPaintType_None)
+    return PR_FALSE;
+  
+  float width = GetStrokeWidth();
+  if (width <= 0)
+    return PR_FALSE;
+  aContext->SetLineWidth(width);
 
-  switch (GetStyleSVG()->mStrokeLinecap) {
+  switch (style->mStrokeLinecap) {
   case NS_STYLE_STROKE_LINECAP_BUTT:
     aContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
     break;
   case NS_STYLE_STROKE_LINECAP_ROUND:
     aContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
     break;
   case NS_STYLE_STROKE_LINECAP_SQUARE:
     aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
     break;
   }
 
-  aContext->SetMiterLimit(GetStyleSVG()->mStrokeMiterlimit);
+  aContext->SetMiterLimit(style->mStrokeMiterlimit);
 
-  switch (GetStyleSVG()->mStrokeLinejoin) {
+  switch (style->mStrokeLinejoin) {
   case NS_STYLE_STROKE_LINEJOIN_MITER:
     aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
     break;
   case NS_STYLE_STROKE_LINEJOIN_ROUND:
     aContext->SetLineJoin(gfxContext::LINE_JOIN_ROUND);
     break;
   case NS_STYLE_STROKE_LINEJOIN_BEVEL:
     aContext->SetLineJoin(gfxContext::LINE_JOIN_BEVEL);
     break;
   }
+
+  return PR_TRUE;
 }
 
-void
+PRBool
 nsSVGGeometryFrame::SetupCairoStrokeHitGeometry(gfxContext *aContext)
 {
-  SetupCairoStrokeGeometry(aContext);
+  if (!SetupCairoStrokeGeometry(aContext))
+    return PR_FALSE;
 
   gfxFloat *dashArray;
   PRUint32 count;
   GetStrokeDashArray(&dashArray, &count);
   if (count > 0) {
     aContext->SetDash(dashArray, count, GetStrokeDashoffset());
     delete [] dashArray;
   }
+  return PR_TRUE;
 }
 
 PRBool
 nsSVGGeometryFrame::SetupCairoStroke(gfxContext *aContext)
 {
-  SetupCairoStrokeHitGeometry(aContext);
+  if (!SetupCairoStrokeHitGeometry(aContext))
+    return PR_FALSE;
 
   float opacity = MaybeOptimizeOpacity(GetStyleSVG()->mStrokeOpacity);
 
-  if (GetStateBits() & NS_STATE_SVG_STROKE_PSERVER) {
-    nsSVGPaintServerFrame *ps = static_cast<nsSVGPaintServerFrame*>
-                                           (GetProperty(nsGkAtoms::stroke));
-    if (ps->SetupPaintServer(aContext, this, opacity))
-      return PR_TRUE;
+  nsSVGPaintServerFrame *ps =
+    GetPaintServer(&GetStyleSVG()->mStroke, nsGkAtoms::stroke);
+  if (ps && ps->SetupPaintServer(aContext, this, opacity))
+    return PR_TRUE;
 
-    // On failure, use the fallback colour in case we have an
-    // objectBoundingBox where the width or height of the object is zero.
-    // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
-  }
-
+  // On failure, use the fallback colour in case we have an
+  // objectBoundingBox where the width or height of the object is zero.
+  // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
   if (GetStyleSVG()->mStroke.mType == eStyleSVGPaintType_Server) {
     SetupCairoColor(aContext,
                     GetStyleSVG()->mStroke.mFallbackColor,
                     opacity);
   } else
     SetupCairoColor(aContext,
                     GetStyleSVG()->mStroke.mPaint.mColor,
                     opacity);
--- a/layout/svg/base/src/nsSVGGeometryFrame.h
+++ b/layout/svg/base/src/nsSVGGeometryFrame.h
@@ -33,97 +33,79 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NS_SVGGEOMETRYFRAME_H__
 #define __NS_SVGGEOMETRYFRAME_H__
 
 #include "nsFrame.h"
-#include "nsWeakReference.h"
-#include "nsISVGValueObserver.h"
 
 class nsSVGPaintServerFrame;
 class gfxContext;
 
 typedef nsFrame nsSVGGeometryFrameBase;
 
 /* nsSVGGeometryFrame is a base class for SVG objects that directly
  * have geometry (circle, ellipse, line, polyline, polygon, path, and
  * glyph frames).  It knows how to convert the style information into
  * cairo context information and stores the fill/stroke paint
  * servers. */
 
-class nsSVGGeometryFrame : public nsSVGGeometryFrameBase,
-                           public nsISVGValueObserver
+class nsSVGGeometryFrame : public nsSVGGeometryFrameBase
 {
 protected:
   nsSVGGeometryFrame(nsStyleContext *aContext) : nsSVGGeometryFrameBase(aContext) {}
 
 public:
   // nsIFrame interface:
-  virtual void Destroy();
   NS_IMETHOD Init(nsIContent* aContent,
                   nsIFrame* aParent,
                   nsIFrame* aPrevInFlow);
-  NS_IMETHOD DidSetStyleContext();
 
   virtual PRBool IsFrameOfType(PRUint32 aFlags) const
   {
     return nsSVGGeometryFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
   }
 
-  // nsISupports interface:
-  NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
-
-  // nsISVGValueObserver interface:
-  NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
-                                     nsISVGValue::modificationType aModType);
-  NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable,
-                                    nsISVGValue::modificationType aModType);
-
   // nsSVGGeometryFrame methods:
   NS_IMETHOD GetCanvasTM(nsIDOMSVGMatrix * *aCanvasTM) = 0;
   PRUint16 GetClipRule();
   PRBool IsClipChild(); 
 
   float GetStrokeWidth();
 
-  // Check if this geometry needs to be filled or stroked.  These also
-  // have the side effect of looking up the paint server if that is
-  // the indicated type and storing it in a property, so need to be
-  // called before SetupCairo{Fill,Stroke}.
-  PRBool HasFill();
-  PRBool HasStroke();
-
   /*
    * Set up a cairo context for filling a path
    * @return PR_FALSE to skip rendering
    */
   PRBool SetupCairoFill(gfxContext *aContext);
-
-  // Set up a cairo context for measuring a stroked path
-  void SetupCairoStrokeGeometry(gfxContext *aContext);
-
-  // Set up a cairo context for hit testing a stroked path
-  void SetupCairoStrokeHitGeometry(gfxContext *aContext);
-
+  /*
+   * Set up a cairo context for measuring a stroked path
+   * @return PR_FALSE if there is no stroke
+   */
+  PRBool SetupCairoStrokeGeometry(gfxContext *aContext);
+  /*
+   * Set up a cairo context for hit testing a stroked path
+   * @return PR_FALSE if there is no stroke
+   */
+  PRBool SetupCairoStrokeHitGeometry(gfxContext *aContext);
   /*
    * Set up a cairo context for stroking a path
    * @return PR_FALSE to skip rendering
    */
   PRBool SetupCairoStroke(gfxContext *aContext);
 
 protected:
-  nsSVGPaintServerFrame *GetPaintServer(const nsStyleSVGPaint *aPaint);
+  nsSVGPaintServerFrame *GetPaintServer(const nsStyleSVGPaint *aPaint,
+                                        nsIAtom *aType);
 
 private:
   nsresult GetStrokeDashArray(double **arr, PRUint32 *count);
   float GetStrokeDashoffset();
-  void RemovePaintServerProperties();
 
   // Returns opacity that should be used in rendering this primitive.
   // In the general case the return value is just the passed opacity.
   // If we can avoid the expense of a specified group opacity, we
   // multiply the passed opacity by the value of the 'opacity'
   // property, and elsewhere pretend the 'opacity' property has a
   // value of 1.
   float MaybeOptimizeOpacity(float aOpacity);
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -333,26 +333,26 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderSta
     return NS_OK;
   }
 
   // We are adding patterns or gradients to the context. Save
   // it so we don't leak them into the next object we draw
   gfx->Save();
   SetupGlobalTransform(gfx);
 
-  if (HasFill() && SetupCairoFill(gfx)) {
+  if (SetupCairoFill(gfx)) {
     gfxMatrix matrix = gfx->CurrentMatrix();
     CharacterIterator iter(this, PR_TRUE);
     iter.SetInitialMatrix(gfx);
 
     FillCharacters(&iter, gfx);
     gfx->SetMatrix(matrix);
   }
 
-  if (HasStroke() && SetupCairoStroke(gfx)) {
+  if (SetupCairoStroke(gfx)) {
     // SetupCairoStroke will clear mTextRun whenever
     // there is a pattern or gradient on the text
     CharacterIterator iter(this, PR_TRUE);
     iter.SetInitialMatrix(gfx);
 
     gfx->NewPath();
     AddCharactersToPath(&iter, gfx);
     gfx->Stroke();
@@ -428,21 +428,20 @@ nsSVGGlyphFrame::UpdateCoveredRegion()
 {
   nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
   SetupGlobalTransform(tmpCtx);
   CharacterIterator iter(this, PR_TRUE);
   iter.SetInitialMatrix(tmpCtx);
   
   gfxRect extent;
 
-  if (HasStroke()) {
+  if (SetupCairoStrokeGeometry(tmpCtx)) {
     AddCharactersToPath(&iter, tmpCtx);
-    SetupCairoStrokeGeometry(tmpCtx);
     extent = tmpCtx->UserToDevice(tmpCtx->GetUserStrokeExtent());
-  } else if (HasFill()) {
+  } else if (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None) {
     AddBoundingBoxesToPath(&iter, tmpCtx);
     tmpCtx->IdentityMatrix();
     extent = tmpCtx->GetUserPathExtent();
   } else {
     extent = gfxRect(0, 0, 0, 0);
   }
 
   mRect = nsSVGUtils::ToAppPixelRect(PresContext(), extent);
--- a/layout/svg/base/src/nsSVGGradientFrame.cpp
+++ b/layout/svg/base/src/nsSVGGradientFrame.cpp
@@ -36,155 +36,67 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMSVGAnimatedNumber.h"
 #include "nsIDOMSVGAnimTransformList.h"
 #include "nsSVGTransformList.h"
 #include "nsSVGMatrix.h"
+#include "nsSVGEffects.h"
 #include "nsIDOMSVGStopElement.h"
 #include "nsSVGGradientElement.h"
 #include "nsSVGGeometryFrame.h"
 #include "nsSVGGradientFrame.h"
 #include "gfxContext.h"
 #include "nsIDOMSVGRect.h"
 #include "gfxPattern.h"
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext,
                                        nsIDOMSVGURIReference *aRef) :
   nsSVGGradientFrameBase(aContext),
-  mNextGrad(nsnull), 
   mLoopFlag(PR_FALSE),
-  mInitialized(PR_FALSE) 
+  mNoHRefURI(PR_FALSE)
 {
   if (aRef) {
     // Get the href
     aRef->GetHref(getter_AddRefs(mHref));
   }
 }
 
-nsSVGGradientFrame::~nsSVGGradientFrame()
-{
-  WillModify(mod_die);
-  // Notify the world that we're dying
-  DidModify(mod_die);
-
-  if (mNextGrad) 
-    mNextGrad->RemoveObserver(this);
-}
-
-//----------------------------------------------------------------------
-// nsISupports methods:
-
-NS_INTERFACE_MAP_BEGIN(nsSVGGradientFrame)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-NS_INTERFACE_MAP_END_INHERITING(nsSVGGradientFrameBase)
-
-//----------------------------------------------------------------------
-// nsISVGValueObserver methods:
-NS_IMETHODIMP
-nsSVGGradientFrame::WillModifySVGObservable(nsISVGValue* observable,
-                                            modificationType aModType)
-{
-  // return if we have an mObservers loop
-  if (mLoopFlag) {
-    // XXXjwatt: we should really send an error to the JavaScript Console here:
-    NS_WARNING("gradient reference loop detected while notifying observers!");
-    return NS_OK;
-  }
-
-  // Don't pass on mod_die - our gradient observers would stop observing us!
-  if (aModType == mod_die)
-    aModType = mod_other;
-
-  WillModify(aModType);
-  return NS_OK;
-}
-                                                                                
-NS_IMETHODIMP
-nsSVGGradientFrame::DidModifySVGObservable(nsISVGValue* observable, 
-                                           nsISVGValue::modificationType aModType)
-{
-  // return if we have an mObservers loop
-  if (mLoopFlag) {
-    // XXXjwatt: we should really send an error to the JavaScript Console here:
-    NS_WARNING("gradient reference loop detected while notifying observers!");
-    return NS_OK;
-  }
-
-  // If we reference another gradient and it's going away, null out mNextGrad
-  if (mNextGrad && aModType == nsISVGValue::mod_die) {
-    nsIFrame *gradient = nsnull;
-    CallQueryInterface(observable, &gradient);
-    if (mNextGrad == gradient)
-      mNextGrad = nsnull;
-  }
-
-  // Don't pass on mod_die - our gradient observers would stop observing us!
-  if (aModType == mod_die)
-    aModType = mod_other;
-
-  DidModify(aModType);
-  return NS_OK;
-}
-
 //----------------------------------------------------------------------
 // nsIFrame methods:
 
 NS_IMETHODIMP
 nsSVGGradientFrame::DidSetStyleContext()
 {
-  WillModify();
-  DidModify();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsSVGGradientFrame::RemoveFrame(nsIAtom*        aListName,
-                                nsIFrame*       aOldFrame)
-{
-  WillModify();
-  DidModify();
-  PRBool result = mFrames.DestroyFrame(aOldFrame);
-  return result ? NS_OK : NS_ERROR_FAILURE;
-}
-
-nsIAtom*
-nsSVGGradientFrame::GetType() const
-{
-  return nsGkAtoms::svgGradientFrame;
+  nsSVGEffects::InvalidateRenderingObservers(this);
+  return nsSVGGradientFrameBase::DidSetStyleContext();
 }
 
 NS_IMETHODIMP
 nsSVGGradientFrame::AttributeChanged(PRInt32         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      PRInt32         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::gradientUnits ||
        aAttribute == nsGkAtoms::gradientTransform ||
        aAttribute == nsGkAtoms::spreadMethod)) {
-    WillModify();
-    DidModify();
-    return NS_OK;
-  } 
-
-  if (aNameSpaceID == kNameSpaceID_XLink &&
-      aAttribute == nsGkAtoms::href) {
-    if (mNextGrad)
-      mNextGrad->RemoveObserver(this);
-    WillModify();
-    GetRefedGradientFromHref();
-    DidModify();
-    return NS_OK;
+    nsSVGEffects::InvalidateRenderingObservers(this);
+  } else if (aNameSpaceID == kNameSpaceID_XLink &&
+             aAttribute == nsGkAtoms::href) {
+    // Blow away our reference, if any
+    DeleteProperty(nsGkAtoms::href);
+    mNoHRefURI = PR_FALSE;
+    // And update whoever references us
+    nsSVGEffects::InvalidateRenderingObservers(this);
   }
 
   return nsSVGGradientFrameBase::AttributeChanged(aNameSpaceID,
                                                   aAttribute, aModType);
 }
 
 //----------------------------------------------------------------------
 
@@ -284,44 +196,37 @@ nsSVGGradientFrame::GetGradientTransform
       rect->GetX(&x);
       rect->GetY(&y);
       rect->GetWidth(&width);
       rect->GetHeight(&height);
       bboxMatrix = gfxMatrix(width, 0, 0, height, x, y);
     }
   }
 
-  nsIContent *gradient = GetGradientWithAttr(nsGkAtoms::gradientTransform);
-  if (!gradient)
-    gradient = mContent;  // use our gradient to get the correct default value
+  nsSVGGradientElement *element =
+    GetGradientWithAttr(nsGkAtoms::gradientTransform, mContent);
 
-  nsSVGGradientElement *gradElement = static_cast<nsSVGGradientElement*>
-                                                 (gradient);
   nsCOMPtr<nsIDOMSVGTransformList> trans;
-  gradElement->mGradientTransform->GetAnimVal(getter_AddRefs(trans));
+  element->mGradientTransform->GetAnimVal(getter_AddRefs(trans));
   nsCOMPtr<nsIDOMSVGMatrix> gradientTransform =
     nsSVGTransformList::GetConsolidationMatrix(trans);
 
   if (!gradientTransform)
     return bboxMatrix;
 
   return nsSVGUtils::ConvertSVGMatrixToThebes(gradientTransform) * bboxMatrix;
 }
 
 PRUint16
 nsSVGGradientFrame::GetSpreadMethod()
 {
-  nsIContent *gradient = GetGradientWithAttr(nsGkAtoms::spreadMethod);
-  if (!gradient)
-    gradient = mContent;  // use our gradient to get the correct default value
+  nsSVGGradientElement *element =
+    GetGradientWithAttr(nsGkAtoms::spreadMethod, mContent);
 
-  nsSVGGradientElement *gradElement = static_cast<nsSVGGradientElement*>
-                                                 (gradient);
-
-  return gradElement->mEnumAttributes[nsSVGGradientElement::SPREADMETHOD].GetAnimValue();
+  return element->mEnumAttributes[nsSVGGradientElement::SPREADMETHOD].GetAnimValue();
 }
 
 //----------------------------------------------------------------------
 // nsSVGPaintServerFrame methods:
 
 PRBool
 nsSVGGradientFrame::SetupPaintServer(gfxContext *aContext,
                                      nsSVGGeometryFrame *aSource,
@@ -382,111 +287,107 @@ nsSVGGradientFrame::SetupPaintServer(gfx
 
   aContext->SetPattern(gradient);
 
   return PR_TRUE;
 }
 
 // Private (helper) methods
 
-void
-nsSVGGradientFrame::GetRefedGradientFromHref()
+nsSVGGradientFrame *
+nsSVGGradientFrame::GetReferencedGradient()
 {
-  mNextGrad = nsnull;
-  mInitialized = PR_TRUE;
+  if (mNoHRefURI)
+    return nsnull;
+
+  nsSVGPaintingProperty *property =
+    static_cast<nsSVGPaintingProperty*>(GetProperty(nsGkAtoms::href));
 
-  // Fetch our gradient element's xlink:href attribute
-  nsAutoString href;
-  mHref->GetAnimVal(href);
-  if (href.IsEmpty()) {
-    return; // no URL
+  if (!property) {
+    // Fetch our gradient element's xlink:href attribute
+    nsAutoString href;
+    mHref->GetAnimVal(href);
+    if (href.IsEmpty()) {
+      mNoHRefURI = PR_TRUE;
+      return nsnull; // no URL
+    }
+
+    // Convert href to an nsIURI
+    nsCOMPtr<nsIURI> targetURI;
+    nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
+    nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
+                                              mContent->GetCurrentDoc(), base);
+
+    property = nsSVGEffects::GetPaintingProperty(targetURI, this, nsGkAtoms::href);
+    if (!property)
+      return nsnull;
   }
 
-  // Convert href to an nsIURI
-  nsCOMPtr<nsIURI> targetURI;
-  nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
-  nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
-                                            mContent->GetCurrentDoc(), base);
+  nsIFrame *result = property->GetReferencedFrame();
+  if (!result)
+    return nsnull;
 
-  // Fetch and store a pointer to the referenced gradient element's frame.
-  // Note that we are using *our* frame tree for this call, otherwise we're
-  // going to have to get the PresShell in each call
-  nsIFrame *nextGrad;
-  if (NS_SUCCEEDED(nsSVGUtils::GetReferencedFrame(&nextGrad, targetURI, mContent,
-                                                  PresContext()->PresShell()))) {
-    nsIAtom* frameType = nextGrad->GetType();
-    if (frameType != nsGkAtoms::svgLinearGradientFrame && 
-        frameType != nsGkAtoms::svgRadialGradientFrame)
-      return;
+  nsIAtom* frameType = result->GetType();
+  if (frameType != nsGkAtoms::svgLinearGradientFrame &&
+      frameType != nsGkAtoms::svgRadialGradientFrame)
+    return nsnull;
 
-    mNextGrad = reinterpret_cast<nsSVGGradientFrame*>(nextGrad);
-
-    // Add ourselves to the observer list
-    if (mNextGrad) {
-      // Can't use the NS_ADD macro here because of nsISupports ambiguity
-      mNextGrad->AddObserver(this);
-    }
-  }
+  return static_cast<nsSVGGradientFrame*>(result);
 }
 
-// This is implemented to return nsnull if the attribute is not set so that
-// GetFx and GetFy can use the values of cx and cy instead of the defaults.
-nsIContent*
-nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName)
+nsSVGGradientElement *
+nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
 {
   if (mContent->HasAttr(kNameSpaceID_None, aAttrName))
-    return mContent;
+    return static_cast<nsSVGGradientElement *>(mContent);
 
-  if (!mInitialized)  // make sure mNextGrad has been initialized
-    GetRefedGradientFromHref();
+  nsSVGGradientElement *grad = static_cast<nsSVGGradientElement *>(aDefault);
 
-  if (!mNextGrad)
-    return nsnull;
-
-  nsIContent *grad = nsnull;
+  nsSVGGradientFrame *next = GetReferencedGradient();
+  if (!next)
+    return grad;
 
   // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad
   mLoopFlag = PR_TRUE;
   // XXXjwatt: we should really send an error to the JavaScript Console here:
-  NS_WARN_IF_FALSE(!mNextGrad->mLoopFlag, "gradient reference loop detected "
-                                          "while inheriting attribute!");
-  if (!mNextGrad->mLoopFlag)
-    grad = mNextGrad->GetGradientWithAttr(aAttrName);
+  NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected "
+                                     "while inheriting attribute!");
+  if (!next->mLoopFlag)
+    grad = next->GetGradientWithAttr(aAttrName, aDefault);
   mLoopFlag = PR_FALSE;
 
   return grad;
 }
 
-nsIContent*
-nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType)
+nsSVGGradientElement *
+nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType,
+                                        nsIContent *aDefault)
 {
   if (GetType() == aGradType && mContent->HasAttr(kNameSpaceID_None, aAttrName))
-    return mContent;
+    return static_cast<nsSVGGradientElement *>(mContent);
 
-  if (!mInitialized)
-    GetRefedGradientFromHref();  // make sure mNextGrad has been initialized
+  nsSVGGradientElement *grad = static_cast<nsSVGGradientElement *>(aDefault);
 
-  if (!mNextGrad)
-    return nsnull;
-
-  nsIContent *grad = nsnull;
+  nsSVGGradientFrame *next = GetReferencedGradient();
+  if (!next)
+    return grad;
 
   // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad
   mLoopFlag = PR_TRUE;
   // XXXjwatt: we should really send an error to the JavaScript Console here:
-  NS_WARN_IF_FALSE(!mNextGrad->mLoopFlag, "gradient reference loop detected "
-                                          "while inheriting attribute!");
-  if (!mNextGrad->mLoopFlag)
-    grad = mNextGrad->GetGradientWithAttr(aAttrName, aGradType);
+  NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected "
+                                     "while inheriting attribute!");
+  if (!next->mLoopFlag)
+    grad = next->GetGradientWithAttr(aAttrName, aGradType, aDefault);
   mLoopFlag = PR_FALSE;
 
   return grad;
 }
 
-PRInt32 
+PRInt32
 nsSVGGradientFrame::GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame)
 {
   PRInt32 stopCount = 0;
   nsIFrame *stopFrame = nsnull;
   for (stopFrame = mFrames.FirstChild(); stopFrame;
        stopFrame = stopFrame->GetNextSibling()) {
     if (stopFrame->GetType() == nsGkAtoms::svgStopFrame) {
       // Is this the one we're looking for?
@@ -497,50 +398,43 @@ nsSVGGradientFrame::GetStopFrame(PRInt32
   if (stopCount > 0) {
     if (aStopFrame)
       *aStopFrame = stopFrame;
     return stopCount;
   }
 
   // Our gradient element doesn't have stops - try to "inherit" them
 
-  if (!mInitialized)
-    GetRefedGradientFromHref();  // make sure mNextGrad has been initialized
-
-  if (!mNextGrad) {
+  nsSVGGradientFrame *next = GetReferencedGradient();
+  if (!next) {
     if (aStopFrame)
       *aStopFrame = nsnull;
     return 0;
   }
 
   // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad
   mLoopFlag = PR_TRUE;
   // XXXjwatt: we should really send an error to the JavaScript Console here:
-  NS_WARN_IF_FALSE(!mNextGrad->mLoopFlag, "gradient reference loop detected "
-                                          "while inheriting stop!");
-  if (!mNextGrad->mLoopFlag)
-    stopCount = mNextGrad->GetStopFrame(aIndex, aStopFrame);
+  NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected "
+                                     "while inheriting stop!");
+  if (!next->mLoopFlag)
+    stopCount = next->GetStopFrame(aIndex, aStopFrame);
   mLoopFlag = PR_FALSE;
 
   return stopCount;
 }
 
 PRUint16
 nsSVGGradientFrame::GetGradientUnits()
 {
   // This getter is called every time the others are called - maybe cache it?
 
-  nsIContent *gradient = GetGradientWithAttr(nsGkAtoms::gradientUnits);
-  if (!gradient)
-    gradient = mContent;  // use our gradient to get the correct default value
-
-  nsSVGGradientElement *gradElement = static_cast<nsSVGGradientElement*>
-                                                 (gradient);
-
-  return gradElement->mEnumAttributes[nsSVGGradientElement::GRADIENTUNITS].GetAnimValue();
+  nsSVGGradientElement *element =
+    GetGradientWithAttr(nsGkAtoms::gradientUnits, mContent);
+  return element->mEnumAttributes[nsSVGGradientElement::GRADIENTUNITS].GetAnimValue();
 }
 
 // -------------------------------------------------------------------------
 // Linear Gradients
 // -------------------------------------------------------------------------
 
 nsIAtom*
 nsSVGLinearGradientFrame::GetType() const
@@ -553,37 +447,31 @@ nsSVGLinearGradientFrame::AttributeChang
                                            nsIAtom*        aAttribute,
                                            PRInt32         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::x1 ||
        aAttribute == nsGkAtoms::y1 ||
        aAttribute == nsGkAtoms::x2 ||
        aAttribute == nsGkAtoms::y2)) {
-    WillModify();
-    DidModify();
-    return NS_OK;
+    nsSVGEffects::InvalidateRenderingObservers(this);
   }
 
   return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
                                               aAttribute, aModType);
 }
 
 //----------------------------------------------------------------------
 
 float
 nsSVGLinearGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName,
                                                   PRUint16 aEnumName)
 {
-  nsIContent *gradient = GetLinearGradientWithAttr(aAtomName);
-  if (!gradient)
-    gradient = mContent;  // use our gradient to get the correct default value
-
   nsSVGLinearGradientElement *element =
-    static_cast<nsSVGLinearGradientElement*>(gradient);
+    GetLinearGradientWithAttr(aAtomName, mContent);
 
   // Object bounding box units are handled by setting the appropriate
   // transform in GetGradientTransform, but we need to handle user
   // space units as part of the individual Get* routines.  Fixes 323669.
 
   PRUint16 gradientUnits = GetGradientUnits();
   if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
     return nsSVGUtils::UserSpace(mSourceContent,
@@ -628,45 +516,38 @@ nsSVGRadialGradientFrame::AttributeChang
                                            PRInt32         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::r ||
        aAttribute == nsGkAtoms::cx ||
        aAttribute == nsGkAtoms::cy ||
        aAttribute == nsGkAtoms::fx ||
        aAttribute == nsGkAtoms::fy)) {
-    WillModify();
-    DidModify();
-    return NS_OK;
+    nsSVGEffects::InvalidateRenderingObservers(this);
   }
 
   return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
                                               aAttribute, aModType);
 }
 
 //----------------------------------------------------------------------
 
 float
 nsSVGRadialGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName,
                                                   PRUint16 aEnumName,
-                                                  nsIContent *aElement)
+                                                  nsSVGRadialGradientElement *aElement)
 {
-  nsIContent *gradient;
+  nsSVGRadialGradientElement *element;
 
   if (aElement) {
-    gradient = aElement;
+    element = aElement;
   } else {
-    gradient = GetRadialGradientWithAttr(aAtomName);
-    if (!gradient)
-      gradient = mContent;  // use our gradient to get the correct default value
+    element = GetRadialGradientWithAttr(aAtomName, mContent);
   }
 
-  nsSVGRadialGradientElement *element =
-    static_cast<nsSVGRadialGradientElement*>(gradient);
-
   // Object bounding box units are handled by setting the appropriate
   // transform in GetGradientTransform, but we need to handle user
   // space units as part of the individual Get* routines.  Fixes 323669.
 
   PRUint16 gradientUnits = GetGradientUnits();
   if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
     return nsSVGUtils::UserSpace(mSourceContent,
                                  &element->mLengthAttributes[aEnumName]);
@@ -683,24 +564,24 @@ already_AddRefed<gfxPattern>
 nsSVGRadialGradientFrame::CreateGradient()
 {
   float cx, cy, r, fx, fy;
 
   cx = GradientLookupAttribute(nsGkAtoms::cx, nsSVGRadialGradientElement::CX);
   cy = GradientLookupAttribute(nsGkAtoms::cy, nsSVGRadialGradientElement::CY);
   r  = GradientLookupAttribute(nsGkAtoms::r,  nsSVGRadialGradientElement::R);
 
-  nsIContent *gradient;
+  nsSVGRadialGradientElement *gradient;
 
-  if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fx)))
+  if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fx, nsnull)))
     fx = cx;  // if fx isn't set, we must use cx
   else
     fx = GradientLookupAttribute(nsGkAtoms::fx, nsSVGRadialGradientElement::FX, gradient);
 
-  if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fy)))
+  if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fy, nsnull)))
     fy = cy;  // if fy isn't set, we must use cy
   else
     fy = GradientLookupAttribute(nsGkAtoms::fy, nsSVGRadialGradientElement::FY, gradient);
 
   if (fx != cx || fy != cy) {
     // The focal point (fFx and fFy) must be clamped to be *inside* - not on -
     // the circumference of the gradient or we'll get rendering anomalies. We
     // calculate the distance from the focal point to the gradient center and
@@ -723,41 +604,41 @@ nsSVGRadialGradientFrame::CreateGradient
   NS_IF_ADDREF(pattern);
   return pattern;
 }
 
 // -------------------------------------------------------------------------
 // Public functions
 // -------------------------------------------------------------------------
 
-nsIFrame* 
+nsIFrame*
 NS_NewSVGLinearGradientFrame(nsIPresShell*   aPresShell,
                              nsIContent*     aContent,
                              nsStyleContext* aContext)
 {
   nsCOMPtr<nsIDOMSVGLinearGradientElement> grad = do_QueryInterface(aContent);
   if (!grad) {
     NS_ERROR("Can't create frame! Content is not an SVG linearGradient");
     return nsnull;
   }
-  
+
   nsCOMPtr<nsIDOMSVGURIReference> aRef = do_QueryInterface(aContent);
   NS_ASSERTION(aRef, "NS_NewSVGLinearGradientFrame -- Content doesn't support nsIDOMSVGURIReference");
 
   return new (aPresShell) nsSVGLinearGradientFrame(aContext, aRef);
 }
 
 nsIFrame*
 NS_NewSVGRadialGradientFrame(nsIPresShell*   aPresShell,
                              nsIContent*     aContent,
                              nsStyleContext* aContext)
 {
   nsCOMPtr<nsIDOMSVGRadialGradientElement> grad = do_QueryInterface(aContent);
   if (!grad) {
     NS_ERROR("Can't create frame! Content is not an SVG radialGradient");
     return nsnull;
   }
-  
+
   nsCOMPtr<nsIDOMSVGURIReference> aRef = do_QueryInterface(aContent);
   NS_ASSERTION(aRef, "NS_NewSVGRadialGradientFrame -- Content doesn't support nsIDOMSVGURIReference");
 
   return new (aPresShell) nsSVGRadialGradientFrame(aContext, aRef);
 }
--- a/layout/svg/base/src/nsSVGGradientFrame.h
+++ b/layout/svg/base/src/nsSVGGradientFrame.h
@@ -43,52 +43,36 @@
 #include "nsISVGValueObserver.h"
 #include "nsWeakReference.h"
 #include "nsIDOMSVGAnimatedString.h"
 #include "nsSVGElement.h"
 #include "gfxPattern.h"
 
 class nsIDOMSVGStopElement;
 
-typedef nsSVGPaintServerFrame  nsSVGGradientFrameBase;
+typedef nsSVGPaintServerFrame nsSVGGradientFrameBase;
 
-class nsSVGGradientFrame : public nsSVGGradientFrameBase,
-                           public nsISVGValueObserver
+/**
+ * Gradients can refer to other gradients. We create an nsSVGPaintingProperty
+ * with property type nsGkAtoms::href to track the referenced gradient.
+ */
+class nsSVGGradientFrame : public nsSVGGradientFrameBase
 {
 protected:
   nsSVGGradientFrame(nsStyleContext* aContext,
                      nsIDOMSVGURIReference *aRef);
 
-  virtual ~nsSVGGradientFrame();
-
 public:
   // nsSVGPaintServerFrame methods:
   virtual PRBool SetupPaintServer(gfxContext *aContext,
                                   nsSVGGeometryFrame *aSource,
                                   float aGraphicOpacity);
 
-  // nsISupports interface:
-  NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
-private:
-  NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
-  NS_IMETHOD_(nsrefcnt) Release() { return 1; }
-
-public:
-  // nsISVGValueObserver interface:
-  NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable, 
-                                     nsISVGValue::modificationType aModType);
-  NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable, 
-                                    nsISVGValue::modificationType aModType);
-
   // nsIFrame interface:
   NS_IMETHOD DidSetStyleContext();
-  NS_IMETHOD RemoveFrame(nsIAtom*        aListName,
-                         nsIFrame*       aOldFrame);
-
-  virtual nsIAtom* GetType() const;  // frame type: nsGkAtoms::svgGradientFrame
 
   NS_IMETHOD AttributeChanged(PRInt32         aNameSpaceID,
                               nsIAtom*        aAttribute,
                               PRInt32         aModType);
 
 #ifdef DEBUG
   // nsIFrameDebug interface:
   NS_IMETHOD GetFrameName(nsAString& aResult) const
@@ -97,110 +81,89 @@ public:
   }
 #endif // DEBUG
 
   // nsISVGChildFrame interface:
   NS_IMETHOD PaintSVG(gfxContext* aContext)
   {
     return NS_OK;  // override - our frames don't directly render
   }
-  
+
 private:
 
-  // Helper methods to aid gradient implementation
-  // ---------------------------------------------
-  // The SVG specification allows gradient elements to reference another
-  // gradient element to "inherit" its attributes or gradient stops. Reference
-  // chains of arbitrary length are allowed, and loop checking is essential!
-  // Use the following helpers to safely get attributes and stops.
-
-  // Parse our xlink:href and set mNextGrad if we reference another gradient.
-  void GetRefedGradientFromHref();
+  // Parse our xlink:href and set up our nsSVGPaintingProperty if we
+  // reference another gradient and we don't have a property. Return
+  // the referenced gradient's frame if available, null otherwise.
+  nsSVGGradientFrame* GetReferencedGradient();
 
   // Helpers to look at our gradient and then along its reference chain (if any)
   // to find the first gradient with the specified attribute.
-  nsIContent* GetGradientWithAttr(nsIAtom *aAttrName);
+  // Returns aDefault if no content with that attribute is found
+  nsSVGGradientElement* GetGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault);
 
   // Some attributes are only valid on one type of gradient, and we *must* get
   // the right type or we won't have the data structures we require.
-  nsIContent* GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType);
+  // Returns aDefault if no content with that attribute is found
+  nsSVGGradientElement* GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType,
+                                            nsIContent *aDefault);
 
   // Optionally get a stop frame (returns stop index/count)
   PRInt32 GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame);
 
   PRUint16 GetSpreadMethod();
   PRUint32 GetStopCount();
   void GetStopInformation(PRInt32 aIndex,
                           float *aOffset, nscolor *aColor, float *aStopOpacity);
   gfxMatrix GetGradientTransform(nsSVGGeometryFrame *aSource);
 
 protected:
   virtual already_AddRefed<gfxPattern> CreateGradient() = 0;
 
   // Use these inline methods instead of GetGradientWithAttr(..., aGradType)
-  nsIContent* GetLinearGradientWithAttr(nsIAtom *aAttrName)
+  nsSVGLinearGradientElement* GetLinearGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
   {
-    return GetGradientWithAttr(aAttrName, nsGkAtoms::svgLinearGradientFrame);
-  }
-  nsIContent* GetRadialGradientWithAttr(nsIAtom *aAttrName)
-  {
-    return GetGradientWithAttr(aAttrName, nsGkAtoms::svgRadialGradientFrame);
+    return static_cast<nsSVGLinearGradientElement*>(
+            GetGradientWithAttr(aAttrName, nsGkAtoms::svgLinearGradientFrame, aDefault));
   }
-
-  // We must loop check notifications too: see bug 330387 comment 18 + testcase
-  // and comment 19. The mLoopFlag check is in Will/DidModifySVGObservable.
-  void WillModify(modificationType aModType = mod_other)
+  nsSVGRadialGradientElement* GetRadialGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
   {
-    mLoopFlag = PR_TRUE;
-    nsSVGValue::WillModify(aModType);
-    mLoopFlag = PR_FALSE;
-  }
-  void DidModify(modificationType aModType = mod_other)
-  {
-    mLoopFlag = PR_TRUE;
-    nsSVGValue::DidModify(aModType);
-    mLoopFlag = PR_FALSE;
+    return static_cast<nsSVGRadialGradientElement*>(
+            GetGradientWithAttr(aAttrName, nsGkAtoms::svgRadialGradientFrame, aDefault));
   }
 
   // Get the value of our gradientUnits attribute
   PRUint16 GetGradientUnits();
 
   // The graphic element our gradient is (currently) being applied to
   nsRefPtr<nsSVGElement>                 mSourceContent;
 
 private:
   // href of the other gradient we reference (if any)
+  // XXX this should go away, we can watch our content directly
   nsCOMPtr<nsIDOMSVGAnimatedString>      mHref;
 
-  // Frame of the gradient we reference (if any). Do NOT use this directly.
-  // Use Get[Xxx]GradientWithAttr instead to ensure proper loop checking.
-  nsSVGGradientFrame                    *mNextGrad;
-
   // Flag to mark this frame as "in use" during recursive calls along our
   // gradient's reference chain so we can detect reference loops. See:
   // http://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementHrefAttribute
   PRPackedBool                           mLoopFlag;
-
-  // Ideally we'd set mNextGrad by implementing Init(), but the frame of the
-  // gradient we reference isn't available at that stage. Our only option is to
-  // set mNextGrad lazily in GetGradientWithAttr, and to make that efficient
-  // we need this flag. Our class size is the same since it just fills padding.
-  PRPackedBool                           mInitialized;
+  // Gradients often don't reference other gradients, so here we cache
+  // the fact that that isn't happening.
+  PRPackedBool                           mNoHRefURI;
 };
 
 
 // -------------------------------------------------------------------------
 // Linear Gradients
 // -------------------------------------------------------------------------
 
 typedef nsSVGGradientFrame nsSVGLinearGradientFrameBase;
 
 class nsSVGLinearGradientFrame : public nsSVGLinearGradientFrameBase
 {
-  friend nsIFrame* NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell, 
+  friend nsIFrame* NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell,
                                                 nsIContent*   aContent,
                                                 nsStyleContext* aContext);
 protected:
   nsSVGLinearGradientFrame(nsStyleContext* aContext,
                            nsIDOMSVGURIReference *aRef) :
     nsSVGLinearGradientFrameBase(aContext, aRef) {}
 
 public:
@@ -227,17 +190,17 @@ protected:
 // -------------------------------------------------------------------------
 // Radial Gradients
 // -------------------------------------------------------------------------
 
 typedef nsSVGGradientFrame nsSVGRadialGradientFrameBase;
 
 class nsSVGRadialGradientFrame : public nsSVGRadialGradientFrameBase
 {
-  friend nsIFrame* NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell, 
+  friend nsIFrame* NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell,
                                                 nsIContent*   aContent,
                                                 nsStyleContext* aContext);
 protected:
   nsSVGRadialGradientFrame(nsStyleContext* aContext,
                            nsIDOMSVGURIReference *aRef) :
     nsSVGRadialGradientFrameBase(aContext, aRef) {}
 
 public:
@@ -253,14 +216,14 @@ public:
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGRadialGradient"), aResult);
   }
 #endif // DEBUG
 
 protected:
   float GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 aEnumName,
-                                nsIContent *aElement = nsnull);
+                                nsSVGRadialGradientElement *aElement = nsnull);
   virtual already_AddRefed<gfxPattern> CreateGradient();
 };
 
 #endif // __NS_SVGGRADIENTFRAME_H__
 
--- a/layout/svg/base/src/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/base/src/nsSVGIntegrationUtils.cpp
@@ -39,16 +39,19 @@
 
 #include "nsSVGUtils.h"
 #include "nsSVGEffects.h"
 #include "nsRegion.h"
 #include "nsLayoutUtils.h"
 #include "nsDisplayList.h"
 #include "nsSVGMatrix.h"
 #include "nsSVGFilterPaintCallback.h"
+#include "nsSVGFilterFrame.h"
+#include "nsSVGClipPathFrame.h"
+#include "nsSVGMaskFrame.h"
 
 // ----------------------------------------------------------------------
 
 PRBool
 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
 {
   const nsStyleSVGReset *style = aFrame->GetStyleSVGReset();
   return style->mFilter || style->mClipPath || style->mMask;
@@ -101,23 +104,22 @@ GetSVGBBox(nsIFrame* aNonSVGFrame, nsIFr
   // 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;
+    effectProperties.mFilter->GetFilterFrame() : 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);
@@ -265,22 +267,19 @@ nsSVGIntegrationUtils::PaintFramesWithEf
    *
    * + 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;
+  nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
+  nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
+  nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
 
   PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE;
 
   if (!isOK) {
     // Some resource is missing. We shouldn't paint anything.
     return;
   }
 
--- a/layout/svg/base/src/nsSVGLeafFrame.cpp
+++ b/layout/svg/base/src/nsSVGLeafFrame.cpp
@@ -30,16 +30,17 @@
  * 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 "nsFrame.h"
+#include "nsSVGEffects.h"
 
 class nsSVGLeafFrame : public nsFrame
 {
   friend nsIFrame*
   NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 protected:
   nsSVGLeafFrame(nsStyleContext* aContext) : nsFrame(aContext) {}
 
@@ -51,15 +52,24 @@ public:
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const
   {
     return MakeFrameName(NS_LITERAL_STRING("SVGLeaf"), aResult);
   }
 #endif
 
+  NS_IMETHOD DidSetStyleContext();
 };
 
 nsIFrame*
 NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSVGLeafFrame(aContext);
 }
+
+NS_IMETHODIMP
+nsSVGLeafFrame::DidSetStyleContext()
+{
+  nsresult rv = nsFrame::DidSetStyleContext();
+  nsSVGEffects::InvalidateRenderingObservers(this);
+  return rv;
+}
--- a/layout/svg/base/src/nsSVGPaintServerFrame.cpp
+++ b/layout/svg/base/src/nsSVGPaintServerFrame.cpp
@@ -30,12 +30,8 @@
  * 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 "nsSVGPaintServerFrame.h"
-
-NS_INTERFACE_MAP_BEGIN(nsSVGPaintServerFrame)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValue)
-NS_INTERFACE_MAP_END_INHERITING(nsSVGPaintServerFrameBase)
--- a/layout/svg/base/src/nsSVGPaintServerFrame.h
+++ b/layout/svg/base/src/nsSVGPaintServerFrame.h
@@ -33,39 +33,31 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NS_SVGPAINTSERVERFRAME_H__
 #define __NS_SVGPAINTSERVERFRAME_H__
 
 #include "nsSVGContainerFrame.h"
-#include "nsSVGValue.h"
 
 class gfxContext;
 class nsSVGGeometryFrame;
 
 typedef nsSVGContainerFrame nsSVGPaintServerFrameBase;
 
-class nsSVGPaintServerFrame : public nsSVGPaintServerFrameBase,
-                              public nsSVGValue
+class nsSVGPaintServerFrame : public nsSVGPaintServerFrameBase
 {
 protected:
   nsSVGPaintServerFrame(nsStyleContext* aContext) :
     nsSVGPaintServerFrameBase(aContext) {}
 
 public:
   /*
    * Configure paint server prior to rendering
    * @return PR_FALSE to skip rendering
    */
   virtual PRBool SetupPaintServer(gfxContext *aContext,
                                   nsSVGGeometryFrame *aSource,
                                   float aOpacity) = 0;
-  // nsISupports interface:
-  NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
-
-  // nsISVGValue interface:
-  NS_IMETHOD SetValueString(const nsAString &aValue) { return NS_OK; }
-  NS_IMETHOD GetValueString(nsAString& aValue) { return NS_ERROR_NOT_IMPLEMENTED; }
 };
 
 #endif // __NS_SVGPAINTSERVERFRAME_H__
--- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp
@@ -358,18 +358,18 @@ nsSVGPathGeometryFrame::GetFrameForPoint
 
   if (fillRule == NS_STYLE_FILL_RULE_EVENODD)
     context.SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
   else
     context.SetFillRule(gfxContext::FILL_RULE_WINDING);
 
   if (mask & HITTEST_MASK_FILL)
     isHit = context.PointInFill(userSpacePoint);
-  if (!isHit && (mask & HITTEST_MASK_STROKE)) {
-    SetupCairoStrokeHitGeometry(&context);
+  if (!isHit && (mask & HITTEST_MASK_STROKE) &&
+      SetupCairoStrokeHitGeometry(&context)) {
     isHit = context.PointInStroke(userSpacePoint);
   }
 
   if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
     return this;
 
   return nsnull;
 }
@@ -426,18 +426,17 @@ nsSVGPathGeometryFrame::UpdateCoveredReg
   mRect.Empty();
 
   gfxContext context(nsSVGUtils::GetThebesComputationalSurface());
 
   GeneratePath(&context);
 
   gfxRect extent;
 
-  if (HasStroke()) {
-    SetupCairoStrokeGeometry(&context);
+  if (SetupCairoStrokeGeometry(&context)) {
     extent = context.GetUserStrokeExtent();
     if (!IsDegeneratePath(extent)) {
       extent = context.UserToDevice(extent);
       mRect = nsSVGUtils::ToAppPixelRect(PresContext(),extent);
     }
   } else {
     context.IdentityMatrix();
     extent = context.GetUserPathExtent();
@@ -648,21 +647,21 @@ nsSVGPathGeometryFrame::Render(nsSVGRend
   case NS_STYLE_SHAPE_RENDERING_CRISPEDGES:
     gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
     break;
   default:
     gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
     break;
   }
 
-  if (HasFill() && SetupCairoFill(gfx)) {
+  if (SetupCairoFill(gfx)) {
     gfx->Fill();
   }
 
-  if (HasStroke() && SetupCairoStroke(gfx)) {
+  if (SetupCairoStroke(gfx)) {
     gfx->Stroke();
   }
 
   gfx->NewPath();
 
   gfx->Restore();
 }
 
--- a/layout/svg/base/src/nsSVGPatternFrame.cpp
+++ b/layout/svg/base/src/nsSVGPatternFrame.cpp
@@ -43,16 +43,17 @@
 #include "nsSVGAnimatedPreserveAspectRatio.h"
 #include "nsStyleContext.h"
 #include "nsINameSpaceManager.h"
 #include "nsISVGChildFrame.h"
 #include "nsIDOMSVGRect.h"
 #include "nsSVGMatrix.h"
 #include "nsSVGRect.h"
 #include "nsSVGUtils.h"
+#include "nsSVGEffects.h"
 #include "nsSVGOuterSVGFrame.h"
 #include "nsSVGPatternElement.h"
 #include "nsSVGGeometryFrame.h"
 #include "nsSVGPatternFrame.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxPattern.h"
 
@@ -63,80 +64,33 @@ static void printRect(char *msg, nsIDOMS
 #endif
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext,
                                      nsIDOMSVGURIReference *aRef) :
   nsSVGPatternFrameBase(aContext),
-  mNextPattern(nsnull),
-  mLoopFlag(PR_FALSE)
+  mLoopFlag(PR_FALSE), mPaintLoopFlag(PR_FALSE),
+  mNoHRefURI(PR_FALSE)
 {
   if (aRef) {
     // Get the hRef
     aRef->GetHref(getter_AddRefs(mHref));
   }
 }
 
-nsSVGPatternFrame::~nsSVGPatternFrame()
-{
-  WillModify(mod_die);
-  if (mNextPattern)
-    mNextPattern->RemoveObserver(this);
-
-  // Notify the world that we're dying
-  DidModify(mod_die);
-}
-
-//----------------------------------------------------------------------
-// nsISupports methods:
-
-NS_INTERFACE_MAP_BEGIN(nsSVGPatternFrame)
-  NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-NS_INTERFACE_MAP_END_INHERITING(nsSVGPatternFrameBase)
-
-//----------------------------------------------------------------------
-// nsISVGValueObserver methods:
-NS_IMETHODIMP
-nsSVGPatternFrame::WillModifySVGObservable(nsISVGValue* observable,
-                                            modificationType aModType)
-{
-  WillModify(aModType);
-  return NS_OK;
-}
-                                                                                
-NS_IMETHODIMP
-nsSVGPatternFrame::DidModifySVGObservable(nsISVGValue* observable, 
-                                          nsISVGValue::modificationType aModType)
-{
-  nsIFrame *pattern = nsnull;
-  CallQueryInterface(observable, &pattern);
-  // Is this a pattern we are observing that is going away?
-  if (mNextPattern && aModType == nsISVGValue::mod_die && pattern) {
-    // Yes, we need to handle this differently
-    if (mNextPattern == pattern) {
-      mNextPattern = nsnull;
-    }
-  }
-  // Something we depend on was modified -- pass it on!
-  DidModify(aModType);
-  return NS_OK;
-}
-
 //----------------------------------------------------------------------
 // nsIFrame methods:
 
 NS_IMETHODIMP
 nsSVGPatternFrame::DidSetStyleContext()
 {
-  WillModify();
-  DidModify();
-  return NS_OK;
+  nsSVGEffects::InvalidateRenderingObservers(this);
+  return nsSVGPatternFrameBase::DidSetStyleContext();
 }
 
 NS_IMETHODIMP
 nsSVGPatternFrame::AttributeChanged(PRInt32         aNameSpaceID,
                                     nsIAtom*        aAttribute,
                                     PRInt32         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
@@ -144,29 +98,26 @@ nsSVGPatternFrame::AttributeChanged(PRIn
        aAttribute == nsGkAtoms::patternContentUnits ||
        aAttribute == nsGkAtoms::patternTransform ||
        aAttribute == nsGkAtoms::x ||
        aAttribute == nsGkAtoms::y ||
        aAttribute == nsGkAtoms::width ||
        aAttribute == nsGkAtoms::height ||
        aAttribute == nsGkAtoms::preserveAspectRatio ||
        aAttribute == nsGkAtoms::viewBox)) {
-    WillModify();
-    DidModify();
-    return NS_OK;
-  } 
+    nsSVGEffects::InvalidateRenderingObservers(this);
+  }
 
   if (aNameSpaceID == kNameSpaceID_XLink &&
       aAttribute == nsGkAtoms::href) {
-    if (mNextPattern)
-      mNextPattern->RemoveObserver(this);
-    mNextPattern = nsnull;
-    WillModify();
-    DidModify();
-    return NS_OK;
+    // Blow away our reference, if any
+    DeleteProperty(nsGkAtoms::href);
+    mNoHRefURI = PR_FALSE;
+    // And update whoever references us
+    nsSVGEffects::InvalidateRenderingObservers(this);
   }
 
   return nsSVGPatternFrameBase::AttributeChanged(aNameSpaceID,
                                                  aAttribute, aModType);
 }
 
 nsIAtom*
 nsSVGPatternFrame::GetType() const
@@ -176,48 +127,48 @@ nsSVGPatternFrame::GetType() const
 
 //----------------------------------------------------------------------
 // nsSVGContainerFrame methods:
 
 // If our GetCanvasTM is getting called, we
 // need to return *our current* transformation
 // matrix, which depends on our units parameters
 // and X, Y, Width, and Height
-already_AddRefed<nsIDOMSVGMatrix> 
+already_AddRefed<nsIDOMSVGMatrix>
 nsSVGPatternFrame::GetCanvasTM()
 {
   nsIDOMSVGMatrix *rCTM;
-  
+
   if (mCTM) {
     rCTM = mCTM;
     NS_IF_ADDREF(rCTM);
   } else {
     // Do we know our rendering parent?
     if (mSource) {
       // Yes, use it!
       mSource->GetCanvasTM(&rCTM);
     } else {
       // No, return an identity
       // We get here when geometry in the <pattern> container is updated
       NS_NewSVGMatrix(&rCTM);
     }
   }
-  return rCTM;  
+  return rCTM;
 }
 
 nsresult
 nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
                                 gfxMatrix* patternMatrix,
                                 nsSVGGeometryFrame *aSource,
                                 float aGraphicOpacity)
 {
   /*
    * General approach:
    *    Set the content geometry stuff
-   *    Calculate our bbox (using x,y,width,height & patternUnits & 
+   *    Calculate our bbox (using x,y,width,height & patternUnits &
    *                        patternTransform)
    *    Create the surface
    *    Calculate the content transformation matrix
    *    Get our children (we may need to get them from another Pattern)
    *    Call SVGPaint on all of our children
    *    Return
    */
   *surface = nsnull;
@@ -336,20 +287,27 @@ nsSVGPatternFrame::PaintPattern(gfxASurf
 
   // OK, now render -- note that we use "firstKid", which
   // we got at the beginning because it takes care of the
   // referenced pattern situation for us
 
   // Set our geometrical parent
   mSource = aSource;
 
-  for (nsIFrame* kid = firstKid; kid;
-       kid = kid->GetNextSibling()) {
-    nsSVGUtils::PaintChildWithEffects(&tmpState, nsnull, kid);
+  // Delay checking mPaintLoopFlag until here so we can give back a clear
+  // surface if there's a loop
+  if (!mPaintLoopFlag) {
+    mPaintLoopFlag = PR_TRUE;
+    for (nsIFrame* kid = firstKid; kid;
+         kid = kid->GetNextSibling()) {
+      nsSVGUtils::PaintChildWithEffects(&tmpState, nsnull, kid);
+    }
+    mPaintLoopFlag = PR_FALSE;
   }
+
   mSource = nsnull;
 
   if (aGraphicOpacity != 1.0f) {
     tmpContext->PopGroupToSource();
     tmpContext->Paint(aGraphicOpacity);
     tmpContext->Restore();
   }
 
@@ -360,273 +318,189 @@ nsSVGPatternFrame::PaintPattern(gfxASurf
 
 /* Will probably need something like this... */
 // How do we handle the insertion of a new frame?
 // We really don't want to rerender this every time,
 // do we?
 NS_IMETHODIMP
 nsSVGPatternFrame::GetPatternFirstChild(nsIFrame **kid)
 {
-  nsresult rv = NS_OK;
+  // Do we have any children ourselves?
+  *kid = mFrames.FirstChild();
+  if (*kid)
+    return NS_OK;
+
+  // No, see if we chain to someone who does
+  nsSVGPatternFrame *next = GetReferencedPattern();
 
-  // Do we have any children ourselves?
-  if (!(*kid = mFrames.FirstChild())) {
-    // No, see if we chain to someone who does
-    if (checkURITarget())
-      rv = mNextPattern->GetPatternFirstChild(kid);
-    else
-      rv = NS_ERROR_FAILURE; // No children = error
+  mLoopFlag = PR_TRUE;
+  if (!next || next->mLoopFlag) {
+    mLoopFlag = PR_FALSE;
+    return NS_ERROR_FAILURE;
   }
+
+  nsresult rv = next->GetPatternFirstChild(kid);
   mLoopFlag = PR_FALSE;
   return rv;
 }
 
 PRUint16
 nsSVGPatternFrame::GetPatternUnits()
 {
-  PRUint16 rv;
-
   // See if we need to get the value from another pattern
-  if (!checkURITarget(nsGkAtoms::patternUnits)) {
-    // No, return the values
-    nsSVGPatternElement *patternElement = static_cast<nsSVGPatternElement*>
-                                                     (mContent);
-    rv = patternElement->mEnumAttributes[nsSVGPatternElement::PATTERNUNITS].GetAnimValue();
-  } else {
-    // Yes, get it from the target
-    rv = mNextPattern->GetPatternUnits();
-  }  
-  mLoopFlag = PR_FALSE;
-  return rv;
+  nsSVGPatternElement *patternElement =
+    GetPatternWithAttr(nsGkAtoms::patternUnits, mContent);
+  return patternElement->mEnumAttributes[nsSVGPatternElement::PATTERNUNITS].GetAnimValue();
 }
 
 PRUint16
 nsSVGPatternFrame::GetPatternContentUnits()
 {
-  PRUint16 rv;
-
-  // See if we need to get the value from another pattern
-  if (!checkURITarget(nsGkAtoms::patternContentUnits)) {
-    // No, return the values
-    nsSVGPatternElement *patternElement = static_cast<nsSVGPatternElement*>
-                                                     (mContent);
-    rv = patternElement->mEnumAttributes[nsSVGPatternElement::PATTERNCONTENTUNITS].GetAnimValue();
-  } else {
-    // Yes, get it from the target
-    rv = mNextPattern->GetPatternContentUnits();
-  }  
-  mLoopFlag = PR_FALSE;
-  return rv;
+  nsSVGPatternElement *patternElement =
+    GetPatternWithAttr(nsGkAtoms::patternContentUnits, mContent);
+  return patternElement->mEnumAttributes[nsSVGPatternElement::PATTERNCONTENTUNITS].GetAnimValue();
 }
 
 gfxMatrix
 nsSVGPatternFrame::GetPatternTransform()
 {
+  nsSVGPatternElement *patternElement =
+    GetPatternWithAttr(nsGkAtoms::patternTransform, mContent);
+
   gfxMatrix matrix;
-  // See if we need to get the value from another pattern
-  if (!checkURITarget(nsGkAtoms::patternTransform)) {
-    // No, return the values
-    nsSVGPatternElement *patternElement = static_cast<nsSVGPatternElement*>
-                                                     (mContent);
-    nsCOMPtr<nsIDOMSVGTransformList> lTrans;
-    patternElement->mPatternTransform->GetAnimVal(getter_AddRefs(lTrans));
-    nsCOMPtr<nsIDOMSVGMatrix> patternTransform =
-      nsSVGTransformList::GetConsolidationMatrix(lTrans);
-    if (patternTransform) {
-      matrix = nsSVGUtils::ConvertSVGMatrixToThebes(patternTransform);
-    }
-  } else {
-    // Yes, get it from the target
-    matrix = mNextPattern->GetPatternTransform();
+  nsCOMPtr<nsIDOMSVGTransformList> lTrans;
+  patternElement->mPatternTransform->GetAnimVal(getter_AddRefs(lTrans));
+  nsCOMPtr<nsIDOMSVGMatrix> patternTransform =
+    nsSVGTransformList::GetConsolidationMatrix(lTrans);
+  if (patternTransform) {
+    matrix = nsSVGUtils::ConvertSVGMatrixToThebes(patternTransform);
   }
-  mLoopFlag = PR_FALSE;
-
   return matrix;
 }
 
 NS_IMETHODIMP
 nsSVGPatternFrame::GetViewBox(nsIDOMSVGRect **aViewBox)
 {
-  // See if we need to get the value from another pattern
-  if (!checkURITarget(nsGkAtoms::viewBox)) {
-    // No, return the values
-    nsCOMPtr<nsIDOMSVGFitToViewBox> patternElement = 
-                                            do_QueryInterface(mContent);
-    nsCOMPtr<nsIDOMSVGAnimatedRect> viewBox;
-    patternElement->GetViewBox(getter_AddRefs(viewBox));
-    viewBox->GetAnimVal(aViewBox);
-  } else {
-    // Yes, get it from the target
-    mNextPattern->GetViewBox(aViewBox);
-  }
-  mLoopFlag = PR_FALSE;
-  return NS_OK;
+  nsSVGPatternElement *patternElement =
+    GetPatternWithAttr(nsGkAtoms::viewBox, mContent);
+
+  nsCOMPtr<nsIDOMSVGAnimatedRect> viewBox;
+  patternElement->GetViewBox(getter_AddRefs(viewBox));
+  return viewBox->GetAnimVal(aViewBox);
 }
 
 NS_IMETHODIMP
-nsSVGPatternFrame::GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio 
+nsSVGPatternFrame::GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
                                           **aPreserveAspectRatio)
 {
-  // See if we need to get the value from another pattern
-  if (!checkURITarget(nsGkAtoms::preserveAspectRatio)) {
-    // No, return the values
-    nsCOMPtr<nsIDOMSVGFitToViewBox> patternElement = 
-                                            do_QueryInterface(mContent);
-    patternElement->GetPreserveAspectRatio(aPreserveAspectRatio);
-  } else {
-    // Yes, get it from the target
-    mNextPattern->GetPreserveAspectRatio(aPreserveAspectRatio);
-  }
-  mLoopFlag = PR_FALSE;
-  return NS_OK;
+  nsSVGPatternElement *patternElement =
+    GetPatternWithAttr(nsGkAtoms::preserveAspectRatio, mContent);
+
+  return patternElement->GetPreserveAspectRatio(aPreserveAspectRatio);
 }
 
 nsSVGLength2 *
 nsSVGPatternFrame::GetX()
 {
-  nsSVGLength2 *rv = nsnull;
-
-  // See if we need to get the value from another pattern
-  if (checkURITarget(nsGkAtoms::x)) {
-    // Yes, get it from the target
-    rv = mNextPattern->GetX();
-  } else {
-    // No, return the values
-    nsSVGPatternElement *pattern =
-      static_cast<nsSVGPatternElement*>(mContent);
-    rv = &pattern->mLengthAttributes[nsSVGPatternElement::X];
-  }
-  mLoopFlag = PR_FALSE;
-  return rv;
+  nsSVGPatternElement *pattern = GetPatternWithAttr(nsGkAtoms::x, mContent);
+  return &pattern->mLengthAttributes[nsSVGPatternElement::X];
 }
 
 nsSVGLength2 *
 nsSVGPatternFrame::GetY()
 {
-  nsSVGLength2 *rv = nsnull;
-
-  // See if we need to get the value from another pattern
-  if (checkURITarget(nsGkAtoms::y)) {
-    // Yes, get it from the target
-    rv = mNextPattern->GetY();
-  } else {
-    // No, return the values
-    nsSVGPatternElement *pattern =
-      static_cast<nsSVGPatternElement*>(mContent);
-    rv = &pattern->mLengthAttributes[nsSVGPatternElement::Y];
-  }
-  mLoopFlag = PR_FALSE;
-  return rv;
+  nsSVGPatternElement *pattern = GetPatternWithAttr(nsGkAtoms::y, mContent);
+  return &pattern->mLengthAttributes[nsSVGPatternElement::Y];
 }
 
 nsSVGLength2 *
 nsSVGPatternFrame::GetWidth()
 {
-  nsSVGLength2 *rv = nsnull;
-
-  // See if we need to get the value from another pattern
-  if (checkURITarget(nsGkAtoms::width)) {
-    // Yes, get it from the target
-    rv = mNextPattern->GetWidth();
-  } else {
-    // No, return the values
-    nsSVGPatternElement *pattern =
-      static_cast<nsSVGPatternElement*>(mContent);
-    rv = &pattern->mLengthAttributes[nsSVGPatternElement::WIDTH];
-  }
-  mLoopFlag = PR_FALSE;
-  return rv;
+  nsSVGPatternElement *pattern = GetPatternWithAttr(nsGkAtoms::width, mContent);
+  return &pattern->mLengthAttributes[nsSVGPatternElement::WIDTH];
 }
 
 nsSVGLength2 *
 nsSVGPatternFrame::GetHeight()
 {
-  nsSVGLength2 *rv = nsnull;
-
-  // See if we need to get the value from another pattern
-  if (checkURITarget(nsGkAtoms::height)) {
-    // Yes, get it from the target
-    rv = mNextPattern->GetHeight();
-  } else {
-    // No, return the values
-    nsSVGPatternElement *pattern =
-      static_cast<nsSVGPatternElement*>(mContent);
-    rv = &pattern->mLengthAttributes[nsSVGPatternElement::HEIGHT];
-  }
-  mLoopFlag = PR_FALSE;
-  return rv;
+  nsSVGPatternElement *pattern = GetPatternWithAttr(nsGkAtoms::height, mContent);
+  return &pattern->mLengthAttributes[nsSVGPatternElement::HEIGHT];
 }
 
 // Private (helper) methods
-PRBool 
-nsSVGPatternFrame::checkURITarget(nsIAtom *attr) {
-  // Was the attribute explicitly set?
-  if (mContent->HasAttr(kNameSpaceID_None, attr)) {
-    // Yes, just return
-    return PR_FALSE;
-  }
-  return checkURITarget();
-}
+nsSVGPatternFrame *
+nsSVGPatternFrame::GetReferencedPattern()
+{
+  if (mNoHRefURI)
+    return nsnull;
+
+  nsSVGPaintingProperty *property =
+    static_cast<nsSVGPaintingProperty*>(GetProperty(nsGkAtoms::href));
 
-PRBool
-nsSVGPatternFrame::checkURITarget(void) {
-  nsIFrame *nextPattern;
-  mLoopFlag = PR_TRUE; // Set our loop detection flag
-  // Have we already figured out the next Pattern?
-  if (mNextPattern != nsnull) {
-    return PR_TRUE;
-  }
+  if (!property) {
+    // Fetch our pattern element's xlink:href attribute
+    nsAutoString href;
+    mHref->GetAnimVal(href);
+    if (href.IsEmpty()) {
+      mNoHRefURI = PR_TRUE;
+      return nsnull; // no URL
+    }
 
-  // check if we reference another pattern to "inherit" its children
-  // or attributes
-  nsAutoString href;
-  mHref->GetAnimVal(href);
-  // Do we have URI?
-  if (href.IsEmpty()) {
-    return PR_FALSE; // No, return the default
+    // Convert href to an nsIURI
+    nsCOMPtr<nsIURI> targetURI;
+    nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
+    nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
+                                              mContent->GetCurrentDoc(), base);
+
+    property = nsSVGEffects::GetPaintingProperty(targetURI, this, nsGkAtoms::href);
+    if (!property)
+      return nsnull;
   }
 
-  nsCOMPtr<nsIURI> targetURI;
-  nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
-  nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI),
-    href, mContent->GetCurrentDoc(), base);
+  nsIFrame *result = property->GetReferencedFrame();
+  if (!result)
+    return nsnull;
+
+  nsIAtom* frameType = result->GetType();
+  if (frameType != nsGkAtoms::svgPatternFrame)
+    return nsnull;
+
+  return static_cast<nsSVGPatternFrame*>(result);
+}
+
+nsSVGPatternElement *
+nsSVGPatternFrame::GetPatternWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
+{
+  if (mContent->HasAttr(kNameSpaceID_None, aAttrName))
+    return static_cast<nsSVGPatternElement *>(mContent);
 
-  // Note that we are using *our* frame tree for this call, 
-  // otherwise we're going to have to get the PresShell in each call
-  if (NS_SUCCEEDED(
-          nsSVGUtils::GetReferencedFrame(&nextPattern, targetURI, 
-                                         mContent, 
-                                         PresContext()->PresShell()))) {
-    nsIAtom* frameType = nextPattern->GetType();
-    if (frameType != nsGkAtoms::svgPatternFrame)
-      return PR_FALSE;
-    mNextPattern = (nsSVGPatternFrame *)nextPattern;
-    // Are we looping?
-    if (mNextPattern->mLoopFlag) {
-      // Yes, remove the reference and return an error
-      NS_WARNING("Pattern loop detected!");
-      mNextPattern = nsnull;
-      return PR_FALSE;
-    }
-    // Add ourselves to the observer list
-    if (mNextPattern) {
-      // Can't use the NS_ADD macro here because of nsISupports ambiguity
-      mNextPattern->AddObserver(this);
-    }
-    return PR_TRUE;
-  }
-  return PR_FALSE;
+  nsSVGPatternElement *pattern = static_cast<nsSVGPatternElement *>(aDefault);
+
+  nsSVGPatternFrame *next = GetReferencedPattern();
+  if (!next)
+    return pattern;
+
+  // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad
+  mLoopFlag = PR_TRUE;
+  // XXXjwatt: we should really send an error to the JavaScript Console here:
+  NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected "
+                                     "while inheriting attribute!");
+  if (!next->mLoopFlag)
+    pattern = next->GetPatternWithAttr(aAttrName, aDefault);
+  mLoopFlag = PR_FALSE;
+
+  return pattern;
 }
 
 // -------------------------------------------------------------------------
 // Helper functions
 // -------------------------------------------------------------------------
 
-nsresult 
-nsSVGPatternFrame::GetPatternRect(nsIDOMSVGRect **patternRect, 
+nsresult
+nsSVGPatternFrame::GetPatternRect(nsIDOMSVGRect **patternRect,
                                   nsIDOMSVGRect *bbox,
                                   nsIDOMSVGMatrix *callerCTM,
                                   nsSVGElement *content)
 {
   // Get our type
   PRUint16 type = GetPatternUnits();
 
   // We need to initialize our box
@@ -672,17 +546,17 @@ nsSVGPatternFrame::ConstructCTM(nsIDOMSV
   // this must be handled in the CTM
   PRUint16 type = GetPatternContentUnits();
 
   if (type == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
     // Use the bounding box
     float width, height;
     callerBBox->GetWidth(&width);
     callerBBox->GetHeight(&height);
-    NS_NewSVGMatrix(getter_AddRefs(tCTM), width, 0.0f, 0.0f, 
+    NS_NewSVGMatrix(getter_AddRefs(tCTM), width, 0.0f, 0.0f,
                     height, 0.0f, 0.0f);
   } else {
     float scale = nsSVGUtils::MaxExpansion(callerCTM);
     NS_NewSVGMatrix(getter_AddRefs(tCTM), scale, 0, 0, scale, 0, 0);
   }
 
   // Do we have a viewbox?
   nsCOMPtr<nsIDOMSVGRect> viewRect;
@@ -743,17 +617,17 @@ nsSVGPatternFrame::GetPatternMatrix(nsID
   float scale = 1.0f / nsSVGUtils::MaxExpansion(callerCTM);
   patternTransform.Scale(scale, scale);
   patternTransform.Translate(gfxPoint(minx, miny));
 
   return patternTransform;
 }
 
 nsresult
-nsSVGPatternFrame::GetCallerGeometry(nsIDOMSVGMatrix **aCTM, 
+nsSVGPatternFrame::GetCallerGeometry(nsIDOMSVGMatrix **aCTM,
                                      nsIDOMSVGRect **aBBox,
                                      nsSVGElement **aContent,
                                      nsSVGGeometryFrame *aSource)
 {
   *aCTM = nsnull;
   *aBBox = nsnull;
   *aContent = nsnull;
 
@@ -776,17 +650,17 @@ nsSVGPatternFrame::GetCallerGeometry(nsI
   // will be in *device coordinates*
   nsISVGChildFrame *callerSVGFrame;
   if (callerType == nsGkAtoms::svgGlyphFrame)
     CallQueryInterface(aSource->GetParent(), &callerSVGFrame);
   else
     CallQueryInterface(aSource, &callerSVGFrame);
 
   callerSVGFrame->SetMatrixPropagation(PR_FALSE);
-  callerSVGFrame->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | 
+  callerSVGFrame->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
                                    nsISVGChildFrame::TRANSFORM_CHANGED );
   callerSVGFrame->GetBBox(aBBox);
   callerSVGFrame->SetMatrixPropagation(PR_TRUE);
   callerSVGFrame->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
                                    nsISVGChildFrame::TRANSFORM_CHANGED);
 
   // Sanity check
   PRUint16 type = GetPatternUnits();
@@ -883,17 +757,17 @@ nsIFrame* NS_NewSVGPatternFrame(nsIPresS
 {
   nsCOMPtr<nsIDOMSVGPatternElement> patternElement = do_QueryInterface(aContent);
   if (!patternElement) {
     NS_ERROR("Can't create frame! Content is not an SVG pattern");
     return nsnull;
   }
 
   nsCOMPtr<nsIDOMSVGURIReference> ref = do_QueryInterface(aContent);
-  NS_ASSERTION(ref, 
+  NS_ASSERTION(ref,
                "NS_NewSVGPatternFrame -- Content doesn't support nsIDOMSVGURIReference");
 
 #ifdef DEBUG_scooter
   printf("NS_NewSVGPatternFrame\n");
 #endif
   return new (aPresShell) nsSVGPatternFrame(aContext, ref);
 }
 
@@ -902,27 +776,27 @@ static void printCTM(char *msg, gfxMatri
 {
   printf("%s {%f,%f,%f,%f,%f,%f}\n", msg,
          aCTM.xx, aCTM.yx, aCTM.xy, aCTM.yy, aCTM.x0, aCTM.y0);
 }
 
 static void printCTM(char *msg, nsIDOMSVGMatrix *aCTM)
 {
   float a,b,c,d,e,f;
-  aCTM->GetA(&a); 
-  aCTM->GetB(&b); 
+  aCTM->GetA(&a);
+  aCTM->GetB(&b);
   aCTM->GetC(&c);
-  aCTM->GetD(&d); 
-  aCTM->GetE(&e); 
+  aCTM->GetD(&d);
+  aCTM->GetE(&e);
   aCTM->GetF(&f);
   printf("%s {%f,%f,%f,%f,%f,%f}\n",msg,a,b,c,d,e,f);
 }
 
 static void printRect(char *msg, nsIDOMSVGRect *aRect)
 {
   float x,y,width,height;
-  aRect->GetX(&x); 
-  aRect->GetY(&y); 
-  aRect->GetWidth(&width); 
-  aRect->GetHeight(&height); 
+  aRect->GetX(&x);
+  aRect->GetY(&y);
+  aRect->GetWidth(&width);
+  aRect->GetHeight(&height);
   printf("%s {%f,%f,%f,%f}\n",msg,x,y,width,height);
 }
 #endif
--- a/layout/svg/base/src/nsSVGPatternFrame.h
+++ b/layout/svg/base/src/nsSVGPatternFrame.h
@@ -34,65 +34,54 @@
  * 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_SVGPATTERNFRAME_H__
 #define __NS_SVGPATTERNFRAME_H__
 
-#include "nsISVGValueObserver.h"
-#include "nsWeakReference.h"
 #include "nsIDOMSVGAnimatedString.h"
 #include "nsIDOMSVGMatrix.h"
 #include "nsSVGPaintServerFrame.h"
 #include "gfxMatrix.h"
 
 class nsIDOMSVGAnimatedPreserveAspectRatio;
 class nsIFrame;
 class nsSVGLength2;
 class nsSVGElement;
 class gfxContext;
 class gfxASurface;
 
 typedef nsSVGPaintServerFrame  nsSVGPatternFrameBase;
 
-class nsSVGPatternFrame : public nsSVGPatternFrameBase,
-                          public nsISVGValueObserver
+/**
+ * Patterns can refer to other patterns. We create an nsSVGPaintingProperty
+ * with property type nsGkAtoms::href to track the referenced pattern.
+ */
+class nsSVGPatternFrame : public nsSVGPatternFrameBase
 {
 public:
-  friend nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell, 
+  friend nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
                                          nsIContent*   aContent,
                                          nsStyleContext* aContext);
 
   nsSVGPatternFrame(nsStyleContext* aContext) : nsSVGPatternFrameBase(aContext) {}
 
   nsresult PaintPattern(gfxASurface **surface,
                         gfxMatrix *patternMatrix,
                         nsSVGGeometryFrame *aSource,
                         float aGraphicOpacity);
 
   // nsSVGPaintServerFrame methods:
   virtual PRBool SetupPaintServer(gfxContext *aContext,
                                   nsSVGGeometryFrame *aSource,
                                   float aGraphicOpacity);
 
-  // nsISupports interface:
-  NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
-private:
-  NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
-  NS_IMETHOD_(nsrefcnt) Release() { return 1; }
-
 public:
-  // nsISVGValueObserver interface:
-  NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable, 
-                                     nsISVGValue::modificationType aModType);
-  NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable, 
-                                    nsISVGValue::modificationType aModType);
-  
   // nsSVGContainerFrame methods:
   virtual already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
 
   // nsIFrame interface:
   NS_IMETHOD DidSetStyleContext();
 
   NS_IMETHOD AttributeChanged(PRInt32         aNameSpaceID,
                               nsIAtom*        aAttribute,
@@ -112,57 +101,62 @@ public:
     return MakeFrameName(NS_LITERAL_STRING("SVGPattern"), aResult);
   }
 #endif // DEBUG
 
 protected:
   nsSVGPatternFrame(nsStyleContext* aContext,
                     nsIDOMSVGURIReference *aRef);
 
-  virtual ~nsSVGPatternFrame();
+  // Internal methods for handling referenced patterns
+  nsSVGPatternFrame* GetReferencedPattern();
+  // Helper to look at our pattern and then along its reference chain (if any)
+  // to find the first pattern with the specified attribute. Returns
+  // null if there isn't one.
+  nsSVGPatternElement* GetPatternWithAttr(nsIAtom *aAttrName, nsIContent *aDefault);
 
-  // Internal methods for handling referenced patterns
-  PRBool checkURITarget(nsIAtom *);
-  PRBool checkURITarget();
   //
   nsSVGLength2 *GetX();
   nsSVGLength2 *GetY();
   nsSVGLength2 *GetWidth();
   nsSVGLength2 *GetHeight();
 
   PRUint16 GetPatternUnits();
   PRUint16 GetPatternContentUnits();
   gfxMatrix GetPatternTransform();
 
-  NS_IMETHOD GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio 
+  NS_IMETHOD GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
                                                      **aPreserveAspectRatio);
   NS_IMETHOD GetPatternFirstChild(nsIFrame **kid);
   NS_IMETHOD GetViewBox(nsIDOMSVGRect * *aMatrix);
   nsresult   GetPatternRect(nsIDOMSVGRect **patternRect,
                             nsIDOMSVGRect *bbox,
                             nsIDOMSVGMatrix *callerCTM,
                             nsSVGElement *content);
   gfxMatrix  GetPatternMatrix(nsIDOMSVGRect *bbox,
                               nsIDOMSVGRect *callerBBox,
                               nsIDOMSVGMatrix *callerCTM);
   nsresult   ConstructCTM(nsIDOMSVGMatrix **ctm,
                           nsIDOMSVGRect *callerBBox,
                           nsIDOMSVGMatrix *callerCTM);
-  nsresult   GetCallerGeometry(nsIDOMSVGMatrix **aCTM, 
+  nsresult   GetCallerGeometry(nsIDOMSVGMatrix **aCTM,
                                nsIDOMSVGRect **aBBox,
-                               nsSVGElement **aContent, 
+                               nsSVGElement **aContent,
                                nsSVGGeometryFrame *aSource);
 
 private:
   // this is a *temporary* reference to the frame of the element currently
   // referencing our pattern.  This must be temporary because different
   // referencing frames will all reference this one frame
-  nsSVGGeometryFrame                     *mSource;
-  nsCOMPtr<nsIDOMSVGMatrix>               mCTM;
+  nsSVGGeometryFrame               *mSource;
+  nsCOMPtr<nsIDOMSVGMatrix>         mCTM;
 
 protected:
-  nsSVGPatternFrame                      *mNextPattern;
-  nsCOMPtr<nsIDOMSVGAnimatedString> 	  mHref;
-  PRPackedBool                            mLoopFlag;
+  nsCOMPtr<nsIDOMSVGAnimatedString> mHref;
+  // This flag is used to detect loops in xlink:href processing
+  PRPackedBool                      mLoopFlag;
+  // This flag is used to detect loops when painting this pattern
+  // ends up recursively painting itself
+  PRPackedBool                      mPaintLoopFlag;
+  PRPackedBool                      mNoHRefURI;
 };
 
 #endif
-
--- a/layout/svg/base/src/nsSVGStopFrame.cpp
+++ b/layout/svg/base/src/nsSVGStopFrame.cpp
@@ -35,17 +35,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMSVGStopElement.h"
 #include "nsStyleContext.h"
 #include "nsFrame.h"
 #include "nsGkAtoms.h"
-#include "nsISVGValue.h"
+#include "nsSVGEffects.h"
 
 // This is a very simple frame whose only purpose is to capture style change
 // events and propagate them to the parent.  Most of the heavy lifting is done
 // within the nsSVGGradientFrame, which is the parent for this frame
 
 typedef nsFrame  nsSVGStopFrameBase;
 
 class nsSVGStopFrame : public nsSVGStopFrameBase
@@ -89,50 +89,36 @@ public:
 // Implementation
 
 //----------------------------------------------------------------------
 // nsIFrame methods:
 
 NS_IMETHODIMP
 nsSVGStopFrame::DidSetStyleContext()
 {
-  // Tell our parent
-  if (mParent)
-    mParent->DidSetStyleContext();
+  nsSVGStopFrameBase::DidSetStyleContext();
+  nsSVGEffects::InvalidateRenderingObservers(this);
   return NS_OK;
 }
 
 nsIAtom *
 nsSVGStopFrame::GetType() const
 {
   return nsGkAtoms::svgStopFrame;
 }
 
 NS_IMETHODIMP
 nsSVGStopFrame::AttributeChanged(PRInt32         aNameSpaceID,
                                  nsIAtom*        aAttribute,
                                  PRInt32         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       aAttribute == nsGkAtoms::offset) {
-
-    // Need to tell our parent gradients that something happened.
-    // Calling {Begin,End}Update on an nsISVGValue, which
-    // nsSVGGradientFrame implements, causes its observers (the
-    // referencing graphics frames) to be notified.
-    if (mParent) {
-      nsISVGValue *svgParent;
-      CallQueryInterface(mParent, &svgParent);
-      if (svgParent) {
-        svgParent->BeginBatchUpdate();
-        svgParent->EndBatchUpdate();
-      }
-    }
-    return NS_OK;
-  } 
+    nsSVGEffects::InvalidateRenderingObservers(this);
+  }
 
   return nsSVGStopFrameBase::AttributeChanged(aNameSpaceID,
                                               aAttribute, aModType);
 }
 
 // -------------------------------------------------------------------------
 // Public functions
 // -------------------------------------------------------------------------
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -589,17 +589,17 @@ nsSVGUtils::FindFilterInvalidation(nsIFr
   rect.ScaleRoundOutInverse(appUnitsPerDevPixel);
 
   while (aFrame) {
     if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
       break;
 
     nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
     if (property) {
-      nsSVGFilterFrame *filter = property->GetFilterFrame(nsnull);
+      nsSVGFilterFrame *filter = property->GetFilterFrame();
       if (filter) {
         rect = filter->GetInvalidationBBox(aFrame, rect);
       }
     }
     aFrame = aFrame->GetParent();
   }
 
   rect.ScaleRoundOut(appUnitsPerDevPixel);
@@ -617,16 +617,18 @@ nsSVGUtils::UpdateFilterRegion(nsIFrame 
 }
 
 void
 nsSVGUtils::UpdateGraphic(nsISVGChildFrame *aSVGFrame)
 {
   nsIFrame *frame;
   CallQueryInterface(aSVGFrame, &frame);
 
+  nsSVGEffects::InvalidateRenderingObservers(frame);
+
   if (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
     return;
 
   nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(frame);
   if (!outerSVGFrame) {
     NS_ERROR("null outerSVGFrame");
     return;
   }
@@ -980,18 +982,17 @@ nsSVGUtils::PaintChildWithEffects(nsSVGR
 
   /* 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);
 
   PRBool isOK = PR_TRUE;
-  nsSVGFilterFrame *filterFrame = effectProperties.mFilter ?
-    effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull;
+  nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
 
   /* 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()) {
     if (filterFrame) {
       if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull)))
@@ -1019,20 +1020,18 @@ nsSVGUtils::PaintChildWithEffects(nsSVGR
    */
 
   if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
     opacity = 1.0f;
 
   gfxContext *gfx = aContext->GetGfxContext();
   PRBool complexEffects = PR_FALSE;
 
-  nsSVGClipPathFrame *clipPathFrame = effectProperties.mClipPath ?
-    effectProperties.mClipPath->GetClipPathFrame(&isOK) : nsnull;
-  nsSVGMaskFrame *maskFrame = effectProperties.mMask ?
-    effectProperties.mMask->GetMaskFrame(&isOK) : nsnull;
+  nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
+  nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
 
   PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE;
 
   if (!isOK) {
     // Some resource is missing. We shouldn't paint anything.
     return;
   }
   
@@ -1100,33 +1099,25 @@ nsSVGUtils::PaintChildWithEffects(nsSVGR
     gfx->Mask(maskSurface);
   } else if (opacity != 1.0f) {
     gfx->Paint(opacity);
   }
 
   gfx->Restore();
 }
 
-void
-nsSVGUtils::UpdateEffects(nsIFrame *aFrame)
-{
-  aFrame->DeleteProperty(nsGkAtoms::filter);
-  aFrame->DeleteProperty(nsGkAtoms::mask);
-  aFrame->DeleteProperty(nsGkAtoms::clipPath);
-}
-
 PRBool
 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
 {
   nsSVGEffects::EffectProperties props =
     nsSVGEffects::GetEffectProperties(aFrame);
   if (!props.mClipPath)
     return PR_TRUE;
 
-  nsSVGClipPathFrame *clipPathFrame = props.mClipPath->GetClipPathFrame(nsnull);
+  nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(nsnull);
   if (!clipPathFrame) {
     // clipPath is not a valid resource, so nothing gets painted, so
     // hit-testing must fail.
     return PR_FALSE;
   }
 
   nsCOMPtr<nsIDOMSVGMatrix> matrix = GetCanvasTM(aFrame);
   return clipPathFrame->ClipHitTest(aFrame, matrix, aPoint);
@@ -1430,18 +1421,19 @@ nsSVGUtils::GetRelativeRect(PRUint16 aUn
 PRBool
 nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
 {
   if (!aFrame->GetStyleSVGReset()->mFilter) {
     nsIAtom *type = aFrame->GetType();
     if (type == nsGkAtoms::svgImageFrame)
       return PR_TRUE;
     if (type == nsGkAtoms::svgPathGeometryFrame) {
-      nsSVGGeometryFrame *geom = static_cast<nsSVGGeometryFrame*>(aFrame);
-      if (!(geom->HasFill() && geom->HasStroke()))
+      const nsStyleSVG *style = aFrame->GetStyleSVG();
+      if (style->mFill.mType == eStyleSVGPaintType_None &&
+          style->mStroke.mType == eStyleSVGPaintType_None)
         return PR_TRUE;
     }
   }
   return PR_FALSE;
 }
 
 float
 nsSVGUtils::MaxExpansion(nsIDOMSVGMatrix *aMatrix)
--- a/layout/svg/base/src/nsSVGUtils.h
+++ b/layout/svg/base/src/nsSVGUtils.h
@@ -85,27 +85,20 @@ class nsISVGChildFrame;
 
 // SVG Frame state bits
 #define NS_STATE_IS_OUTER_SVG         0x00100000
 
 #define NS_STATE_SVG_HAS_MARKERS      0x00200000
 
 #define NS_STATE_SVG_DIRTY            0x00400000
 
-/* Do we have a paint server for fill with a valid URL? */
-#define NS_STATE_SVG_FILL_PSERVER     0x00800000
-/* 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 0x00800000
 
-/* are we the child of a non-display container? */
-#define NS_STATE_SVG_NONDISPLAY_CHILD 0x02000000
-
-#define NS_STATE_SVG_PROPAGATE_TRANSFORM 0x04000000
+#define NS_STATE_SVG_PROPAGATE_TRANSFORM 0x01000000
 
 /**
  * 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
@@ -348,23 +341,16 @@ public:
 
   /* 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
-   */
-  static void
-  UpdateEffects(nsIFrame *aFrame);
-
   /* Hit testing - check if point hits the clipPath of indicated
    * frame.  Returns true if no clipPath set. */
   static PRBool
   HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint);
   
   /* Hit testing - check if point hits any children of frame. */
 
   static nsIFrame *