Bumping manifests a=b2g-bump
[gecko.git] / layout / svg / nsSVGMarkerFrame.cpp
blobc880200bc6417ce6ad3c1711534d1414ed3e03e9
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/. */
6 // Main header first:
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;
20 nsContainerFrame*
21 NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
23 return new (aPresShell) nsSVGMarkerFrame(aContext);
26 NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerFrame)
28 //----------------------------------------------------------------------
29 // nsIFrame methods:
31 nsresult
32 nsSVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID,
33 nsIAtom* aAttribute,
34 int32_t aModType)
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);
52 #ifdef DEBUG
53 void
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);
62 #endif /* DEBUG */
64 nsIAtom *
65 nsSVGMarkerFrame::GetType() const
67 return nsGkAtoms::svgMarkerFrame;
70 //----------------------------------------------------------------------
71 // nsSVGContainerFrame methods:
73 gfxMatrix
74 nsSVGMarkerFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
76 NS_ASSERTION(mMarkedFrame, "null nsSVGPathGeometry frame");
78 if (mInUse2) {
79 // We're going to be bailing drawing the marker, so return an identity.
80 return gfxMatrix();
83 SVGMarkerElement *content = static_cast<SVGMarkerElement*>(mContent);
85 mInUse2 = true;
86 gfxMatrix markedTM = mMarkedFrame->GetCanvasTM(aFor, aTransformRoot);
87 mInUse2 = false;
89 Matrix markerTM = content->GetMarkerTransform(mStrokeWidth, mX, mY,
90 mAutoAngle, mIsStart);
91 Matrix viewBoxTM = content->GetViewBoxTransform();
93 return ThebesMatrix(viewBoxTM * markerTM) * markedTM;
96 static nsIFrame*
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");
102 return kid;
105 nsresult
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.
113 if (mInUse)
114 return NS_OK;
116 AutoMarkerReferencer markerRef(this, aMarkedFrame);
118 SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent);
119 if (!marker->HasValidDimensions()) {
120 return NS_OK;
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.
127 return NS_OK;
130 mStrokeWidth = aStrokeWidth;
131 mX = aMark->x;
132 mY = aMark->y;
133 mAutoAngle = aMark->angle;
134 mIsStart = aMark->type == nsSVGMark::eStart;
136 gfxContext *gfx = aContext->ThebesContext();
138 if (StyleDisplay()->IsScrollableOverflow()) {
139 gfx->Save();
140 gfxRect clipRect =
141 nsSVGUtils::GetClipRectForFrame(this, viewBox.x, viewBox.y,
142 viewBox.width, viewBox.height);
143 nsSVGUtils::SetClipRect(gfx, GetCanvasTM(nsISVGChildFrame::FOR_PAINTING),
144 clipRect);
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())
154 gfx->Restore();
156 return NS_OK;
159 SVGBBox
160 nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
161 uint32_t aFlags,
162 nsSVGPathGeometryFrame *aMarkedFrame,
163 const nsSVGMark *aMark,
164 float aStrokeWidth)
166 SVGBBox bbox;
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.
171 if (mInUse)
172 return bbox;
174 AutoMarkerReferencer markerRef(this, aMarkedFrame);
176 SVGMarkerElement *content = static_cast<SVGMarkerElement*>(mContent);
177 if (!content->HasValidDimensions()) {
178 return bbox;
181 const nsSVGViewBoxRect viewBox = content->GetViewBoxRect();
183 if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
184 return bbox;
187 mStrokeWidth = aStrokeWidth;
188 mX = aMark->x;
189 mY = aMark->y;
190 mAutoAngle = aMark->angle;
191 mIsStart = aMark->type == nsSVGMark::eStart;
193 Matrix markerTM =
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));
208 return bbox;
211 void
212 nsSVGMarkerFrame::SetParentCoordCtxProvider(SVGSVGElement *aContext)
214 SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent);
215 marker->SetParentCoordCtxProvider(aContext);
218 //----------------------------------------------------------------------
219 // helper class
221 nsSVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer(
222 nsSVGMarkerFrame *aFrame,
223 nsSVGPathGeometryFrame *aMarkedFrame
224 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
225 : mFrame(aFrame)
227 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
228 mFrame->mInUse = true;
229 mFrame->mMarkedFrame = aMarkedFrame;
231 SVGSVGElement *ctx =
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
247 nsContainerFrame*
248 NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell,
249 nsStyleContext* aContext)
251 return new (aPresShell) nsSVGMarkerAnonChildFrame(aContext);
254 NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerAnonChildFrame)
256 #ifdef DEBUG
257 void
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);
266 #endif
268 nsIAtom *
269 nsSVGMarkerAnonChildFrame::GetType() const
271 return nsGkAtoms::svgMarkerAnonChildFrame;