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