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 // Main header first:
7 #include "nsSVGMarkerFrame.h"
8
9 // Keep others in (case-insensitive) order:
10 #include "gfxContext.h"
11 #include "nsSVGEffects.h"
12 #include "mozilla/dom/SVGMarkerElement.h"
13 #include "nsSVGPathGeometryElement.h"
14 #include "nsSVGPathGeometryFrame.h"
15
16 using namespace mozilla::dom;
17 using namespace mozilla::gfx;
18
19 nsContainerFrame*
NS_NewSVGMarkerFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)20 NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
21 {
22 return new (aPresShell) nsSVGMarkerFrame(aContext);
23 }
24
NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerFrame)25 NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerFrame)
26
27 //----------------------------------------------------------------------
28 // nsIFrame methods:
29
30 nsresult
31 nsSVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID,
32 nsIAtom* aAttribute,
33 int32_t aModType)
34 {
35 if (aNameSpaceID == kNameSpaceID_None &&
36 (aAttribute == nsGkAtoms::markerUnits ||
37 aAttribute == nsGkAtoms::refX ||
38 aAttribute == nsGkAtoms::refY ||
39 aAttribute == nsGkAtoms::markerWidth ||
40 aAttribute == nsGkAtoms::markerHeight ||
41 aAttribute == nsGkAtoms::orient ||
42 aAttribute == nsGkAtoms::preserveAspectRatio ||
43 aAttribute == nsGkAtoms::viewBox)) {
44 nsSVGEffects::InvalidateDirectRenderingObservers(this);
45 }
46
47 return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
48 aAttribute, aModType);
49 }
50
51 #ifdef DEBUG
52 void
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)53 nsSVGMarkerFrame::Init(nsIContent* aContent,
54 nsContainerFrame* aParent,
55 nsIFrame* aPrevInFlow)
56 {
57 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::marker), "Content is not an SVG marker");
58
59 nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
60 }
61 #endif /* DEBUG */
62
63 nsIAtom *
GetType() const64 nsSVGMarkerFrame::GetType() const
65 {
66 return nsGkAtoms::svgMarkerFrame;
67 }
68
69 //----------------------------------------------------------------------
70 // nsSVGContainerFrame methods:
71
72 gfxMatrix
GetCanvasTM()73 nsSVGMarkerFrame::GetCanvasTM()
74 {
75 NS_ASSERTION(mMarkedFrame, "null nsSVGPathGeometry frame");
76
77 if (mInUse2) {
78 // We're going to be bailing drawing the marker, so return an identity.
79 return gfxMatrix();
80 }
81
82 SVGMarkerElement *content = static_cast<SVGMarkerElement*>(mContent);
83
84 mInUse2 = true;
85 gfxMatrix markedTM = mMarkedFrame->GetCanvasTM();
86 mInUse2 = false;
87
88 Matrix markerTM = content->GetMarkerTransform(mStrokeWidth, mX, mY,
89 mAutoAngle, mIsStart);
90 Matrix viewBoxTM = content->GetViewBoxTransform();
91
92 return ThebesMatrix(viewBoxTM * markerTM) * markedTM;
93 }
94
95 static nsIFrame*
GetAnonymousChildFrame(nsIFrame * aFrame)96 GetAnonymousChildFrame(nsIFrame* aFrame)
97 {
98 nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
99 MOZ_ASSERT(kid && kid->GetType() == nsGkAtoms::svgMarkerAnonChildFrame,
100 "expected to find anonymous child of marker frame");
101 return kid;
102 }
103
104 nsresult
PaintMark(gfxContext & aContext,const gfxMatrix & aToMarkedFrameUserSpace,nsSVGPathGeometryFrame * aMarkedFrame,nsSVGMark * aMark,float aStrokeWidth)105 nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
106 const gfxMatrix& aToMarkedFrameUserSpace,
107 nsSVGPathGeometryFrame *aMarkedFrame,
108 nsSVGMark *aMark, float aStrokeWidth)
109 {
110 // If the flag is set when we get here, it means this marker frame
111 // has already been used painting the current mark, and the document
112 // has a marker reference loop.
113 if (mInUse)
114 return NS_OK;
115
116 AutoMarkerReferencer markerRef(this, aMarkedFrame);
117
118 SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent);
119 if (!marker->HasValidDimensions()) {
120 return NS_OK;
121 }
122
123 const nsSVGViewBoxRect viewBox = marker->GetViewBoxRect();
124
125 if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
126 // We must disable rendering if the viewBox width or height are zero.
127 return NS_OK;
128 }
129
130 mStrokeWidth = aStrokeWidth;
131 mX = aMark->x;
132 mY = aMark->y;
133 mAutoAngle = aMark->angle;
134 mIsStart = aMark->type == nsSVGMark::eStart;
135
136 Matrix viewBoxTM = marker->GetViewBoxTransform();
137
138 Matrix markerTM = marker->GetMarkerTransform(mStrokeWidth, mX, mY,
139 mAutoAngle, mIsStart);
140
141 gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(markerTM) *
142 aToMarkedFrameUserSpace;
143
144 if (StyleDisplay()->IsScrollableOverflow()) {
145 aContext.Save();
146 gfxRect clipRect =
147 nsSVGUtils::GetClipRectForFrame(this, viewBox.x, viewBox.y,
148 viewBox.width, viewBox.height);
149 nsSVGUtils::SetClipRect(&aContext, markTM, clipRect);
150 }
151
152
153 nsIFrame* kid = GetAnonymousChildFrame(this);
154 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
155 // The CTM of each frame referencing us may be different.
156 SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
157 DrawResult result = nsSVGUtils::PaintFrameWithEffects(kid, aContext, markTM);
158
159 if (StyleDisplay()->IsScrollableOverflow())
160 aContext.Restore();
161
162 return (result == DrawResult::SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
163 }
164
165 SVGBBox
GetMarkBBoxContribution(const Matrix & aToBBoxUserspace,uint32_t aFlags,nsSVGPathGeometryFrame * aMarkedFrame,const nsSVGMark * aMark,float aStrokeWidth)166 nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
167 uint32_t aFlags,
168 nsSVGPathGeometryFrame *aMarkedFrame,
169 const nsSVGMark *aMark,
170 float aStrokeWidth)
171 {
172 SVGBBox bbox;
173
174 // If the flag is set when we get here, it means this marker frame
175 // has already been used in calculating the current mark bbox, and
176 // the document has a marker reference loop.
177 if (mInUse)
178 return bbox;
179
180 AutoMarkerReferencer markerRef(this, aMarkedFrame);
181
182 SVGMarkerElement *content = static_cast<SVGMarkerElement*>(mContent);
183 if (!content->HasValidDimensions()) {
184 return bbox;
185 }
186
187 const nsSVGViewBoxRect viewBox = content->GetViewBoxRect();
188
189 if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
190 return bbox;
191 }
192
193 mStrokeWidth = aStrokeWidth;
194 mX = aMark->x;
195 mY = aMark->y;
196 mAutoAngle = aMark->angle;
197 mIsStart = aMark->type == nsSVGMark::eStart;
198
199 Matrix markerTM =
200 content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle, mIsStart);
201 Matrix viewBoxTM = content->GetViewBoxTransform();
202
203 Matrix tm = viewBoxTM * markerTM * aToBBoxUserspace;
204
205 nsISVGChildFrame* child = do_QueryFrame(GetAnonymousChildFrame(this));
206 // When we're being called to obtain the invalidation area, we need to
207 // pass down all the flags so that stroke is included. However, once DOM
208 // getBBox() accepts flags, maybe we should strip some of those here?
209
210 // We need to include zero width/height vertical/horizontal lines, so we have
211 // to use UnionEdges.
212 bbox.UnionEdges(child->GetBBoxContribution(tm, aFlags));
213
214 return bbox;
215 }
216
217 void
SetParentCoordCtxProvider(SVGSVGElement * aContext)218 nsSVGMarkerFrame::SetParentCoordCtxProvider(SVGSVGElement *aContext)
219 {
220 SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent);
221 marker->SetParentCoordCtxProvider(aContext);
222 }
223
224 //----------------------------------------------------------------------
225 // helper class
226
AutoMarkerReferencer(nsSVGMarkerFrame * aFrame,nsSVGPathGeometryFrame * aMarkedFrame MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)227 nsSVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer(
228 nsSVGMarkerFrame *aFrame,
229 nsSVGPathGeometryFrame *aMarkedFrame
230 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
231 : mFrame(aFrame)
232 {
233 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
234 mFrame->mInUse = true;
235 mFrame->mMarkedFrame = aMarkedFrame;
236
237 SVGSVGElement *ctx =
238 static_cast<nsSVGElement*>(aMarkedFrame->GetContent())->GetCtx();
239 mFrame->SetParentCoordCtxProvider(ctx);
240 }
241
~AutoMarkerReferencer()242 nsSVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer()
243 {
244 mFrame->SetParentCoordCtxProvider(nullptr);
245
246 mFrame->mMarkedFrame = nullptr;
247 mFrame->mInUse = false;
248 }
249
250 //----------------------------------------------------------------------
251 // Implementation of nsSVGMarkerAnonChildFrame
252
253 nsContainerFrame*
NS_NewSVGMarkerAnonChildFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)254 NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell,
255 nsStyleContext* aContext)
256 {
257 return new (aPresShell) nsSVGMarkerAnonChildFrame(aContext);
258 }
259
NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerAnonChildFrame)260 NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerAnonChildFrame)
261
262 #ifdef DEBUG
263 void
264 nsSVGMarkerAnonChildFrame::Init(nsIContent* aContent,
265 nsContainerFrame* aParent,
266 nsIFrame* aPrevInFlow)
267 {
268 MOZ_ASSERT(aParent->GetType() == nsGkAtoms::svgMarkerFrame,
269 "Unexpected parent");
270 nsSVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
271 }
272 #endif
273
274 nsIAtom *
GetType() const275 nsSVGMarkerAnonChildFrame::GetType() const
276 {
277 return nsGkAtoms::svgMarkerAnonChildFrame;
278 }
279