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