Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / svg / SVGAngle.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23 #include "core/svg/SVGAngle.h"
24
25 #include "bindings/v8/ExceptionState.h"
26 #include "bindings/v8/ExceptionStatePlaceholder.h"
27 #include "core/dom/ExceptionCode.h"
28 #include "core/svg/SVGAnimationElement.h"
29 #include "core/svg/SVGParserUtilities.h"
30 #include "wtf/MathExtras.h"
31 #include "wtf/text/WTFString.h"
32
33 namespace WebCore {
34
35 template<> const SVGEnumerationStringEntries& getStaticStringEntries<SVGMarkerOrientType>()
36 {
37     DEFINE_STATIC_LOCAL(SVGEnumerationStringEntries, entries, ());
38     if (entries.isEmpty()) {
39         entries.append(std::make_pair(SVGMarkerOrientAuto, "auto"));
40         entries.append(std::make_pair(SVGMarkerOrientAngle, "angle"));
41     }
42     return entries;
43 }
44
45 SVGMarkerOrientEnumeration::SVGMarkerOrientEnumeration(SVGAngle* angle)
46     : SVGEnumeration<SVGMarkerOrientType>(SVGMarkerOrientAngle)
47     , m_angle(angle)
48 {
49 }
50
51 SVGMarkerOrientEnumeration::~SVGMarkerOrientEnumeration()
52 {
53 }
54
55 void SVGMarkerOrientEnumeration::notifyChange()
56 {
57     ASSERT(m_angle);
58     m_angle->orientTypeChanged();
59 }
60
61 void SVGMarkerOrientEnumeration::add(PassRefPtr<SVGPropertyBase>, SVGElement*)
62 {
63     // SVGMarkerOrientEnumeration is only animated via SVGAngle
64     ASSERT_NOT_REACHED();
65 }
66
67 void SVGMarkerOrientEnumeration::calculateAnimatedValue(SVGAnimationElement*, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement)
68 {
69     // SVGMarkerOrientEnumeration is only animated via SVGAngle
70     ASSERT_NOT_REACHED();
71 }
72
73 float SVGMarkerOrientEnumeration::calculateDistance(PassRefPtr<SVGPropertyBase> to, SVGElement* contextElement)
74 {
75     // SVGMarkerOrientEnumeration is only animated via SVGAngle
76     ASSERT_NOT_REACHED();
77     return -1.0;
78 }
79
80 SVGAngle::SVGAngle()
81     : SVGPropertyBase(classType())
82     , m_unitType(SVG_ANGLETYPE_UNSPECIFIED)
83     , m_valueInSpecifiedUnits(0)
84     , m_orientType(SVGMarkerOrientEnumeration::create(this))
85 {
86 }
87
88 SVGAngle::SVGAngle(SVGAngleType unitType, float valueInSpecifiedUnits, SVGMarkerOrientType orientType)
89     : SVGPropertyBase(classType())
90     , m_unitType(unitType)
91     , m_valueInSpecifiedUnits(valueInSpecifiedUnits)
92     , m_orientType(SVGMarkerOrientEnumeration::create(this))
93 {
94     m_orientType->setEnumValue(orientType);
95 }
96
97 SVGAngle::~SVGAngle()
98 {
99 }
100
101 PassRefPtr<SVGAngle> SVGAngle::clone() const
102 {
103     return adoptRef(new SVGAngle(m_unitType, m_valueInSpecifiedUnits, m_orientType->enumValue()));
104 }
105
106 PassRefPtr<SVGPropertyBase> SVGAngle::cloneForAnimation(const String& value) const
107 {
108     RefPtr<SVGAngle> point = create();
109     point->setValueAsString(value, IGNORE_EXCEPTION);
110     return point.release();
111 }
112
113 float SVGAngle::value() const
114 {
115     switch (m_unitType) {
116     case SVG_ANGLETYPE_GRAD:
117         return grad2deg(m_valueInSpecifiedUnits);
118     case SVG_ANGLETYPE_RAD:
119         return rad2deg(m_valueInSpecifiedUnits);
120     case SVG_ANGLETYPE_UNSPECIFIED:
121     case SVG_ANGLETYPE_UNKNOWN:
122     case SVG_ANGLETYPE_DEG:
123         return m_valueInSpecifiedUnits;
124     }
125
126     ASSERT_NOT_REACHED();
127     return 0;
128 }
129
130 void SVGAngle::setValue(float value)
131 {
132     switch (m_unitType) {
133     case SVG_ANGLETYPE_GRAD:
134         m_valueInSpecifiedUnits = deg2grad(value);
135         break;
136     case SVG_ANGLETYPE_RAD:
137         m_valueInSpecifiedUnits = deg2rad(value);
138         break;
139     case SVG_ANGLETYPE_UNSPECIFIED:
140     case SVG_ANGLETYPE_UNKNOWN:
141     case SVG_ANGLETYPE_DEG:
142         m_valueInSpecifiedUnits = value;
143         break;
144     }
145     m_orientType->setEnumValue(SVGMarkerOrientAngle);
146 }
147
148 template<typename CharType>
149 static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const CharType* end)
150 {
151     // If there's no unit given, the angle type is unspecified.
152     if (ptr == end)
153         return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
154
155     const CharType firstChar = *ptr;
156
157     // If the unit contains only one character, the angle type is unknown.
158     ++ptr;
159     if (ptr == end)
160         return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
161
162     const CharType secondChar = *ptr;
163
164     // If the unit contains only two characters, the angle type is unknown.
165     ++ptr;
166     if (ptr == end)
167         return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
168
169     const CharType thirdChar = *ptr;
170     if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g')
171         return SVGAngle::SVG_ANGLETYPE_DEG;
172     if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd')
173         return SVGAngle::SVG_ANGLETYPE_RAD;
174
175     // If the unit contains three characters, but is not deg or rad, then it's unknown.
176     ++ptr;
177     if (ptr == end)
178         return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
179
180     const CharType fourthChar = *ptr;
181
182     if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
183         return SVGAngle::SVG_ANGLETYPE_GRAD;
184
185     return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
186 }
187
188 String SVGAngle::valueAsString() const
189 {
190     switch (m_unitType) {
191     case SVG_ANGLETYPE_DEG: {
192         DEFINE_STATIC_LOCAL(String, degString, ("deg"));
193         return String::number(m_valueInSpecifiedUnits) + degString;
194     }
195     case SVG_ANGLETYPE_RAD: {
196         DEFINE_STATIC_LOCAL(String, radString, ("rad"));
197         return String::number(m_valueInSpecifiedUnits) + radString;
198     }
199     case SVG_ANGLETYPE_GRAD: {
200         DEFINE_STATIC_LOCAL(String, gradString, ("grad"));
201         return String::number(m_valueInSpecifiedUnits) + gradString;
202     }
203     case SVG_ANGLETYPE_UNSPECIFIED:
204     case SVG_ANGLETYPE_UNKNOWN:
205         return String::number(m_valueInSpecifiedUnits);
206     }
207
208     ASSERT_NOT_REACHED();
209     return String();
210 }
211
212 template<typename CharType>
213 static bool parseValue(const String& value, float& valueInSpecifiedUnits, SVGAngle::SVGAngleType& unitType)
214 {
215     const CharType* ptr = value.getCharacters<CharType>();
216     const CharType* end = ptr + value.length();
217
218     if (!parseNumber(ptr, end, valueInSpecifiedUnits, false))
219         return false;
220
221     unitType = stringToAngleType(ptr, end);
222     if (unitType == SVGAngle::SVG_ANGLETYPE_UNKNOWN)
223         return false;
224
225     return true;
226 }
227
228 void SVGAngle::setValueAsString(const String& value, ExceptionState& exceptionState)
229 {
230     if (value.isEmpty()) {
231         newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
232         return;
233     }
234
235     if (value == "auto") {
236         newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
237         m_orientType->setEnumValue(SVGMarkerOrientAuto);
238         return;
239     }
240
241     float valueInSpecifiedUnits = 0;
242     SVGAngleType unitType = SVG_ANGLETYPE_UNKNOWN;
243
244     bool success = value.is8Bit() ? parseValue<LChar>(value, valueInSpecifiedUnits, unitType)
245                                   : parseValue<UChar>(value, valueInSpecifiedUnits, unitType);
246     if (!success) {
247         exceptionState.throwDOMException(SyntaxError, "The value provided ('" + value + "') is invalid.");
248         return;
249     }
250
251     m_orientType->setEnumValue(SVGMarkerOrientAngle);
252     m_unitType = unitType;
253     m_valueInSpecifiedUnits = valueInSpecifiedUnits;
254 }
255
256 void SVGAngle::newValueSpecifiedUnits(SVGAngleType unitType, float valueInSpecifiedUnits)
257 {
258     m_orientType->setEnumValue(SVGMarkerOrientAngle);
259     m_unitType = unitType;
260     m_valueInSpecifiedUnits = valueInSpecifiedUnits;
261 }
262
263 void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& exceptionState)
264 {
265     if (m_unitType == SVG_ANGLETYPE_UNKNOWN) {
266         exceptionState.throwDOMException(NotSupportedError, "Cannot convert from unknown or invalid units.");
267         return;
268     }
269
270     if (unitType == m_unitType)
271         return;
272
273     switch (m_unitType) {
274     case SVG_ANGLETYPE_RAD:
275         switch (unitType) {
276         case SVG_ANGLETYPE_GRAD:
277             m_valueInSpecifiedUnits = rad2grad(m_valueInSpecifiedUnits);
278             break;
279         case SVG_ANGLETYPE_UNSPECIFIED:
280         case SVG_ANGLETYPE_DEG:
281             m_valueInSpecifiedUnits = rad2deg(m_valueInSpecifiedUnits);
282             break;
283         case SVG_ANGLETYPE_RAD:
284         case SVG_ANGLETYPE_UNKNOWN:
285             ASSERT_NOT_REACHED();
286             break;
287         }
288         break;
289     case SVG_ANGLETYPE_GRAD:
290         switch (unitType) {
291         case SVG_ANGLETYPE_RAD:
292             m_valueInSpecifiedUnits = grad2rad(m_valueInSpecifiedUnits);
293             break;
294         case SVG_ANGLETYPE_UNSPECIFIED:
295         case SVG_ANGLETYPE_DEG:
296             m_valueInSpecifiedUnits = grad2deg(m_valueInSpecifiedUnits);
297             break;
298         case SVG_ANGLETYPE_GRAD:
299         case SVG_ANGLETYPE_UNKNOWN:
300             ASSERT_NOT_REACHED();
301             break;
302         }
303         break;
304     case SVG_ANGLETYPE_UNSPECIFIED:
305         // Spec: For angles, a unitless value is treated the same as if degrees were specified.
306     case SVG_ANGLETYPE_DEG:
307         switch (unitType) {
308         case SVG_ANGLETYPE_RAD:
309             m_valueInSpecifiedUnits = deg2rad(m_valueInSpecifiedUnits);
310             break;
311         case SVG_ANGLETYPE_GRAD:
312             m_valueInSpecifiedUnits = deg2grad(m_valueInSpecifiedUnits);
313             break;
314         case SVG_ANGLETYPE_UNSPECIFIED:
315             break;
316         case SVG_ANGLETYPE_DEG:
317         case SVG_ANGLETYPE_UNKNOWN:
318             ASSERT_NOT_REACHED();
319             break;
320         }
321         break;
322     case SVG_ANGLETYPE_UNKNOWN:
323         ASSERT_NOT_REACHED();
324         break;
325     }
326
327     m_unitType = unitType;
328     m_orientType->setEnumValue(SVGMarkerOrientAngle);
329 }
330
331 void SVGAngle::add(PassRefPtr<SVGPropertyBase> other, SVGElement*)
332 {
333     RefPtr<SVGAngle> otherAngle = toSVGAngle(other);
334
335     // Only respect by animations, if from and by are both specified in angles (and not eg. 'auto').
336     if (orientType()->enumValue() != SVGMarkerOrientAngle || otherAngle->orientType()->enumValue() != SVGMarkerOrientAngle)
337         return;
338
339     setValue(value() + otherAngle->value());
340 }
341
342 void SVGAngle::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDuration, SVGElement*)
343 {
344     ASSERT(animationElement);
345     bool isToAnimation = animationElement->animationMode() == ToAnimation;
346
347     RefPtr<SVGAngle> fromAngle = isToAnimation ? this : toSVGAngle(from);
348     RefPtr<SVGAngle> toAngle = toSVGAngle(to);
349     RefPtr<SVGAngle> toAtEndOfDurationAngle = toSVGAngle(toAtEndOfDuration);
350
351     SVGMarkerOrientType fromOrientType = fromAngle->orientType()->enumValue();
352     SVGMarkerOrientType toOrientType = toAngle->orientType()->enumValue();
353
354     if (fromOrientType != toOrientType) {
355         // Animating from eg. auto to 90deg, or auto to 90deg.
356         if (fromOrientType == SVGMarkerOrientAngle) {
357             // Animating from an angle value to eg. 'auto' - this disabled additive as 'auto' is a keyword..
358             if (toOrientType == SVGMarkerOrientAuto) {
359                 if (percentage < 0.5f) {
360                     newValueSpecifiedUnits(fromAngle->unitType(), fromAngle->valueInSpecifiedUnits());
361                     return;
362                 }
363                 orientType()->setEnumValue(SVGMarkerOrientAuto);
364                 return;
365             }
366             m_valueInSpecifiedUnits = 0;
367             orientType()->setEnumValue(SVGMarkerOrientUnknown);
368             return;
369         }
370     }
371
372     // From 'auto' to 'auto'.
373     if (fromOrientType == SVGMarkerOrientAuto) {
374         m_valueInSpecifiedUnits = 0;
375         orientType()->setEnumValue(SVGMarkerOrientAuto);
376         return;
377     }
378
379     // If the enumeration value is not angle or auto, its unknown.
380     if (fromOrientType != SVGMarkerOrientAngle) {
381         m_valueInSpecifiedUnits = 0;
382         orientType()->setEnumValue(SVGMarkerOrientUnknown);
383         return;
384     }
385
386     // Regular from angle to angle animation, with all features like additive etc.
387     float animatedValue = value();
388     animationElement->animateAdditiveNumber(percentage, repeatCount, fromAngle->value(), toAngle->value(), toAtEndOfDurationAngle->value(), animatedValue);
389     orientType()->setEnumValue(SVGMarkerOrientAngle);
390     setValue(animatedValue);
391 }
392
393 float SVGAngle::calculateDistance(PassRefPtr<SVGPropertyBase> other, SVGElement*)
394 {
395     return fabsf(value() - toSVGAngle(other)->value());
396 }
397
398 void SVGAngle::orientTypeChanged()
399 {
400     if (orientType()->enumValue() == SVGMarkerOrientAuto) {
401         m_unitType = SVG_ANGLETYPE_UNSPECIFIED;
402         m_valueInSpecifiedUnits = 0;
403     }
404 }
405
406 }