Reduce use of deprecatedCharacters in WebCore
[WebKit-https.git] / Source / WebCore / svg / SVGAnimateMotionElement.cpp
1 /*
2  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
3  * Copyright (C) 2007 Rob Buis <buis@kde.org>
4  * Copyright (C) 2008 Apple Inc. 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 "SVGAnimateMotionElement.h"
24
25 #include "AffineTransform.h"
26 #include "Attribute.h"
27 #include "ElementIterator.h"
28 #include "RenderObject.h"
29 #include "RenderSVGResource.h"
30 #include "SVGElementInstance.h"
31 #include "SVGImageElement.h"
32 #include "SVGMPathElement.h"
33 #include "SVGNames.h"
34 #include "SVGParserUtilities.h"
35 #include "SVGPathData.h"
36 #include "SVGPathElement.h"
37 #include "SVGPathUtilities.h"
38 #include "SVGTransformList.h"
39 #include <wtf/MathExtras.h>
40 #include <wtf/StdLibExtras.h>
41 #include <wtf/text/StringView.h>
42
43 namespace WebCore {
44     
45 using namespace SVGNames;
46
47 inline SVGAnimateMotionElement::SVGAnimateMotionElement(const QualifiedName& tagName, Document& document)
48     : SVGAnimationElement(tagName, document)
49     , m_hasToPointAtEndOfDuration(false)
50 {
51     setCalcMode(CalcModePaced);
52     ASSERT(hasTagName(animateMotionTag));
53 }
54
55 PassRefPtr<SVGAnimateMotionElement> SVGAnimateMotionElement::create(const QualifiedName& tagName, Document& document)
56 {
57     return adoptRef(new SVGAnimateMotionElement(tagName, document));
58 }
59
60 bool SVGAnimateMotionElement::hasValidAttributeType()
61 {
62     SVGElement* targetElement = this->targetElement();
63     if (!targetElement)
64         return false;
65
66     // We don't have a special attribute name to verify the animation type. Check the element name instead.
67     if (!targetElement->isSVGGraphicsElement())
68         return false;
69     // Spec: SVG 1.1 section 19.2.15
70     // FIXME: svgTag is missing. Needs to be checked, if transforming <svg> could cause problems.
71     if (targetElement->hasTagName(gTag)
72         || targetElement->hasTagName(defsTag)
73         || targetElement->hasTagName(useTag)
74         || isSVGImageElement(targetElement)
75         || targetElement->hasTagName(switchTag)
76         || targetElement->hasTagName(pathTag)
77         || targetElement->hasTagName(rectTag)
78         || targetElement->hasTagName(circleTag)
79         || targetElement->hasTagName(ellipseTag)
80         || targetElement->hasTagName(lineTag)
81         || targetElement->hasTagName(polylineTag)
82         || targetElement->hasTagName(polygonTag)
83         || targetElement->hasTagName(textTag)
84         || targetElement->hasTagName(clipPathTag)
85         || targetElement->hasTagName(maskTag)
86         || targetElement->hasTagName(SVGNames::aTag)
87         || targetElement->hasTagName(foreignObjectTag)
88         )
89         return true;
90     return false;
91 }
92
93 bool SVGAnimateMotionElement::hasValidAttributeName()
94 {
95     // AnimateMotion does not use attributeName so it is always valid.
96     return true;
97 }
98
99 bool SVGAnimateMotionElement::isSupportedAttribute(const QualifiedName& attrName)
100 {
101     DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
102     if (supportedAttributes.isEmpty())
103         supportedAttributes.add(SVGNames::pathAttr);
104     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
105 }
106
107 void SVGAnimateMotionElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
108 {
109     if (!isSupportedAttribute(name)) {
110         SVGAnimationElement::parseAttribute(name, value);
111         return;
112     }
113
114     if (name == SVGNames::pathAttr) {
115         m_path = Path();
116         buildPathFromString(value, m_path);
117         updateAnimationPath();
118         return;
119     }
120
121     ASSERT_NOT_REACHED();
122 }
123     
124 SVGAnimateMotionElement::RotateMode SVGAnimateMotionElement::rotateMode() const
125 {
126     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, autoVal, ("auto", AtomicString::ConstructFromLiteral));
127     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, autoReverse, ("auto-reverse", AtomicString::ConstructFromLiteral));
128     const AtomicString& rotate = getAttribute(SVGNames::rotateAttr);
129     if (rotate == autoVal)
130         return RotateAuto;
131     if (rotate == autoReverse)
132         return RotateAutoReverse;
133     return RotateAngle;
134 }
135
136 void SVGAnimateMotionElement::updateAnimationPath()
137 {
138     m_animationPath = Path();
139     bool foundMPath = false;
140
141     for (auto& mPath : childrenOfType<SVGMPathElement>(*this)) {
142         SVGPathElement* pathElement = mPath.pathElement();
143         if (pathElement) {
144             updatePathFromGraphicsElement(pathElement, m_animationPath);
145             foundMPath = true;
146             break;
147         }
148     }
149
150     if (!foundMPath && fastHasAttribute(SVGNames::pathAttr))
151         m_animationPath = m_path;
152
153     updateAnimationMode();
154 }
155
156 static bool parsePoint(const String& s, FloatPoint& point)
157 {
158     if (s.isEmpty())
159         return false;
160     auto upconvertedCharacters = StringView(s).upconvertedCharacters();
161     const UChar* cur = upconvertedCharacters;
162     const UChar* end = cur + s.length();
163     
164     if (!skipOptionalSVGSpaces(cur, end))
165         return false;
166     
167     float x = 0;
168     if (!parseNumber(cur, end, x))
169         return false;
170     
171     float y = 0;
172     if (!parseNumber(cur, end, y))
173         return false;
174     
175     point = FloatPoint(x, y);
176     
177     // disallow anything except spaces at the end
178     return !skipOptionalSVGSpaces(cur, end);
179 }
180     
181 void SVGAnimateMotionElement::resetAnimatedType()
182 {
183     if (!hasValidAttributeType())
184         return;
185     SVGElement* targetElement = this->targetElement();
186     if (!targetElement)
187         return;
188     if (AffineTransform* transform = targetElement->supplementalTransform())
189         transform->makeIdentity();
190 }
191
192 void SVGAnimateMotionElement::clearAnimatedType(SVGElement* targetElement)
193 {
194     if (!targetElement)
195         return;
196     if (AffineTransform* transform = targetElement->supplementalTransform())
197         transform->makeIdentity();
198 }
199
200 bool SVGAnimateMotionElement::calculateToAtEndOfDurationValue(const String& toAtEndOfDurationString)
201 {
202     parsePoint(toAtEndOfDurationString, m_toPointAtEndOfDuration);
203     m_hasToPointAtEndOfDuration = true;
204     return true;
205 }
206
207 bool SVGAnimateMotionElement::calculateFromAndToValues(const String& fromString, const String& toString)
208 {
209     m_hasToPointAtEndOfDuration = false;
210     parsePoint(fromString, m_fromPoint);
211     parsePoint(toString, m_toPoint);
212     return true;
213 }
214     
215 bool SVGAnimateMotionElement::calculateFromAndByValues(const String& fromString, const String& byString)
216 {
217     m_hasToPointAtEndOfDuration = false;
218     if (animationMode() == ByAnimation && !isAdditive())
219         return false;
220     parsePoint(fromString, m_fromPoint);
221     FloatPoint byPoint;
222     parsePoint(byString, byPoint);
223     m_toPoint = FloatPoint(m_fromPoint.x() + byPoint.x(), m_fromPoint.y() + byPoint.y());
224     return true;
225 }
226
227 void SVGAnimateMotionElement::buildTransformForProgress(AffineTransform* transform, float percentage)
228 {
229     ASSERT(!m_animationPath.isEmpty());
230
231     bool ok = false;
232     float positionOnPath = m_animationPath.length() * percentage;
233     FloatPoint position = m_animationPath.pointAtLength(positionOnPath, ok);
234     if (!ok)
235         return;
236     transform->translate(position.x(), position.y());
237     RotateMode rotateMode = this->rotateMode();
238     if (rotateMode != RotateAuto && rotateMode != RotateAutoReverse)
239         return;
240     float angle = m_animationPath.normalAngleAtLength(positionOnPath, ok);
241     if (rotateMode == RotateAutoReverse)
242         angle += 180;
243     transform->rotate(angle);
244 }
245
246 void SVGAnimateMotionElement::calculateAnimatedValue(float percentage, unsigned repeatCount, SVGSMILElement*)
247 {
248     SVGElement* targetElement = this->targetElement();
249     if (!targetElement)
250         return;
251     AffineTransform* transform = targetElement->supplementalTransform();
252     if (!transform)
253         return;
254
255     if (RenderObject* targetRenderer = targetElement->renderer())
256         targetRenderer->setNeedsTransformUpdate();
257
258     if (!isAdditive())
259         transform->makeIdentity();
260
261     if (animationMode() != PathAnimation) {
262         FloatPoint toPointAtEndOfDuration = m_toPoint;
263         if (isAccumulated() && repeatCount && m_hasToPointAtEndOfDuration)
264             toPointAtEndOfDuration = m_toPointAtEndOfDuration;
265
266         float animatedX = 0;
267         animateAdditiveNumber(percentage, repeatCount, m_fromPoint.x(), m_toPoint.x(), toPointAtEndOfDuration.x(), animatedX);
268
269         float animatedY = 0;
270         animateAdditiveNumber(percentage, repeatCount, m_fromPoint.y(), m_toPoint.y(), toPointAtEndOfDuration.y(), animatedY);
271
272         transform->translate(animatedX, animatedY);
273         return;
274     }
275
276     buildTransformForProgress(transform, percentage);
277
278     // Handle accumulate="sum".
279     if (isAccumulated() && repeatCount) {
280         for (unsigned i = 0; i < repeatCount; ++i)
281             buildTransformForProgress(transform, 1);
282     }
283 }
284
285 void SVGAnimateMotionElement::applyResultsToTarget()
286 {
287     // We accumulate to the target element transform list so there is not much to do here.
288     SVGElement* targetElement = this->targetElement();
289     if (!targetElement)
290         return;
291
292     if (RenderElement* renderer = targetElement->renderer())
293         RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
294
295     AffineTransform* t = targetElement->supplementalTransform();
296     if (!t)
297         return;
298
299     // ...except in case where we have additional instances in <use> trees.
300     for (auto* instance : targetElement->instancesForElement()) {
301         SVGElement* shadowTreeElement = instance->shadowTreeElement();
302         ASSERT(shadowTreeElement);
303         AffineTransform* transform = shadowTreeElement->supplementalTransform();
304         if (!transform)
305             continue;
306         transform->setMatrix(t->a(), t->b(), t->c(), t->d(), t->e(), t->f());
307         if (RenderElement* renderer = shadowTreeElement->renderer()) {
308             renderer->setNeedsTransformUpdate();
309             RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
310         }
311     }
312 }
313
314 float SVGAnimateMotionElement::calculateDistance(const String& fromString, const String& toString)
315 {
316     FloatPoint from;
317     FloatPoint to;
318     if (!parsePoint(fromString, from))
319         return -1;
320     if (!parsePoint(toString, to))
321         return -1;
322     FloatSize diff = to - from;
323     return sqrtf(diff.width() * diff.width() + diff.height() * diff.height());
324 }
325
326 void SVGAnimateMotionElement::updateAnimationMode()
327 {
328     if (!m_animationPath.isEmpty())
329         setAnimationMode(PathAnimation);
330     else
331         SVGAnimationElement::updateAnimationMode();
332 }
333
334 }