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