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