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