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