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