From 2d338f4adfe17b1932d979e5f6f42081e9d2639c Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 20 Jul 2012 14:12:29 -0400 Subject: [PATCH] Bug 614732 - Implement display list based painting and hit-testing for SVG. r=roc. --HG-- extra : rebase_source : 77e55885fbbf428008f5be787ddeb7e561c0d9bd --- .../svg/content/src/nsSVGGraphicElement.cpp | 26 +++---- content/svg/content/src/nsSVGSVGElement.cpp | 20 ++--- layout/base/nsCSSFrameConstructor.cpp | 18 ++++- layout/base/nsDisplayItemTypes.h | 2 + layout/base/nsDisplayList.cpp | 14 ++++ layout/base/nsDisplayList.h | 5 +- layout/generic/nsFrame.cpp | 51 ++++++++---- layout/generic/nsFrame.h | 11 ++- layout/generic/nsIFrame.h | 3 +- layout/reftests/svg/filters/reftest.list | 2 +- layout/reftests/svg/image/reftest.list | 2 +- layout/reftests/svg/reftest.list | 4 +- layout/reftests/svg/smil/reftest.list | 2 +- layout/svg/base/src/nsSVGContainerFrame.cpp | 23 +++++- layout/svg/base/src/nsSVGContainerFrame.h | 4 + .../svg/base/src/nsSVGForeignObjectFrame.cpp | 49 ++++++++++++ layout/svg/base/src/nsSVGForeignObjectFrame.h | 7 ++ layout/svg/base/src/nsSVGGlyphFrame.cpp | 66 ++++++++++++++++ layout/svg/base/src/nsSVGGlyphFrame.h | 5 ++ layout/svg/base/src/nsSVGImageFrame.cpp | 3 + layout/svg/base/src/nsSVGInnerSVGFrame.cpp | 10 +++ layout/svg/base/src/nsSVGIntegrationUtils.cpp | 58 +++++++++++--- layout/svg/base/src/nsSVGIntegrationUtils.h | 2 +- layout/svg/base/src/nsSVGOuterSVGFrame.cpp | 36 +++++++-- .../svg/base/src/nsSVGPathGeometryFrame.cpp | 77 +++++++++++++++++-- layout/svg/base/src/nsSVGPathGeometryFrame.h | 8 ++ layout/svg/base/src/nsSVGSwitchFrame.cpp | 26 +++++++ layout/svg/base/src/nsSVGTextFrame.cpp | 19 +++++ layout/svg/base/src/nsSVGTextFrame.h | 4 + layout/svg/base/src/nsSVGUtils.cpp | 8 ++ 30 files changed, 485 insertions(+), 80 deletions(-) diff --git a/content/svg/content/src/nsSVGGraphicElement.cpp b/content/svg/content/src/nsSVGGraphicElement.cpp index 28e74d46db4..7b443427d04 100644 --- a/content/svg/content/src/nsSVGGraphicElement.cpp +++ b/content/svg/content/src/nsSVGGraphicElement.cpp @@ -158,27 +158,19 @@ nsSVGGraphicElement::GetAttributeChangeHint(const nsIAtom* aAttribute, // will be called on us and our ancestors. nsIFrame* frame = const_cast(this)->GetPrimaryFrame(); - if (frame && frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) { - // No need to do anything. - } else if (aModType == nsIDOMMutationEvent::ADDITION || - aModType == nsIDOMMutationEvent::REMOVAL) { - // In order to handle view creation/destruction and stacking context - // changes, the code in nsStyleDisplay::CalcDifference uses - // nsChangeHint_ReconstructFrame if the transform was added/removed. - // XXXSDL Currently we don't need to reconstruct SVG frames when their - // transform is set/unset since we don't currently create GFX layers for - // SVG transforms, but we will after bug 614732 is fixed. Also change the - // assertion in ApplyRenderingChangeToTree when we do that. - NS_UpdateHint(retval, nsChangeHint_UpdateOverflow); + if (!frame || (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) { + return retval; // no change + } + if (aModType == nsIDOMMutationEvent::ADDITION || + aModType == nsIDOMMutationEvent::REMOVAL) { + // Reconstruct the frame tree to handle stacking context changes: + NS_UpdateHint(retval, nsChangeHint_ReconstructFrame); } else { NS_ABORT_IF_FALSE(aModType == nsIDOMMutationEvent::MODIFICATION, "Unknown modification type."); // We just assume the old and new transforms are different. - // XXXSDL Once we use GFX layers for SVG transforms, we will need to pass - // the nsChangeHint_UpdateTransformLayer hint too. Note that the - // assertion in ApplyRenderingChangeToTree will fail if that hint is - // passed on nsIDOMMutationEvent::REMOVAL though. - NS_UpdateHint(retval, nsChangeHint_UpdateOverflow); + NS_UpdateHint(retval, NS_CombineHint(nsChangeHint_UpdateOverflow, + nsChangeHint_UpdateTransformLayer)); } } return retval; diff --git a/content/svg/content/src/nsSVGSVGElement.cpp b/content/svg/content/src/nsSVGSVGElement.cpp index 216a83d0fcb..0498d30d215 100644 --- a/content/svg/content/src/nsSVGSVGElement.cpp +++ b/content/svg/content/src/nsSVGSVGElement.cpp @@ -942,19 +942,21 @@ nsSVGSVGElement::ChildrenOnlyTransformChanged() NS_STATE_SVG_NONDISPLAY_CHILD), "Non-display SVG frames don't maintain overflow rects"); + nsChangeHint changeHint; + bool hasChildrenOnlyTransform = HasViewBoxOrSyntheticViewBox() || (IsRoot() && (mCurrentTranslate != nsSVGTranslatePoint(0.0f, 0.0f) || mCurrentScale != 1.0f)); - // XXXSDL Currently we don't destroy frames if - // hasChildrenOnlyTransform != mHasChildrenOnlyTransform - // but we should once we start using GFX layers for SVG transforms - // (see the comment in nsSVGGraphicElement::GetAttributeChangeHint). - - nsChangeHint changeHint = - nsChangeHint(nsChangeHint_RepaintFrame | - nsChangeHint_UpdateOverflow | - nsChangeHint_ChildrenOnlyTransform); + if (hasChildrenOnlyTransform != mHasChildrenOnlyTransform) { + // Reconstruct the frame tree to handle stacking context changes: + changeHint = nsChangeHint_ReconstructFrame; + } else { + // We just assume the old and new transforms are different. + changeHint = nsChangeHint(nsChangeHint_RepaintFrame | + nsChangeHint_UpdateOverflow | + nsChangeHint_ChildrenOnlyTransform); + } nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint); diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index e4f31951ca8..9fa250ccbad 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -7730,6 +7730,21 @@ DoApplyRenderingChangeToTree(nsIFrame* aFrame, // will be invalidated by nsFrame::FinishAndStoreOverflowArea. aFrame->InvalidateTransformLayer(); } + if (aChange & nsChangeHint_ChildrenOnlyTransform) { + // The long comment in ProcessRestyledFrames that precedes the + // |frame->GetContent()->GetPrimaryFrame()| and abort applies here too. + nsIFrame *f = aFrame->GetContent()->GetPrimaryFrame(); + NS_ABORT_IF_FALSE(f->IsFrameOfType(nsIFrame::eSVG | + nsIFrame::eSVGContainer), + "Children-only transforms only expected on SVG frames"); + nsIFrame* childFrame = f->GetFirstPrincipalChild(); + for ( ; childFrame; childFrame = childFrame->GetNextSibling()) { + childFrame->MarkLayersActive(nsChangeHint_UpdateTransformLayer); + // Invalidate the old transformed area. The new transformed area + // will be invalidated by nsFrame::FinishAndStoreOverflowArea. + childFrame->InvalidateTransformLayer(); + } + } } } @@ -8016,7 +8031,8 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList) didReflowThisFrame = true; } if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView | - nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer)) { + nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer | + nsChangeHint_ChildrenOnlyTransform)) { ApplyRenderingChangeToTree(presContext, frame, hint); didInvalidate = true; } diff --git a/layout/base/nsDisplayItemTypes.h b/layout/base/nsDisplayItemTypes.h index 11f93bdf7ae..542cae67ebe 100644 --- a/layout/base/nsDisplayItemTypes.h +++ b/layout/base/nsDisplayItemTypes.h @@ -57,7 +57,9 @@ enum Type { TYPE_SELECTION_OVERLAY, TYPE_SOLID_COLOR, TYPE_SVG_EFFECTS, + TYPE_SVG_GLYPHS, TYPE_SVG_OUTER_SVG, + TYPE_SVG_PATH_GEOMETRY, TYPE_TABLE_CELL_BACKGROUND, TYPE_TABLE_CELL_SELECTION, TYPE_TABLE_ROW_BACKGROUND, diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index beb462d6220..0bc27065f82 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -33,6 +33,7 @@ #include "nsBoxFrame.h" #include "nsViewportFrame.h" #include "nsSVGEffects.h" +#include "nsSVGElement.h" #include "nsSVGClipPathFrame.h" #include "sampler.h" @@ -3295,6 +3296,19 @@ nsDisplaySVGEffects::BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerParameters& aContainerParameters) { + const nsIContent* content = mFrame->GetContent(); + bool hasSVGLayout = (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); + if (hasSVGLayout) { + nsISVGChildFrame *svgChildFrame = do_QueryFrame(mFrame); + if (!svgChildFrame || !mFrame->GetContent()->IsSVG()) { + NS_ASSERTION(false, "why?"); + return nsnull; + } + if (!static_cast(content)->HasValidDimensions()) { + return nsnull; // The SVG spec says not to draw filters for this + } + } + float opacity = mFrame->GetStyleDisplay()->mOpacity; if (opacity == 0.0f) return nsnull; diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 2db337031fd..415ad061781 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -256,10 +256,9 @@ public: /** * Returns true if we're currently building a display list that's - * directly or indirectly under an nsDisplayTransform or SVG - * foreignObject. + * directly or indirectly under an nsDisplayTransform. */ - bool IsInTransform() { return mInTransform; } + bool IsInTransform() const { return mInTransform; } /** * Indicate whether or not we're directly or indirectly under and * nsDisplayTransform or SVG foreignObject. diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index ac786a734ad..edf8c8ace7f 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1451,15 +1451,28 @@ nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder, return DisplayOutlineUnconditional(aBuilder, aLists); } +inline static bool IsSVGContentWithCSSClip(const nsIFrame *aFrame) +{ + // The CSS spec says that the 'clip' property only applies to absolutely + // positioned elements, whereas the SVG spec says that it applies to SVG + // elements regardless of the value of the 'position' property. Here we obey + // the CSS spec for outer- (since that's what we generally do), but + // obey the SVG spec for other SVG elements to which 'clip' applies. + nsIAtom *tag = aFrame->GetContent()->Tag(); + return (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) && + (tag == nsGkAtoms::svg || tag == nsGkAtoms::foreignObject); +} + bool nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, const nsSize& aSize) const { NS_PRECONDITION(aRect, "Must have aRect out parameter"); - if (!aDisp->IsAbsolutelyPositioned() || - !(aDisp->mClipFlags & NS_STYLE_CLIP_RECT)) + if (!(aDisp->mClipFlags & NS_STYLE_CLIP_RECT) || + !(aDisp->IsAbsolutelyPositioned() || IsSVGContentWithCSSClip(this))) { return false; + } *aRect = aDisp->mClip; if (NS_STYLE_CLIP_RIGHT_AUTO & aDisp->mClipFlags) { @@ -1751,8 +1764,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nsRect dirtyRect = aDirtyRect; bool inTransform = aBuilder->IsInTransform(); - if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && - disp->HasTransform()) { + if (IsTransformed()) { if (aBuilder->IsForPainting() && nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) { dirtyRect = GetVisualOverflowRectRelativeToSelf(); @@ -1803,8 +1815,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect); } - // Mark the display list items for absolutely positioned children MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect); + // Preserve3DChildren() also guarantees that applyAbsPosClipping and usingSVGEffects are false // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy if (Preserves3DChildren()) { @@ -1915,7 +1927,9 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, /* Else, if the list is non-empty and there is CSS group opacity without SVG * effects, wrap it up in an opacity item. */ - else if (disp->mOpacity < 1.0f && !resultList.IsEmpty()) { + else if (disp->mOpacity < 1.0f && + !nsSVGUtils::CanOptimizeOpacity(this) && + !resultList.IsEmpty()) { rv = resultList.AppendNewToTop( new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList)); if (NS_FAILED(rv)) @@ -1933,8 +1947,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, * We also traverse into sublists created by nsDisplayWrapList or nsDisplayOpacity, so that * we find all the correct children. */ - if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && - disp->HasTransform() && !resultList.IsEmpty()) { + if (IsTransformed() && !resultList.IsEmpty()) { if (Preserves3DChildren()) { rv = WrapPreserve3DList(this, aBuilder, &resultList); if (NS_FAILED(rv)) @@ -1965,11 +1978,14 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsIFrame* child = aChild; if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return NS_OK; + + bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT); // true if this is a real or pseudo stacking context bool pseudoStackingContext = (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0; - if ((aFlags & DISPLAY_CHILD_INLINE) && + if (!isSVG && + (aFlags & DISPLAY_CHILD_INLINE) && !child->IsFrameOfType(eLineParticipant)) { // child is a non-inline frame in an inline context, i.e., // it acts like inline-block or inline-table. Therefore it is a @@ -2020,7 +2036,6 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, } } - // Mark the display list items for absolutely positioned children child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty); if (childType != nsGkAtoms::placeholderFrame && @@ -2070,8 +2085,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, || child->IsTransformed() || nsSVGIntegrationUtils::UsingEffectsForFrame(child); - bool isPositioned = disp->IsPositioned(); - if (isVisuallyAtomic || isPositioned || disp->IsFloating() || + bool isPositioned = !isSVG && disp->IsPositioned(); + if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating()) || + ((disp->mClipFlags & NS_STYLE_CLIP_RECT) && + IsSVGContentWithCSSClip(this)) || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { // If you change this, also change IsPseudoStackingContextFromStyle() pseudoStackingContext = true; @@ -2178,16 +2195,20 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // Make sure the root of a fixed position frame sub-tree gets the // correct displaylist item type. nsDisplayItem* item; - if (!child->GetParent()->GetParent() && + if (!isSVG && !child->GetParent()->GetParent() && disp->mPosition == NS_STYLE_POSITION_FIXED) { item = new (aBuilder) nsDisplayFixedPosition(aBuilder, child, &list); } else { item = new (aBuilder) nsDisplayWrapList(aBuilder, child, &list); } - rv = aLists.PositionedDescendants()->AppendNewToTop(item); + if (isSVG) { + rv = aLists.Content()->AppendNewToTop(item); + } else { + rv = aLists.PositionedDescendants()->AppendNewToTop(item); + } NS_ENSURE_SUCCESS(rv, rv); } - } else if (disp->IsFloating()) { + } else if (!isSVG && disp->IsFloating()) { if (!list.IsEmpty()) { rv = aLists.Floats()->AppendNewToTop(new (aBuilder) nsDisplayWrapList(aBuilder, child, &list)); diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h index 837461d67b8..92388f046d2 100644 --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -571,11 +571,18 @@ public: nsIAtom* type = aFrame->GetType(); if (type == nsGkAtoms::tableFrame || type == nsGkAtoms::tableCellFrame || - type == nsGkAtoms::bcTableCellFrame) { + type == nsGkAtoms::bcTableCellFrame || + type == nsGkAtoms::svgOuterSVGFrame || + type == nsGkAtoms::svgInnerSVGFrame || + type == nsGkAtoms::svgForeignObjectFrame) { return true; } } - + + if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { + return false; + } + // If we're paginated and a block, and have NS_BLOCK_CLIP_PAGINATED_OVERFLOW // set, then we want to clip our overflow. return diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index bbca4d3350f..8138ae83b7e 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -2476,7 +2476,8 @@ public: /** * Determines whether this frame is a pseudo stacking context, looking * only as style --- i.e., assuming that it's in-flow and not a replaced - * element. + * element and not an SVG element. + * XXX maybe check IsTransformed()? */ bool IsPseudoStackingContextFromStyle() { const nsStyleDisplay* disp = GetStyleDisplay(); diff --git a/layout/reftests/svg/filters/reftest.list b/layout/reftests/svg/filters/reftest.list index 34f672c85e2..76264bd2eaa 100644 --- a/layout/reftests/svg/filters/reftest.list +++ b/layout/reftests/svg/filters/reftest.list @@ -79,7 +79,7 @@ fails == filter-marked-line-01.svg pass.svg # bug 477704 == feDisplacementMap-colour-01.svg feDisplacementMap-colour-01-ref.svg == feDisplacementMap-scale-01.svg pass.svg -== feDistantLight-filterRes-01.svg feDistantLight-filterRes-01-ref.svg +fuzzy-if(cocoaWidget&&layersGPUAccelerated,2,93) == feDistantLight-filterRes-01.svg feDistantLight-filterRes-01-ref.svg == feMorphology-radius-negative-01.svg pass.svg == feMorphology-radius-negative-02.svg pass.svg diff --git a/layout/reftests/svg/image/reftest.list b/layout/reftests/svg/image/reftest.list index f0342f8d91c..554e034ef74 100644 --- a/layout/reftests/svg/image/reftest.list +++ b/layout/reftests/svg/image/reftest.list @@ -4,7 +4,7 @@ == image-filter-01.svg image-filter-01-ref.svg == image-load-01.svg ../pass.svg == image-opacity-01.svg image-opacity-01-ref.svg -== image-opacity-02.svg image-opacity-02-ref.svg +fuzzy-if(Android,4,34) == image-opacity-02.svg image-opacity-02-ref.svg # Bug 776039 for Android == image-rotate-01.svg image-rotate-01-ref.svg == image-rotate-02a.svg image-rotate-02-ref.svg == image-rotate-02b.svg image-rotate-02-ref.svg diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index 2c157cd8364..30bb50fc4a0 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -96,8 +96,8 @@ fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),1,2) == clipPath-and-shape-re == dynamic-small-object-scaled-up-02.svg pass.svg == dynamic-switch-01.svg pass.svg == dynamic-text-01.svg dynamic-text-01-ref.svg -== dynamic-text-02.svg dynamic-text-02-ref.svg -== dynamic-text-03.svg dynamic-text-03-ref.svg +fuzzy-if(d2d&&layersGPUAccelerated,2,12739) == dynamic-text-02.svg dynamic-text-02-ref.svg # bug 776038 for Win7 +fuzzy-if(d2d&&layersGPUAccelerated,2,10539) == dynamic-text-03.svg dynamic-text-03-ref.svg # bug 776038 for Win7 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == dynamic-text-04.svg dynamic-text-04-ref.svg # bug 421587 for WinXP == dynamic-text-05.svg pass.svg == dynamic-text-06.svg pass.svg diff --git a/layout/reftests/svg/smil/reftest.list b/layout/reftests/svg/smil/reftest.list index 0291e3c4058..c9304ad308a 100644 --- a/layout/reftests/svg/smil/reftest.list +++ b/layout/reftests/svg/smil/reftest.list @@ -230,7 +230,7 @@ random == anim-text-x-y-dx-dy-01.svg anim-text-x-y-dx-dy-01-ref.svg # bug 579588 == anim-pattern-attr-presence-01.svg anim-pattern-attr-presence-01-ref.svg fails == anim-pattern-attr-presence-02.svg anim-pattern-attr-presence-02-ref.svg # ^ bug 621651 -== anim-gradient-attr-presence-01.svg anim-gradient-attr-presence-01-ref.svg +fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,2) == anim-gradient-attr-presence-01.svg anim-gradient-attr-presence-01-ref.svg == api-sanity-1.svg lime.svg diff --git a/layout/svg/base/src/nsSVGContainerFrame.cpp b/layout/svg/base/src/nsSVGContainerFrame.cpp index be5c879a58b..1225826f201 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.cpp +++ b/layout/svg/base/src/nsSVGContainerFrame.cpp @@ -93,6 +93,17 @@ nsSVGDisplayContainerFrame::Init(nsIContent* aContent, return rv; } +NS_IMETHODIMP +nsSVGDisplayContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + if (!static_cast(mContent)->HasValidDimensions()) { + return NS_OK; + } + return BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists); +} + NS_IMETHODIMP nsSVGDisplayContainerFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, @@ -167,8 +178,7 @@ nsSVGDisplayContainerFrame::IsSVGTransformed(gfxMatrix *aOwnTransform, } nsSVGElement *content = static_cast(mContent); - const SVGAnimatedTransformList *list = content->GetAnimatedTransformList(); - if ((list && !list->GetAnimValue().IsEmpty()) || + if (content->GetAnimatedTransformList() || content->GetAnimateMotionTransform()) { if (aOwnTransform) { *aOwnTransform = content->PrependLocalTransformsTo(gfxMatrix(), @@ -186,6 +196,11 @@ NS_IMETHODIMP nsSVGDisplayContainerFrame::PaintSVG(nsRenderingContext* aContext, const nsIntRect *aDirtyRect) { + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only painting of non-display " + "SVG should take this code path"); + const nsStyleDisplay *display = mStyleContext->GetStyleDisplay(); if (display->mOpacity == 0.0) return NS_OK; @@ -201,6 +216,10 @@ nsSVGDisplayContainerFrame::PaintSVG(nsRenderingContext* aContext, NS_IMETHODIMP_(nsIFrame*) nsSVGDisplayContainerFrame::GetFrameForPoint(const nsPoint &aPoint) { + NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only hit-testing of a " + "clipPath's contents should take this code path"); return nsSVGUtils::HitTestChildren(this, aPoint); } diff --git a/layout/svg/base/src/nsSVGContainerFrame.h b/layout/svg/base/src/nsSVGContainerFrame.h index bece867f4bd..4f6ad068b98 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.h +++ b/layout/svg/base/src/nsSVGContainerFrame.h @@ -118,6 +118,10 @@ public: nsIFrame* aParent, nsIFrame* aPrevInFlow); + NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists); + virtual bool IsSVGTransformed(gfxMatrix *aOwnTransform = nsnull, gfxMatrix *aFromParentTransform = nsnull) const; diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index 4541e081f2e..57a977f16e0 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -167,6 +167,17 @@ nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext, return NS_OK; } +NS_IMETHODIMP +nsSVGForeignObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + if (!static_cast(mContent)->HasValidDimensions()) { + return NS_OK; + } + return BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists); +} + void nsSVGForeignObjectFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY, @@ -198,6 +209,31 @@ nsSVGForeignObjectFrame::InvalidateInternal(const nsRect& aDamageRect, region->Or(*region, aDamageRect + nsPoint(aX, aY)); } +bool +nsSVGForeignObjectFrame::IsSVGTransformed(gfxMatrix *aOwnTransform, + gfxMatrix *aFromParentTransform) const +{ + bool foundTransform = false; + + // Check if our parent has children-only transforms: + nsIFrame *parent = GetParent(); + if (parent && + parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) { + foundTransform = static_cast(parent)-> + HasChildrenOnlyTransform(aFromParentTransform); + } + + nsSVGElement *content = static_cast(mContent); + if (content->GetAnimatedTransformList()) { + if (aOwnTransform) { + *aOwnTransform = content->PrependLocalTransformsTo(gfxMatrix(), + nsSVGElement::eUserSpaceToParent); + } + foundTransform = true; + } + return foundTransform; +} + /** * Returns the app unit canvas bounds of a userspace rect. @@ -218,6 +254,11 @@ NS_IMETHODIMP nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext, const nsIntRect *aDirtyRect) { + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only painting of non-display " + "SVG should take this code path"); + if (IsDisabled()) return NS_OK; @@ -236,6 +277,9 @@ nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext, /* Check if we need to draw anything. */ if (aDirtyRect) { + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "Display lists handle dirty rect intersection test"); // Transform the dirty rect into app units in our userspace. gfxMatrix invmatrix = canvasTM; invmatrix.Invert(); @@ -297,6 +341,11 @@ nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext, NS_IMETHODIMP_(nsIFrame*) nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint) { + NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only hit-testing of a " + "clipPath's contents should take this code path"); + if (IsDisabled() || (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) return nsnull; diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.h b/layout/svg/base/src/nsSVGForeignObjectFrame.h index c4511d4616b..ab109eb87d2 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.h +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h @@ -49,6 +49,10 @@ public: const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); + NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists); + /** * Get the "type" of the frame * @@ -66,6 +70,9 @@ public: nscoord aX, nscoord aY, nsIFrame* aForChild, PRUint32 aFlags); + virtual bool IsSVGTransformed(gfxMatrix *aOwnTransform, + gfxMatrix *aFromParentTransform) const; + #ifdef DEBUG NS_IMETHOD GetFrameName(nsAString& aResult) const { diff --git a/layout/svg/base/src/nsSVGGlyphFrame.cpp b/layout/svg/base/src/nsSVGGlyphFrame.cpp index 180222efd0b..0a2050c7ae5 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -13,6 +13,7 @@ #include "gfxPlatform.h" #include "mozilla/LookAndFeel.h" #include "nsBidiPresUtils.h" +#include "nsDisplayList.h" #include "nsDOMError.h" #include "nsIDOMSVGRect.h" #include "nsRenderingContext.h" @@ -198,6 +199,59 @@ private: bool mInError; }; + +class nsDisplaySVGGlyphs : public nsDisplayItem { +public: + nsDisplaySVGGlyphs(nsDisplayListBuilder* aBuilder, + nsSVGGlyphFrame* aFrame) + : nsDisplayItem(aBuilder, aFrame) + { + MOZ_COUNT_CTOR(nsDisplaySVGGlyphs); + NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplaySVGGlyphs() { + MOZ_COUNT_DTOR(nsDisplaySVGGlyphs); + } +#endif + + NS_DISPLAY_DECL_NAME("nsDisplaySVGGlyphs", TYPE_SVG_GLYPHS) + + virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, + HitTestState* aState, nsTArray *aOutFrames); + virtual void Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx); +}; + +void +nsDisplaySVGGlyphs::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, + HitTestState* aState, nsTArray *aOutFrames) +{ + nsSVGGlyphFrame *frame = static_cast(mFrame); + nsPoint pointRelativeToReferenceFrame = aRect.Center(); + // ToReferenceFrame() includes frame->GetPosition(), our user space position. + nsPoint userSpacePt = pointRelativeToReferenceFrame - + (ToReferenceFrame() - frame->GetPosition()); + if (frame->GetFrameForPoint(userSpacePt)) { + aOutFrames->AppendElement(frame); + } +} + +void +nsDisplaySVGGlyphs::Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) +{ + // ToReferenceFrame includes our mRect offset, but painting takes + // account of that too. To avoid double counting, we subtract that + // here. + nsPoint offset = ToReferenceFrame() - mFrame->GetPosition(); + + aCtx->PushState(); + aCtx->Translate(offset); + static_cast(mFrame)->PaintSVG(aCtx, nsnull); + aCtx->PopState(); +} + //---------------------------------------------------------------------- // Implementation @@ -295,6 +349,18 @@ nsSVGGlyphFrame::GetType() const return nsGkAtoms::svgGlyphFrame; } +NS_IMETHODIMP +nsSVGGlyphFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + if (GetStyleFont()->mFont.size <= 0) { + return NS_OK; + } + return aLists.Content()->AppendNewToTop( + new (aBuilder) nsDisplaySVGGlyphs(aBuilder, this)); +} + //---------------------------------------------------------------------- // nsISVGChildFrame methods diff --git a/layout/svg/base/src/nsSVGGlyphFrame.h b/layout/svg/base/src/nsSVGGlyphFrame.h index 3776166898a..cb3d23b010a 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.h +++ b/layout/svg/base/src/nsSVGGlyphFrame.h @@ -15,6 +15,7 @@ class CharacterIterator; class gfxContext; +class nsDisplaySVGGlyphs; class nsIDOMSVGRect; class nsRenderingContext; class nsSVGGlyphFrame; @@ -144,6 +145,10 @@ public: } #endif + NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists); + // nsISVGChildFrame interface: // These four always use the global transform, even if NS_STATE_NONDISPLAY_CHILD NS_IMETHOD PaintSVG(nsRenderingContext *aContext, diff --git a/layout/svg/base/src/nsSVGImageFrame.cpp b/layout/svg/base/src/nsSVGImageFrame.cpp index 161234584d9..955079201aa 100644 --- a/layout/svg/base/src/nsSVGImageFrame.cpp +++ b/layout/svg/base/src/nsSVGImageFrame.cpp @@ -341,6 +341,9 @@ nsSVGImageFrame::PaintSVG(nsRenderingContext *aContext, nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); nsRect dirtyRect; // only used if aDirtyRect is non-null if (aDirtyRect) { + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "Display lists handle dirty rect intersection test"); dirtyRect = aDirtyRect->ToAppUnits(appUnitsPerDevPx); // Adjust dirtyRect to match our local coordinate system. nsRect rootRect = diff --git a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp index 7756f1c3663..2266f5cd78a 100644 --- a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp +++ b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp @@ -57,6 +57,11 @@ NS_IMETHODIMP nsSVGInnerSVGFrame::PaintSVG(nsRenderingContext *aContext, const nsIntRect *aDirtyRect) { + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only painting of non-display " + "SVG should take this code path"); + gfxContextAutoSaveRestore autoSR; if (GetStyleDisplay()->IsScrollableOverflow()) { @@ -211,6 +216,11 @@ nsSVGInnerSVGFrame::AttributeChanged(PRInt32 aNameSpaceID, NS_IMETHODIMP_(nsIFrame*) nsSVGInnerSVGFrame::GetFrameForPoint(const nsPoint &aPoint) { + NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only hit-testing of non-display " + "SVG should take this code path"); + if (GetStyleDisplay()->IsScrollableOverflow()) { nsSVGElement *content = static_cast(mContent); nsSVGContainerFrame *parent = static_cast(mParent); diff --git a/layout/svg/base/src/nsSVGIntegrationUtils.cpp b/layout/svg/base/src/nsSVGIntegrationUtils.cpp index 1c77fb6b6da..c45b52ca4cc 100644 --- a/layout/svg/base/src/nsSVGIntegrationUtils.cpp +++ b/layout/svg/base/src/nsSVGIntegrationUtils.cpp @@ -13,6 +13,7 @@ #include "nsRenderingContext.h" #include "nsSVGClipPathFrame.h" #include "nsSVGEffects.h" +#include "nsSVGElement.h" #include "nsSVGFilterFrame.h" #include "nsSVGFilterPaintCallback.h" #include "nsSVGMaskFrame.h" @@ -143,9 +144,10 @@ GetPreEffectsVisualOverflowUnion(nsIFrame* aFirstContinuation, bool nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) { - if (aFrame->IsFrameOfType(nsIFrame::eSVG)) { - return false; - } + // Even when SVG display lists are disabled, returning true for SVG frames + // does not adversely affect any of our callers. Therefore we don't bother + // 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->GetStyleSVGReset(); return (style->mFilter || style->mClipPath || style->mMask); } @@ -153,6 +155,13 @@ nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) /* static */ nsPoint nsSVGIntegrationUtils::GetOffsetToUserSpace(nsIFrame* aFrame) { + if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { + // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the + // covered region relative to the nsSVGOuterSVGFrame, which is absolutely + // not what we want. SVG frames are always in user space, so they have + // no offset adjustment to make. + return nsPoint(); + } // We could allow aFrame to be any continuation, but since that would require // a GetPrevContinuation() virtual call and conditional returns, and since // all our current consumers always pass in the first continuation, we don't @@ -375,7 +384,10 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx, { #ifdef DEBUG nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame); - NS_ASSERTION(!svgChildFrame, "Should never be called on an SVG frame"); + NS_ASSERTION(!svgChildFrame || + (NS_SVGDisplayListPaintingEnabled() && + !(aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)), + "Should not use nsSVGIntegrationUtils on this SVG frame"); #endif /* SVG defines the following rendering model: @@ -392,10 +404,27 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx, * + Merge opacity and masking if both used together. */ + const nsIContent* content = aFrame->GetContent(); + bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); + if (hasSVGLayout) { + nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame); + if (!svgChildFrame || !aFrame->GetContent()->IsSVG()) { + NS_ASSERTION(false, "why?"); + return; + } + if (!static_cast(content)->HasValidDimensions()) { + return; // The SVG spec says not to draw _anything_ + } + } + float opacity = aFrame->GetStyleDisplay()->mOpacity; if (opacity == 0.0f) { return; } + if (opacity != 1.0f && + hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) { + opacity = 1.0f; + } /* Properties are added lazily and may have been removed by a restyle, so make sure all applicable ones are set again. */ @@ -420,10 +449,17 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx, PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); nsPoint firstFrameOffset = GetOffsetToUserSpace(firstFrame); - nsPoint offset = (aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset). - ToNearestPixels(appUnitsPerDevPixel). - ToAppUnits(appUnitsPerDevPixel); - aCtx->Translate(offset); + nsPoint offset = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset; + nsPoint offsetWithoutSVGGeomFramePos = offset; + nsPoint svgGeomFramePos; + if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry)) { + // SVG leaf frames apply their offset themselves, we need to unapply it at + // various points below to prevent it being double counted. + svgGeomFramePos = aFrame->GetPosition(); + offsetWithoutSVGGeomFramePos -= svgGeomFramePos; + } + + aCtx->Translate(offsetWithoutSVGGeomFramePos); gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame); @@ -433,7 +469,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx, if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) { complexEffects = true; gfx->Save(); - aCtx->IntersectClip(aFrame->GetVisualOverflowRect()); + aCtx->IntersectClip(aFrame->GetVisualOverflowRect() + svgGeomFramePos); gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); } @@ -448,13 +484,13 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx, /* Paint the child */ if (filterFrame) { RegularFramePaintCallback callback(aBuilder, aLayerManager, - offset); + offsetWithoutSVGGeomFramePos); nsRect dirtyRect = aDirtyRect - offset; filterFrame->PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRect); } else { gfx->SetMatrix(matrixAutoSaveRestore.Matrix()); aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder); - aCtx->Translate(offset); + aCtx->Translate(offsetWithoutSVGGeomFramePos); } if (clipPathFrame && isTrivialClip) { diff --git a/layout/svg/base/src/nsSVGIntegrationUtils.h b/layout/svg/base/src/nsSVGIntegrationUtils.h index 8ba23bdefb5..c36fae50e1f 100644 --- a/layout/svg/base/src/nsSVGIntegrationUtils.h +++ b/layout/svg/base/src/nsSVGIntegrationUtils.h @@ -28,7 +28,7 @@ class nsSVGIntegrationUtils MOZ_FINAL { public: /** - * Returns true if a non-SVG frame has SVG effects. + * Returns true if SVG effects are currently applied to this frame. */ static bool UsingEffectsForFrame(const nsIFrame* aFrame); diff --git a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp index 14a260e0cc3..33a4d864a72 100644 --- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp @@ -396,6 +396,17 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages."); + nsSVGSVGElement *svgElem = static_cast(mContent); + + nsSVGOuterSVGAnonChildFrame *anonKid = + static_cast(GetFirstPrincipalChild()); + + if (mState & NS_FRAME_FIRST_REFLOW) { + // Initialize + svgElem->mHasChildrenOnlyTransform = + anonKid->HasChildrenOnlyTransform(nsnull); + } + // If our SVG viewport has changed, update our content and notify. // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace @@ -403,8 +414,6 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedWidth()), nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedHeight())); - nsSVGSVGElement *svgElem = static_cast(mContent); - PRUint32 changeBits = 0; if (newViewportSize != svgElem->GetViewportSize()) { changeBits |= COORD_CONTEXT_CHANGED; @@ -419,9 +428,6 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, NotifyViewportOrTransformChanged(changeBits); } - nsSVGOuterSVGAnonChildFrame *anonKid = - static_cast(GetFirstPrincipalChild()); - if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) { // Now that we've marked the necessary children as dirty, call // UpdateBounds() on them: @@ -469,6 +475,9 @@ nsSVGOuterSVGFrame::DidReflow(nsPresContext* aPresContext, //---------------------------------------------------------------------- // container methods +/** + * Used to paint/hit-test SVG when SVG display lists are disabled. + */ class nsDisplayOuterSVG : public nsDisplayItem { public: nsDisplayOuterSVG(nsDisplayListBuilder* aBuilder, @@ -630,9 +639,20 @@ nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayList childItems; - rv = childItems.AppendNewToTop( - new (aBuilder) nsDisplayOuterSVG(aBuilder, this)); - NS_ENSURE_SUCCESS(rv, rv); + if ((aBuilder->IsForEventDelivery() && + NS_SVGDisplayListHitTestingEnabled()) || + NS_SVGDisplayListPaintingEnabled()) { + nsDisplayList *nonContentList = &childItems; + nsDisplayListSet set(nonContentList, nonContentList, nonContentList, + &childItems, nonContentList, nonContentList); + nsresult rv = + BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, set); + NS_ENSURE_SUCCESS(rv, rv); + } else { + rv = childItems.AppendNewToTop( + new (aBuilder) nsDisplayOuterSVG(aBuilder, this)); + NS_ENSURE_SUCCESS(rv, rv); + } // Clip to our _content_ box: nsRect clipRect = diff --git a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp index 1a1af9acea6..1c56de7ce42 100644 --- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp @@ -9,6 +9,7 @@ // Keep others in (case-insensitive) order: #include "gfxContext.h" #include "gfxPlatform.h" +#include "nsDisplayList.h" #include "nsGkAtoms.h" #include "nsRenderingContext.h" #include "nsSVGEffects.h" @@ -40,6 +41,61 @@ NS_QUERYFRAME_HEAD(nsSVGPathGeometryFrame) NS_QUERYFRAME_ENTRY(nsISVGChildFrame) NS_QUERYFRAME_TAIL_INHERITING(nsSVGPathGeometryFrameBase) +//---------------------------------------------------------------------- +// Display list item: + +class nsDisplaySVGPathGeometry : public nsDisplayItem { +public: + nsDisplaySVGPathGeometry(nsDisplayListBuilder* aBuilder, + nsSVGPathGeometryFrame* aFrame) + : nsDisplayItem(aBuilder, aFrame) + { + MOZ_COUNT_CTOR(nsDisplaySVGPathGeometry); + NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplaySVGPathGeometry() { + MOZ_COUNT_DTOR(nsDisplaySVGPathGeometry); + } +#endif + + NS_DISPLAY_DECL_NAME("nsDisplaySVGPathGeometry", TYPE_SVG_PATH_GEOMETRY) + + virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, + HitTestState* aState, nsTArray *aOutFrames); + virtual void Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx); +}; + +void +nsDisplaySVGPathGeometry::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, + HitTestState* aState, nsTArray *aOutFrames) +{ + nsSVGPathGeometryFrame *frame = static_cast(mFrame); + nsPoint pointRelativeToReferenceFrame = aRect.Center(); + // ToReferenceFrame() includes frame->GetPosition(), our user space position. + nsPoint userSpacePt = pointRelativeToReferenceFrame - + (ToReferenceFrame() - frame->GetPosition()); + if (frame->GetFrameForPoint(userSpacePt)) { + aOutFrames->AppendElement(frame); + } +} + +void +nsDisplaySVGPathGeometry::Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) +{ + // ToReferenceFrame includes our mRect offset, but painting takes + // account of that too. To avoid double counting, we subtract that + // here. + nsPoint offset = ToReferenceFrame() - mFrame->GetPosition(); + + aCtx->PushState(); + aCtx->Translate(offset); + static_cast(mFrame)->PaintSVG(aCtx, nsnull); + aCtx->PopState(); +} + //---------------------------------------------------------------------- // nsIFrame methods @@ -92,8 +148,7 @@ nsSVGPathGeometryFrame::IsSVGTransformed(gfxMatrix *aOwnTransform, } nsSVGElement *content = static_cast(mContent); - const SVGAnimatedTransformList *list = content->GetAnimatedTransformList(); - if ((list && !list->GetAnimValue().IsEmpty()) || + if (content->GetAnimatedTransformList() || content->GetAnimateMotionTransform()) { if (aOwnTransform) { *aOwnTransform = content->PrependLocalTransformsTo(gfxMatrix(), @@ -104,6 +159,18 @@ nsSVGPathGeometryFrame::IsSVGTransformed(gfxMatrix *aOwnTransform, return foundTransform; } +NS_IMETHODIMP +nsSVGPathGeometryFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + if (!static_cast(mContent)->HasValidDimensions()) { + return NS_OK; + } + return aLists.Content()->AppendNewToTop( + new (aBuilder) nsDisplaySVGPathGeometry(aBuilder, this)); +} + //---------------------------------------------------------------------- // nsISVGChildFrame methods @@ -154,6 +221,9 @@ NS_IMETHODIMP_(nsIFrame*) nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint) { gfxMatrix canvasTM = GetCanvasTM(FOR_HIT_TESTING); + if (canvasTM.IsSingular()) { + return nsnull; + } PRUint16 fillRule, hitTestFlags; if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) { hitTestFlags = SVG_HIT_TEST_FILL; @@ -162,9 +232,6 @@ nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint) hitTestFlags = GetHitTestFlags(); // XXX once bug 614732 is fixed, aPoint won't need any conversion in order // to compare it with mRect. - if (canvasTM.IsSingular()) { - return nsnull; - } nsPoint point = nsSVGUtils::TransformOuterSVGPointToChildFrame(aPoint, canvasTM, PresContext()); if (!hitTestFlags || ((hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) && diff --git a/layout/svg/base/src/nsSVGPathGeometryFrame.h b/layout/svg/base/src/nsSVGPathGeometryFrame.h index ed8dcdb6e58..e2beafd7396 100644 --- a/layout/svg/base/src/nsSVGPathGeometryFrame.h +++ b/layout/svg/base/src/nsSVGPathGeometryFrame.h @@ -17,6 +17,7 @@ #include "nsSVGUtils.h" class gfxContext; +class nsDisplaySVGPathGeometry; class nsIAtom; class nsIFrame; class nsIPresShell; @@ -34,6 +35,9 @@ class nsSVGPathGeometryFrame : public nsSVGPathGeometryFrameBase, { friend nsIFrame* NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); + + friend class nsDisplaySVGPathGeometry; + protected: nsSVGPathGeometryFrame(nsStyleContext* aContext) : nsSVGPathGeometryFrameBase(aContext) @@ -69,6 +73,10 @@ public: } #endif + NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists); + // nsSVGGeometryFrame methods gfxMatrix GetCanvasTM(PRUint32 aFor); diff --git a/layout/svg/base/src/nsSVGSwitchFrame.cpp b/layout/svg/base/src/nsSVGSwitchFrame.cpp index e2364cd77a8..ab33f97d648 100644 --- a/layout/svg/base/src/nsSVGSwitchFrame.cpp +++ b/layout/svg/base/src/nsSVGSwitchFrame.cpp @@ -46,6 +46,10 @@ public: } #endif + NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists); + // nsISVGChildFrame interface: NS_IMETHOD PaintSVG(nsRenderingContext* aContext, const nsIntRect *aDirtyRect); NS_IMETHODIMP_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint); @@ -88,10 +92,27 @@ nsSVGSwitchFrame::GetType() const return nsGkAtoms::svgSwitchFrame; } +NS_IMETHODIMP +nsSVGSwitchFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + nsIFrame* kid = GetActiveChildFrame(); + if (kid) { + return BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); + } + return NS_OK; +} + NS_IMETHODIMP nsSVGSwitchFrame::PaintSVG(nsRenderingContext* aContext, const nsIntRect *aDirtyRect) { + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only painting of non-display " + "SVG should take this code path"); + const nsStyleDisplay *display = mStyleContext->GetStyleDisplay(); if (display->mOpacity == 0.0) return NS_OK; @@ -107,6 +128,11 @@ nsSVGSwitchFrame::PaintSVG(nsRenderingContext* aContext, NS_IMETHODIMP_(nsIFrame*) nsSVGSwitchFrame::GetFrameForPoint(const nsPoint &aPoint) { + NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only hit-testing of non-display " + "SVG should take this code path"); + nsIFrame *kid = GetActiveChildFrame(); if (kid) { nsISVGChildFrame* svgFrame = do_QueryFrame(kid); diff --git a/layout/svg/base/src/nsSVGTextFrame.cpp b/layout/svg/base/src/nsSVGTextFrame.cpp index 7b718b3cca6..be00aa0e6cf 100644 --- a/layout/svg/base/src/nsSVGTextFrame.cpp +++ b/layout/svg/base/src/nsSVGTextFrame.cpp @@ -76,6 +76,15 @@ nsSVGTextFrame::GetType() const return nsGkAtoms::svgTextFrame; } +NS_IMETHODIMP +nsSVGTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + UpdateGlyphPositioning(true); + return nsSVGTextFrameBase::BuildDisplayList(aBuilder, aDirtyRect, aLists); +} + //---------------------------------------------------------------------- // nsSVGTextContainerFrame PRUint32 @@ -197,6 +206,11 @@ NS_IMETHODIMP nsSVGTextFrame::PaintSVG(nsRenderingContext* aContext, const nsIntRect *aDirtyRect) { + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only painting of non-display " + "SVG should take this code path"); + UpdateGlyphPositioning(true); return nsSVGTextFrameBase::PaintSVG(aContext, aDirtyRect); @@ -205,6 +219,11 @@ nsSVGTextFrame::PaintSVG(nsRenderingContext* aContext, NS_IMETHODIMP_(nsIFrame*) nsSVGTextFrame::GetFrameForPoint(const nsPoint &aPoint) { + NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || + (mState & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only hit-testing of non-display " + "SVG should take this code path"); + UpdateGlyphPositioning(true); return nsSVGTextFrameBase::GetFrameForPoint(aPoint); diff --git a/layout/svg/base/src/nsSVGTextFrame.h b/layout/svg/base/src/nsSVGTextFrame.h index 60b910df616..5bfb3e8d2bc 100644 --- a/layout/svg/base/src/nsSVGTextFrame.h +++ b/layout/svg/base/src/nsSVGTextFrame.h @@ -51,6 +51,10 @@ public: } #endif + NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists); + // nsISVGChildFrame interface: virtual void NotifySVGChanged(PRUint32 aFlags); // Override these four to ensure that UpdateGlyphPositioning is called diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index 10a4335b7cc..4b0f3f1add7 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -1118,6 +1118,11 @@ nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext, const nsIntRect *aDirtyRect, nsIFrame *aFrame) { + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || + (aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD), + "If display lists are enabled, only painting of non-display " + "SVG should take this code path"); + nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame); if (!svgChildFrame) return; @@ -1618,6 +1623,9 @@ nsSVGUtils::GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH, bool nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame) { + if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { + return false; + } nsIAtom *type = aFrame->GetType(); if (type != nsGkAtoms::svgImageFrame && type != nsGkAtoms::svgPathGeometryFrame) {