From 5ad302fb3b68db1f3e096349ea6e9ff04899c92a Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Sat, 25 Apr 2009 01:17:43 +0200 Subject: [PATCH] Bug 463939. When an SVG element's geometry changes and it has an ancestor with a filter effect, invalidate the nearest SVG viewport ancestor because we don't have a good way to compute precise invalidation bounds. r=roc --HG-- extra : rebase_source : 4f0b385d07b989137f83b80d6cd0907ee1dcd599 --- .../reftests/svg/filter-invalidation-01.svg | 25 +++++ layout/reftests/svg/reftest.list | 1 + layout/svg/base/src/nsSVGContainerFrame.cpp | 1 + layout/svg/base/src/nsSVGContainerFrame.h | 2 + layout/svg/base/src/nsSVGInnerSVGFrame.cpp | 64 +---------- layout/svg/base/src/nsSVGInnerSVGFrame.h | 101 ++++++++++++++++++ layout/svg/base/src/nsSVGUtils.cpp | 62 ++++++++++- layout/svg/base/src/nsSVGUtils.h | 8 ++ 8 files changed, 201 insertions(+), 63 deletions(-) create mode 100644 layout/reftests/svg/filter-invalidation-01.svg create mode 100644 layout/svg/base/src/nsSVGInnerSVGFrame.h diff --git a/layout/reftests/svg/filter-invalidation-01.svg b/layout/reftests/svg/filter-invalidation-01.svg new file mode 100644 index 000000000000..de1811248f17 --- /dev/null +++ b/layout/reftests/svg/filter-invalidation-01.svg @@ -0,0 +1,25 @@ + + + + Test that the area that's covered by a filtered elemnt is invalidated when it moves + + + + + + + diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index fef8b2606d77..49575c89870d 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -59,6 +59,7 @@ include moz-only/reftest.list == filter-basic-02.svg pass.svg == filter-basic-03.svg pass.svg == filter-foreignObject-01.svg pass.svg +== filter-invalidation-01.svg pass.svg == filter-translated-01.svg filter-translated-01-ref.svg == foreignObject-01.svg pass.svg == foreignObject-02.svg foreignObject-02-ref.svg diff --git a/layout/svg/base/src/nsSVGContainerFrame.cpp b/layout/svg/base/src/nsSVGContainerFrame.cpp index 0082303f4b7a..746cb1180054 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.cpp +++ b/layout/svg/base/src/nsSVGContainerFrame.cpp @@ -39,6 +39,7 @@ #include "nsSVGOuterSVGFrame.h" NS_QUERYFRAME_HEAD(nsSVGDisplayContainerFrame) + NS_QUERYFRAME_ENTRY(nsSVGDisplayContainerFrame) NS_QUERYFRAME_ENTRY(nsISVGChildFrame) NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrame) diff --git a/layout/svg/base/src/nsSVGContainerFrame.h b/layout/svg/base/src/nsSVGContainerFrame.h index 9d6154304008..8b1b587ea276 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.h +++ b/layout/svg/base/src/nsSVGContainerFrame.h @@ -83,6 +83,8 @@ protected: nsSVGContainerFrame(aContext) {} public: + NS_DECLARE_FRAME_ACCESSOR(nsSVGDisplayContainerFrame); + NS_DECL_QUERYFRAME // nsIFrame: diff --git a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp index e756c374ef4c..519264235359 100644 --- a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp +++ b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp @@ -36,6 +36,7 @@ * * ***** END LICENSE BLOCK ***** */ +#include "nsSVGInnerSVGFrame.h" #include "nsIFrame.h" #include "nsISVGChildFrame.h" #include "nsSVGOuterSVGFrame.h" @@ -45,68 +46,6 @@ #include "nsSVGContainerFrame.h" #include "gfxContext.h" -typedef nsSVGDisplayContainerFrame nsSVGInnerSVGFrameBase; - -class nsSVGInnerSVGFrame : public nsSVGInnerSVGFrameBase, - public nsISVGSVGFrame -{ - friend nsIFrame* - NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); -protected: - nsSVGInnerSVGFrame(nsStyleContext* aContext) : - nsSVGInnerSVGFrameBase(aContext) {} - -public: - NS_DECL_QUERYFRAME - - // We don't define an AttributeChanged method since changes to the - // 'x', 'y', 'width' and 'height' attributes of our content object - // are handled in nsSVGSVGElement::DidModifySVGObservable - -#ifdef DEBUG - NS_IMETHOD Init(nsIContent* aContent, - nsIFrame* aParent, - nsIFrame* aPrevInFlow); -#endif - - /** - * Get the "type" of the frame - * - * @see nsGkAtoms::svgInnerSVGFrame - */ - virtual nsIAtom* GetType() const; - -#ifdef DEBUG - NS_IMETHOD GetFrameName(nsAString& aResult) const - { - return MakeFrameName(NS_LITERAL_STRING("SVGInnerSVG"), aResult); - } -#endif - - // nsISVGChildFrame interface: - NS_IMETHOD PaintSVG(nsSVGRenderState *aContext, const nsIntRect *aDirtyRect); - virtual void NotifySVGChanged(PRUint32 aFlags); - NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint); - - // nsSVGContainerFrame methods: - virtual already_AddRefed GetCanvasTM(); - - // nsISupportsWeakReference - // implementation inherited from nsSupportsWeakReference - - // nsISVGSVGFrame interface: - NS_IMETHOD SuspendRedraw(); - NS_IMETHOD UnsuspendRedraw(); - NS_IMETHOD NotifyViewportChange(); - -protected: - - nsCOMPtr mCanvasTM; -}; - -//---------------------------------------------------------------------- -// Implementation - nsIFrame* NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { @@ -117,6 +56,7 @@ NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) // nsIFrame methods NS_QUERYFRAME_HEAD(nsSVGInnerSVGFrame) + NS_QUERYFRAME_ENTRY(nsSVGInnerSVGFrame) NS_QUERYFRAME_ENTRY(nsISVGSVGFrame) NS_QUERYFRAME_TAIL_INHERITING(nsSVGInnerSVGFrameBase) diff --git a/layout/svg/base/src/nsSVGInnerSVGFrame.h b/layout/svg/base/src/nsSVGInnerSVGFrame.h new file mode 100644 index 000000000000..8a80231bcc1d --- /dev/null +++ b/layout/svg/base/src/nsSVGInnerSVGFrame.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla SVG project. + * + * The Initial Developer of the Original Code is + * Crocodile Clips Ltd.. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alex Fritze (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsSVGContainerFrame.h" +#include "nsISVGSVGFrame.h" + +typedef nsSVGDisplayContainerFrame nsSVGInnerSVGFrameBase; + +class nsSVGInnerSVGFrame : public nsSVGInnerSVGFrameBase, + public nsISVGSVGFrame +{ + friend nsIFrame* + NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); +protected: + nsSVGInnerSVGFrame(nsStyleContext* aContext) : + nsSVGInnerSVGFrameBase(aContext) {} + +public: + NS_DECLARE_FRAME_ACCESSOR(nsSVGInnerSVGFrame); + + NS_DECL_QUERYFRAME + + // We don't define an AttributeChanged method since changes to the + // 'x', 'y', 'width' and 'height' attributes of our content object + // are handled in nsSVGSVGElement::DidModifySVGObservable + +#ifdef DEBUG + NS_IMETHOD Init(nsIContent* aContent, + nsIFrame* aParent, + nsIFrame* aPrevInFlow); +#endif + + /** + * Get the "type" of the frame + * + * @see nsGkAtoms::svgInnerSVGFrame + */ + virtual nsIAtom* GetType() const; + +#ifdef DEBUG + NS_IMETHOD GetFrameName(nsAString& aResult) const + { + return MakeFrameName(NS_LITERAL_STRING("SVGInnerSVG"), aResult); + } +#endif + + // nsISVGChildFrame interface: + NS_IMETHOD PaintSVG(nsSVGRenderState *aContext, const nsIntRect *aDirtyRect); + virtual void NotifySVGChanged(PRUint32 aFlags); + NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint); + + // nsSVGContainerFrame methods: + virtual already_AddRefed GetCanvasTM(); + + // nsISupportsWeakReference + // implementation inherited from nsSupportsWeakReference + + // nsISVGSVGFrame interface: + NS_IMETHOD SuspendRedraw(); + NS_IMETHOD UnsuspendRedraw(); + NS_IMETHOD NotifyViewportChange(); + +protected: + + nsCOMPtr mCanvasTM; +}; diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index 4e85abc45b62..4a54ad7059af 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -62,6 +62,7 @@ #include "nsSVGPoint.h" #include "nsDOMError.h" #include "nsSVGOuterSVGFrame.h" +#include "nsSVGInnerSVGFrame.h" #include "nsSVGPreserveAspectRatio.h" #include "nsSVGMatrix.h" #include "nsSVGClipPathFrame.h" @@ -462,6 +463,24 @@ nsSVGUtils::GetNearestViewportElement(nsIContent *aContent, return NS_OK; } +nsSVGDisplayContainerFrame* +nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame) +{ + NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected"); + if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) { + return nsnull; + } + while ((aFrame = aFrame->GetParent())) { + NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected"); + if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame || + aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) { + return do_QueryFrame(aFrame); + } + } + NS_NOTREACHED("This is not reached. It's only needed to compile."); + return nsnull; +} + nsresult nsSVGUtils::GetFarthestViewportElement(nsIContent *aContent, nsIDOMSVGElement * *aFarthestViewportElement) @@ -552,7 +571,48 @@ nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect) nsSVGFilterFrame *filter = nsSVGEffects::GetFilterFrame(aFrame); if (filter) { - rect = filter->GetInvalidationBBox(aFrame, rect); + // When we are under AttributeChanged, we can no longer get the old bbox + // by calling GetBBox(), and we need that to set up the filter region + // with the correct position. :-( + //rect = filter->GetInvalidationBBox(aFrame, rect); + + // XXX [perf] As a horrible workaround, for now we just invalidate the + // entire area of the nearest viewport establishing frame that doesnt + // have overflow:visible. See bug 463939. + nsSVGDisplayContainerFrame* viewportFrame = GetNearestSVGViewport(aFrame); + while (viewportFrame && !viewportFrame->GetStyleDisplay()->IsScrollableOverflow()) { + viewportFrame = GetNearestSVGViewport(viewportFrame); + } + if (!viewportFrame) { + viewportFrame = GetOuterSVGFrame(aFrame); + } + if (viewportFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) { + nsRect r = viewportFrame->GetOverflowRect(); + // GetOverflowRect is relative to our border box, but we need it + // relative to our content box. + nsMargin bp = viewportFrame->GetUsedBorderAndPadding(); + viewportFrame->ApplySkipSides(bp); + r.MoveBy(-bp.left, -bp.top); + return r; + } + NS_ASSERTION(viewportFrame->GetType() == nsGkAtoms::svgInnerSVGFrame, + "Wrong frame type"); + nsSVGInnerSVGFrame* innerSvg = do_QueryFrame(static_cast(viewportFrame)); + nsSVGDisplayContainerFrame* innerSvgParent = do_QueryFrame(viewportFrame->GetParent()); + float x, y, width, height; + static_cast(innerSvg->GetContent())-> + GetAnimatedLengthValues(&x, &y, &width, &height, nsnull); + nsCOMPtr domctm = nsSVGUtils::GetCanvasTM(innerSvgParent); + gfxMatrix ctm = nsSVGUtils::ConvertSVGMatrixToThebes(domctm); + gfxRect bounds = ctm.TransformBounds(gfxRect(x, y, width, height)); + bounds.RoundOut(); + nsIntRect r; + if (NS_SUCCEEDED(nsSVGUtils::GfxRectToIntRect(bounds, &r))) { + rect = r; + } else { + NS_NOTREACHED("Not going to invalidate the correct area"); + } + aFrame = viewportFrame; } aFrame = aFrame->GetParent(); } diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index a182106488c0..5767e49e469c 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -79,6 +79,7 @@ struct nsStyleFont; class nsSVGEnum; class nsISVGChildFrame; class nsSVGGeometryFrame; +class nsSVGDisplayContainerFrame; #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -256,6 +257,13 @@ public: static nsresult GetNearestViewportElement(nsIContent *aContent, nsIDOMSVGElement * *aNearestViewportElement); + /** + * Gets the nearest nsSVGInnerSVGFrame or nsSVGOuterSVGFrame frame. aFrame + * must be an SVG frame. If aFrame is of type nsGkAtoms::svgOuterSVGFrame, + * returns nsnull. + */ + static nsSVGDisplayContainerFrame* GetNearestSVGViewport(nsIFrame *aFrame); + /* * Get the farthest viewport element */