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#if ENABLE(SVG)
27#include "SVGRadialGradientElement.h"
28
29#include "Attribute.h"
30#include "FloatConversion.h"
31#include "FloatPoint.h"
32#include "RadialGradientAttributes.h"
33#include "RenderSVGResourceRadialGradient.h"
34#include "SVGElementInstance.h"
35#include "SVGNames.h"
36#include "SVGStopElement.h"
37#include "SVGTransform.h"
38#include "SVGTransformList.h"
39#include "SVGUnitTypes.h"
40
41namespace WebCore {
42
43// Animated property definitions
44DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cxAttr, Cx, cx)
45DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cyAttr, Cy, cy)
46DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::rAttr, R, r)
47DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fxAttr, Fx, fx)
48DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fyAttr, Fy, fy)
49DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::frAttr, Fr, fr)
50
51BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGRadialGradientElement)
52    REGISTER_LOCAL_ANIMATED_PROPERTY(cx)
53    REGISTER_LOCAL_ANIMATED_PROPERTY(cy)
54    REGISTER_LOCAL_ANIMATED_PROPERTY(r)
55    REGISTER_LOCAL_ANIMATED_PROPERTY(fx)
56    REGISTER_LOCAL_ANIMATED_PROPERTY(fy)
57    REGISTER_LOCAL_ANIMATED_PROPERTY(fr)
58    REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGradientElement)
59END_REGISTER_ANIMATED_PROPERTIES
60
61inline SVGRadialGradientElement::SVGRadialGradientElement(const QualifiedName& tagName, Document* document)
62    : SVGGradientElement(tagName, document)
63    , m_cx(LengthModeWidth, "50%")
64    , m_cy(LengthModeHeight, "50%")
65    , m_r(LengthModeOther, "50%")
66    , m_fx(LengthModeWidth)
67    , m_fy(LengthModeHeight)
68    , m_fr(LengthModeOther, "0%")
69{
70    // Spec: If the cx/cy/r/fr attribute is not specified, the effect is as if a value of "50%" were specified.
71    ASSERT(hasTagName(SVGNames::radialGradientTag));
72    registerAnimatedPropertiesForSVGRadialGradientElement();
73}
74
75PassRefPtr<SVGRadialGradientElement> SVGRadialGradientElement::create(const QualifiedName& tagName, Document* document)
76{
77    return adoptRef(new SVGRadialGradientElement(tagName, document));
78}
79
80bool SVGRadialGradientElement::isSupportedAttribute(const QualifiedName& attrName)
81{
82    DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
83    if (supportedAttributes.isEmpty()) {
84        supportedAttributes.add(SVGNames::cxAttr);
85        supportedAttributes.add(SVGNames::cyAttr);
86        supportedAttributes.add(SVGNames::fxAttr);
87        supportedAttributes.add(SVGNames::fyAttr);
88        supportedAttributes.add(SVGNames::rAttr);
89        supportedAttributes.add(SVGNames::frAttr);
90    }
91    return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
92}
93
94void SVGRadialGradientElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
95{
96    SVGParsingError parseError = NoError;
97
98    if (!isSupportedAttribute(name))
99        SVGGradientElement::parseAttribute(name, value);
100    else if (name == SVGNames::cxAttr)
101        setCxBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
102    else if (name == SVGNames::cyAttr)
103        setCyBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
104    else if (name == SVGNames::rAttr)
105        setRBaseValue(SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths));
106    else if (name == SVGNames::fxAttr)
107        setFxBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
108    else if (name == SVGNames::fyAttr)
109        setFyBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
110    else if (name == SVGNames::frAttr)
111        setFrBaseValue(SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths));
112    else
113        ASSERT_NOT_REACHED();
114
115    reportAttributeParsingError(parseError, name, value);
116}
117
118void SVGRadialGradientElement::svgAttributeChanged(const QualifiedName& attrName)
119{
120    if (!isSupportedAttribute(attrName)) {
121        SVGGradientElement::svgAttributeChanged(attrName);
122        return;
123    }
124
125    SVGElementInstance::InvalidationGuard invalidationGuard(this);
126
127    updateRelativeLengthsInformation();
128
129    if (RenderObject* object = renderer())
130        object->setNeedsLayout(true);
131}
132
133RenderObject* SVGRadialGradientElement::createRenderer(RenderArena* arena, RenderStyle*)
134{
135    return new (arena) RenderSVGResourceRadialGradient(this);
136}
137
138bool SVGRadialGradientElement::collectGradientAttributes(RadialGradientAttributes& attributes)
139{
140    HashSet<SVGGradientElement*> processedGradients;
141
142    bool isRadial = true;
143    SVGGradientElement* current = this;
144
145    while (current) {
146        if (!current->renderer())
147            return false;
148
149        if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr))
150            attributes.setSpreadMethod(current->spreadMethod());
151
152        if (!attributes.hasGradientUnits() && current->hasAttribute(SVGNames::gradientUnitsAttr))
153            attributes.setGradientUnits(current->gradientUnits());
154
155        if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) {
156            AffineTransform transform;
157            current->gradientTransform().concatenate(transform);
158            attributes.setGradientTransform(transform);
159        }
160
161        if (!attributes.hasStops()) {
162            const Vector<Gradient::ColorStop>& stops(current->buildStops());
163            if (!stops.isEmpty())
164                attributes.setStops(stops);
165        }
166
167        if (isRadial) {
168            SVGRadialGradientElement* radial = static_cast<SVGRadialGradientElement*>(current);
169
170            if (!attributes.hasCx() && current->hasAttribute(SVGNames::cxAttr))
171                attributes.setCx(radial->cx());
172
173            if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr))
174                attributes.setCy(radial->cy());
175
176            if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr))
177                attributes.setR(radial->r());
178
179            if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr))
180                attributes.setFx(radial->fx());
181
182            if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr))
183                attributes.setFy(radial->fy());
184
185            if (!attributes.hasFr() && current->hasAttribute(SVGNames::frAttr))
186                attributes.setFr(radial->fr());
187        }
188
189        processedGradients.add(current);
190
191        // Respect xlink:href, take attributes from referenced element
192        Node* refNode = SVGURIReference::targetElementFromIRIString(current->href(), document());
193        if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) {
194            current = static_cast<SVGGradientElement*>(refNode);
195
196            // Cycle detection
197            if (processedGradients.contains(current)) {
198                current = 0;
199                break;
200            }
201
202            isRadial = current->hasTagName(SVGNames::radialGradientTag);
203        } else
204            current = 0;
205    }
206
207    // Handle default values for fx/fy
208    if (!attributes.hasFx())
209        attributes.setFx(attributes.cx());
210
211    if (!attributes.hasFy())
212        attributes.setFy(attributes.cy());
213    return true;
214}
215
216bool SVGRadialGradientElement::selfHasRelativeLengths() const
217{
218    return cx().isRelative()
219        || cy().isRelative()
220        || r().isRelative()
221        || fx().isRelative()
222        || fy().isRelative()
223        || fr().isRelative();
224}
225
226}
227
228#endif // ENABLE(SVG)
229