/src/mozilla-central/dom/svg/SVGMarkerElement.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "mozilla/ArrayUtils.h" |
8 | | |
9 | | #include "nsGkAtoms.h" |
10 | | #include "nsCOMPtr.h" |
11 | | #include "SVGAnimatedPreserveAspectRatio.h" |
12 | | #include "nsError.h" |
13 | | #include "mozilla/dom/SVGAngle.h" |
14 | | #include "mozilla/dom/SVGLengthBinding.h" |
15 | | #include "mozilla/dom/SVGMarkerElement.h" |
16 | | #include "mozilla/dom/SVGMarkerElementBinding.h" |
17 | | #include "mozilla/gfx/Matrix.h" |
18 | | #include "mozilla/FloatingPoint.h" |
19 | | #include "SVGContentUtils.h" |
20 | | |
21 | | using namespace mozilla::gfx; |
22 | | using namespace mozilla::dom::SVGMarkerElement_Binding; |
23 | | |
24 | | NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Marker) |
25 | | |
26 | | namespace mozilla { |
27 | | namespace dom { |
28 | | |
29 | | using namespace SVGAngle_Binding; |
30 | | |
31 | | JSObject* |
32 | | SVGMarkerElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) |
33 | 0 | { |
34 | 0 | return SVGMarkerElement_Binding::Wrap(aCx, this, aGivenProto); |
35 | 0 | } |
36 | | |
37 | | nsSVGElement::LengthInfo SVGMarkerElement::sLengthInfo[4] = |
38 | | { |
39 | | { &nsGkAtoms::refX, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, |
40 | | { &nsGkAtoms::refY, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, |
41 | | { &nsGkAtoms::markerWidth, 3, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, |
42 | | { &nsGkAtoms::markerHeight, 3, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, |
43 | | }; |
44 | | |
45 | | nsSVGEnumMapping SVGMarkerElement::sUnitsMap[] = { |
46 | | {&nsGkAtoms::strokeWidth, SVG_MARKERUNITS_STROKEWIDTH}, |
47 | | {&nsGkAtoms::userSpaceOnUse, SVG_MARKERUNITS_USERSPACEONUSE}, |
48 | | {nullptr, 0} |
49 | | }; |
50 | | |
51 | | nsSVGElement::EnumInfo SVGMarkerElement::sEnumInfo[1] = |
52 | | { |
53 | | { &nsGkAtoms::markerUnits, |
54 | | sUnitsMap, |
55 | | SVG_MARKERUNITS_STROKEWIDTH |
56 | | } |
57 | | }; |
58 | | |
59 | | nsSVGElement::AngleInfo SVGMarkerElement::sAngleInfo[1] = |
60 | | { |
61 | | { &nsGkAtoms::orient, 0, SVG_ANGLETYPE_UNSPECIFIED } |
62 | | }; |
63 | | |
64 | | //---------------------------------------------------------------------- |
65 | | // Implementation |
66 | | |
67 | | nsresult |
68 | | nsSVGOrientType::SetBaseValue(uint16_t aValue, |
69 | | nsSVGElement *aSVGElement) |
70 | 0 | { |
71 | 0 | if (aValue == SVG_MARKER_ORIENT_AUTO || |
72 | 0 | aValue == SVG_MARKER_ORIENT_ANGLE || |
73 | 0 | aValue == SVG_MARKER_ORIENT_AUTO_START_REVERSE) { |
74 | 0 | SetBaseValue(aValue); |
75 | 0 | aSVGElement->SetAttr( |
76 | 0 | kNameSpaceID_None, nsGkAtoms::orient, nullptr, |
77 | 0 | (aValue == SVG_MARKER_ORIENT_AUTO ? |
78 | 0 | NS_LITERAL_STRING("auto") : |
79 | 0 | aValue == SVG_MARKER_ORIENT_ANGLE ? |
80 | 0 | NS_LITERAL_STRING("0") : |
81 | 0 | NS_LITERAL_STRING("auto-start-reverse")), |
82 | 0 | true); |
83 | 0 | return NS_OK; |
84 | 0 | } |
85 | 0 | return NS_ERROR_DOM_TYPE_ERR; |
86 | 0 | } |
87 | | |
88 | | already_AddRefed<SVGAnimatedEnumeration> |
89 | | nsSVGOrientType::ToDOMAnimatedEnum(nsSVGElement *aSVGElement) |
90 | 0 | { |
91 | 0 | RefPtr<SVGAnimatedEnumeration> toReturn = |
92 | 0 | new DOMAnimatedEnum(this, aSVGElement); |
93 | 0 | return toReturn.forget(); |
94 | 0 | } |
95 | | |
96 | | SVGMarkerElement::SVGMarkerElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
97 | | : SVGMarkerElementBase(std::move(aNodeInfo)), mCoordCtx(nullptr) |
98 | 0 | { |
99 | 0 | } |
100 | | |
101 | | //---------------------------------------------------------------------- |
102 | | // nsINode methods |
103 | | |
104 | | NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGMarkerElement) |
105 | | |
106 | | //---------------------------------------------------------------------- |
107 | | |
108 | | already_AddRefed<SVGAnimatedRect> |
109 | | SVGMarkerElement::ViewBox() |
110 | 0 | { |
111 | 0 | return mViewBox.ToSVGAnimatedRect(this); |
112 | 0 | } |
113 | | |
114 | | already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> |
115 | | SVGMarkerElement::PreserveAspectRatio() |
116 | 0 | { |
117 | 0 | return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this); |
118 | 0 | } |
119 | | |
120 | | //---------------------------------------------------------------------- |
121 | | |
122 | | already_AddRefed<SVGAnimatedLength> |
123 | | SVGMarkerElement::RefX() |
124 | 0 | { |
125 | 0 | return mLengthAttributes[REFX].ToDOMAnimatedLength(this); |
126 | 0 | } |
127 | | |
128 | | already_AddRefed<SVGAnimatedLength> |
129 | | SVGMarkerElement::RefY() |
130 | 0 | { |
131 | 0 | return mLengthAttributes[REFY].ToDOMAnimatedLength(this); |
132 | 0 | } |
133 | | |
134 | | already_AddRefed<SVGAnimatedEnumeration> |
135 | | SVGMarkerElement::MarkerUnits() |
136 | 0 | { |
137 | 0 | return mEnumAttributes[MARKERUNITS].ToDOMAnimatedEnum(this); |
138 | 0 | } |
139 | | |
140 | | already_AddRefed<SVGAnimatedLength> |
141 | | SVGMarkerElement::MarkerWidth() |
142 | 0 | { |
143 | 0 | return mLengthAttributes[MARKERWIDTH].ToDOMAnimatedLength(this); |
144 | 0 | } |
145 | | |
146 | | already_AddRefed<SVGAnimatedLength> |
147 | | SVGMarkerElement::MarkerHeight() |
148 | 0 | { |
149 | 0 | return mLengthAttributes[MARKERHEIGHT].ToDOMAnimatedLength(this); |
150 | 0 | } |
151 | | |
152 | | already_AddRefed<SVGAnimatedEnumeration> |
153 | | SVGMarkerElement::OrientType() |
154 | 0 | { |
155 | 0 | return mOrientType.ToDOMAnimatedEnum(this); |
156 | 0 | } |
157 | | |
158 | | already_AddRefed<SVGAnimatedAngle> |
159 | | SVGMarkerElement::OrientAngle() |
160 | 0 | { |
161 | 0 | return mAngleAttributes[ORIENT].ToDOMAnimatedAngle(this); |
162 | 0 | } |
163 | | |
164 | | void SVGMarkerElement::SetOrientToAuto() |
165 | 0 | { |
166 | 0 | SetAttr(kNameSpaceID_None, nsGkAtoms::orient, nullptr, |
167 | 0 | NS_LITERAL_STRING("auto"), true); |
168 | 0 | } |
169 | | |
170 | | void |
171 | | SVGMarkerElement::SetOrientToAngle(SVGAngle& angle, ErrorResult& rv) |
172 | 0 | { |
173 | 0 | float f = angle.Value(); |
174 | 0 | if (!IsFinite(f)) { |
175 | 0 | rv.Throw(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR); |
176 | 0 | return; |
177 | 0 | } |
178 | 0 | mOrientType.SetBaseValue(SVG_MARKER_ORIENT_ANGLE); |
179 | 0 | mAngleAttributes[ORIENT].SetBaseValue(f, angle.UnitType(), this, true); |
180 | 0 | } |
181 | | |
182 | | //---------------------------------------------------------------------- |
183 | | // nsIContent methods |
184 | | |
185 | | NS_IMETHODIMP_(bool) |
186 | | SVGMarkerElement::IsAttributeMapped(const nsAtom* name) const |
187 | 0 | { |
188 | 0 | static const MappedAttributeEntry* const map[] = { |
189 | 0 | sFEFloodMap, |
190 | 0 | sFiltersMap, |
191 | 0 | sFontSpecificationMap, |
192 | 0 | sGradientStopMap, |
193 | 0 | sLightingEffectsMap, |
194 | 0 | sMarkersMap, |
195 | 0 | sTextContentElementsMap, |
196 | 0 | sViewportsMap, |
197 | 0 | sColorMap, |
198 | 0 | sFillStrokeMap, |
199 | 0 | sGraphicsMap |
200 | 0 | }; |
201 | 0 |
|
202 | 0 | return FindAttributeDependence(name, map) || |
203 | 0 | SVGMarkerElementBase::IsAttributeMapped(name); |
204 | 0 | } |
205 | | |
206 | | //---------------------------------------------------------------------- |
207 | | // nsSVGElement methods |
208 | | |
209 | | bool |
210 | | SVGMarkerElement::ParseAttribute(int32_t aNameSpaceID, nsAtom* aName, |
211 | | const nsAString& aValue, |
212 | | nsIPrincipal* aMaybeScriptedPrincipal, |
213 | | nsAttrValue& aResult) |
214 | 0 | { |
215 | 0 | if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::orient) { |
216 | 0 | if (aValue.EqualsLiteral("auto")) { |
217 | 0 | mOrientType.SetBaseValue(SVG_MARKER_ORIENT_AUTO); |
218 | 0 | aResult.SetTo(aValue); |
219 | 0 | mAngleAttributes[ORIENT].SetBaseValue(0.f, SVG_ANGLETYPE_UNSPECIFIED, |
220 | 0 | this, false); |
221 | 0 | return true; |
222 | 0 | } |
223 | 0 | if (aValue.EqualsLiteral("auto-start-reverse")) { |
224 | 0 | mOrientType.SetBaseValue(SVG_MARKER_ORIENT_AUTO_START_REVERSE); |
225 | 0 | aResult.SetTo(aValue); |
226 | 0 | mAngleAttributes[ORIENT].SetBaseValue(0.f, SVG_ANGLETYPE_UNSPECIFIED, |
227 | 0 | this, false); |
228 | 0 | return true; |
229 | 0 | } |
230 | 0 | mOrientType.SetBaseValue(SVG_MARKER_ORIENT_ANGLE); |
231 | 0 | } |
232 | 0 | return SVGMarkerElementBase::ParseAttribute(aNameSpaceID, aName, |
233 | 0 | aValue, |
234 | 0 | aMaybeScriptedPrincipal, |
235 | 0 | aResult); |
236 | 0 | } |
237 | | |
238 | | nsresult |
239 | | SVGMarkerElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, |
240 | | const nsAttrValue* aValue, |
241 | | const nsAttrValue* aOldValue, |
242 | | nsIPrincipal* aMaybeScriptedPrincipal, |
243 | | bool aNotify) |
244 | 0 | { |
245 | 0 | if (!aValue && aNamespaceID == kNameSpaceID_None && |
246 | 0 | aName == nsGkAtoms::orient) { |
247 | 0 | mOrientType.SetBaseValue(SVG_MARKER_ORIENT_ANGLE); |
248 | 0 | } |
249 | 0 |
|
250 | 0 | return SVGMarkerElementBase::AfterSetAttr(aNamespaceID, aName, |
251 | 0 | aValue, aOldValue, |
252 | 0 | aMaybeScriptedPrincipal, |
253 | 0 | aNotify); |
254 | 0 | } |
255 | | |
256 | | //---------------------------------------------------------------------- |
257 | | // nsSVGElement methods |
258 | | |
259 | | void |
260 | | SVGMarkerElement::SetParentCoordCtxProvider(SVGViewportElement *aContext) |
261 | 0 | { |
262 | 0 | mCoordCtx = aContext; |
263 | 0 | mViewBoxToViewportTransform = nullptr; |
264 | 0 | } |
265 | | |
266 | | /* virtual */ bool |
267 | | SVGMarkerElement::HasValidDimensions() const |
268 | 0 | { |
269 | 0 | return (!mLengthAttributes[MARKERWIDTH].IsExplicitlySet() || |
270 | 0 | mLengthAttributes[MARKERWIDTH].GetAnimValInSpecifiedUnits() > 0) && |
271 | 0 | (!mLengthAttributes[MARKERHEIGHT].IsExplicitlySet() || |
272 | 0 | mLengthAttributes[MARKERHEIGHT].GetAnimValInSpecifiedUnits() > 0); |
273 | 0 | } |
274 | | |
275 | | nsSVGElement::LengthAttributesInfo |
276 | | SVGMarkerElement::GetLengthInfo() |
277 | 0 | { |
278 | 0 | return LengthAttributesInfo(mLengthAttributes, sLengthInfo, |
279 | 0 | ArrayLength(sLengthInfo)); |
280 | 0 | } |
281 | | |
282 | | nsSVGElement::AngleAttributesInfo |
283 | | SVGMarkerElement::GetAngleInfo() |
284 | 0 | { |
285 | 0 | return AngleAttributesInfo(mAngleAttributes, sAngleInfo, |
286 | 0 | ArrayLength(sAngleInfo)); |
287 | 0 | } |
288 | | |
289 | | nsSVGElement::EnumAttributesInfo |
290 | | SVGMarkerElement::GetEnumInfo() |
291 | 0 | { |
292 | 0 | return EnumAttributesInfo(mEnumAttributes, sEnumInfo, |
293 | 0 | ArrayLength(sEnumInfo)); |
294 | 0 | } |
295 | | |
296 | | nsSVGViewBox * |
297 | | SVGMarkerElement::GetViewBox() |
298 | 0 | { |
299 | 0 | return &mViewBox; |
300 | 0 | } |
301 | | |
302 | | SVGAnimatedPreserveAspectRatio * |
303 | | SVGMarkerElement::GetPreserveAspectRatio() |
304 | 0 | { |
305 | 0 | return &mPreserveAspectRatio; |
306 | 0 | } |
307 | | |
308 | | //---------------------------------------------------------------------- |
309 | | // public helpers |
310 | | |
311 | | gfx::Matrix |
312 | | SVGMarkerElement::GetMarkerTransform(float aStrokeWidth, |
313 | | const nsSVGMark& aMark) |
314 | 0 | { |
315 | 0 | float scale = mEnumAttributes[MARKERUNITS].GetAnimValue() == |
316 | 0 | SVG_MARKERUNITS_STROKEWIDTH ? aStrokeWidth : 1.0f; |
317 | 0 |
|
318 | 0 | float angle; |
319 | 0 | switch (mOrientType.GetAnimValueInternal()) { |
320 | 0 | case SVG_MARKER_ORIENT_AUTO: |
321 | 0 | angle = aMark.angle; |
322 | 0 | break; |
323 | 0 | case SVG_MARKER_ORIENT_AUTO_START_REVERSE: |
324 | 0 | angle = aMark.angle + (aMark.type == nsSVGMark::eStart ? M_PI : 0.0f); |
325 | 0 | break; |
326 | 0 | default: // SVG_MARKER_ORIENT_ANGLE |
327 | 0 | angle = mAngleAttributes[ORIENT].GetAnimValue() * M_PI / 180.0f; |
328 | 0 | break; |
329 | 0 | } |
330 | 0 | |
331 | 0 | return gfx::Matrix(cos(angle) * scale, sin(angle) * scale, |
332 | 0 | -sin(angle) * scale, cos(angle) * scale, |
333 | 0 | aMark.x, aMark.y); |
334 | 0 | } |
335 | | |
336 | | nsSVGViewBoxRect |
337 | | SVGMarkerElement::GetViewBoxRect() |
338 | 0 | { |
339 | 0 | if (mViewBox.HasRect()) { |
340 | 0 | return mViewBox.GetAnimValue(); |
341 | 0 | } |
342 | 0 | return nsSVGViewBoxRect( |
343 | 0 | 0, 0, |
344 | 0 | mLengthAttributes[MARKERWIDTH].GetAnimValue(mCoordCtx), |
345 | 0 | mLengthAttributes[MARKERHEIGHT].GetAnimValue(mCoordCtx)); |
346 | 0 | } |
347 | | |
348 | | gfx::Matrix |
349 | | SVGMarkerElement::GetViewBoxTransform() |
350 | 0 | { |
351 | 0 | if (!mViewBoxToViewportTransform) { |
352 | 0 | float viewportWidth = |
353 | 0 | mLengthAttributes[MARKERWIDTH].GetAnimValue(mCoordCtx); |
354 | 0 | float viewportHeight = |
355 | 0 | mLengthAttributes[MARKERHEIGHT].GetAnimValue(mCoordCtx); |
356 | 0 |
|
357 | 0 | nsSVGViewBoxRect viewbox = GetViewBoxRect(); |
358 | 0 |
|
359 | 0 | MOZ_ASSERT(viewbox.width > 0.0f && viewbox.height > 0.0f, |
360 | 0 | "Rendering should be disabled"); |
361 | 0 |
|
362 | 0 | gfx::Matrix viewBoxTM = |
363 | 0 | SVGContentUtils::GetViewBoxTransform(viewportWidth, viewportHeight, |
364 | 0 | viewbox.x, viewbox.y, |
365 | 0 | viewbox.width, viewbox.height, |
366 | 0 | mPreserveAspectRatio); |
367 | 0 |
|
368 | 0 | float refX = mLengthAttributes[REFX].GetAnimValue(mCoordCtx); |
369 | 0 | float refY = mLengthAttributes[REFY].GetAnimValue(mCoordCtx); |
370 | 0 |
|
371 | 0 | gfx::Point ref = viewBoxTM.TransformPoint(gfx::Point(refX, refY)); |
372 | 0 |
|
373 | 0 | Matrix TM = viewBoxTM; |
374 | 0 | TM.PostTranslate(-ref.x, -ref.y); |
375 | 0 |
|
376 | 0 | mViewBoxToViewportTransform = new gfx::Matrix(TM); |
377 | 0 | } |
378 | 0 |
|
379 | 0 | return *mViewBoxToViewportTransform; |
380 | 0 | } |
381 | | |
382 | | } // namespace dom |
383 | | } // namespace mozilla |