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/. */
7 #include "nsSVGMarkerFrame.h"
9 // Keep others in (case-insensitive) order:
10 #include "gfxContext.h"
11 #include "nsRenderingContext.h"
12 #include "nsSVGEffects.h"
13 #include "mozilla/dom/SVGMarkerElement.h"
14 #include "nsSVGPathGeometryElement.h"
15 #include "nsSVGPathGeometryFrame.h"
17 using namespace mozilla::dom
;
18 using namespace mozilla::gfx
;
21 NS_NewSVGMarkerFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
23 return new (aPresShell
) nsSVGMarkerFrame(aContext
);
26 NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerFrame
)
28 //----------------------------------------------------------------------
32 nsSVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID
,
36 if (aNameSpaceID
== kNameSpaceID_None
&&
37 (aAttribute
== nsGkAtoms::markerUnits
||
38 aAttribute
== nsGkAtoms::refX
||
39 aAttribute
== nsGkAtoms::refY
||
40 aAttribute
== nsGkAtoms::markerWidth
||
41 aAttribute
== nsGkAtoms::markerHeight
||
42 aAttribute
== nsGkAtoms::orient
||
43 aAttribute
== nsGkAtoms::preserveAspectRatio
||
44 aAttribute
== nsGkAtoms::viewBox
)) {
45 nsSVGEffects::InvalidateDirectRenderingObservers(this);
48 return nsSVGMarkerFrameBase::AttributeChanged(aNameSpaceID
,
49 aAttribute
, aModType
);
54 nsSVGMarkerFrame::Init(nsIContent
* aContent
,
55 nsContainerFrame
* aParent
,
56 nsIFrame
* aPrevInFlow
)
58 NS_ASSERTION(aContent
->IsSVG(nsGkAtoms::marker
), "Content is not an SVG marker");
60 nsSVGMarkerFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
65 nsSVGMarkerFrame::GetType() const
67 return nsGkAtoms::svgMarkerFrame
;
70 //----------------------------------------------------------------------
71 // nsSVGContainerFrame methods:
74 nsSVGMarkerFrame::GetCanvasTM(uint32_t aFor
, nsIFrame
* aTransformRoot
)
76 NS_ASSERTION(mMarkedFrame
, "null nsSVGPathGeometry frame");
79 // We're going to be bailing drawing the marker, so return an identity.
83 SVGMarkerElement
*content
= static_cast<SVGMarkerElement
*>(mContent
);
86 gfxMatrix markedTM
= mMarkedFrame
->GetCanvasTM(aFor
, aTransformRoot
);
89 Matrix markerTM
= content
->GetMarkerTransform(mStrokeWidth
, mX
, mY
,
90 mAutoAngle
, mIsStart
);
91 Matrix viewBoxTM
= content
->GetViewBoxTransform();
93 return ThebesMatrix(viewBoxTM
* markerTM
) * markedTM
;
97 GetAnonymousChildFrame(nsIFrame
* aFrame
)
99 nsIFrame
* kid
= aFrame
->GetFirstPrincipalChild();
100 MOZ_ASSERT(kid
&& kid
->GetType() == nsGkAtoms::svgMarkerAnonChildFrame
,
101 "expected to find anonymous child of marker frame");
106 nsSVGMarkerFrame::PaintMark(nsRenderingContext
*aContext
,
107 nsSVGPathGeometryFrame
*aMarkedFrame
,
108 nsSVGMark
*aMark
, float aStrokeWidth
)
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.
116 AutoMarkerReferencer
markerRef(this, aMarkedFrame
);
118 SVGMarkerElement
*marker
= static_cast<SVGMarkerElement
*>(mContent
);
119 if (!marker
->HasValidDimensions()) {
123 const nsSVGViewBoxRect viewBox
= marker
->GetViewBoxRect();
125 if (viewBox
.width
<= 0.0f
|| viewBox
.height
<= 0.0f
) {
126 // We must disable rendering if the viewBox width or height are zero.
130 mStrokeWidth
= aStrokeWidth
;
133 mAutoAngle
= aMark
->angle
;
134 mIsStart
= aMark
->type
== nsSVGMark::eStart
;
136 gfxContext
*gfx
= aContext
->ThebesContext();
138 if (StyleDisplay()->IsScrollableOverflow()) {
141 nsSVGUtils::GetClipRectForFrame(this, viewBox
.x
, viewBox
.y
,
142 viewBox
.width
, viewBox
.height
);
143 nsSVGUtils::SetClipRect(gfx
, GetCanvasTM(nsISVGChildFrame::FOR_PAINTING
),
147 nsIFrame
* kid
= GetAnonymousChildFrame(this);
148 nsISVGChildFrame
* SVGFrame
= do_QueryFrame(kid
);
149 // The CTM of each frame referencing us may be different.
150 SVGFrame
->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED
);
151 nsSVGUtils::PaintFrameWithEffects(aContext
, nullptr, kid
);
153 if (StyleDisplay()->IsScrollableOverflow())
160 nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix
&aToBBoxUserspace
,
162 nsSVGPathGeometryFrame
*aMarkedFrame
,
163 const nsSVGMark
*aMark
,
168 // If the flag is set when we get here, it means this marker frame
169 // has already been used in calculating the current mark bbox, and
170 // the document has a marker reference loop.
174 AutoMarkerReferencer
markerRef(this, aMarkedFrame
);
176 SVGMarkerElement
*content
= static_cast<SVGMarkerElement
*>(mContent
);
177 if (!content
->HasValidDimensions()) {
181 const nsSVGViewBoxRect viewBox
= content
->GetViewBoxRect();
183 if (viewBox
.width
<= 0.0f
|| viewBox
.height
<= 0.0f
) {
187 mStrokeWidth
= aStrokeWidth
;
190 mAutoAngle
= aMark
->angle
;
191 mIsStart
= aMark
->type
== nsSVGMark::eStart
;
194 content
->GetMarkerTransform(mStrokeWidth
, mX
, mY
, mAutoAngle
, mIsStart
);
195 Matrix viewBoxTM
= content
->GetViewBoxTransform();
197 Matrix tm
= viewBoxTM
* markerTM
* aToBBoxUserspace
;
199 nsISVGChildFrame
* child
= do_QueryFrame(GetAnonymousChildFrame(this));
200 // When we're being called to obtain the invalidation area, we need to
201 // pass down all the flags so that stroke is included. However, once DOM
202 // getBBox() accepts flags, maybe we should strip some of those here?
204 // We need to include zero width/height vertical/horizontal lines, so we have
205 // to use UnionEdges.
206 bbox
.UnionEdges(child
->GetBBoxContribution(tm
, aFlags
));
212 nsSVGMarkerFrame::SetParentCoordCtxProvider(SVGSVGElement
*aContext
)
214 SVGMarkerElement
*marker
= static_cast<SVGMarkerElement
*>(mContent
);
215 marker
->SetParentCoordCtxProvider(aContext
);
218 //----------------------------------------------------------------------
221 nsSVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer(
222 nsSVGMarkerFrame
*aFrame
,
223 nsSVGPathGeometryFrame
*aMarkedFrame
224 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL
)
227 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
228 mFrame
->mInUse
= true;
229 mFrame
->mMarkedFrame
= aMarkedFrame
;
232 static_cast<nsSVGElement
*>(aMarkedFrame
->GetContent())->GetCtx();
233 mFrame
->SetParentCoordCtxProvider(ctx
);
236 nsSVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer()
238 mFrame
->SetParentCoordCtxProvider(nullptr);
240 mFrame
->mMarkedFrame
= nullptr;
241 mFrame
->mInUse
= false;
244 //----------------------------------------------------------------------
245 // Implementation of nsSVGMarkerAnonChildFrame
248 NS_NewSVGMarkerAnonChildFrame(nsIPresShell
* aPresShell
,
249 nsStyleContext
* aContext
)
251 return new (aPresShell
) nsSVGMarkerAnonChildFrame(aContext
);
254 NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerAnonChildFrame
)
258 nsSVGMarkerAnonChildFrame::Init(nsIContent
* aContent
,
259 nsContainerFrame
* aParent
,
260 nsIFrame
* aPrevInFlow
)
262 NS_ABORT_IF_FALSE(aParent
->GetType() == nsGkAtoms::svgMarkerFrame
,
263 "Unexpected parent");
264 nsSVGMarkerAnonChildFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
269 nsSVGMarkerAnonChildFrame::GetType() const
271 return nsGkAtoms::svgMarkerAnonChildFrame
;