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 * Copyright (C) 2018-2019 Apple Inc. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25#include "config.h"
26#include "SVGRadialGradientElement.h"
27
28#include "FloatConversion.h"
29#include "FloatPoint.h"
30#include "RadialGradientAttributes.h"
31#include "RenderSVGResourceRadialGradient.h"
32#include "SVGNames.h"
33#include "SVGStopElement.h"
34#include "SVGUnitTypes.h"
35#include <wtf/IsoMallocInlines.h>
36#include <wtf/NeverDestroyed.h>
37
38namespace WebCore {
39
40WTF_MAKE_ISO_ALLOCATED_IMPL(SVGRadialGradientElement);
41
42inline SVGRadialGradientElement::SVGRadialGradientElement(const QualifiedName& tagName, Document& document)
43 : SVGGradientElement(tagName, document)
44{
45 // Spec: If the cx/cy/r/fr attribute is not specified, the effect is as if a value of "50%" were specified.
46 ASSERT(hasTagName(SVGNames::radialGradientTag));
47
48 static std::once_flag onceFlag;
49 std::call_once(onceFlag, [] {
50 PropertyRegistry::registerProperty<SVGNames::cxAttr, &SVGRadialGradientElement::m_cx>();
51 PropertyRegistry::registerProperty<SVGNames::cyAttr, &SVGRadialGradientElement::m_cy>();
52 PropertyRegistry::registerProperty<SVGNames::rAttr, &SVGRadialGradientElement::m_r>();
53 PropertyRegistry::registerProperty<SVGNames::fxAttr, &SVGRadialGradientElement::m_fx>();
54 PropertyRegistry::registerProperty<SVGNames::fyAttr, &SVGRadialGradientElement::m_fy>();
55 PropertyRegistry::registerProperty<SVGNames::frAttr, &SVGRadialGradientElement::m_fr>();
56 });
57}
58
59Ref<SVGRadialGradientElement> SVGRadialGradientElement::create(const QualifiedName& tagName, Document& document)
60{
61 return adoptRef(*new SVGRadialGradientElement(tagName, document));
62}
63
64void SVGRadialGradientElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
65{
66 SVGParsingError parseError = NoError;
67
68 if (name == SVGNames::cxAttr)
69 m_cx->setBaseValInternal(SVGLengthValue::construct(LengthModeWidth, value, parseError));
70 else if (name == SVGNames::cyAttr)
71 m_cy->setBaseValInternal(SVGLengthValue::construct(LengthModeHeight, value, parseError));
72 else if (name == SVGNames::rAttr)
73 m_r->setBaseValInternal(SVGLengthValue::construct(LengthModeOther, value, parseError, ForbidNegativeLengths));
74 else if (name == SVGNames::fxAttr)
75 m_fx->setBaseValInternal(SVGLengthValue::construct(LengthModeWidth, value, parseError));
76 else if (name == SVGNames::fyAttr)
77 m_fy->setBaseValInternal(SVGLengthValue::construct(LengthModeHeight, value, parseError));
78 else if (name == SVGNames::frAttr)
79 m_fr->setBaseValInternal(SVGLengthValue::construct(LengthModeOther, value, parseError, ForbidNegativeLengths));
80
81 reportAttributeParsingError(parseError, name, value);
82
83 SVGGradientElement::parseAttribute(name, value);
84}
85
86void SVGRadialGradientElement::svgAttributeChanged(const QualifiedName& attrName)
87{
88 if (PropertyRegistry::isKnownAttribute(attrName)) {
89 InstanceInvalidationGuard guard(*this);
90 updateRelativeLengthsInformation();
91 if (RenderObject* object = renderer())
92 object->setNeedsLayout();
93 return;
94 }
95
96 SVGGradientElement::svgAttributeChanged(attrName);
97}
98
99RenderPtr<RenderElement> SVGRadialGradientElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
100{
101 return createRenderer<RenderSVGResourceRadialGradient>(*this, WTFMove(style));
102}
103
104static void setGradientAttributes(SVGGradientElement& element, RadialGradientAttributes& attributes, bool isRadial = true)
105{
106 if (!attributes.hasSpreadMethod() && element.hasAttribute(SVGNames::spreadMethodAttr))
107 attributes.setSpreadMethod(element.spreadMethod());
108
109 if (!attributes.hasGradientUnits() && element.hasAttribute(SVGNames::gradientUnitsAttr))
110 attributes.setGradientUnits(element.gradientUnits());
111
112 if (!attributes.hasGradientTransform() && element.hasAttribute(SVGNames::gradientTransformAttr))
113 attributes.setGradientTransform(element.gradientTransform().concatenate());
114
115 if (!attributes.hasStops()) {
116 const Vector<Gradient::ColorStop>& stops(element.buildStops());
117 if (!stops.isEmpty())
118 attributes.setStops(stops);
119 }
120
121 if (isRadial) {
122 SVGRadialGradientElement& radial = downcast<SVGRadialGradientElement>(element);
123
124 if (!attributes.hasCx() && element.hasAttribute(SVGNames::cxAttr))
125 attributes.setCx(radial.cx());
126
127 if (!attributes.hasCy() && element.hasAttribute(SVGNames::cyAttr))
128 attributes.setCy(radial.cy());
129
130 if (!attributes.hasR() && element.hasAttribute(SVGNames::rAttr))
131 attributes.setR(radial.r());
132
133 if (!attributes.hasFx() && element.hasAttribute(SVGNames::fxAttr))
134 attributes.setFx(radial.fx());
135
136 if (!attributes.hasFy() && element.hasAttribute(SVGNames::fyAttr))
137 attributes.setFy(radial.fy());
138
139 if (!attributes.hasFr() && element.hasAttribute(SVGNames::frAttr))
140 attributes.setFr(radial.fr());
141 }
142}
143
144bool SVGRadialGradientElement::collectGradientAttributes(RadialGradientAttributes& attributes)
145{
146 if (!renderer())
147 return false;
148
149 HashSet<SVGGradientElement*> processedGradients;
150 SVGGradientElement* current = this;
151
152 setGradientAttributes(*current, attributes);
153 processedGradients.add(current);
154
155 while (true) {
156 // Respect xlink:href, take attributes from referenced element
157 auto target = SVGURIReference::targetElementFromIRIString(current->href(), treeScope());
158 if (is<SVGGradientElement>(target.element)) {
159 current = downcast<SVGGradientElement>(target.element.get());
160
161 // Cycle detection
162 if (processedGradients.contains(current))
163 break;
164
165 if (!current->renderer())
166 return false;
167
168 setGradientAttributes(*current, attributes, current->hasTagName(SVGNames::radialGradientTag));
169 processedGradients.add(current);
170 } else
171 break;
172 }
173
174 // Handle default values for fx/fy
175 if (!attributes.hasFx())
176 attributes.setFx(attributes.cx());
177
178 if (!attributes.hasFy())
179 attributes.setFy(attributes.cy());
180
181 return true;
182}
183
184bool SVGRadialGradientElement::selfHasRelativeLengths() const
185{
186 return cx().isRelative()
187 || cy().isRelative()
188 || r().isRelative()
189 || fx().isRelative()
190 || fy().isRelative()
191 || fr().isRelative();
192}
193
194}
195