michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Keep in (case-insensitive) order: michael@0: #include "gfxRect.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "nsSVGGFrame.h" michael@0: #include "mozilla/dom/SVGSwitchElement.h" michael@0: #include "nsSVGUtils.h" michael@0: michael@0: class nsRenderingContext; michael@0: michael@0: using namespace mozilla::gfx; michael@0: michael@0: typedef nsSVGGFrame nsSVGSwitchFrameBase; michael@0: michael@0: class nsSVGSwitchFrame : public nsSVGSwitchFrameBase michael@0: { michael@0: friend nsIFrame* michael@0: NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); michael@0: protected: michael@0: nsSVGSwitchFrame(nsStyleContext* aContext) : michael@0: nsSVGSwitchFrameBase(aContext) {} michael@0: michael@0: public: michael@0: NS_DECL_FRAMEARENA_HELPERS michael@0: michael@0: #ifdef DEBUG michael@0: virtual void Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: /** michael@0: * Get the "type" of the frame michael@0: * michael@0: * @see nsGkAtoms::svgSwitchFrame michael@0: */ michael@0: virtual nsIAtom* GetType() const MOZ_OVERRIDE; michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("SVGSwitch"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) MOZ_OVERRIDE; michael@0: michael@0: // nsISVGChildFrame interface: michael@0: virtual nsresult PaintSVG(nsRenderingContext* aContext, michael@0: const nsIntRect *aDirtyRect, michael@0: nsIFrame* aTransformRoot) MOZ_OVERRIDE; michael@0: nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE; michael@0: nsRect GetCoveredRegion() MOZ_OVERRIDE; michael@0: virtual void ReflowSVG() MOZ_OVERRIDE; michael@0: virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace, michael@0: uint32_t aFlags) MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: nsIFrame *GetActiveChildFrame(); michael@0: }; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Implementation michael@0: michael@0: nsIFrame* michael@0: NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsSVGSwitchFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGSwitchFrame) michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsSVGSwitchFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_ASSERTION(aContent->IsSVG(nsGkAtoms::svgSwitch), michael@0: "Content is not an SVG switch"); michael@0: michael@0: nsSVGSwitchFrameBase::Init(aContent, aParent, aPrevInFlow); michael@0: } michael@0: #endif /* DEBUG */ michael@0: michael@0: nsIAtom * michael@0: nsSVGSwitchFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::svgSwitchFrame; michael@0: } michael@0: michael@0: void michael@0: nsSVGSwitchFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: nsIFrame* kid = GetActiveChildFrame(); michael@0: if (kid) { michael@0: BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsSVGSwitchFrame::PaintSVG(nsRenderingContext* aContext, michael@0: const nsIntRect *aDirtyRect, michael@0: nsIFrame* aTransformRoot) michael@0: { michael@0: NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || michael@0: (mState & NS_FRAME_IS_NONDISPLAY), michael@0: "If display lists are enabled, only painting of non-display " michael@0: "SVG should take this code path"); michael@0: michael@0: if (StyleDisplay()->mOpacity == 0.0) michael@0: return NS_OK; michael@0: michael@0: nsIFrame *kid = GetActiveChildFrame(); michael@0: if (kid) { michael@0: nsSVGUtils::PaintFrameWithEffects(aContext, aDirtyRect, kid, aTransformRoot); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsIFrame* michael@0: nsSVGSwitchFrame::GetFrameForPoint(const nsPoint &aPoint) michael@0: { michael@0: NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || michael@0: (mState & NS_FRAME_IS_NONDISPLAY), michael@0: "If display lists are enabled, only hit-testing of non-display " michael@0: "SVG should take this code path"); michael@0: michael@0: nsIFrame *kid = GetActiveChildFrame(); michael@0: nsISVGChildFrame* svgFrame = do_QueryFrame(kid); michael@0: if (svgFrame) { michael@0: return svgFrame->GetFrameForPoint(aPoint); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRect michael@0: nsSVGSwitchFrame::GetCoveredRegion() michael@0: { michael@0: nsRect rect; michael@0: michael@0: nsIFrame *kid = GetActiveChildFrame(); michael@0: nsISVGChildFrame* child = do_QueryFrame(kid); michael@0: if (child) { michael@0: rect = child->GetCoveredRegion(); michael@0: } michael@0: return rect; michael@0: } michael@0: michael@0: void michael@0: nsSVGSwitchFrame::ReflowSVG() michael@0: { michael@0: NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), michael@0: "This call is probably a wasteful mistake"); michael@0: michael@0: NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), michael@0: "ReflowSVG mechanism not designed for this"); michael@0: michael@0: if (!nsSVGUtils::NeedsReflowSVG(this)) { michael@0: return; michael@0: } michael@0: michael@0: // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame, michael@0: // then our outer- has previously had its initial reflow. In that case michael@0: // we need to make sure that that bit has been removed from ourself _before_ michael@0: // recursing over our children to ensure that they know too. Otherwise, we michael@0: // need to remove it _after_ recursing over our children so that they know michael@0: // the initial reflow is currently underway. michael@0: michael@0: bool isFirstReflow = (mState & NS_FRAME_FIRST_REFLOW); michael@0: michael@0: bool outerSVGHasHadFirstReflow = michael@0: (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0; michael@0: michael@0: if (outerSVGHasHadFirstReflow) { michael@0: mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children michael@0: } michael@0: michael@0: nsOverflowAreas overflowRects; michael@0: michael@0: nsIFrame *child = GetActiveChildFrame(); michael@0: nsISVGChildFrame* svgChild = do_QueryFrame(child); michael@0: if (svgChild) { michael@0: NS_ABORT_IF_FALSE(!(child->GetStateBits() & NS_FRAME_IS_NONDISPLAY), michael@0: "Check for this explicitly in the |if|, then"); michael@0: svgChild->ReflowSVG(); michael@0: michael@0: // We build up our child frame overflows here instead of using michael@0: // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same michael@0: // frame list, and we're iterating over that list now anyway. michael@0: ConsiderChildOverflow(overflowRects, child); michael@0: } michael@0: michael@0: if (isFirstReflow) { michael@0: // Make sure we have our filter property (if any) before calling michael@0: // FinishAndStoreOverflow (subsequent filter changes are handled off michael@0: // nsChangeHint_UpdateEffects): michael@0: nsSVGEffects::UpdateEffects(this); michael@0: } michael@0: michael@0: FinishAndStoreOverflow(overflowRects, mRect.Size()); michael@0: michael@0: // Remove state bits after FinishAndStoreOverflow so that it doesn't michael@0: // invalidate on first reflow: michael@0: mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: } michael@0: michael@0: SVGBBox michael@0: nsSVGSwitchFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace, michael@0: uint32_t aFlags) michael@0: { michael@0: nsIFrame* kid = GetActiveChildFrame(); michael@0: nsISVGChildFrame* svgKid = do_QueryFrame(kid); michael@0: if (svgKid) { michael@0: nsIContent *content = kid->GetContent(); michael@0: gfxMatrix transform = ThebesMatrix(aToBBoxUserspace); michael@0: if (content->IsSVG()) { michael@0: transform = static_cast(content)-> michael@0: PrependLocalTransformsTo(transform); michael@0: } michael@0: return svgKid->GetBBoxContribution(ToMatrix(transform), aFlags); michael@0: } michael@0: return SVGBBox(); michael@0: } michael@0: michael@0: nsIFrame * michael@0: nsSVGSwitchFrame::GetActiveChildFrame() michael@0: { michael@0: nsIContent *activeChild = michael@0: static_cast(mContent)->GetActiveChild(); michael@0: michael@0: if (activeChild) { michael@0: for (nsIFrame* kid = mFrames.FirstChild(); kid; michael@0: kid = kid->GetNextSibling()) { michael@0: michael@0: if (activeChild == kid->GetContent()) { michael@0: return kid; michael@0: } michael@0: } michael@0: } michael@0: return nullptr; michael@0: }