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/SVGLinearGradientElement.h"
27
28#include "core/rendering/svg/RenderSVGResourceLinearGradient.h"
29#include "core/svg/LinearGradientAttributes.h"
30#include "core/svg/SVGElementInstance.h"
31#include "core/svg/SVGLength.h"
32#include "core/svg/SVGTransformList.h"
33
34namespace WebCore {
35
36// Animated property definitions
37DEFINE_ANIMATED_LENGTH(SVGLinearGradientElement, SVGNames::x1Attr, X1, x1)
38DEFINE_ANIMATED_LENGTH(SVGLinearGradientElement, SVGNames::y1Attr, Y1, y1)
39DEFINE_ANIMATED_LENGTH(SVGLinearGradientElement, SVGNames::x2Attr, X2, x2)
40DEFINE_ANIMATED_LENGTH(SVGLinearGradientElement, SVGNames::y2Attr, Y2, y2)
41
42BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGLinearGradientElement)
43    REGISTER_LOCAL_ANIMATED_PROPERTY(x1)
44    REGISTER_LOCAL_ANIMATED_PROPERTY(y1)
45    REGISTER_LOCAL_ANIMATED_PROPERTY(x2)
46    REGISTER_LOCAL_ANIMATED_PROPERTY(y2)
47    REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGradientElement)
48END_REGISTER_ANIMATED_PROPERTIES
49
50inline SVGLinearGradientElement::SVGLinearGradientElement(Document& document)
51    : SVGGradientElement(SVGNames::linearGradientTag, document)
52    , m_x1(LengthModeWidth)
53    , m_y1(LengthModeHeight)
54    , m_x2(LengthModeWidth, "100%")
55    , m_y2(LengthModeHeight)
56{
57    // Spec: If the x2 attribute is not specified, the effect is as if a value of "100%" were specified.
58    ScriptWrappable::init(this);
59    registerAnimatedPropertiesForSVGLinearGradientElement();
60}
61
62PassRefPtr<SVGLinearGradientElement> SVGLinearGradientElement::create(Document& document)
63{
64    return adoptRef(new SVGLinearGradientElement(document));
65}
66
67bool SVGLinearGradientElement::isSupportedAttribute(const QualifiedName& attrName)
68{
69    DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
70    if (supportedAttributes.isEmpty()) {
71        supportedAttributes.add(SVGNames::x1Attr);
72        supportedAttributes.add(SVGNames::x2Attr);
73        supportedAttributes.add(SVGNames::y1Attr);
74        supportedAttributes.add(SVGNames::y2Attr);
75    }
76    return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
77}
78
79void SVGLinearGradientElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
80{
81    SVGParsingError parseError = NoError;
82
83    if (!isSupportedAttribute(name))
84        SVGGradientElement::parseAttribute(name, value);
85    else if (name == SVGNames::x1Attr)
86        setX1BaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
87    else if (name == SVGNames::y1Attr)
88        setY1BaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
89    else if (name == SVGNames::x2Attr)
90        setX2BaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
91    else if (name == SVGNames::y2Attr)
92        setY2BaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
93    else
94        ASSERT_NOT_REACHED();
95
96    reportAttributeParsingError(parseError, name, value);
97}
98
99void SVGLinearGradientElement::svgAttributeChanged(const QualifiedName& attrName)
100{
101    if (!isSupportedAttribute(attrName)) {
102        SVGGradientElement::svgAttributeChanged(attrName);
103        return;
104    }
105
106    SVGElementInstance::InvalidationGuard invalidationGuard(this);
107
108    updateRelativeLengthsInformation();
109
110    RenderSVGResourceContainer* renderer = toRenderSVGResourceContainer(this->renderer());
111    if (renderer)
112        renderer->invalidateCacheAndMarkForLayout();
113}
114
115RenderObject* SVGLinearGradientElement::createRenderer(RenderStyle*)
116{
117    return new RenderSVGResourceLinearGradient(this);
118}
119
120bool SVGLinearGradientElement::collectGradientAttributes(LinearGradientAttributes& attributes)
121{
122    HashSet<SVGGradientElement*> processedGradients;
123
124    bool isLinear = true;
125    SVGGradientElement* current = this;
126
127    while (current) {
128        if (!current->renderer())
129            return false;
130
131        if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr))
132            attributes.setSpreadMethod(current->spreadMethodCurrentValue());
133
134        if (!attributes.hasGradientUnits() && current->hasAttribute(SVGNames::gradientUnitsAttr))
135            attributes.setGradientUnits(current->gradientUnitsCurrentValue());
136
137        if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) {
138            AffineTransform transform;
139            current->gradientTransformCurrentValue().concatenate(transform);
140            attributes.setGradientTransform(transform);
141        }
142
143        if (!attributes.hasStops()) {
144            const Vector<Gradient::ColorStop>& stops(current->buildStops());
145            if (!stops.isEmpty())
146                attributes.setStops(stops);
147        }
148
149        if (isLinear) {
150            SVGLinearGradientElement* linear = toSVGLinearGradientElement(current);
151
152            if (!attributes.hasX1() && current->hasAttribute(SVGNames::x1Attr))
153                attributes.setX1(linear->x1CurrentValue());
154
155            if (!attributes.hasY1() && current->hasAttribute(SVGNames::y1Attr))
156                attributes.setY1(linear->y1CurrentValue());
157
158            if (!attributes.hasX2() && current->hasAttribute(SVGNames::x2Attr))
159                attributes.setX2(linear->x2CurrentValue());
160
161            if (!attributes.hasY2() && current->hasAttribute(SVGNames::y2Attr))
162                attributes.setY2(linear->y2CurrentValue());
163        }
164
165        processedGradients.add(current);
166
167        // Respect xlink:href, take attributes from referenced element
168        Node* refNode = SVGURIReference::targetElementFromIRIString(current->hrefCurrentValue(), document());
169        if (refNode && (refNode->hasTagName(SVGNames::linearGradientTag) || refNode->hasTagName(SVGNames::radialGradientTag))) {
170            current = toSVGGradientElement(refNode);
171
172            // Cycle detection
173            if (processedGradients.contains(current)) {
174                current = 0;
175                break;
176            }
177
178            isLinear = current->hasTagName(SVGNames::linearGradientTag);
179        } else
180            current = 0;
181    }
182
183    return true;
184}
185
186bool SVGLinearGradientElement::selfHasRelativeLengths() const
187{
188    return x1CurrentValue().isRelative()
189        || y1CurrentValue().isRelative()
190        || x2CurrentValue().isRelative()
191        || y2CurrentValue().isRelative();
192}
193
194}
195