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