Have is<>(T*) function do a null check on the pointer argument
[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 #include <wtf/NeverDestroyed.h>
55
56 namespace WebCore {
57
58 // Define custom animated property 'd'.
59 const SVGPropertyInfo* SVGPathElement::dPropertyInfo()
60 {
61     static const SVGPropertyInfo* s_propertyInfo = 0;
62     if (!s_propertyInfo) {
63         s_propertyInfo = new SVGPropertyInfo(AnimatedPath,
64                                              PropertyIsReadWrite,
65                                              SVGNames::dAttr,
66                                              SVGNames::dAttr.localName(),
67                                              &SVGPathElement::synchronizeD,
68                                              &SVGPathElement::lookupOrCreateDWrapper);
69     }
70     return s_propertyInfo;
71 }
72
73 // Animated property definitions
74 DEFINE_ANIMATED_NUMBER(SVGPathElement, SVGNames::pathLengthAttr, PathLength, pathLength)
75 DEFINE_ANIMATED_BOOLEAN(SVGPathElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
76
77 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGPathElement)
78     REGISTER_LOCAL_ANIMATED_PROPERTY(d)
79     REGISTER_LOCAL_ANIMATED_PROPERTY(pathLength)
80     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
81     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
82 END_REGISTER_ANIMATED_PROPERTIES
83
84 inline SVGPathElement::SVGPathElement(const QualifiedName& tagName, Document& document)
85     : SVGGraphicsElement(tagName, document)
86     , m_pathByteStream(std::make_unique<SVGPathByteStream>())
87     , m_pathSegList(PathSegUnalteredRole)
88     , m_isAnimValObserved(false)
89 {
90     ASSERT(hasTagName(SVGNames::pathTag));
91     registerAnimatedPropertiesForSVGPathElement();
92 }
93
94 PassRefPtr<SVGPathElement> SVGPathElement::create(const QualifiedName& tagName, Document& document)
95 {
96     return adoptRef(new SVGPathElement(tagName, document));
97 }
98
99 float SVGPathElement::getTotalLength()
100 {
101     float totalLength = 0;
102     getTotalLengthOfSVGPathByteStream(pathByteStream(), totalLength);
103     return totalLength;
104 }
105
106 SVGPoint SVGPathElement::getPointAtLength(float length)
107 {
108     SVGPoint point;
109     getPointAtLengthOfSVGPathByteStream(pathByteStream(), length, point);
110     return point;
111 }
112
113 unsigned SVGPathElement::getPathSegAtLength(float length)
114 {
115     unsigned pathSeg = 0;
116     getSVGPathSegAtLengthFromSVGPathByteStream(pathByteStream(), length, pathSeg);
117     return pathSeg;
118 }
119
120 PassRefPtr<SVGPathSegClosePath> SVGPathElement::createSVGPathSegClosePath(SVGPathSegRole role)
121 {
122     return SVGPathSegClosePath::create(this, role);
123 }
124
125 PassRefPtr<SVGPathSegMovetoAbs> SVGPathElement::createSVGPathSegMovetoAbs(float x, float y, SVGPathSegRole role)
126 {
127     return SVGPathSegMovetoAbs::create(this, role, x, y);
128 }
129
130 PassRefPtr<SVGPathSegMovetoRel> SVGPathElement::createSVGPathSegMovetoRel(float x, float y, SVGPathSegRole role)
131 {
132     return SVGPathSegMovetoRel::create(this, role, x, y);
133 }
134
135 PassRefPtr<SVGPathSegLinetoAbs> SVGPathElement::createSVGPathSegLinetoAbs(float x, float y, SVGPathSegRole role)
136 {
137     return SVGPathSegLinetoAbs::create(this, role, x, y);
138 }
139
140 PassRefPtr<SVGPathSegLinetoRel> SVGPathElement::createSVGPathSegLinetoRel(float x, float y, SVGPathSegRole role)
141 {
142     return SVGPathSegLinetoRel::create(this, role, x, y);
143 }
144
145 PassRefPtr<SVGPathSegCurvetoCubicAbs> SVGPathElement::createSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2, SVGPathSegRole role)
146 {
147     return SVGPathSegCurvetoCubicAbs::create(this, role, x, y, x1, y1, x2, y2);
148 }
149
150 PassRefPtr<SVGPathSegCurvetoCubicRel> SVGPathElement::createSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2, SVGPathSegRole role)
151 {
152     return SVGPathSegCurvetoCubicRel::create(this, role, x, y, x1, y1, x2, y2);
153 }
154
155 PassRefPtr<SVGPathSegCurvetoQuadraticAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1, SVGPathSegRole role)
156 {
157     return SVGPathSegCurvetoQuadraticAbs::create(this, role, x, y, x1, y1);
158 }
159
160 PassRefPtr<SVGPathSegCurvetoQuadraticRel> SVGPathElement::createSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1, SVGPathSegRole role)
161 {
162     return SVGPathSegCurvetoQuadraticRel::create(this, role, x, y, x1, y1);
163 }
164
165 PassRefPtr<SVGPathSegArcAbs> SVGPathElement::createSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, SVGPathSegRole role)
166 {
167     return SVGPathSegArcAbs::create(this, role, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
168 }
169
170 PassRefPtr<SVGPathSegArcRel> SVGPathElement::createSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, SVGPathSegRole role)
171 {
172     return SVGPathSegArcRel::create(this, role, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
173 }
174
175 PassRefPtr<SVGPathSegLinetoHorizontalAbs> SVGPathElement::createSVGPathSegLinetoHorizontalAbs(float x, SVGPathSegRole role)
176 {
177     return SVGPathSegLinetoHorizontalAbs::create(this, role, x);
178 }
179
180 PassRefPtr<SVGPathSegLinetoHorizontalRel> SVGPathElement::createSVGPathSegLinetoHorizontalRel(float x, SVGPathSegRole role)
181 {
182     return SVGPathSegLinetoHorizontalRel::create(this, role, x);
183 }
184
185 PassRefPtr<SVGPathSegLinetoVerticalAbs> SVGPathElement::createSVGPathSegLinetoVerticalAbs(float y, SVGPathSegRole role)
186 {
187     return SVGPathSegLinetoVerticalAbs::create(this, role, y);
188 }
189
190 PassRefPtr<SVGPathSegLinetoVerticalRel> SVGPathElement::createSVGPathSegLinetoVerticalRel(float y, SVGPathSegRole role)
191 {
192     return SVGPathSegLinetoVerticalRel::create(this, role, y);
193 }
194
195 PassRefPtr<SVGPathSegCurvetoCubicSmoothAbs> SVGPathElement::createSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2, SVGPathSegRole role)
196 {
197     return SVGPathSegCurvetoCubicSmoothAbs::create(this, role, x, y, x2, y2);
198 }
199
200 PassRefPtr<SVGPathSegCurvetoCubicSmoothRel> SVGPathElement::createSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2, SVGPathSegRole role)
201 {
202     return SVGPathSegCurvetoCubicSmoothRel::create(this, role, x, y, x2, y2);
203 }
204
205 PassRefPtr<SVGPathSegCurvetoQuadraticSmoothAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y, SVGPathSegRole role)
206 {
207     return SVGPathSegCurvetoQuadraticSmoothAbs::create(this, role, x, y);
208 }
209
210 PassRefPtr<SVGPathSegCurvetoQuadraticSmoothRel> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothRel(float x, float y, SVGPathSegRole role)
211 {
212     return SVGPathSegCurvetoQuadraticSmoothRel::create(this, role, x, y);
213 }
214
215 bool SVGPathElement::isSupportedAttribute(const QualifiedName& attrName)
216 {
217     static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes;
218     if (supportedAttributes.get().isEmpty()) {
219         SVGLangSpace::addSupportedAttributes(supportedAttributes);
220         SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
221         supportedAttributes.get().add(SVGNames::dAttr);
222         supportedAttributes.get().add(SVGNames::pathLengthAttr);
223     }
224     return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
225 }
226
227 void SVGPathElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
228 {
229     if (!isSupportedAttribute(name)) {
230         SVGGraphicsElement::parseAttribute(name, value);
231         return;
232     }
233
234     if (name == SVGNames::dAttr) {
235         if (!buildSVGPathByteStreamFromString(value, m_pathByteStream.get(), UnalteredParsing))
236             document().accessSVGExtensions().reportError("Problem parsing d=\"" + value + "\"");
237         return;
238     }
239
240     if (name == SVGNames::pathLengthAttr) {
241         setPathLengthBaseValue(value.toFloat());
242         if (pathLengthBaseValue() < 0)
243             document().accessSVGExtensions().reportError("A negative value for path attribute <pathLength> is not allowed");
244         return;
245     }
246
247     if (SVGLangSpace::parseAttribute(name, value))
248         return;
249     if (SVGExternalResourcesRequired::parseAttribute(name, value))
250         return;
251
252     ASSERT_NOT_REACHED();
253 }
254
255 void SVGPathElement::svgAttributeChanged(const QualifiedName& attrName)
256 {
257     if (!isSupportedAttribute(attrName)) {
258         SVGGraphicsElement::svgAttributeChanged(attrName);
259         return;
260     }
261
262     SVGElementInstance::InvalidationGuard invalidationGuard(this);
263
264     RenderSVGPath* renderer = toRenderSVGPath(this->renderer());
265
266     if (attrName == SVGNames::dAttr) {
267         if (m_pathSegList.shouldSynchronize && !SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff>(this, dPropertyInfo())->isAnimating()) {
268             SVGPathSegList newList(PathSegUnalteredRole);
269             buildSVGPathSegListFromByteStream(m_pathByteStream.get(), this, newList, UnalteredParsing);
270             m_pathSegList.value = newList;
271         }
272
273         if (renderer)
274             renderer->setNeedsShapeUpdate();
275
276         invalidateMPathDependencies();
277     }
278
279     if (renderer)
280         RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
281 }
282
283 void SVGPathElement::invalidateMPathDependencies()
284 {
285     // <mpath> can only reference <path> but this dependency is not handled in
286     // markForLayoutAndParentResourceInvalidation so we update any mpath dependencies manually.
287     if (HashSet<SVGElement*>* dependencies = document().accessSVGExtensions().setOfElementsReferencingTarget(this)) {
288         for (auto* element : *dependencies) {
289             if (is<SVGMPathElement>(*element))
290                 downcast<SVGMPathElement>(*element).targetPathChanged();
291         }
292     }
293 }
294
295 Node::InsertionNotificationRequest SVGPathElement::insertedInto(ContainerNode& rootParent)
296 {
297     SVGGraphicsElement::insertedInto(rootParent);
298     invalidateMPathDependencies();
299     return InsertionDone;
300 }
301
302 void SVGPathElement::removedFrom(ContainerNode& rootParent)
303 {
304     SVGGraphicsElement::removedFrom(rootParent);
305     invalidateMPathDependencies();
306 }
307
308 SVGPathByteStream* SVGPathElement::pathByteStream() const
309 {
310     SVGAnimatedProperty* property = SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff>(this, dPropertyInfo());
311     if (!property || !property->isAnimating())
312         return m_pathByteStream.get();
313     return static_cast<SVGAnimatedPathSegListPropertyTearOff*>(property)->animatedPathByteStream();
314 }
315
316 PassRefPtr<SVGAnimatedProperty> SVGPathElement::lookupOrCreateDWrapper(SVGElement* contextElement)
317 {
318     ASSERT(contextElement);
319     SVGPathElement& ownerType = downcast<SVGPathElement>(*contextElement);
320
321     if (SVGAnimatedProperty* property = SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff>(&ownerType, dPropertyInfo()))
322         return property;
323
324     // Build initial SVGPathSegList.
325     buildSVGPathSegListFromByteStream(ownerType.m_pathByteStream.get(), &ownerType, ownerType.m_pathSegList.value, UnalteredParsing);
326
327     return SVGAnimatedProperty::lookupOrCreateWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff, SVGPathSegList>
328         (&ownerType, dPropertyInfo(), ownerType.m_pathSegList.value);
329 }
330
331 void SVGPathElement::synchronizeD(SVGElement* contextElement)
332 {
333     ASSERT(contextElement);
334     SVGPathElement& ownerType = downcast<SVGPathElement>(*contextElement);
335     if (!ownerType.m_pathSegList.shouldSynchronize)
336         return;
337     ownerType.m_pathSegList.synchronize(&ownerType, dPropertyInfo()->attributeName, ownerType.m_pathSegList.value.valueAsString());
338 }
339
340 SVGPathSegListPropertyTearOff* SVGPathElement::pathSegList()
341 {
342     m_pathSegList.shouldSynchronize = true;
343     return static_cast<SVGPathSegListPropertyTearOff*>(static_pointer_cast<SVGAnimatedPathSegListPropertyTearOff>(lookupOrCreateDWrapper(this))->baseVal());
344 }
345
346 SVGPathSegListPropertyTearOff* SVGPathElement::normalizedPathSegList()
347 {
348     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
349     return 0;
350 }
351
352 SVGPathSegListPropertyTearOff* SVGPathElement::animatedPathSegList()
353 {
354     m_pathSegList.shouldSynchronize = true;
355     m_isAnimValObserved = true;
356     return static_cast<SVGPathSegListPropertyTearOff*>(static_pointer_cast<SVGAnimatedPathSegListPropertyTearOff>(lookupOrCreateDWrapper(this))->animVal());
357 }
358
359 SVGPathSegListPropertyTearOff* SVGPathElement::animatedNormalizedPathSegList()
360 {
361     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
362     return 0;
363 }
364
365 void SVGPathElement::pathSegListChanged(SVGPathSegRole role, ListModification listModification)
366 {
367     switch (role) {
368     case PathSegNormalizedRole:
369         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
370         break;
371     case PathSegUnalteredRole:
372         if (listModification == ListModificationAppend) {
373             ASSERT(!m_pathSegList.value.isEmpty());
374             appendSVGPathByteStreamFromSVGPathSeg(m_pathSegList.value.last(), m_pathByteStream.get(), UnalteredParsing);
375         } else
376             buildSVGPathByteStreamFromSVGPathSegList(m_pathSegList.value, m_pathByteStream.get(), UnalteredParsing);
377         break;
378     case PathSegUndefinedRole:
379         return;
380     }
381
382     invalidateSVGAttributes();
383     
384     RenderSVGPath* renderer = toRenderSVGPath(this->renderer());
385     if (!renderer)
386         return;
387
388     renderer->setNeedsShapeUpdate();
389     RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
390 }
391
392 FloatRect SVGPathElement::getBBox(StyleUpdateStrategy styleUpdateStrategy)
393 {
394     if (styleUpdateStrategy == AllowStyleUpdate)
395         document().updateLayoutIgnorePendingStylesheets();
396
397     RenderSVGPath* renderer = toRenderSVGPath(this->renderer());
398
399     // FIXME: Eventually we should support getBBox for detached elements.
400     if (!renderer)
401         return FloatRect();
402
403     return renderer->path().boundingRect();
404 }
405
406 RenderPtr<RenderElement> SVGPathElement::createElementRenderer(PassRef<RenderStyle> style)
407 {
408     // By default, any subclass is expected to do path-based drawing
409     return createRenderer<RenderSVGPath>(*this, WTF::move(style));
410 }
411
412 }