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