Update To 11.40.268.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/core/v8/ExceptionState.h"
26 #include "bindings/core/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 blink {
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         entries.append(std::make_pair(SVGMarkerOrientAutoStartReverse, "auto-start-reverse"));
42     }
43     return entries;
44 }
45
46 template<> unsigned short getMaxExposedEnumValue<SVGMarkerOrientType>()
47 {
48     return SVGMarkerOrientAngle;
49 }
50
51 SVGMarkerOrientEnumeration::SVGMarkerOrientEnumeration(SVGAngle* angle)
52     : SVGEnumeration<SVGMarkerOrientType>(SVGMarkerOrientAngle)
53     , m_angle(angle)
54 {
55 }
56
57 SVGMarkerOrientEnumeration::~SVGMarkerOrientEnumeration()
58 {
59 }
60
61 void SVGMarkerOrientEnumeration::notifyChange()
62 {
63     ASSERT(m_angle);
64     m_angle->orientTypeChanged();
65 }
66
67 void SVGMarkerOrientEnumeration::add(PassRefPtrWillBeRawPtr<SVGPropertyBase>, SVGElement*)
68 {
69     // SVGMarkerOrientEnumeration is only animated via SVGAngle
70     ASSERT_NOT_REACHED();
71 }
72
73 void SVGMarkerOrientEnumeration::calculateAnimatedValue(SVGAnimationElement*, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement)
74 {
75     // SVGMarkerOrientEnumeration is only animated via SVGAngle
76     ASSERT_NOT_REACHED();
77 }
78
79 float SVGMarkerOrientEnumeration::calculateDistance(PassRefPtr<SVGPropertyBase> to, SVGElement* contextElement)
80 {
81     // SVGMarkerOrientEnumeration is only animated via SVGAngle
82     ASSERT_NOT_REACHED();
83     return -1.0;
84 }
85
86 SVGAngle::SVGAngle()
87     : m_unitType(SVG_ANGLETYPE_UNSPECIFIED)
88     , m_valueInSpecifiedUnits(0)
89     , m_orientType(SVGMarkerOrientEnumeration::create(this))
90 {
91 }
92
93 SVGAngle::SVGAngle(SVGAngleType unitType, float valueInSpecifiedUnits, SVGMarkerOrientType orientType)
94     : m_unitType(unitType)
95     , m_valueInSpecifiedUnits(valueInSpecifiedUnits)
96     , m_orientType(SVGMarkerOrientEnumeration::create(this))
97 {
98     m_orientType->setEnumValue(orientType);
99 }
100
101 SVGAngle::~SVGAngle()
102 {
103 }
104
105 PassRefPtr<SVGAngle> SVGAngle::clone() const
106 {
107     return adoptRef(new SVGAngle(m_unitType, m_valueInSpecifiedUnits, m_orientType->enumValue()));
108 }
109
110 float SVGAngle::value() const
111 {
112     switch (m_unitType) {
113     case SVG_ANGLETYPE_GRAD:
114         return grad2deg(m_valueInSpecifiedUnits);
115     case SVG_ANGLETYPE_RAD:
116         return rad2deg(m_valueInSpecifiedUnits);
117     case SVG_ANGLETYPE_TURN:
118         return turn2deg(m_valueInSpecifiedUnits);
119     case SVG_ANGLETYPE_UNSPECIFIED:
120     case SVG_ANGLETYPE_UNKNOWN:
121     case SVG_ANGLETYPE_DEG:
122         return m_valueInSpecifiedUnits;
123     }
124
125     ASSERT_NOT_REACHED();
126     return 0;
127 }
128
129 void SVGAngle::setValue(float value)
130 {
131     switch (m_unitType) {
132     case SVG_ANGLETYPE_GRAD:
133         m_valueInSpecifiedUnits = deg2grad(value);
134         break;
135     case SVG_ANGLETYPE_RAD:
136         m_valueInSpecifiedUnits = deg2rad(value);
137         break;
138     case SVG_ANGLETYPE_TURN:
139         m_valueInSpecifiedUnits = deg2turn(value);
140         break;
141     case SVG_ANGLETYPE_UNSPECIFIED:
142     case SVG_ANGLETYPE_UNKNOWN:
143     case SVG_ANGLETYPE_DEG:
144         m_valueInSpecifiedUnits = value;
145         break;
146     }
147     m_orientType->setEnumValue(SVGMarkerOrientAngle);
148 }
149
150 template<typename CharType>
151 static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const CharType* end)
152 {
153     // If there's no unit given, the angle type is unspecified.
154     if (ptr == end)
155         return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
156
157     SVGAngle::SVGAngleType type = SVGAngle::SVG_ANGLETYPE_UNKNOWN;
158     const CharType firstChar = *ptr++;
159
160     if (isHTMLSpace<CharType>(firstChar)) {
161         type = SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
162     } else if (end - ptr >= 2) {
163         const CharType secondChar = *ptr++;
164         const CharType thirdChar = *ptr++;
165         if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g') {
166             type = SVGAngle::SVG_ANGLETYPE_DEG;
167         } else if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd') {
168             type = SVGAngle::SVG_ANGLETYPE_RAD;
169         } else if (ptr != end) {
170             const CharType fourthChar = *ptr++;
171             if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
172                 type = SVGAngle::SVG_ANGLETYPE_GRAD;
173             else if (firstChar == 't' && secondChar == 'u' && thirdChar == 'r' && fourthChar == 'n')
174                 type = SVGAngle::SVG_ANGLETYPE_TURN;
175         }
176     }
177
178     if (!skipOptionalSVGSpaces(ptr, end))
179         return type;
180
181     return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
182 }
183
184 String SVGAngle::valueAsString() const
185 {
186     switch (m_unitType) {
187     case SVG_ANGLETYPE_DEG: {
188         DEFINE_STATIC_LOCAL(String, degString, ("deg"));
189         return String::number(m_valueInSpecifiedUnits) + degString;
190     }
191     case SVG_ANGLETYPE_RAD: {
192         DEFINE_STATIC_LOCAL(String, radString, ("rad"));
193         return String::number(m_valueInSpecifiedUnits) + radString;
194     }
195     case SVG_ANGLETYPE_GRAD: {
196         DEFINE_STATIC_LOCAL(String, gradString, ("grad"));
197         return String::number(m_valueInSpecifiedUnits) + gradString;
198     }
199     case SVG_ANGLETYPE_TURN: {
200         DEFINE_STATIC_LOCAL(String, turnString, ("turn"));
201         return String::number(m_valueInSpecifiedUnits) + turnString;
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, AllowLeadingWhitespace))
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     if (value == "auto-start-reverse") {
241         newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
242         m_orientType->setEnumValue(SVGMarkerOrientAutoStartReverse);
243         return;
244     }
245
246     float valueInSpecifiedUnits = 0;
247     SVGAngleType unitType = SVG_ANGLETYPE_UNKNOWN;
248
249     bool success = value.is8Bit() ? parseValue<LChar>(value, valueInSpecifiedUnits, unitType)
250                                   : parseValue<UChar>(value, valueInSpecifiedUnits, unitType);
251     if (!success) {
252         exceptionState.throwDOMException(SyntaxError, "The value provided ('" + value + "') is invalid.");
253         return;
254     }
255
256     m_orientType->setEnumValue(SVGMarkerOrientAngle);
257     m_unitType = unitType;
258     m_valueInSpecifiedUnits = valueInSpecifiedUnits;
259 }
260
261 void SVGAngle::newValueSpecifiedUnits(SVGAngleType unitType, float valueInSpecifiedUnits)
262 {
263     m_orientType->setEnumValue(SVGMarkerOrientAngle);
264     m_unitType = unitType;
265     m_valueInSpecifiedUnits = valueInSpecifiedUnits;
266 }
267
268 void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& exceptionState)
269 {
270     if (m_unitType == SVG_ANGLETYPE_UNKNOWN) {
271         exceptionState.throwDOMException(NotSupportedError, "Cannot convert from unknown or invalid units.");
272         return;
273     }
274
275     if (unitType == m_unitType)
276         return;
277
278     switch (m_unitType) {
279     case SVG_ANGLETYPE_TURN:
280         switch (unitType) {
281         case SVG_ANGLETYPE_GRAD:
282             m_valueInSpecifiedUnits = turn2grad(m_valueInSpecifiedUnits);
283             break;
284         case SVG_ANGLETYPE_UNSPECIFIED:
285         case SVG_ANGLETYPE_DEG:
286             m_valueInSpecifiedUnits = turn2deg(m_valueInSpecifiedUnits);
287             break;
288         case SVG_ANGLETYPE_RAD:
289             m_valueInSpecifiedUnits = deg2rad(turn2deg(m_valueInSpecifiedUnits));
290             break;
291         case SVG_ANGLETYPE_TURN:
292         case SVG_ANGLETYPE_UNKNOWN:
293             ASSERT_NOT_REACHED();
294             break;
295         }
296         break;
297     case SVG_ANGLETYPE_RAD:
298         switch (unitType) {
299         case SVG_ANGLETYPE_GRAD:
300             m_valueInSpecifiedUnits = rad2grad(m_valueInSpecifiedUnits);
301             break;
302         case SVG_ANGLETYPE_UNSPECIFIED:
303         case SVG_ANGLETYPE_DEG:
304             m_valueInSpecifiedUnits = rad2deg(m_valueInSpecifiedUnits);
305             break;
306         case SVG_ANGLETYPE_TURN:
307             m_valueInSpecifiedUnits = deg2turn(rad2deg(m_valueInSpecifiedUnits));
308             break;
309         case SVG_ANGLETYPE_RAD:
310         case SVG_ANGLETYPE_UNKNOWN:
311             ASSERT_NOT_REACHED();
312             break;
313         }
314         break;
315     case SVG_ANGLETYPE_GRAD:
316         switch (unitType) {
317         case SVG_ANGLETYPE_RAD:
318             m_valueInSpecifiedUnits = grad2rad(m_valueInSpecifiedUnits);
319             break;
320         case SVG_ANGLETYPE_UNSPECIFIED:
321         case SVG_ANGLETYPE_DEG:
322             m_valueInSpecifiedUnits = grad2deg(m_valueInSpecifiedUnits);
323             break;
324         case SVG_ANGLETYPE_TURN:
325             m_valueInSpecifiedUnits = grad2turn(m_valueInSpecifiedUnits);
326             break;
327         case SVG_ANGLETYPE_GRAD:
328         case SVG_ANGLETYPE_UNKNOWN:
329             ASSERT_NOT_REACHED();
330             break;
331         }
332         break;
333     case SVG_ANGLETYPE_UNSPECIFIED:
334         // Spec: For angles, a unitless value is treated the same as if degrees were specified.
335     case SVG_ANGLETYPE_DEG:
336         switch (unitType) {
337         case SVG_ANGLETYPE_RAD:
338             m_valueInSpecifiedUnits = deg2rad(m_valueInSpecifiedUnits);
339             break;
340         case SVG_ANGLETYPE_GRAD:
341             m_valueInSpecifiedUnits = deg2grad(m_valueInSpecifiedUnits);
342             break;
343         case SVG_ANGLETYPE_TURN:
344             m_valueInSpecifiedUnits = deg2turn(m_valueInSpecifiedUnits);
345             break;
346         case SVG_ANGLETYPE_UNSPECIFIED:
347         case SVG_ANGLETYPE_DEG:
348             break;
349         case SVG_ANGLETYPE_UNKNOWN:
350             ASSERT_NOT_REACHED();
351             break;
352         }
353         break;
354     case SVG_ANGLETYPE_UNKNOWN:
355         ASSERT_NOT_REACHED();
356         break;
357     }
358
359     m_unitType = unitType;
360     m_orientType->setEnumValue(SVGMarkerOrientAngle);
361 }
362
363 void SVGAngle::add(PassRefPtrWillBeRawPtr<SVGPropertyBase> other, SVGElement*)
364 {
365     RefPtr<SVGAngle> otherAngle = toSVGAngle(other);
366
367     // Only respect by animations, if from and by are both specified in angles (and not eg. 'auto').
368     if (orientType()->enumValue() != SVGMarkerOrientAngle || otherAngle->orientType()->enumValue() != SVGMarkerOrientAngle)
369         return;
370
371     setValue(value() + otherAngle->value());
372 }
373
374 void SVGAngle::assign(const SVGAngle& other)
375 {
376     SVGMarkerOrientType otherOrientType = other.orientType()->enumValue();
377     if (otherOrientType == SVGMarkerOrientAngle)
378         newValueSpecifiedUnits(other.unitType(), other.valueInSpecifiedUnits());
379     else
380         m_orientType->setEnumValue(otherOrientType);
381 }
382
383 void SVGAngle::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDuration, SVGElement*)
384 {
385     ASSERT(animationElement);
386     bool isToAnimation = animationElement->animationMode() == ToAnimation;
387
388     RefPtr<SVGAngle> fromAngle = isToAnimation ? this : toSVGAngle(from);
389     RefPtr<SVGAngle> toAngle = toSVGAngle(to);
390     SVGMarkerOrientType fromOrientType = fromAngle->orientType()->enumValue();
391     SVGMarkerOrientType toOrientType = toAngle->orientType()->enumValue();
392
393     if (fromOrientType != toOrientType) {
394         // Fall back to discrete animation.
395         assign(percentage < 0.5f ? *fromAngle : *toAngle);
396         return;
397     }
398
399     switch (fromOrientType) {
400     // From 'auto' to 'auto', or 'auto-start-reverse' to 'auto-start-reverse'
401     case SVGMarkerOrientAuto:
402     case SVGMarkerOrientAutoStartReverse:
403         orientType()->setEnumValue(fromOrientType);
404         return;
405
406     // Regular from angle to angle animation, with all features like additive etc.
407     case SVGMarkerOrientAngle:
408         {
409             float animatedValue = value();
410             RefPtr<SVGAngle> toAtEndOfDurationAngle = toSVGAngle(toAtEndOfDuration);
411
412             animationElement->animateAdditiveNumber(percentage, repeatCount, fromAngle->value(), toAngle->value(), toAtEndOfDurationAngle->value(), animatedValue);
413             orientType()->setEnumValue(SVGMarkerOrientAngle);
414             setValue(animatedValue);
415         }
416         return;
417
418     // If the enumeration value is not angle or auto, its unknown.
419     default:
420         m_valueInSpecifiedUnits = 0;
421         orientType()->setEnumValue(SVGMarkerOrientUnknown);
422         return;
423     }
424 }
425
426 float SVGAngle::calculateDistance(PassRefPtr<SVGPropertyBase> other, SVGElement*)
427 {
428     return fabsf(value() - toSVGAngle(other)->value());
429 }
430
431 void SVGAngle::orientTypeChanged()
432 {
433     if (orientType()->enumValue() == SVGMarkerOrientAuto || orientType()->enumValue() == SVGMarkerOrientAutoStartReverse) {
434         m_unitType = SVG_ANGLETYPE_UNSPECIFIED;
435         m_valueInSpecifiedUnits = 0;
436     }
437 }
438
439 }