Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : // Keep in (case-insensitive) order:
7 : #include "gfxRect.h"
8 : #include "nsSVGEffects.h"
9 : #include "nsSVGGFrame.h"
10 : #include "mozilla/dom/SVGSwitchElement.h"
11 : #include "nsSVGUtils.h"
12 :
13 : using namespace mozilla::gfx;
14 : using namespace mozilla::image;
15 :
16 0 : class nsSVGSwitchFrame final : public nsSVGGFrame
17 : {
18 : friend nsIFrame*
19 : NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
20 : protected:
21 0 : explicit nsSVGSwitchFrame(nsStyleContext* aContext)
22 0 : : nsSVGGFrame(aContext, kClassID)
23 0 : {}
24 :
25 : public:
26 0 : NS_DECL_FRAMEARENA_HELPERS(nsSVGSwitchFrame)
27 :
28 : #ifdef DEBUG
29 : virtual void Init(nsIContent* aContent,
30 : nsContainerFrame* aParent,
31 : nsIFrame* aPrevInFlow) override;
32 : #endif
33 :
34 : #ifdef DEBUG_FRAME_DUMP
35 0 : virtual nsresult GetFrameName(nsAString& aResult) const override
36 : {
37 0 : return MakeFrameName(NS_LITERAL_STRING("SVGSwitch"), aResult);
38 : }
39 : #endif
40 :
41 : virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
42 : const nsRect& aDirtyRect,
43 : const nsDisplayListSet& aLists) override;
44 :
45 : // nsSVGDisplayableFrame interface:
46 : virtual void PaintSVG(gfxContext& aContext,
47 : const gfxMatrix& aTransform,
48 : imgDrawingParams& aPackage,
49 : const nsIntRect* aDirtyRect = nullptr) override;
50 : nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
51 : virtual void ReflowSVG() override;
52 : virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
53 : uint32_t aFlags) override;
54 :
55 : private:
56 : nsIFrame *GetActiveChildFrame();
57 : };
58 :
59 : //----------------------------------------------------------------------
60 : // Implementation
61 :
62 : nsIFrame*
63 0 : NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
64 : {
65 0 : return new (aPresShell) nsSVGSwitchFrame(aContext);
66 : }
67 :
68 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGSwitchFrame)
69 :
70 : #ifdef DEBUG
71 : void
72 0 : nsSVGSwitchFrame::Init(nsIContent* aContent,
73 : nsContainerFrame* aParent,
74 : nsIFrame* aPrevInFlow)
75 : {
76 0 : NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::svgSwitch),
77 : "Content is not an SVG switch");
78 :
79 0 : nsSVGGFrame::Init(aContent, aParent, aPrevInFlow);
80 0 : }
81 : #endif /* DEBUG */
82 :
83 : void
84 0 : nsSVGSwitchFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
85 : const nsRect& aDirtyRect,
86 : const nsDisplayListSet& aLists)
87 : {
88 0 : nsIFrame* kid = GetActiveChildFrame();
89 0 : if (kid) {
90 0 : BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
91 : }
92 0 : }
93 :
94 : void
95 0 : nsSVGSwitchFrame::PaintSVG(gfxContext& aContext,
96 : const gfxMatrix& aTransform,
97 : imgDrawingParams& aImgParams,
98 : const nsIntRect* aDirtyRect)
99 : {
100 0 : NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
101 : (mState & NS_FRAME_IS_NONDISPLAY),
102 : "If display lists are enabled, only painting of non-display "
103 : "SVG should take this code path");
104 :
105 0 : if (StyleEffects()->mOpacity == 0.0){
106 0 : return;
107 : }
108 :
109 0 : nsIFrame *kid = GetActiveChildFrame();
110 0 : if (kid) {
111 0 : gfxMatrix tm = aTransform;
112 0 : if (kid->GetContent()->IsSVGElement()) {
113 0 : tm = static_cast<nsSVGElement*>(kid->GetContent())->
114 0 : PrependLocalTransformsTo(tm, eUserSpaceToParent);
115 : }
116 0 : nsSVGUtils::PaintFrameWithEffects(kid, aContext, tm, aImgParams, aDirtyRect);
117 : }
118 : }
119 :
120 :
121 : nsIFrame*
122 0 : nsSVGSwitchFrame::GetFrameForPoint(const gfxPoint& aPoint)
123 : {
124 0 : NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
125 : (mState & NS_FRAME_IS_NONDISPLAY),
126 : "If display lists are enabled, only hit-testing of non-display "
127 : "SVG should take this code path");
128 :
129 0 : nsIFrame *kid = GetActiveChildFrame();
130 0 : nsSVGDisplayableFrame* svgFrame = do_QueryFrame(kid);
131 0 : if (svgFrame) {
132 : // Transform the point from our SVG user space to our child's.
133 0 : gfxPoint point = aPoint;
134 : gfxMatrix m =
135 0 : static_cast<const nsSVGElement*>(mContent)->
136 0 : PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
137 0 : m = static_cast<const nsSVGElement*>(kid->GetContent())->
138 0 : PrependLocalTransformsTo(m, eUserSpaceToParent);
139 0 : if (!m.IsIdentity()) {
140 0 : if (!m.Invert()) {
141 0 : return nullptr;
142 : }
143 0 : point = m.TransformPoint(point);
144 : }
145 0 : return svgFrame->GetFrameForPoint(point);
146 : }
147 :
148 0 : return nullptr;
149 : }
150 :
151 : void
152 0 : nsSVGSwitchFrame::ReflowSVG()
153 : {
154 0 : NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
155 : "This call is probably a wasteful mistake");
156 :
157 0 : MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
158 : "ReflowSVG mechanism not designed for this");
159 :
160 0 : if (!nsSVGUtils::NeedsReflowSVG(this)) {
161 0 : return;
162 : }
163 :
164 : // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
165 : // then our outer-<svg> has previously had its initial reflow. In that case
166 : // we need to make sure that that bit has been removed from ourself _before_
167 : // recursing over our children to ensure that they know too. Otherwise, we
168 : // need to remove it _after_ recursing over our children so that they know
169 : // the initial reflow is currently underway.
170 :
171 0 : bool isFirstReflow = (mState & NS_FRAME_FIRST_REFLOW);
172 :
173 : bool outerSVGHasHadFirstReflow =
174 0 : (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
175 :
176 0 : if (outerSVGHasHadFirstReflow) {
177 0 : mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
178 : }
179 :
180 0 : nsOverflowAreas overflowRects;
181 :
182 0 : nsIFrame *child = GetActiveChildFrame();
183 0 : nsSVGDisplayableFrame* svgChild = do_QueryFrame(child);
184 0 : if (svgChild) {
185 0 : MOZ_ASSERT(!(child->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
186 : "Check for this explicitly in the |if|, then");
187 0 : svgChild->ReflowSVG();
188 :
189 : // We build up our child frame overflows here instead of using
190 : // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
191 : // frame list, and we're iterating over that list now anyway.
192 0 : ConsiderChildOverflow(overflowRects, child);
193 : }
194 :
195 0 : if (isFirstReflow) {
196 : // Make sure we have our filter property (if any) before calling
197 : // FinishAndStoreOverflow (subsequent filter changes are handled off
198 : // nsChangeHint_UpdateEffects):
199 0 : nsSVGEffects::UpdateEffects(this);
200 : }
201 :
202 0 : FinishAndStoreOverflow(overflowRects, mRect.Size());
203 :
204 : // Remove state bits after FinishAndStoreOverflow so that it doesn't
205 : // invalidate on first reflow:
206 : mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
207 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
208 : }
209 :
210 : SVGBBox
211 0 : nsSVGSwitchFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
212 : uint32_t aFlags)
213 : {
214 0 : nsIFrame* kid = GetActiveChildFrame();
215 0 : nsSVGDisplayableFrame* svgKid = do_QueryFrame(kid);
216 0 : if (svgKid) {
217 0 : nsIContent *content = kid->GetContent();
218 0 : gfxMatrix transform = ThebesMatrix(aToBBoxUserspace);
219 0 : if (content->IsSVGElement()) {
220 : transform = static_cast<nsSVGElement*>(content)->
221 0 : PrependLocalTransformsTo(transform);
222 : }
223 0 : return svgKid->GetBBoxContribution(ToMatrix(transform), aFlags);
224 : }
225 0 : return SVGBBox();
226 : }
227 :
228 : nsIFrame *
229 0 : nsSVGSwitchFrame::GetActiveChildFrame()
230 : {
231 : nsIContent *activeChild =
232 0 : static_cast<mozilla::dom::SVGSwitchElement*>(mContent)->GetActiveChild();
233 :
234 0 : if (activeChild) {
235 0 : for (nsIFrame* kid = mFrames.FirstChild(); kid;
236 : kid = kid->GetNextSibling()) {
237 :
238 0 : if (activeChild == kid->GetContent()) {
239 0 : return kid;
240 : }
241 : }
242 : }
243 0 : return nullptr;
244 : }
|