1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "SVGMarkerFrame.h"
10 // Keep others in (case-insensitive) order:
11 #include "gfxContext.h"
12 #include "mozilla/PresShell.h"
13 #include "mozilla/SVGGeometryFrame.h"
14 #include "mozilla/SVGObserverUtils.h"
15 #include "mozilla/SVGUtils.h"
16 #include "mozilla/dom/SVGGeometryElement.h"
17 #include "mozilla/dom/SVGMarkerElement.h"
19 using namespace mozilla::dom
;
20 using namespace mozilla::gfx
;
21 using namespace mozilla::image
;
23 nsContainerFrame
* NS_NewSVGMarkerFrame(mozilla::PresShell
* aPresShell
,
24 mozilla::ComputedStyle
* aStyle
) {
25 return new (aPresShell
)
26 mozilla::SVGMarkerFrame(aStyle
, aPresShell
->GetPresContext());
31 NS_IMPL_FRAMEARENA_HELPERS(SVGMarkerFrame
)
33 //----------------------------------------------------------------------
36 nsresult
SVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID
,
39 if (aNameSpaceID
== kNameSpaceID_None
&&
40 (aAttribute
== nsGkAtoms::markerUnits
|| aAttribute
== nsGkAtoms::refX
||
41 aAttribute
== nsGkAtoms::refY
|| aAttribute
== nsGkAtoms::markerWidth
||
42 aAttribute
== nsGkAtoms::markerHeight
||
43 aAttribute
== nsGkAtoms::orient
||
44 aAttribute
== nsGkAtoms::preserveAspectRatio
||
45 aAttribute
== nsGkAtoms::viewBox
)) {
46 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
49 return SVGContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
54 void SVGMarkerFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
55 nsIFrame
* aPrevInFlow
) {
56 NS_ASSERTION(aContent
->IsSVGElement(nsGkAtoms::marker
),
57 "Content is not an SVG marker");
59 SVGContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
63 //----------------------------------------------------------------------
64 // SVGContainerFrame methods:
66 gfxMatrix
SVGMarkerFrame::GetCanvasTM() {
67 NS_ASSERTION(mMarkedFrame
, "null SVGGeometry frame");
70 // We're going to be bailing drawing the marker, so return an identity.
74 SVGMarkerElement
* content
= static_cast<SVGMarkerElement
*>(GetContent());
77 gfxMatrix markedTM
= mMarkedFrame
->GetCanvasTM();
80 Matrix viewBoxTM
= content
->GetViewBoxTransform();
82 return ThebesMatrix(viewBoxTM
* mMarkerTM
) * markedTM
;
85 static nsIFrame
* GetAnonymousChildFrame(nsIFrame
* aFrame
) {
86 nsIFrame
* kid
= aFrame
->PrincipalChildList().FirstChild();
87 MOZ_ASSERT(kid
&& kid
->IsSVGMarkerAnonChildFrame(),
88 "expected to find anonymous child of marker frame");
92 void SVGMarkerFrame::PaintMark(gfxContext
& aContext
,
93 const gfxMatrix
& aToMarkedFrameUserSpace
,
94 SVGGeometryFrame
* aMarkedFrame
,
95 const SVGMark
& aMark
, float aStrokeWidth
,
96 imgDrawingParams
& aImgParams
) {
97 // If the flag is set when we get here, it means this marker frame
98 // has already been used painting the current mark, and the document
99 // has a marker reference loop.
104 AutoMarkerReferencer
markerRef(this, aMarkedFrame
);
106 SVGMarkerElement
* marker
= static_cast<SVGMarkerElement
*>(GetContent());
107 if (!marker
->HasValidDimensions()) {
111 const SVGViewBox viewBox
= marker
->GetViewBox();
113 if (viewBox
.width
<= 0.0f
|| viewBox
.height
<= 0.0f
) {
114 // We must disable rendering if the viewBox width or height are zero.
118 Matrix viewBoxTM
= marker
->GetViewBoxTransform();
120 mMarkerTM
= marker
->GetMarkerTransform(aStrokeWidth
, aMark
);
122 gfxMatrix markTM
= ThebesMatrix(viewBoxTM
) * ThebesMatrix(mMarkerTM
) *
123 aToMarkedFrameUserSpace
;
125 if (StyleDisplay()->IsScrollableOverflow()) {
127 gfxRect clipRect
= SVGUtils::GetClipRectForFrame(
128 this, viewBox
.x
, viewBox
.y
, viewBox
.width
, viewBox
.height
);
129 SVGUtils::SetClipRect(&aContext
, markTM
, clipRect
);
132 nsIFrame
* kid
= GetAnonymousChildFrame(this);
133 ISVGDisplayableFrame
* SVGFrame
= do_QueryFrame(kid
);
134 // The CTM of each frame referencing us may be different.
135 SVGFrame
->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED
);
136 SVGUtils::PaintFrameWithEffects(kid
, aContext
, markTM
, aImgParams
);
138 if (StyleDisplay()->IsScrollableOverflow()) aContext
.Restore();
141 SVGBBox
SVGMarkerFrame::GetMarkBBoxContribution(const Matrix
& aToBBoxUserspace
,
143 SVGGeometryFrame
* aMarkedFrame
,
144 const SVGMark
& aMark
,
145 float aStrokeWidth
) {
148 // If the flag is set when we get here, it means this marker frame
149 // has already been used in calculating the current mark bbox, and
150 // the document has a marker reference loop.
155 AutoMarkerReferencer
markerRef(this, aMarkedFrame
);
157 SVGMarkerElement
* content
= static_cast<SVGMarkerElement
*>(GetContent());
158 if (!content
->HasValidDimensions()) {
162 const SVGViewBox viewBox
= content
->GetViewBox();
164 if (viewBox
.width
<= 0.0f
|| viewBox
.height
<= 0.0f
) {
168 mMarkerTM
= content
->GetMarkerTransform(aStrokeWidth
, aMark
);
169 Matrix viewBoxTM
= content
->GetViewBoxTransform();
171 Matrix tm
= viewBoxTM
* mMarkerTM
* aToBBoxUserspace
;
173 ISVGDisplayableFrame
* child
= do_QueryFrame(GetAnonymousChildFrame(this));
174 // When we're being called to obtain the invalidation area, we need to
175 // pass down all the flags so that stroke is included. However, once DOM
176 // getBBox() accepts flags, maybe we should strip some of those here?
178 // We need to include zero width/height vertical/horizontal lines, so we have
179 // to use UnionEdges.
180 bbox
.UnionEdges(child
->GetBBoxContribution(tm
, aFlags
));
185 void SVGMarkerFrame::SetParentCoordCtxProvider(SVGViewportElement
* aContext
) {
186 SVGMarkerElement
* marker
= static_cast<SVGMarkerElement
*>(GetContent());
187 marker
->SetParentCoordCtxProvider(aContext
);
190 void SVGMarkerFrame::AppendDirectlyOwnedAnonBoxes(
191 nsTArray
<OwnedAnonBox
>& aResult
) {
192 aResult
.AppendElement(OwnedAnonBox(GetAnonymousChildFrame(this)));
195 //----------------------------------------------------------------------
198 SVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer(
199 SVGMarkerFrame
* aFrame
, SVGGeometryFrame
* aMarkedFrame
)
201 mFrame
->mInUse
= true;
202 mFrame
->mMarkedFrame
= aMarkedFrame
;
204 SVGViewportElement
* ctx
=
205 static_cast<SVGElement
*>(aMarkedFrame
->GetContent())->GetCtx();
206 mFrame
->SetParentCoordCtxProvider(ctx
);
209 SVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer() {
210 mFrame
->SetParentCoordCtxProvider(nullptr);
212 mFrame
->mMarkedFrame
= nullptr;
213 mFrame
->mInUse
= false;
216 } // namespace mozilla
218 //----------------------------------------------------------------------
219 // Implementation of SVGMarkerAnonChildFrame
221 nsContainerFrame
* NS_NewSVGMarkerAnonChildFrame(
222 mozilla::PresShell
* aPresShell
, mozilla::ComputedStyle
* aStyle
) {
223 return new (aPresShell
)
224 mozilla::SVGMarkerAnonChildFrame(aStyle
, aPresShell
->GetPresContext());
229 NS_IMPL_FRAMEARENA_HELPERS(SVGMarkerAnonChildFrame
)
232 void SVGMarkerAnonChildFrame::Init(nsIContent
* aContent
,
233 nsContainerFrame
* aParent
,
234 nsIFrame
* aPrevInFlow
) {
235 MOZ_ASSERT(aParent
->IsSVGMarkerFrame(), "Unexpected parent");
236 SVGDisplayContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
240 } // namespace mozilla