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