/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // Keep in (case-insensitive) order: #include "gfxMatrix.h" #include "gfxRect.h" #include "nsSVGEffects.h" #include "nsSVGGFrame.h" #include "nsSVGSwitchElement.h" #include "nsSVGUtils.h" class nsRenderingContext; typedef nsSVGGFrame nsSVGSwitchFrameBase; class nsSVGSwitchFrame : public nsSVGSwitchFrameBase { friend nsIFrame* NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); protected: nsSVGSwitchFrame(nsStyleContext* aContext) : nsSVGSwitchFrameBase(aContext) {} public: NS_DECL_FRAMEARENA_HELPERS #ifdef DEBUG NS_IMETHOD Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* aPrevInFlow); #endif /** * Get the "type" of the frame * * @see nsGkAtoms::svgSwitchFrame */ virtual nsIAtom* GetType() const; #ifdef DEBUG NS_IMETHOD GetFrameName(nsAString& aResult) const { return MakeFrameName(NS_LITERAL_STRING("SVGSwitch"), aResult); } #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); NS_IMETHODIMP_(nsRect) GetCoveredRegion(); virtual void ReflowSVG(); virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, uint32_t aFlags); private: nsIFrame *GetActiveChildFrame(); }; //---------------------------------------------------------------------- // Implementation nsIFrame* NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) { return new (aPresShell) nsSVGSwitchFrame(aContext); } NS_IMPL_FRAMEARENA_HELPERS(nsSVGSwitchFrame) #ifdef DEBUG NS_IMETHODIMP nsSVGSwitchFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* aPrevInFlow) { nsCOMPtr svgSwitch = do_QueryInterface(aContent); NS_ASSERTION(svgSwitch, "Content is not an SVG switch\n"); return nsSVGSwitchFrameBase::Init(aContent, aParent, aPrevInFlow); } #endif /* DEBUG */ nsIAtom * 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; nsIFrame *kid = GetActiveChildFrame(); if (kid) { nsSVGUtils::PaintFrameWithEffects(aContext, aDirtyRect, kid); } return NS_OK; } 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); if (svgFrame) { return svgFrame->GetFrameForPoint(aPoint); } } return nullptr; } NS_IMETHODIMP_(nsRect) nsSVGSwitchFrame::GetCoveredRegion() { nsRect rect; nsIFrame *kid = GetActiveChildFrame(); if (kid) { nsISVGChildFrame* child = do_QueryFrame(kid); if (child) { rect = child->GetCoveredRegion(); } } return rect; } void nsSVGSwitchFrame::ReflowSVG() { NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), "This call is probably a wasteful mistake"); NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD), "ReflowSVG mechanism not designed for this"); if (!nsSVGUtils::NeedsReflowSVG(this)) { return; } // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame, // then our outer- has previously had its initial reflow. In that case // we need to make sure that that bit has been removed from ourself _before_ // recursing over our children to ensure that they know too. Otherwise, we // need to remove it _after_ recursing over our children so that they know // the initial reflow is currently underway. bool outerSVGHasHadFirstReflow = (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0; if (outerSVGHasHadFirstReflow) { mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children } nsOverflowAreas overflowRects; nsIFrame *child = GetActiveChildFrame(); if (child) { nsISVGChildFrame* svgChild = do_QueryFrame(child); if (svgChild) { NS_ABORT_IF_FALSE(!(child->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD), "Check for this explicitly in the |if|, then"); svgChild->ReflowSVG(); // We build up our child frame overflows here instead of using // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same // frame list, and we're iterating over that list now anyway. ConsiderChildOverflow(overflowRects, child); } } if (mState & NS_FRAME_FIRST_REFLOW) { // Make sure we have our filter property (if any) before calling // FinishAndStoreOverflow (subsequent filter changes are handled off // nsChangeHint_UpdateEffects): nsSVGEffects::UpdateEffects(this); } // We only invalidate if we are dirty, if our outer- has already had its // initial reflow (since if it hasn't, its entire area will be invalidated // when it gets that initial reflow), and if our parent is not dirty (since // if it is, then it will invalidate its entire new area, which will include // our new area). bool invalidate = (mState & NS_FRAME_IS_DIRTY) && !(GetParent()->GetStateBits() & (NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY)); FinishAndStoreOverflow(overflowRects, mRect.Size()); // Remove state bits after FinishAndStoreOverflow so that it doesn't // invalidate on first reflow: mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN); if (invalidate) { // XXXSDL Let FinishAndStoreOverflow do this. nsSVGUtils::InvalidateBounds(this, true); } } SVGBBox nsSVGSwitchFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, uint32_t aFlags) { nsIFrame* kid = GetActiveChildFrame(); if (kid) { nsISVGChildFrame* svgKid = do_QueryFrame(kid); if (svgKid) { nsIContent *content = kid->GetContent(); gfxMatrix transform = aToBBoxUserspace; if (content->IsSVG()) { transform = static_cast(content)-> PrependLocalTransformsTo(aToBBoxUserspace); } return svgKid->GetBBoxContribution(transform, aFlags); } } return SVGBBox(); } nsIFrame * nsSVGSwitchFrame::GetActiveChildFrame() { nsIContent *activeChild = static_cast(mContent)->GetActiveChild(); if (activeChild) { for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { if (activeChild == kid->GetContent()) { return kid; } } } return nullptr; }