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