diff --git a/content/svg/content/src/SVGFilterElement.cpp b/content/svg/content/src/SVGFilterElement.cpp index 79a1ba28bb2..b5cda94fb03 100644 --- a/content/svg/content/src/SVGFilterElement.cpp +++ b/content/svg/content/src/SVGFilterElement.cpp @@ -160,7 +160,7 @@ SVGFilterElement::Invalidate() nsTObserverArray::ForwardIterator iter(*observers); while (iter.HasMore()) { nsCOMPtr obs(iter.GetNext()); - nsCOMPtr filter = do_QueryInterface(obs); + nsCOMPtr filter = do_QueryInterface(obs); if (filter) filter->Invalidate(); } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index e5d88185f30..cbb1ef5319e 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4971,7 +4971,7 @@ ComputeOutlineAndEffectsRect(nsIFrame* aFrame, // For SVG frames, we only need to account for filters. // TODO: We could also take account of clipPath and mask to reduce the // visual overflow, but that's not essential. - if (aFrame->StyleSVGReset()->SingleFilter()) { + if (aFrame->StyleSVGReset()->HasFilters()) { if (aStoreRectProperties) { aFrame->Properties(). Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r)); diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index f72a6636557..c29360e51b4 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2626,13 +2626,8 @@ struct nsStyleSVGReset { nsChangeHint_ClearAncestorIntrinsics); } - // The backend only supports one SVG reference right now. - // Eventually, it will support multiple chained SVG reference filters and CSS - // filter functions. - nsIURI* SingleFilter() const { - return (mFilters.Length() == 1 && - mFilters[0].GetType() == NS_STYLE_FILTER_URL) ? - mFilters[0].GetURL() : nullptr; + bool HasFilters() const { + return mFilters.Length() > 0; } nsCOMPtr mClipPath; // [reset] diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index 011e44459f5..761532c98d9 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -214,12 +214,72 @@ nsSVGRenderingObserver::ContentRemoved(nsIDocument *aDocument, DoUpdate(); } -NS_IMPL_ISUPPORTS_INHERITED1(nsSVGFilterProperty, - nsSVGIDRenderingObserver, - nsISVGFilterProperty) +NS_IMPL_ISUPPORTS1(nsSVGFilterProperty, nsISupports) + +nsSVGFilterProperty::nsSVGFilterProperty(const nsTArray &aFilters, + nsIFrame *aFilteredFrame) : + mFilters(aFilters) +{ + for (uint32_t i = 0; i < mFilters.Length(); i++) { + if (mFilters[i].GetType() != NS_STYLE_FILTER_URL) + continue; + + nsSVGFilterReference *reference = + new nsSVGFilterReference(mFilters[i].GetURL(), aFilteredFrame); + NS_ADDREF(reference); + mReferences.AppendElement(reference); + } +} + +nsSVGFilterProperty::~nsSVGFilterProperty() +{ + for (uint32_t i = 0; i < mReferences.Length(); i++) { + NS_RELEASE(mReferences[i]); + } +} + +bool +nsSVGFilterProperty::ReferencesValidResources() +{ + for (uint32_t i = 0; i < mReferences.Length(); i++) { + if (!mReferences[i]->ReferencesValidResource()) + return false; + } + return true; +} + +bool +nsSVGFilterProperty::IsInObserverLists() const +{ + for (uint32_t i = 0; i < mReferences.Length(); i++) { + if (!mReferences[i]->IsInObserverList()) + return false; + } + return true; +} + +void +nsSVGFilterProperty::Invalidate() +{ + for (uint32_t i = 0; i < mReferences.Length(); i++) { + mReferences[i]->Invalidate(); + } +} nsSVGFilterFrame * nsSVGFilterProperty::GetFilterFrame() +{ + // Eventually, callers will ask nsSVGFilterProperty for an nsStyleFilter + // chain, not a single nsSVGFilterFrame, and this function will go away. + return mReferences.Length() > 0 ? mReferences[0]->GetFilterFrame() : nullptr; +} + +NS_IMPL_ISUPPORTS_INHERITED1(nsSVGFilterReference, + nsSVGIDRenderingObserver, + nsISVGFilterReference); + +nsSVGFilterFrame * +nsSVGFilterReference::GetFilterFrame() { return static_cast (GetReferencedFrame(nsGkAtoms::svgFilterFrame, nullptr)); @@ -235,7 +295,7 @@ InvalidateAllContinuations(nsIFrame* aFrame) } void -nsSVGFilterProperty::DoUpdate() +nsSVGFilterReference::DoUpdate() { nsSVGIDRenderingObserver::DoUpdate(); if (!mFrame) @@ -334,10 +394,6 @@ nsSVGPaintingProperty::DoUpdate() } } -static nsSVGRenderingObserver * -CreateFilterProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) -{ return new nsSVGFilterProperty(aURI, aFrame, aReferenceImage); } - static nsSVGRenderingObserver * CreateMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) { return new nsSVGMarkerProperty(aURI, aFrame, aReferenceImage); } @@ -371,6 +427,26 @@ GetEffectProperty(nsIURI *aURI, nsIFrame *aFrame, return prop; } +static nsSVGFilterProperty* +GetOrCreateFilterProperty(nsIFrame *aFrame) +{ + const nsStyleSVGReset* style = aFrame->StyleSVGReset(); + if (!style->HasFilters()) + return nullptr; + + FrameProperties props = aFrame->Properties(); + nsSVGFilterProperty *prop = + static_cast(props.Get(nsSVGEffects::FilterProperty())); + if (prop) + return prop; + prop = new nsSVGFilterProperty(style->mFilters, aFrame); + if (!prop) + return nullptr; + NS_ADDREF(prop); + props.Set(nsSVGEffects::FilterProperty(), static_cast(prop)); + return prop; +} + nsSVGMarkerProperty * nsSVGEffects::GetMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, const FramePropertyDescriptor *aProp) @@ -438,9 +514,7 @@ nsSVGEffects::GetEffectProperties(nsIFrame *aFrame) EffectProperties result; const nsStyleSVGReset *style = aFrame->StyleSVGReset(); - result.mFilter = static_cast - (GetEffectProperty(style->SingleFilter(), aFrame, FilterProperty(), - CreateFilterProperty)); + result.mFilter = GetOrCreateFilterProperty(aFrame); result.mClipPath = GetPaintingProperty(style->mClipPath, aFrame, ClipPathProperty()); result.mMask = @@ -515,8 +589,7 @@ nsSVGEffects::UpdateEffects(nsIFrame *aFrame) // Ensure that the filter is repainted correctly // We can't do that in DoUpdate as the referenced frame may not be valid - GetEffectProperty(aFrame->StyleSVGReset()->SingleFilter(), - aFrame, FilterProperty(), CreateFilterProperty); + GetOrCreateFilterProperty(aFrame); if (aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame && static_cast(aFrame->GetContent())->IsMarkable()) { @@ -536,7 +609,7 @@ nsSVGEffects::GetFilterProperty(nsIFrame *aFrame) { NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); - if (!aFrame->StyleSVGReset()->SingleFilter()) + if (!aFrame->StyleSVGReset()->HasFilters()) return nullptr; return static_cast diff --git a/layout/svg/nsSVGEffects.h b/layout/svg/nsSVGEffects.h index 2cd1bbc178a..6d077c27298 100644 --- a/layout/svg/nsSVGEffects.h +++ b/layout/svg/nsSVGEffects.h @@ -150,12 +150,25 @@ protected: nsIPresShell *mFramePresShell; }; -class nsSVGFilterProperty : - public nsSVGIDRenderingObserver, public nsISVGFilterProperty { +/** + * In a filter chain, there can be multiple SVG reference filters. + * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2); + * + * This class keeps track of one SVG reference filter in a filter chain. + * e.g. url(#svg-filter-1) + * + * It fires invalidations when the SVG filter element's id changes or when + * the SVG filter element's content changes. + * + * The nsSVGFilterProperty class manages a list of nsSVGFilterReferences. + */ +class nsSVGFilterReference : + public nsSVGIDRenderingObserver, public nsISVGFilterReference { public: - nsSVGFilterProperty(nsIURI *aURI, nsIFrame *aFilteredFrame, - bool aReferenceImage) - : nsSVGIDRenderingObserver(aURI, aFilteredFrame, aReferenceImage) {} + nsSVGFilterReference(nsIURI *aURI, nsIFrame *aFilteredFrame) + : nsSVGIDRenderingObserver(aURI, aFilteredFrame, false) {} + + bool ReferencesValidResource() { return GetFilterFrame(); } /** * @return the filter frame, or null if there is no filter frame @@ -165,14 +178,48 @@ public: // nsISupports NS_DECL_ISUPPORTS - // nsISVGFilterProperty - virtual void Invalidate() MOZ_OVERRIDE { DoUpdate(); } + // nsISVGFilterReference + virtual void Invalidate() MOZ_OVERRIDE { DoUpdate(); }; private: - // nsSVGRenderingObserver + // nsSVGIDRenderingObserver virtual void DoUpdate() MOZ_OVERRIDE; }; +/** + * This class manages a list of nsSVGFilterReferences, which represent SVG + * reference filters in a filter chain. + * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2); + * + * In the above example, the nsSVGFilterProperty will manage two + * nsSVGFilterReferences, one for each SVG reference filter. CSS filters like + * "blur(10px)" don't reference filter elements, so they don't need an + * nsSVGFilterReference. The style system invalidates changes to CSS filters. + */ +class nsSVGFilterProperty : public nsISupports { +public: + nsSVGFilterProperty(const nsTArray &aFilters, + nsIFrame *aFilteredFrame); + virtual ~nsSVGFilterProperty(); + + const nsTArray& GetFilters() { return mFilters; } + bool ReferencesValidResources(); + bool IsInObserverLists() const; + void Invalidate(); + + /** + * @return the filter frame, or null if there is no filter frame + */ + nsSVGFilterFrame *GetFilterFrame(); + + // nsISupports + NS_DECL_ISUPPORTS + +private: + nsTArray mReferences; + nsTArray mFilters; +}; + class nsSVGMarkerProperty : public nsSVGIDRenderingObserver { public: nsSVGMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 8a185a1f300..dd68f895131 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -152,7 +152,7 @@ nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) // checking the SDL prefs here, since we don't know if we're being called for // painting or hit-testing anyway. const nsStyleSVGReset *style = aFrame->StyleSVGReset(); - return (style->SingleFilter() || style->mClipPath || style->mMask); + return (style->HasFilters() || style->mClipPath || style->mMask); } /* static */ nsPoint @@ -302,7 +302,7 @@ nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame, return aInvalidRect; nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame); - if (!prop || !prop->IsInObserverList()) { + if (!prop || !prop->IsInObserverLists()) { return aInvalidRect; } diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index 2276d95ca9a..b480a557d32 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -930,7 +930,7 @@ nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame) type != nsGkAtoms::svgPathGeometryFrame) { return false; } - if (aFrame->StyleSVGReset()->SingleFilter()) { + if (aFrame->StyleSVGReset()->HasFilters()) { return false; } // XXX The SVG WG is intending to allow fill, stroke and markers on diff --git a/layout/svg/nsSVGUtils.h b/layout/svg/nsSVGUtils.h index 5ca00c3bced..c18adc47609 100644 --- a/layout/svg/nsSVGUtils.h +++ b/layout/svg/nsSVGUtils.h @@ -161,18 +161,18 @@ private: }; -#define NS_ISVGFILTERPROPERTY_IID \ +#define NS_ISVGFILTERREFERENCE_IID \ { 0x9744ee20, 0x1bcf, 0x4c62, \ { 0x86, 0x7d, 0xd3, 0x7a, 0x91, 0x60, 0x3e, 0xef } } -class nsISVGFilterProperty : public nsISupports +class nsISVGFilterReference : public nsISupports { public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISVGFILTERPROPERTY_IID) + NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISVGFILTERREFERENCE_IID) virtual void Invalidate() = 0; }; -NS_DEFINE_STATIC_IID_ACCESSOR(nsISVGFilterProperty, NS_ISVGFILTERPROPERTY_IID) +NS_DEFINE_STATIC_IID_ACCESSOR(nsISVGFilterReference, NS_ISVGFILTERREFERENCE_IID) /** * General functions used by all of SVG layout and possibly content code.