2513456d512d218f6fd49b3e2f52f50de9eb9f83
[WebKit-https.git] / Source / WebCore / svg / SVGLinearGradientElement.cpp
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
40 namespace WebCore {
41
42 // Animated property definitions
43 DEFINE_ANIMATED_LENGTH(SVGLinearGradientElement, SVGNames::x1Attr, X1, x1)
44 DEFINE_ANIMATED_LENGTH(SVGLinearGradientElement, SVGNames::y1Attr, Y1, y1)
45 DEFINE_ANIMATED_LENGTH(SVGLinearGradientElement, SVGNames::x2Attr, X2, x2)
46 DEFINE_ANIMATED_LENGTH(SVGLinearGradientElement, SVGNames::y2Attr, Y2, y2)
47
48 BEGIN_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)
54 END_REGISTER_ANIMATED_PROPERTIES
55
56 inline 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
68 PassRefPtr<SVGLinearGradientElement> SVGLinearGradientElement::create(const QualifiedName& tagName, Document& document)
69 {
70     return adoptRef(new SVGLinearGradientElement(tagName, document));
71 }
72
73 bool 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
85 void 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
105 void 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
120 RenderPtr<RenderElement> SVGLinearGradientElement::createElementRenderer(PassRef<RenderStyle> style)
121 {
122     return createRenderer<RenderSVGResourceLinearGradient>(*this, WTF::move(style));
123 }
124
125 static 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 = downcast<SVGLinearGradientElement>(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
162 bool 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
196 bool SVGLinearGradientElement::selfHasRelativeLengths() const
197 {
198     return x1().isRelative()
199         || y1().isRelative()
200         || x2().isRelative()
201         || y2().isRelative();
202 }
203
204 }