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 * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
5 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
6 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "config.h"
25
26#include "core/svg/SVGRadialGradientElement.h"
27
28#include "core/rendering/svg/RenderSVGResourceRadialGradient.h"
29#include "core/svg/RadialGradientAttributes.h"
30#include "core/svg/SVGElementInstance.h"
31#include "core/svg/SVGTransformList.h"
32
33namespace WebCore {
34
35// Animated property definitions
36DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cxAttr, Cx, cx)
37DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cyAttr, Cy, cy)
38DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::rAttr, R, r)
39DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fxAttr, Fx, fx)
40DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fyAttr, Fy, fy)
41DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::frAttr, Fr, fr)
42
43BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGRadialGradientElement)
44    REGISTER_LOCAL_ANIMATED_PROPERTY(cx)
45    REGISTER_LOCAL_ANIMATED_PROPERTY(cy)
46    REGISTER_LOCAL_ANIMATED_PROPERTY(r)
47    REGISTER_LOCAL_ANIMATED_PROPERTY(fx)
48    REGISTER_LOCAL_ANIMATED_PROPERTY(fy)
49    REGISTER_LOCAL_ANIMATED_PROPERTY(fr)
50    REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGradientElement)
51END_REGISTER_ANIMATED_PROPERTIES
52
53inline SVGRadialGradientElement::SVGRadialGradientElement(Document& document)
54    : SVGGradientElement(SVGNames::radialGradientTag, document)
55    , m_cx(LengthModeWidth, "50%")
56    , m_cy(LengthModeHeight, "50%")
57    , m_r(LengthModeOther, "50%")
58    , m_fx(LengthModeWidth)
59    , m_fy(LengthModeHeight)
60    , m_fr(LengthModeOther, "0%")
61{
62    // Spec: If the cx/cy/r/fr attribute is not specified, the effect is as if a value of "50%" were specified.
63    ScriptWrappable::init(this);
64    registerAnimatedPropertiesForSVGRadialGradientElement();
65}
66
67PassRefPtr<SVGRadialGradientElement> SVGRadialGradientElement::create(Document& document)
68{
69    return adoptRef(new SVGRadialGradientElement(document));
70}
71
72bool SVGRadialGradientElement::isSupportedAttribute(const QualifiedName& attrName)
73{
74    DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
75    if (supportedAttributes.isEmpty()) {
76        supportedAttributes.add(SVGNames::cxAttr);
77        supportedAttributes.add(SVGNames::cyAttr);
78        supportedAttributes.add(SVGNames::fxAttr);
79        supportedAttributes.add(SVGNames::fyAttr);
80        supportedAttributes.add(SVGNames::rAttr);
81        supportedAttributes.add(SVGNames::frAttr);
82    }
83    return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
84}
85
86void SVGRadialGradientElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
87{
88    SVGParsingError parseError = NoError;
89
90    if (!isSupportedAttribute(name))
91        SVGGradientElement::parseAttribute(name, value);
92    else if (name == SVGNames::cxAttr)
93        setCxBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
94    else if (name == SVGNames::cyAttr)
95        setCyBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
96    else if (name == SVGNames::rAttr)
97        setRBaseValue(SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths));
98    else if (name == SVGNames::fxAttr)
99        setFxBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
100    else if (name == SVGNames::fyAttr)
101        setFyBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
102    else if (name == SVGNames::frAttr)
103        setFrBaseValue(SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths));
104    else
105        ASSERT_NOT_REACHED();
106
107    reportAttributeParsingError(parseError, name, value);
108}
109
110void SVGRadialGradientElement::svgAttributeChanged(const QualifiedName& attrName)
111{
112    if (!isSupportedAttribute(attrName)) {
113        SVGGradientElement::svgAttributeChanged(attrName);
114        return;
115    }
116
117    SVGElementInstance::InvalidationGuard invalidationGuard(this);
118
119    updateRelativeLengthsInformation();
120
121    RenderSVGResourceContainer* renderer = toRenderSVGResourceContainer(this->renderer());
122    if (renderer)
123        renderer->invalidateCacheAndMarkForLayout();
124}
125
126RenderObject* SVGRadialGradientElement::createRenderer(RenderStyle*)
127{
128    return new RenderSVGResourceRadialGradient(this);
129}
130
131bool SVGRadialGradientElement::collectGradientAttributes(RadialGradientAttributes& attributes)
132{
133    HashSet<SVGGradientElement*> processedGradients;
134
135    bool isRadial = true;
136    SVGGradientElement* current = this;
137
138    while (current) {
139        if (!current->renderer())
140            return false;
141
142        if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr))
143            attributes.setSpreadMethod(current->spreadMethodCurrentValue());
144
145        if (!attributes.hasGradientUnits() && current->hasAttribute(SVGNames::gradientUnitsAttr))
146            attributes.setGradientUnits(current->gradientUnitsCurrentValue());
147
148        if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) {
149            AffineTransform transform;
150            current->gradientTransformCurrentValue().concatenate(transform);
151            attributes.setGradientTransform(transform);
152        }
153
154        if (!attributes.hasStops()) {
155            const Vector<Gradient::ColorStop>& stops(current->buildStops());
156            if (!stops.isEmpty())
157                attributes.setStops(stops);
158        }
159
160        if (isRadial) {
161            SVGRadialGradientElement* radial = toSVGRadialGradientElement(current);
162
163            if (!attributes.hasCx() && current->hasAttribute(SVGNames::cxAttr))
164                attributes.setCx(radial->cxCurrentValue());
165
166            if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr))
167                attributes.setCy(radial->cyCurrentValue());
168
169            if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr))
170                attributes.setR(radial->rCurrentValue());
171
172            if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr))
173                attributes.setFx(radial->fxCurrentValue());
174
175            if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr))
176                attributes.setFy(radial->fyCurrentValue());
177
178            if (!attributes.hasFr() && current->hasAttribute(SVGNames::frAttr))
179                attributes.setFr(radial->frCurrentValue());
180        }
181
182        processedGradients.add(current);
183
184        // Respect xlink:href, take attributes from referenced element
185        Node* refNode = SVGURIReference::targetElementFromIRIString(current->hrefCurrentValue(), document());
186        if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) {
187            current = toSVGGradientElement(refNode);
188
189            // Cycle detection
190            if (processedGradients.contains(current)) {
191                current = 0;
192                break;
193            }
194
195            isRadial = current->hasTagName(SVGNames::radialGradientTag);
196        } else
197            current = 0;
198    }
199
200    // Handle default values for fx/fy
201    if (!attributes.hasFx())
202        attributes.setFx(attributes.cx());
203
204    if (!attributes.hasFy())
205        attributes.setFy(attributes.cy());
206    return true;
207}
208
209bool SVGRadialGradientElement::selfHasRelativeLengths() const
210{
211    return cxCurrentValue().isRelative()
212        || cyCurrentValue().isRelative()
213        || rCurrentValue().isRelative()
214        || fxCurrentValue().isRelative()
215        || fyCurrentValue().isRelative()
216        || frCurrentValue().isRelative();
217}
218
219}
220