From 09cfc5fa8ccfdab2af580e2916498d34e58adfe8 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Wed, 1 Oct 2008 13:51:05 +1300 Subject: [PATCH] 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 | 4 +- layout/base/nsIPresShell.h | 9 +- layout/base/nsLayoutUtils.cpp | 1 - layout/generic/nsFrame.cpp | 28 +- layout/generic/nsIFrame.h | 27 +- layout/reftests/svg/dynamic-clipPath-01.svg | 2 +- layout/reftests/svg/reftest.list | 7 + layout/style/nsStyleContext.cpp | 4 +- layout/style/nsStyleStruct.cpp | 67 ++- layout/svg/base/src/Makefile.in | 5 +- layout/svg/base/src/nsSVGClipPathFrame.h | 5 +- layout/svg/base/src/nsSVGContainerFrame.h | 3 +- layout/svg/base/src/nsSVGEffects.cpp | 353 ++++++++++----- layout/svg/base/src/nsSVGEffects.h | 189 +++++--- .../svg/base/src/nsSVGForeignObjectFrame.cpp | 3 +- layout/svg/base/src/nsSVGGeometryFrame.cpp | 236 ++-------- layout/svg/base/src/nsSVGGeometryFrame.h | 44 +- layout/svg/base/src/nsSVGGlyphFrame.cpp | 9 +- layout/svg/base/src/nsSVGGradientFrame.cpp | 315 +++++-------- layout/svg/base/src/nsSVGGradientFrame.h | 95 ++-- layout/svg/base/src/nsSVGIntegrationUtils.cpp | 15 +- layout/svg/base/src/nsSVGLeafFrame.cpp | 10 + layout/svg/base/src/nsSVGPaintServerFrame.cpp | 4 - layout/svg/base/src/nsSVGPaintServerFrame.h | 10 +- .../svg/base/src/nsSVGPathGeometryFrame.cpp | 11 +- layout/svg/base/src/nsSVGPatternFrame.cpp | 414 ++++++------------ layout/svg/base/src/nsSVGPatternFrame.h | 54 +-- layout/svg/base/src/nsSVGStopFrame.cpp | 24 +- layout/svg/base/src/nsSVGUtils.cpp | 28 +- layout/svg/base/src/nsSVGUtils.h | 18 +- 30 files changed, 889 insertions(+), 1105 deletions(-) diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 3c28319a91f..a9876e5dae7 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -148,7 +148,7 @@ #include "nsMathMLParts.h" #endif #ifdef MOZ_SVG -#include "nsSVGUtils.h" +#include "nsSVGEffects.h" #endif nsIFrame* @@ -9826,7 +9826,7 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList) 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) { diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index af3b5723289..a01cc85ad45 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -102,11 +102,10 @@ 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 @@ -165,6 +164,8 @@ public: * 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). diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 6c817e739a7..9a7fceb8d77 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -2629,7 +2629,6 @@ nsLayoutUtils::CalculateContentBottom(nsIFrame* aFrame) childList = aFrame->GetAdditionalChildListName(nextListID); nextListID++; } while (childList); - } return contentBottom; diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 7cf7208e3ef..cfc5e4d6daa 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -122,6 +122,7 @@ #ifdef MOZ_SVG #include "nsSVGIntegrationUtils.h" +#include "nsSVGEffects.h" #endif #include "gfxContext.h" @@ -430,7 +431,8 @@ nsFrame::Init(nsIContent* aContent, // 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(); @@ -442,7 +444,7 @@ nsFrame::Init(nsIContent* aContent, 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(); @@ -495,6 +497,10 @@ nsFrame::RemoveFrame(nsIAtom* aListName, 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(); @@ -675,7 +681,7 @@ nsIFrame::GetPaddingRect() const PRBool nsIFrame::IsTransformed() const { - return (mState & NS_FRAME_MAY_BE_TRANSFORMED) && + return (mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) && GetStyleDisplay()->HasTransform(); } @@ -1198,7 +1204,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, /* 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) { @@ -1318,7 +1325,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, /* 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; @@ -1468,7 +1476,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // 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) @@ -3684,7 +3692,7 @@ nsIFrame::InvalidateInternalAfterResize(const nsRect& aDamageRect, nscoord aX, * * 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 @@ -3762,7 +3770,7 @@ nsIFrame::GetTransformMatrix(nsIFrame **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) @@ -3932,7 +3940,7 @@ nsIFrame::GetOverflowRectRelativeToParent() const 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 @@ -5632,7 +5640,7 @@ nsIFrame::FinishAndStoreOverflow(nsRect* aOverflowArea, nsSize aNewSize) *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); diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index e50f3e5f489..79e7b7259e4 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -105,10 +105,10 @@ 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 @@ -236,7 +236,9 @@ enum { // 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, @@ -1582,22 +1584,23 @@ public: 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 \n\n
// 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 diff --git a/layout/reftests/svg/dynamic-clipPath-01.svg b/layout/reftests/svg/dynamic-clipPath-01.svg index bac72256bfe..b256f9c4f6b 100644 --- a/layout/reftests/svg/dynamic-clipPath-01.svg +++ b/layout/reftests/svg/dynamic-clipPath-01.svg @@ -4,7 +4,7 @@ --> Testing that dynamic changes to the element for a given ID are reflected in clip-path diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index b9362c01213..e1f07bd558c 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -19,7 +19,14 @@ include moz-only/reftest.list == 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 diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index 5d6c16913e0..2ff9b18220f 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -450,6 +450,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther) #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 @@ -475,9 +476,6 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther) // 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 diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 013d8501f6e..121b618e55c 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -803,12 +803,39 @@ nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource) 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); - !EqualURIs(mMarkerEnd, aOther.mMarkerEnd) || + if (mTextRendering != aOther.mTextRendering) { + NS_UpdateHint(hint, nsChangeHint_RepaintFrame); + // May be needed for non-svg frames + NS_UpdateHint(hint, nsChangeHint_ReflowFrame); + } + + 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) || @@ -823,28 +850,32 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const 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 mClipParentMatrix; + // recursion prevention flag + PRPackedBool mInUse; // nsSVGContainerFrame methods: virtual already_AddRefed GetCanvasTM(); - - // recursion prevention flag - PRPackedBool mInUse; }; #endif diff --git a/layout/svg/base/src/nsSVGContainerFrame.h b/layout/svg/base/src/nsSVGContainerFrame.h index 6871f962739..4579cb12392 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.h +++ b/layout/svg/base/src/nsSVGContainerFrame.h @@ -71,7 +71,8 @@ public: virtual PRBool IsFrameOfType(PRUint32 aFlags) const { - return nsSVGContainerFrameBase::IsFrameOfType(aFlags & ~(nsIFrame::eSVG)); + return nsSVGContainerFrameBase::IsFrameOfType( + aFlags & ~(nsIFrame::eSVG | nsIFrame::eSVGContainer)); } }; diff --git a/layout/svg/base/src/nsSVGEffects.cpp b/layout/svg/base/src/nsSVGEffects.cpp index 8d639bfa427..fc17faa0faf 100644 --- a/layout/svg/base/src/nsSVGEffects.cpp +++ b/layout/svg/base/src/nsSVGEffects.cpp @@ -38,12 +38,18 @@ #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); @@ -52,22 +58,45 @@ nsSVGPropertyBase::nsSVGPropertyBase(nsIURI *aURI, } } -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(mElement.get())->GetPrimaryFrame() == mReferencedFrame, + "Cached frame is incorrect!"); + return mReferencedFrame; + } + if (mElement.get()) { nsIFrame *frame = static_cast(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; } @@ -75,57 +104,93 @@ nsSVGPropertyBase::GetReferencedFrame(nsIAtom* aFrameType, PRBool* aOK) } 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 +nsSVGRenderingObserver::AttributeChanged(nsIDocument *aDocument, + nsIContent *aContent, + PRInt32 aNameSpaceID, + nsIAtom *aAttribute, + PRInt32 aModType, + PRUint32 aStateMask) { DoUpdate(); } void -nsSVGPropertyBase::ContentAppended(nsIDocument *aDocument, - nsIContent *aContainer, - PRInt32 aNewIndexInContainer) +nsSVGRenderingObserver::ContentAppended(nsIDocument *aDocument, + nsIContent *aContainer, + PRInt32 aNewIndexInContainer) { DoUpdate(); } void -nsSVGPropertyBase::ContentInserted(nsIDocument *aDocument, - nsIContent *aContainer, - nsIContent *aChild, - PRInt32 aIndexInContainer) +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 + (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()); @@ -134,96 +199,69 @@ nsSVGFilterProperty::UpdateRect() } } +static void +InvalidateAllContinuations(nsIFrame* aFrame) +{ + for (nsIFrame* f = aFrame; f; f = f->GetNextContinuation()) { + f->InvalidateOverflowRect(); + } +} + void nsSVGFilterProperty::DoUpdate() { - nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(mFrame); - if (outerSVGFrame) { - outerSVGFrame->Invalidate(mFilterRect); - UpdateRect(); - outerSVGFrame->Invalidate(mFilterRect); - } -} - -void -nsSVGFilterProperty::ParentChainChanged(nsIContent *aContent) -{ - 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(aFrame->GetProperty(aProp)); + nsSVGRenderingObserver *prop = + static_cast(aFrame->GetProperty(aProp)); if (prop) return prop; prop = aCreate(aURI, aFrame); @@ -236,6 +274,13 @@ GetEffectProperty(nsIURI *aURI, nsIFrame *aFrame, nsIAtom *aProp, return prop; } +nsSVGPaintingProperty * +nsSVGEffects::GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, nsIAtom *aProp) +{ + return static_cast( + GetEffectProperty(aURI, aFrame, aProp, CreatePaintingProperty)); +} + nsSVGEffects::EffectProperties nsSVGEffects::GetEffectProperties(nsIFrame *aFrame) { @@ -245,19 +290,36 @@ nsSVGEffects::GetEffectProperties(nsIFrame *aFrame) const nsStyleSVGReset *style = aFrame->GetStyleSVGReset(); result.mFilter = static_cast (GetEffectProperty(style->mFilter, aFrame, nsGkAtoms::filter, CreateFilterProperty)); - result.mClipPath = static_cast - (GetEffectProperty(style->mClipPath, aFrame, nsGkAtoms::clipPath, CreateClipPathProperty)); - result.mMask = static_cast - (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 + (mClipPath->GetReferencedFrame(nsGkAtoms::svgClipPathFrame, aOK)); +} + +nsSVGMaskFrame * +nsSVGEffects::EffectProperties::GetMaskFrame(PRBool *aOK) { + if (!mMask) + return nsnull; + return static_cast + (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 * @@ -270,3 +332,98 @@ nsSVGEffects::GetFilterProperty(nsIFrame *aFrame) return static_cast(aFrame->GetProperty(nsGkAtoms::filter)); } + +static PLDHashOperator PR_CALLBACK +GatherEnumerator(nsVoidPtrHashKey* aEntry, void* aArg) +{ + nsTArray* array = + static_cast*>(aArg); + array->AppendElement(static_cast( + const_cast(aEntry->GetKey()))); + return PL_DHASH_REMOVE; +} + +void +nsSVGRenderingObserverList::InvalidateAll() +{ + if (mObservers.Count() == 0) + return; + + nsAutoTArray 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(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(); + } +} diff --git a/layout/svg/base/src/nsSVGEffects.h b/layout/svg/base/src/nsSVGEffects.h index 1d0534e5707..d6a0aeb8065 100644 --- a/layout/svg/base/src/nsSVGEffects.h +++ b/layout/svg/base/src/nsSVGEffects.h @@ -43,14 +43,26 @@ #include "nsReferencedElement.h" #include "nsStubMutationObserver.h" #include "nsSVGUtils.h" -#include "nsSVGFilterFrame.h" -#include "nsSVGClipPathFrame.h" -#include "nsSVGMaskFrame.h" +#include "nsTHashtable.h" -class nsSVGPropertyBase : public nsStubMutationObserver { +class nsSVGClipPathFrame; +class nsSVGFilterFrame; +class nsSVGMaskFrame; + +/* + * 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 @@ -61,13 +73,22 @@ public: 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) { @@ -85,41 +106,41 @@ protected: */ 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 - (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 @@ -129,69 +150,125 @@ private: 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 - (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 - (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 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_*/ diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index 4dcbad2cf95..4afa4e89f84 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -78,7 +78,8 @@ nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* 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); } //---------------------------------------------------------------------- diff --git a/layout/svg/base/src/nsSVGGeometryFrame.cpp b/layout/svg/base/src/nsSVGGeometryFrame.cpp index 568cc8ca1ca..e75d49d9b03 100644 --- a/layout/svg/base/src/nsSVGGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGGeometryFrame.cpp @@ -40,26 +40,11 @@ #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, @@ -71,99 +56,21 @@ nsSVGGeometryFrame::Init(nsIContent* aContent, 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(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(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(); @@ -172,16 +79,7 @@ nsSVGGeometryFrame::GetPaintServer(const nsStyleSVGPaint *aPaint) type != nsGkAtoms::svgPatternFrame) return nsnull; - // Loop check for pattern - if (type == nsGkAtoms::svgPatternFrame && - nsContentUtils::ContentIsDescendantOf(mContent, result->GetContent())) - return nsnull; - - nsSVGPaintServerFrame *server = - static_cast(result); - - server->AddObserver(this); - return server; + return static_cast(result); } float @@ -255,60 +153,6 @@ nsSVGGeometryFrame::GetClipRule() return GetStyleSVG()->mClipRule; } -static void -PServerPropertyDtor(void *aObject, nsIAtom *aPropertyName, - void *aPropertyValue, void *aData) -{ - nsIFrame *ps = static_cast(aPropertyValue); - nsSVGUtils::RemoveObserver(static_cast(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() { @@ -357,17 +201,14 @@ nsSVGGeometryFrame::SetupCairoFill(gfxContext *aContext) float opacity = MaybeOptimizeOpacity(GetStyleSVG()->mFillOpacity); - if (GetStateBits() & NS_STATE_SVG_FILL_PSERVER) { - nsSVGPaintServerFrame *ps = static_cast - (GetProperty(nsGkAtoms::fill)); - if (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 - } + 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 if (GetStyleSVG()->mFill.mType == eStyleSVGPaintType_Server) { SetupCairoColor(aContext, GetStyleSVG()->mFill.mFallbackColor, @@ -380,12 +221,19 @@ nsSVGGeometryFrame::SetupCairoFill(gfxContext *aContext) 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; @@ -397,9 +245,9 @@ nsSVGGeometryFrame::SetupCairoStrokeGeometry(gfxContext *aContext) 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; @@ -410,12 +258,15 @@ nsSVGGeometryFrame::SetupCairoStrokeGeometry(gfxContext *aContext) 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; @@ -424,26 +275,25 @@ nsSVGGeometryFrame::SetupCairoStrokeHitGeometry(gfxContext *aContext) 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 - (GetProperty(nsGkAtoms::stroke)); - if (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 - } + 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 if (GetStyleSVG()->mStroke.mType == eStyleSVGPaintType_Server) { SetupCairoColor(aContext, GetStyleSVG()->mStroke.mFallbackColor, diff --git a/layout/svg/base/src/nsSVGGeometryFrame.h b/layout/svg/base/src/nsSVGGeometryFrame.h index 687acc1f801..37d63e1110d 100644 --- a/layout/svg/base/src/nsSVGGeometryFrame.h +++ b/layout/svg/base/src/nsSVGGeometryFrame.h @@ -38,8 +38,6 @@ #define __NS_SVGGEOMETRYFRAME_H__ #include "nsFrame.h" -#include "nsWeakReference.h" -#include "nsISVGValueObserver.h" class nsSVGPaintServerFrame; class gfxContext; @@ -52,34 +50,22 @@ typedef nsFrame nsSVGGeometryFrameBase; * 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(); @@ -87,25 +73,21 @@ public: 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 @@ -113,12 +95,12 @@ public: 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. diff --git a/layout/svg/base/src/nsSVGGlyphFrame.cpp b/layout/svg/base/src/nsSVGGlyphFrame.cpp index eafc10dc274..3b55be7ce95 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -338,7 +338,7 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext, nsIntRect *aDirtyRect) gfx->Save(); SetupGlobalTransform(gfx); - if (HasFill() && SetupCairoFill(gfx)) { + if (SetupCairoFill(gfx)) { gfxMatrix matrix = gfx->CurrentMatrix(); CharacterIterator iter(this, PR_TRUE); iter.SetInitialMatrix(gfx); @@ -347,7 +347,7 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext, nsIntRect *aDirtyRect) 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); @@ -433,11 +433,10 @@ nsSVGGlyphFrame::UpdateCoveredRegion() 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(); diff --git a/layout/svg/base/src/nsSVGGradientFrame.cpp b/layout/svg/base/src/nsSVGGradientFrame.cpp index f165508a8b7..c36b4ca13e9 100644 --- a/layout/svg/base/src/nsSVGGradientFrame.cpp +++ b/layout/svg/base/src/nsSVGGradientFrame.cpp @@ -41,6 +41,7 @@ #include "nsIDOMSVGAnimTransformList.h" #include "nsSVGTransformList.h" #include "nsSVGMatrix.h" +#include "nsSVGEffects.h" #include "nsIDOMSVGStopElement.h" #include "nsSVGGradientElement.h" #include "nsSVGGeometryFrame.h" @@ -55,9 +56,8 @@ nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext, nsIDOMSVGURIReference *aRef) : nsSVGGradientFrameBase(aContext), - mNextGrad(nsnull), mLoopFlag(PR_FALSE), - mInitialized(PR_FALSE) + mNoHRefURI(PR_FALSE) { if (aRef) { // Get the href @@ -65,97 +65,14 @@ nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext, } } -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 @@ -167,19 +84,14 @@ nsSVGGradientFrame::AttributeChanged(PRInt32 aNameSpaceID, (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, @@ -289,14 +201,11 @@ nsSVGGradientFrame::GetGradientTransform(nsSVGGeometryFrame *aSource) } } - 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 - (gradient); nsCOMPtr trans; - gradElement->mGradientTransform->GetAnimVal(getter_AddRefs(trans)); + element->mGradientTransform->GetAnimVal(getter_AddRefs(trans)); nsCOMPtr gradientTransform = nsSVGTransformList::GetConsolidationMatrix(trans); @@ -309,14 +218,10 @@ nsSVGGradientFrame::GetGradientTransform(nsSVGGeometryFrame *aSource) 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 - (gradient); - - return gradElement->mEnumAttributes[nsSVGGradientElement::SPREADMETHOD].GetAnimValue(); + return element->mEnumAttributes[nsSVGGradientElement::SPREADMETHOD].GetAnimValue(); } //---------------------------------------------------------------------- @@ -387,101 +292,97 @@ nsSVGGradientFrame::SetupPaintServer(gfxContext *aContext, // Private (helper) methods -void -nsSVGGradientFrame::GetRefedGradientFromHref() +nsSVGGradientFrame * +nsSVGGradientFrame::GetReferencedGradient() { - mNextGrad = nsnull; - mInitialized = PR_TRUE; + if (mNoHRefURI) + return nsnull; - // Fetch our gradient element's xlink:href attribute - nsAutoString href; - mHref->GetAnimVal(href); - if (href.IsEmpty()) { - return; // no URL - } + nsSVGPaintingProperty *property = + static_cast(GetProperty(nsGkAtoms::href)); - // Convert href to an nsIURI - nsCOMPtr targetURI; - nsCOMPtr base = mContent->GetBaseURI(); - nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, - mContent->GetCurrentDoc(), base); - - // 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; - - mNextGrad = reinterpret_cast(nextGrad); - - // Add ourselves to the observer list - if (mNextGrad) { - // Can't use the NS_ADD macro here because of nsISupports ambiguity - mNextGrad->AddObserver(this); + 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 targetURI; + nsCOMPtr base = mContent->GetBaseURI(); + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, + mContent->GetCurrentDoc(), base); + + property = nsSVGEffects::GetPaintingProperty(targetURI, this, nsGkAtoms::href); + if (!property) + return nsnull; } + + nsIFrame *result = property->GetReferencedFrame(); + if (!result) + return nsnull; + + nsIAtom* frameType = result->GetType(); + if (frameType != nsGkAtoms::svgLinearGradientFrame && + frameType != nsGkAtoms::svgRadialGradientFrame) + return nsnull; + + return static_cast(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(mContent); - if (!mInitialized) // make sure mNextGrad has been initialized - GetRefedGradientFromHref(); + nsSVGGradientElement *grad = static_cast(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(mContent); - if (!mInitialized) - GetRefedGradientFromHref(); // make sure mNextGrad has been initialized + nsSVGGradientElement *grad = static_cast(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; @@ -502,10 +403,8 @@ nsSVGGradientFrame::GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame) // 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; @@ -514,10 +413,10 @@ nsSVGGradientFrame::GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame) // 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; @@ -528,14 +427,9 @@ 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 - (gradient); - - return gradElement->mEnumAttributes[nsSVGGradientElement::GRADIENTUNITS].GetAnimValue(); + nsSVGGradientElement *element = + GetGradientWithAttr(nsGkAtoms::gradientUnits, mContent); + return element->mEnumAttributes[nsSVGGradientElement::GRADIENTUNITS].GetAnimValue(); } // ------------------------------------------------------------------------- @@ -558,9 +452,7 @@ nsSVGLinearGradientFrame::AttributeChanged(PRInt32 aNameSpaceID, aAttribute == nsGkAtoms::y1 || aAttribute == nsGkAtoms::x2 || aAttribute == nsGkAtoms::y2)) { - WillModify(); - DidModify(); - return NS_OK; + nsSVGEffects::InvalidateRenderingObservers(this); } return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, @@ -573,12 +465,8 @@ 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(gradient); + GetLinearGradientWithAttr(aAtomName, mContent); // Object bounding box units are handled by setting the appropriate // transform in GetGradientTransform, but we need to handle user @@ -633,9 +521,7 @@ nsSVGRadialGradientFrame::AttributeChanged(PRInt32 aNameSpaceID, aAttribute == nsGkAtoms::cy || aAttribute == nsGkAtoms::fx || aAttribute == nsGkAtoms::fy)) { - WillModify(); - DidModify(); - return NS_OK; + nsSVGEffects::InvalidateRenderingObservers(this); } return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, @@ -647,21 +533,16 @@ nsSVGRadialGradientFrame::AttributeChanged(PRInt32 aNameSpaceID, 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(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. @@ -688,14 +569,14 @@ nsSVGRadialGradientFrame::CreateGradient() 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); @@ -728,7 +609,7 @@ nsSVGRadialGradientFrame::CreateGradient() // Public functions // ------------------------------------------------------------------------- -nsIFrame* +nsIFrame* NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext) @@ -738,7 +619,7 @@ NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell, NS_ERROR("Can't create frame! Content is not an SVG linearGradient"); return nsnull; } - + nsCOMPtr aRef = do_QueryInterface(aContent); NS_ASSERTION(aRef, "NS_NewSVGLinearGradientFrame -- Content doesn't support nsIDOMSVGURIReference"); @@ -755,7 +636,7 @@ NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell, NS_ERROR("Can't create frame! Content is not an SVG radialGradient"); return nsnull; } - + nsCOMPtr aRef = do_QueryInterface(aContent); NS_ASSERTION(aRef, "NS_NewSVGRadialGradientFrame -- Content doesn't support nsIDOMSVGURIReference"); diff --git a/layout/svg/base/src/nsSVGGradientFrame.h b/layout/svg/base/src/nsSVGGradientFrame.h index c721fa86b1d..f8a9184a763 100644 --- a/layout/svg/base/src/nsSVGGradientFrame.h +++ b/layout/svg/base/src/nsSVGGradientFrame.h @@ -48,42 +48,26 @@ 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, @@ -102,26 +86,24 @@ public: { 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); @@ -136,28 +118,15 @@ protected: virtual already_AddRefed 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); + return static_cast( + GetGradientWithAttr(aAttrName, nsGkAtoms::svgLinearGradientFrame, aDefault)); } - nsIContent* GetRadialGradientWithAttr(nsIAtom *aAttrName) + nsSVGRadialGradientElement* GetRadialGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault) { - return GetGradientWithAttr(aAttrName, nsGkAtoms::svgRadialGradientFrame); - } - - // 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) - { - 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( + GetGradientWithAttr(aAttrName, nsGkAtoms::svgRadialGradientFrame, aDefault)); } // Get the value of our gradientUnits attribute @@ -168,22 +137,16 @@ protected: private: // href of the other gradient we reference (if any) + // XXX this should go away, we can watch our content directly nsCOMPtr 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; }; @@ -195,7 +158,7 @@ typedef nsSVGGradientFrame nsSVGLinearGradientFrameBase; class nsSVGLinearGradientFrame : public nsSVGLinearGradientFrameBase { - friend nsIFrame* NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell, + friend nsIFrame* NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext); protected: @@ -232,7 +195,7 @@ typedef nsSVGGradientFrame nsSVGRadialGradientFrameBase; class nsSVGRadialGradientFrame : public nsSVGRadialGradientFrameBase { - friend nsIFrame* NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell, + friend nsIFrame* NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext); protected: @@ -258,7 +221,7 @@ public: protected: float GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 aEnumName, - nsIContent *aElement = nsnull); + nsSVGRadialGradientElement *aElement = nsnull); virtual already_AddRefed CreateGradient(); }; diff --git a/layout/svg/base/src/nsSVGIntegrationUtils.cpp b/layout/svg/base/src/nsSVGIntegrationUtils.cpp index 1f454d5f436..ce4587adcea 100644 --- a/layout/svg/base/src/nsSVGIntegrationUtils.cpp +++ b/layout/svg/base/src/nsSVGIntegrationUtils.cpp @@ -44,6 +44,9 @@ #include "nsDisplayList.h" #include "nsSVGMatrix.h" #include "nsSVGFilterPaintCallback.h" +#include "nsSVGFilterFrame.h" +#include "nsSVGClipPathFrame.h" +#include "nsSVGMaskFrame.h" // ---------------------------------------------------------------------- @@ -106,13 +109,12 @@ 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; @@ -270,12 +272,9 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsIRenderingContext* aCtx, */ 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; diff --git a/layout/svg/base/src/nsSVGLeafFrame.cpp b/layout/svg/base/src/nsSVGLeafFrame.cpp index 34e1c205428..2421012242c 100644 --- a/layout/svg/base/src/nsSVGLeafFrame.cpp +++ b/layout/svg/base/src/nsSVGLeafFrame.cpp @@ -35,6 +35,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nsFrame.h" +#include "nsSVGEffects.h" class nsSVGLeafFrame : public nsFrame { @@ -56,6 +57,7 @@ public: } #endif + NS_IMETHOD DidSetStyleContext(); }; nsIFrame* @@ -63,3 +65,11 @@ NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsSVGLeafFrame(aContext); } + +NS_IMETHODIMP +nsSVGLeafFrame::DidSetStyleContext() +{ + nsresult rv = nsFrame::DidSetStyleContext(); + nsSVGEffects::InvalidateRenderingObservers(this); + return rv; +} diff --git a/layout/svg/base/src/nsSVGPaintServerFrame.cpp b/layout/svg/base/src/nsSVGPaintServerFrame.cpp index b0662762200..b1b2cb9fc4f 100644 --- a/layout/svg/base/src/nsSVGPaintServerFrame.cpp +++ b/layout/svg/base/src/nsSVGPaintServerFrame.cpp @@ -35,7 +35,3 @@ * ***** END LICENSE BLOCK ***** */ #include "nsSVGPaintServerFrame.h" - -NS_INTERFACE_MAP_BEGIN(nsSVGPaintServerFrame) - NS_INTERFACE_MAP_ENTRY(nsISVGValue) -NS_INTERFACE_MAP_END_INHERITING(nsSVGPaintServerFrameBase) diff --git a/layout/svg/base/src/nsSVGPaintServerFrame.h b/layout/svg/base/src/nsSVGPaintServerFrame.h index db685b68b1e..72691be9c13 100644 --- a/layout/svg/base/src/nsSVGPaintServerFrame.h +++ b/layout/svg/base/src/nsSVGPaintServerFrame.h @@ -38,15 +38,13 @@ #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) : @@ -60,12 +58,6 @@ public: 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__ diff --git a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp index 0c5ffd7ec83..359ec476e5b 100644 --- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp @@ -363,8 +363,8 @@ nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint) 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); } @@ -431,8 +431,7 @@ nsSVGPathGeometryFrame::UpdateCoveredRegion() gfxRect extent; - if (HasStroke()) { - SetupCairoStrokeGeometry(&context); + if (SetupCairoStrokeGeometry(&context)) { extent = context.GetUserStrokeExtent(); if (!IsDegeneratePath(extent)) { extent = context.UserToDevice(extent); @@ -653,11 +652,11 @@ nsSVGPathGeometryFrame::Render(nsSVGRenderState *aContext) break; } - if (HasFill() && SetupCairoFill(gfx)) { + if (SetupCairoFill(gfx)) { gfx->Fill(); } - if (HasStroke() && SetupCairoStroke(gfx)) { + if (SetupCairoStroke(gfx)) { gfx->Stroke(); } diff --git a/layout/svg/base/src/nsSVGPatternFrame.cpp b/layout/svg/base/src/nsSVGPatternFrame.cpp index 09ff28e2e4b..558cad70df6 100644 --- a/layout/svg/base/src/nsSVGPatternFrame.cpp +++ b/layout/svg/base/src/nsSVGPatternFrame.cpp @@ -48,6 +48,7 @@ #include "nsSVGMatrix.h" #include "nsSVGRect.h" #include "nsSVGUtils.h" +#include "nsSVGEffects.h" #include "nsSVGOuterSVGFrame.h" #include "nsSVGPatternElement.h" #include "nsSVGGeometryFrame.h" @@ -68,8 +69,8 @@ static void printRect(char *msg, nsIDOMSVGRect *aRect); 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 @@ -77,61 +78,14 @@ nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext, } } -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 @@ -149,19 +103,16 @@ nsSVGPatternFrame::AttributeChanged(PRInt32 aNameSpaceID, 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, @@ -181,11 +132,11 @@ nsSVGPatternFrame::GetType() const // need to return *our current* transformation // matrix, which depends on our units parameters // and X, Y, Width, and Height -already_AddRefed +already_AddRefed nsSVGPatternFrame::GetCanvasTM() { nsIDOMSVGMatrix *rCTM; - + if (mCTM) { rCTM = mCTM; NS_IF_ADDREF(rCTM); @@ -200,7 +151,7 @@ nsSVGPatternFrame::GetCanvasTM() NS_NewSVGMatrix(&rCTM); } } - return rCTM; + return rCTM; } nsresult @@ -212,7 +163,7 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface, /* * 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 @@ -341,10 +292,17 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface, // 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) { @@ -365,16 +323,21 @@ nsSVGPatternFrame::PaintPattern(gfxASurface** surface, NS_IMETHODIMP nsSVGPatternFrame::GetPatternFirstChild(nsIFrame **kid) { - nsresult rv = NS_OK; - // 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 + *kid = mFrames.FirstChild(); + if (*kid) + return NS_OK; + + // No, see if we chain to someone who does + nsSVGPatternFrame *next = GetReferencedPattern(); + + mLoopFlag = PR_TRUE; + if (!next || next->mLoopFlag) { + mLoopFlag = PR_FALSE; + return NS_ERROR_FAILURE; } + + nsresult rv = next->GetPatternFirstChild(kid); mLoopFlag = PR_FALSE; return rv; } @@ -382,246 +345,157 @@ nsSVGPatternFrame::GetPatternFirstChild(nsIFrame **kid) 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 - (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 - (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() { - 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 - (mContent); - nsCOMPtr lTrans; - patternElement->mPatternTransform->GetAnimVal(getter_AddRefs(lTrans)); - nsCOMPtr patternTransform = - nsSVGTransformList::GetConsolidationMatrix(lTrans); - if (patternTransform) { - matrix = nsSVGUtils::ConvertSVGMatrixToThebes(patternTransform); - } - } else { - // Yes, get it from the target - matrix = mNextPattern->GetPatternTransform(); - } - mLoopFlag = PR_FALSE; + nsSVGPatternElement *patternElement = + GetPatternWithAttr(nsGkAtoms::patternTransform, mContent); + gfxMatrix matrix; + nsCOMPtr lTrans; + patternElement->mPatternTransform->GetAnimVal(getter_AddRefs(lTrans)); + nsCOMPtr patternTransform = + nsSVGTransformList::GetConsolidationMatrix(lTrans); + if (patternTransform) { + matrix = nsSVGUtils::ConvertSVGMatrixToThebes(patternTransform); + } 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 patternElement = - do_QueryInterface(mContent); - nsCOMPtr 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 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 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(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(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(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(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; +nsSVGPatternFrame * +nsSVGPatternFrame::GetReferencedPattern() +{ + if (mNoHRefURI) + return nsnull; + + nsSVGPaintingProperty *property = + static_cast(GetProperty(nsGkAtoms::href)); + + 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 + } + + // Convert href to an nsIURI + nsCOMPtr targetURI; + nsCOMPtr base = mContent->GetBaseURI(); + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, + mContent->GetCurrentDoc(), base); + + property = nsSVGEffects::GetPaintingProperty(targetURI, this, nsGkAtoms::href); + if (!property) + return nsnull; } - return checkURITarget(); + + nsIFrame *result = property->GetReferencedFrame(); + if (!result) + return nsnull; + + nsIAtom* frameType = result->GetType(); + if (frameType != nsGkAtoms::svgPatternFrame) + return nsnull; + + return static_cast(result); } -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; - } +nsSVGPatternElement * +nsSVGPatternFrame::GetPatternWithAttr(nsIAtom *aAttrName, nsIContent *aDefault) +{ + if (mContent->HasAttr(kNameSpaceID_None, aAttrName)) + return static_cast(mContent); - // 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 - } + nsSVGPatternElement *pattern = static_cast(aDefault); - nsCOMPtr targetURI; - nsCOMPtr base = mContent->GetBaseURI(); - nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), - href, mContent->GetCurrentDoc(), base); + nsSVGPatternFrame *next = GetReferencedPattern(); + if (!next) + return pattern; - // 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; + // 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) @@ -677,7 +551,7 @@ nsSVGPatternFrame::ConstructCTM(nsIDOMSVGMatrix **aCTM, 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); @@ -748,7 +622,7 @@ nsSVGPatternFrame::GetPatternMatrix(nsIDOMSVGRect *bbox, } nsresult -nsSVGPatternFrame::GetCallerGeometry(nsIDOMSVGMatrix **aCTM, +nsSVGPatternFrame::GetCallerGeometry(nsIDOMSVGMatrix **aCTM, nsIDOMSVGRect **aBBox, nsSVGElement **aContent, nsSVGGeometryFrame *aSource) @@ -781,7 +655,7 @@ nsSVGPatternFrame::GetCallerGeometry(nsIDOMSVGMatrix **aCTM, 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); @@ -888,7 +762,7 @@ nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell, } nsCOMPtr ref = do_QueryInterface(aContent); - NS_ASSERTION(ref, + NS_ASSERTION(ref, "NS_NewSVGPatternFrame -- Content doesn't support nsIDOMSVGURIReference"); #ifdef DEBUG_scooter @@ -907,11 +781,11 @@ static void printCTM(char *msg, gfxMatrix aCTM) 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); } @@ -919,10 +793,10 @@ static void printCTM(char *msg, nsIDOMSVGMatrix *aCTM) 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 diff --git a/layout/svg/base/src/nsSVGPatternFrame.h b/layout/svg/base/src/nsSVGPatternFrame.h index 6b43456f166..e1f8111ab54 100644 --- a/layout/svg/base/src/nsSVGPatternFrame.h +++ b/layout/svg/base/src/nsSVGPatternFrame.h @@ -39,8 +39,6 @@ #ifndef __NS_SVGPATTERNFRAME_H__ #define __NS_SVGPATTERNFRAME_H__ -#include "nsISVGValueObserver.h" -#include "nsWeakReference.h" #include "nsIDOMSVGAnimatedString.h" #include "nsIDOMSVGMatrix.h" #include "nsSVGPaintServerFrame.h" @@ -55,11 +53,14 @@ 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); @@ -75,19 +76,7 @@ public: 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 GetCanvasTM(); @@ -117,11 +106,13 @@ protected: nsSVGPatternFrame(nsStyleContext* aContext, nsIDOMSVGURIReference *aRef); - virtual ~nsSVGPatternFrame(); - // Internal methods for handling referenced patterns - PRBool checkURITarget(nsIAtom *); - PRBool checkURITarget(); + 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); + // nsSVGLength2 *GetX(); nsSVGLength2 *GetY(); @@ -132,7 +123,7 @@ protected: PRUint16 GetPatternContentUnits(); gfxMatrix GetPatternTransform(); - NS_IMETHOD GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio + NS_IMETHOD GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio **aPreserveAspectRatio); NS_IMETHOD GetPatternFirstChild(nsIFrame **kid); NS_IMETHOD GetViewBox(nsIDOMSVGRect * *aMatrix); @@ -146,23 +137,26 @@ protected: 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 mCTM; + nsSVGGeometryFrame *mSource; + nsCOMPtr mCTM; protected: - nsSVGPatternFrame *mNextPattern; - nsCOMPtr mHref; - PRPackedBool mLoopFlag; + nsCOMPtr 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 - diff --git a/layout/svg/base/src/nsSVGStopFrame.cpp b/layout/svg/base/src/nsSVGStopFrame.cpp index 0bffb713d79..bd1102795fd 100644 --- a/layout/svg/base/src/nsSVGStopFrame.cpp +++ b/layout/svg/base/src/nsSVGStopFrame.cpp @@ -40,7 +40,7 @@ #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 @@ -94,9 +94,8 @@ public: NS_IMETHODIMP nsSVGStopFrame::DidSetStyleContext() { - // Tell our parent - if (mParent) - mParent->DidSetStyleContext(); + nsSVGStopFrameBase::DidSetStyleContext(); + nsSVGEffects::InvalidateRenderingObservers(this); return NS_OK; } @@ -113,21 +112,8 @@ nsSVGStopFrame::AttributeChanged(PRInt32 aNameSpaceID, { 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); diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index 7b39b018b63..78dfca4706d 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -594,7 +594,7 @@ nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect) nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame); if (property) { - nsSVGFilterFrame *filter = property->GetFilterFrame(nsnull); + nsSVGFilterFrame *filter = property->GetFilterFrame(); if (filter) { rect = filter->GetInvalidationBBox(aFrame, rect); } @@ -622,6 +622,8 @@ nsSVGUtils::UpdateGraphic(nsISVGChildFrame *aSVGFrame) nsIFrame *frame; CallQueryInterface(aSVGFrame, &frame); + nsSVGEffects::InvalidateRenderingObservers(frame); + if (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) return; @@ -985,8 +987,7 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, 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 @@ -1024,10 +1025,8 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, 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; @@ -1105,14 +1104,6 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, 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) { @@ -1121,7 +1112,7 @@ nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint) 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. @@ -1435,8 +1426,9 @@ nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame) if (type == nsGkAtoms::svgImageFrame) return PR_TRUE; if (type == nsGkAtoms::svgPathGeometryFrame) { - nsSVGGeometryFrame *geom = static_cast(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; } } diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index 7ca02b55b3f..9c0963852f9 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -90,17 +90,10 @@ class nsISVGChildFrame; #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 0x02000000 +#define NS_STATE_SVG_NONDISPLAY_CHILD 0x00800000 -#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. @@ -353,13 +346,6 @@ public: 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