Avoid copy-prone idiom "for (auto item : collection)"
[WebKit-https.git] / Source / WebCore / svg / SVGPathElement.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22 #include "SVGPathElement.h"
23
24 #include "Attribute.h"
25 #include "RenderSVGPath.h"
26 #include "RenderSVGResource.h"
27 #include "SVGElementInstance.h"
28 #include "SVGMPathElement.h"
29 #include "SVGNames.h"
30 #include "SVGPathSegArcAbs.h"
31 #include "SVGPathSegArcRel.h"
32 #include "SVGPathSegClosePath.h"
33 #include "SVGPathSegCurvetoCubicAbs.h"
34 #include "SVGPathSegCurvetoCubicRel.h"
35 #include "SVGPathSegCurvetoCubicSmoothAbs.h"
36 #include "SVGPathSegCurvetoCubicSmoothRel.h"
37 #include "SVGPathSegCurvetoQuadraticAbs.h"
38 #include "SVGPathSegCurvetoQuadraticRel.h"
39 #include "SVGPathSegCurvetoQuadraticSmoothAbs.h"
40 #include "SVGPathSegCurvetoQuadraticSmoothRel.h"
41 #include "SVGPathSegLinetoAbs.h"
42 #include "SVGPathSegLinetoHorizontalAbs.h"
43 #include "SVGPathSegLinetoHorizontalRel.h"
44 #include "SVGPathSegLinetoRel.h"
45 #include "SVGPathSegLinetoVerticalAbs.h"
46 #include "SVGPathSegLinetoVerticalRel.h"
47 #include "SVGPathSegList.h"
48 #include "SVGPathSegListBuilder.h"
49 #include "SVGPathSegListPropertyTearOff.h"
50 #include "SVGPathSegMovetoAbs.h"
51 #include "SVGPathSegMovetoRel.h"
52 #include "SVGPathUtilities.h"
53 #include "SVGSVGElement.h"
54
55 namespace WebCore {
56
57 // Define custom animated property 'd'.
58 const SVGPropertyInfo* SVGPathElement::dPropertyInfo()
59 {
60     static const SVGPropertyInfo* s_propertyInfo = 0;
61     if (!s_propertyInfo) {
62         s_propertyInfo = new SVGPropertyInfo(AnimatedPath,
63                                              PropertyIsReadWrite,
64                                              SVGNames::dAttr,
65                                              SVGNames::dAttr.localName(),
66                                              &SVGPathElement::synchronizeD,
67                                              &SVGPathElement::lookupOrCreateDWrapper);
68     }
69     return s_propertyInfo;
70 }
71
72 // Animated property definitions
73 DEFINE_ANIMATED_NUMBER(SVGPathElement, SVGNames::pathLengthAttr, PathLength, pathLength)
74 DEFINE_ANIMATED_BOOLEAN(SVGPathElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
75
76 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGPathElement)
77     REGISTER_LOCAL_ANIMATED_PROPERTY(d)
78     REGISTER_LOCAL_ANIMATED_PROPERTY(pathLength)
79     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
80     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
81 END_REGISTER_ANIMATED_PROPERTIES
82
83 inline SVGPathElement::SVGPathElement(const QualifiedName& tagName, Document& document)
84     : SVGGraphicsElement(tagName, document)
85     , m_pathByteStream(std::make_unique<SVGPathByteStream>())
86     , m_pathSegList(PathSegUnalteredRole)
87     , m_isAnimValObserved(false)
88 {
89     ASSERT(hasTagName(SVGNames::pathTag));
90     registerAnimatedPropertiesForSVGPathElement();
91 }
92
93 PassRefPtr<SVGPathElement> SVGPathElement::create(const QualifiedName& tagName, Document& document)
94 {
95     return adoptRef(new SVGPathElement(tagName, document));
96 }
97
98 float SVGPathElement::getTotalLength()
99 {
100     float totalLength = 0;
101     getTotalLengthOfSVGPathByteStream(pathByteStream(), totalLength);
102     return totalLength;
103 }
104
105 SVGPoint SVGPathElement::getPointAtLength(float length)
106 {
107     SVGPoint point;
108     getPointAtLengthOfSVGPathByteStream(pathByteStream(), length, point);
109     return point;
110 }
111
112 unsigned SVGPathElement::getPathSegAtLength(float length)
113 {
114     unsigned pathSeg = 0;
115     getSVGPathSegAtLengthFromSVGPathByteStream(pathByteStream(), length, pathSeg);
116     return pathSeg;
117 }
118
119 PassRefPtr<SVGPathSegClosePath> SVGPathElement::createSVGPathSegClosePath(SVGPathSegRole role)
120 {
121     return SVGPathSegClosePath::create(this, role);
122 }
123
124 PassRefPtr<SVGPathSegMovetoAbs> SVGPathElement::createSVGPathSegMovetoAbs(float x, float y, SVGPathSegRole role)
125 {
126     return SVGPathSegMovetoAbs::create(this, role, x, y);
127 }
128
129 PassRefPtr<SVGPathSegMovetoRel> SVGPathElement::createSVGPathSegMovetoRel(float x, float y, SVGPathSegRole role)
130 {
131     return SVGPathSegMovetoRel::create(this, role, x, y);
132 }
133
134 PassRefPtr<SVGPathSegLinetoAbs> SVGPathElement::createSVGPathSegLinetoAbs(float x, float y, SVGPathSegRole role)
135 {
136     return SVGPathSegLinetoAbs::create(this, role, x, y);
137 }
138
139 PassRefPtr<SVGPathSegLinetoRel> SVGPathElement::createSVGPathSegLinetoRel(float x, float y, SVGPathSegRole role)
140 {
141     return SVGPathSegLinetoRel::create(this, role, x, y);
142 }
143
144 PassRefPtr<SVGPathSegCurvetoCubicAbs> SVGPathElement::createSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2, SVGPathSegRole role)
145 {
146     return SVGPathSegCurvetoCubicAbs::create(this, role, x, y, x1, y1, x2, y2);
147 }
148
149 PassRefPtr<SVGPathSegCurvetoCubicRel> SVGPathElement::createSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2, SVGPathSegRole role)
150 {
151     return SVGPathSegCurvetoCubicRel::create(this, role, x, y, x1, y1, x2, y2);
152 }
153
154 PassRefPtr<SVGPathSegCurvetoQuadraticAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1, SVGPathSegRole role)
155 {
156     return SVGPathSegCurvetoQuadraticAbs::create(this, role, x, y, x1, y1);
157 }
158
159 PassRefPtr<SVGPathSegCurvetoQuadraticRel> SVGPathElement::createSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1, SVGPathSegRole role)
160 {
161     return SVGPathSegCurvetoQuadraticRel::create(this, role, x, y, x1, y1);
162 }
163
164 PassRefPtr<SVGPathSegArcAbs> SVGPathElement::createSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, SVGPathSegRole role)
165 {
166     return SVGPathSegArcAbs::create(this, role, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
167 }
168
169 PassRefPtr<SVGPathSegArcRel> SVGPathElement::createSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, SVGPathSegRole role)
170 {
171     return SVGPathSegArcRel::create(this, role, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
172 }
173
174 PassRefPtr<SVGPathSegLinetoHorizontalAbs> SVGPathElement::createSVGPathSegLinetoHorizontalAbs(float x, SVGPathSegRole role)
175 {
176     return SVGPathSegLinetoHorizontalAbs::create(this, role, x);
177 }
178
179 PassRefPtr<SVGPathSegLinetoHorizontalRel> SVGPathElement::createSVGPathSegLinetoHorizontalRel(float x, SVGPathSegRole role)
180 {
181     return SVGPathSegLinetoHorizontalRel::create(this, role, x);
182 }
183
184 PassRefPtr<SVGPathSegLinetoVerticalAbs> SVGPathElement::createSVGPathSegLinetoVerticalAbs(float y, SVGPathSegRole role)
185 {
186     return SVGPathSegLinetoVerticalAbs::create(this, role, y);
187 }
188
189 PassRefPtr<SVGPathSegLinetoVerticalRel> SVGPathElement::createSVGPathSegLinetoVerticalRel(float y, SVGPathSegRole role)
190 {
191     return SVGPathSegLinetoVerticalRel::create(this, role, y);
192 }
193
194 PassRefPtr<SVGPathSegCurvetoCubicSmoothAbs> SVGPathElement::createSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2, SVGPathSegRole role)
195 {
196     return SVGPathSegCurvetoCubicSmoothAbs::create(this, role, x, y, x2, y2);
197 }
198
199 PassRefPtr<SVGPathSegCurvetoCubicSmoothRel> SVGPathElement::createSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2, SVGPathSegRole role)
200 {
201     return SVGPathSegCurvetoCubicSmoothRel::create(this, role, x, y, x2, y2);
202 }
203
204 PassRefPtr<SVGPathSegCurvetoQuadraticSmoothAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y, SVGPathSegRole role)
205 {
206     return SVGPathSegCurvetoQuadraticSmoothAbs::create(this, role, x, y);
207 }
208
209 PassRefPtr<SVGPathSegCurvetoQuadraticSmoothRel> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothRel(float x, float y, SVGPathSegRole role)
210 {
211     return SVGPathSegCurvetoQuadraticSmoothRel::create(this, role, x, y);
212 }
213
214 bool SVGPathElement::isSupportedAttribute(const QualifiedName& attrName)
215 {
216     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
217     if (supportedAttributes.isEmpty()) {
218         SVGLangSpace::addSupportedAttributes(supportedAttributes);
219         SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
220         supportedAttributes.add(SVGNames::dAttr);
221         supportedAttributes.add(SVGNames::pathLengthAttr);
222     }
223     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
224 }
225
226 void SVGPathElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
227 {
228     if (!isSupportedAttribute(name)) {
229         SVGGraphicsElement::parseAttribute(name, value);
230         return;
231     }
232
233     if (name == SVGNames::dAttr) {
234         if (!buildSVGPathByteStreamFromString(value, m_pathByteStream.get(), UnalteredParsing))
235             document().accessSVGExtensions()->reportError("Problem parsing d=\"" + value + "\"");
236         return;
237     }
238
239     if (name == SVGNames::pathLengthAttr) {
240         setPathLengthBaseValue(value.toFloat());
241         if (pathLengthBaseValue() < 0)
242             document().accessSVGExtensions()->reportError("A negative value for path attribute <pathLength> is not allowed");
243         return;
244     }
245
246     if (SVGLangSpace::parseAttribute(name, value))
247         return;
248     if (SVGExternalResourcesRequired::parseAttribute(name, value))
249         return;
250
251     ASSERT_NOT_REACHED();
252 }
253
254 void SVGPathElement::svgAttributeChanged(const QualifiedName& attrName)
255 {
256     if (!isSupportedAttribute(attrName)) {
257         SVGGraphicsElement::svgAttributeChanged(attrName);
258         return;
259     }
260
261     SVGElementInstance::InvalidationGuard invalidationGuard(this);
262
263     RenderSVGPath* renderer = toRenderSVGPath(this->renderer());
264
265     if (attrName == SVGNames::dAttr) {
266         if (m_pathSegList.shouldSynchronize && !SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff>(this, dPropertyInfo())->isAnimating()) {
267             SVGPathSegList newList(PathSegUnalteredRole);
268             buildSVGPathSegListFromByteStream(m_pathByteStream.get(), this, newList, UnalteredParsing);
269             m_pathSegList.value = newList;
270         }
271
272         if (renderer)
273             renderer->setNeedsShapeUpdate();
274
275         invalidateMPathDependencies();
276     }
277
278     if (renderer)
279         RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
280 }
281
282 void SVGPathElement::invalidateMPathDependencies()
283 {
284     // <mpath> can only reference <path> but this dependency is not handled in
285     // markForLayoutAndParentResourceInvalidation so we update any mpath dependencies manually.
286     if (HashSet<SVGElement*>* dependencies = document().accessSVGExtensions()->setOfElementsReferencingTarget(this)) {
287         for (auto* element : *dependencies) {
288             if (element->hasTagName(SVGNames::mpathTag))
289                 toSVGMPathElement(element)->targetPathChanged();
290         }
291     }
292 }
293
294 Node::InsertionNotificationRequest SVGPathElement::insertedInto(ContainerNode& rootParent)
295 {
296     SVGGraphicsElement::insertedInto(rootParent);
297     invalidateMPathDependencies();
298     return InsertionDone;
299 }
300
301 void SVGPathElement::removedFrom(ContainerNode& rootParent)
302 {
303     SVGGraphicsElement::removedFrom(rootParent);
304     invalidateMPathDependencies();
305 }
306
307 SVGPathByteStream* SVGPathElement::pathByteStream() const
308 {
309     SVGAnimatedProperty* property = SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff>(this, dPropertyInfo());
310     if (!property || !property->isAnimating())
311         return m_pathByteStream.get();
312     return static_cast<SVGAnimatedPathSegListPropertyTearOff*>(property)->animatedPathByteStream();
313 }
314
315 PassRefPtr<SVGAnimatedProperty> SVGPathElement::lookupOrCreateDWrapper(SVGElement* contextElement)
316 {
317     ASSERT(contextElement);
318     SVGPathElement* ownerType = toSVGPathElement(contextElement);
319
320     if (SVGAnimatedProperty* property = SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff>(ownerType, dPropertyInfo()))
321         return property;
322
323     // Build initial SVGPathSegList.
324     buildSVGPathSegListFromByteStream(ownerType->m_pathByteStream.get(), ownerType, ownerType->m_pathSegList.value, UnalteredParsing);
325
326     return SVGAnimatedProperty::lookupOrCreateWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff, SVGPathSegList>
327            (ownerType, dPropertyInfo(), ownerType->m_pathSegList.value);
328 }
329
330 void SVGPathElement::synchronizeD(SVGElement* contextElement)
331 {
332     ASSERT(contextElement);
333     SVGPathElement* ownerType = toSVGPathElement(contextElement);
334     if (!ownerType->m_pathSegList.shouldSynchronize)
335         return;
336     ownerType->m_pathSegList.synchronize(ownerType, dPropertyInfo()->attributeName, ownerType->m_pathSegList.value.valueAsString());
337 }
338
339 SVGPathSegListPropertyTearOff* SVGPathElement::pathSegList()
340 {
341     m_pathSegList.shouldSynchronize = true;
342     return static_cast<SVGPathSegListPropertyTearOff*>(static_pointer_cast<SVGAnimatedPathSegListPropertyTearOff>(lookupOrCreateDWrapper(this))->baseVal());
343 }
344
345 SVGPathSegListPropertyTearOff* SVGPathElement::normalizedPathSegList()
346 {
347     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
348     return 0;
349 }
350
351 SVGPathSegListPropertyTearOff* SVGPathElement::animatedPathSegList()
352 {
353     m_pathSegList.shouldSynchronize = true;
354     m_isAnimValObserved = true;
355     return static_cast<SVGPathSegListPropertyTearOff*>(static_pointer_cast<SVGAnimatedPathSegListPropertyTearOff>(lookupOrCreateDWrapper(this))->animVal());
356 }
357
358 SVGPathSegListPropertyTearOff* SVGPathElement::animatedNormalizedPathSegList()
359 {
360     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
361     return 0;
362 }
363
364 void SVGPathElement::pathSegListChanged(SVGPathSegRole role, ListModification listModification)
365 {
366     switch (role) {
367     case PathSegNormalizedRole:
368         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
369         break;
370     case PathSegUnalteredRole:
371         if (listModification == ListModificationAppend) {
372             ASSERT(!m_pathSegList.value.isEmpty());
373             appendSVGPathByteStreamFromSVGPathSeg(m_pathSegList.value.last(), m_pathByteStream.get(), UnalteredParsing);
374         } else
375             buildSVGPathByteStreamFromSVGPathSegList(m_pathSegList.value, m_pathByteStream.get(), UnalteredParsing);
376         break;
377     case PathSegUndefinedRole:
378         return;
379     }
380
381     invalidateSVGAttributes();
382     
383     RenderSVGPath* renderer = toRenderSVGPath(this->renderer());
384     if (!renderer)
385         return;
386
387     renderer->setNeedsShapeUpdate();
388     RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
389 }
390
391 FloatRect SVGPathElement::getBBox(StyleUpdateStrategy styleUpdateStrategy)
392 {
393     if (styleUpdateStrategy == AllowStyleUpdate)
394         document().updateLayoutIgnorePendingStylesheets();
395
396     RenderSVGPath* renderer = toRenderSVGPath(this->renderer());
397
398     // FIXME: Eventually we should support getBBox for detached elements.
399     if (!renderer)
400         return FloatRect();
401
402     return renderer->path().boundingRect();
403 }
404
405 RenderPtr<RenderElement> SVGPathElement::createElementRenderer(PassRef<RenderStyle> style)
406 {
407     // By default, any subclass is expected to do path-based drawing
408     return createRenderer<RenderSVGPath>(*this, std::move(style));
409 }
410
411 }