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 "SVGLinearGradientElement.h"
27
28#include "Document.h"
29#include "FloatPoint.h"
30#include "LinearGradientAttributes.h"
31#include "RenderSVGResourceLinearGradient.h"
32#include "SVGLengthValue.h"
33#include "SVGNames.h"
34#include "SVGUnitTypes.h"
35#include <wtf/IsoMallocInlines.h>
36#include <wtf/NeverDestroyed.h>
37
38namespace WebCore {
39
40WTF_MAKE_ISO_ALLOCATED_IMPL(SVGLinearGradientElement);
41
42inline SVGLinearGradientElement::SVGLinearGradientElement(const QualifiedName& tagName, Document& document)
43 : SVGGradientElement(tagName, document)
44{
45 // Spec: If the x2 attribute is not specified, the effect is as if a value of "100%" were specified.
46 ASSERT(hasTagName(SVGNames::linearGradientTag));
47
48 static std::once_flag onceFlag;
49 std::call_once(onceFlag, [] {
50 PropertyRegistry::registerProperty<SVGNames::x1Attr, &SVGLinearGradientElement::m_x1>();
51 PropertyRegistry::registerProperty<SVGNames::y1Attr, &SVGLinearGradientElement::m_y1>();
52 PropertyRegistry::registerProperty<SVGNames::x2Attr, &SVGLinearGradientElement::m_x2>();
53 PropertyRegistry::registerProperty<SVGNames::y2Attr, &SVGLinearGradientElement::m_y2>();
54 });
55}
56
57Ref<SVGLinearGradientElement> SVGLinearGradientElement::create(const QualifiedName& tagName, Document& document)
58{
59 return adoptRef(*new SVGLinearGradientElement(tagName, document));
60}
61
62void SVGLinearGradientElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
63{
64 SVGParsingError parseError = NoError;
65
66 if (name == SVGNames::x1Attr)
67 m_x1->setBaseValInternal(SVGLengthValue::construct(LengthModeWidth, value, parseError));
68 else if (name == SVGNames::y1Attr)
69 m_y1->setBaseValInternal(SVGLengthValue::construct(LengthModeHeight, value, parseError));
70 else if (name == SVGNames::x2Attr)
71 m_x2->setBaseValInternal(SVGLengthValue::construct(LengthModeWidth, value, parseError));
72 else if (name == SVGNames::y2Attr)
73 m_y2->setBaseValInternal(SVGLengthValue::construct(LengthModeHeight, value, parseError));
74
75 reportAttributeParsingError(parseError, name, value);
76
77 SVGGradientElement::parseAttribute(name, value);
78}
79
80void SVGLinearGradientElement::svgAttributeChanged(const QualifiedName& attrName)
81{
82 if (PropertyRegistry::isKnownAttribute(attrName)) {
83 InstanceInvalidationGuard guard(*this);
84 updateRelativeLengthsInformation();
85 if (RenderObject* object = renderer())
86 object->setNeedsLayout();
87 return;
88 }
89
90 SVGGradientElement::svgAttributeChanged(attrName);
91}
92
93RenderPtr<RenderElement> SVGLinearGradientElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
94{
95 return createRenderer<RenderSVGResourceLinearGradient>(*this, WTFMove(style));
96}
97
98static void setGradientAttributes(SVGGradientElement& element, LinearGradientAttributes& attributes, bool isLinear = true)
99{
100 if (!attributes.hasSpreadMethod() && element.hasAttribute(SVGNames::spreadMethodAttr))
101 attributes.setSpreadMethod(element.spreadMethod());
102
103 if (!attributes.hasGradientUnits() && element.hasAttribute(SVGNames::gradientUnitsAttr))
104 attributes.setGradientUnits(element.gradientUnits());
105
106 if (!attributes.hasGradientTransform() && element.hasAttribute(SVGNames::gradientTransformAttr))
107 attributes.setGradientTransform(element.gradientTransform().concatenate());
108
109 if (!attributes.hasStops()) {
110 const Vector<Gradient::ColorStop>& stops(element.buildStops());
111 if (!stops.isEmpty())
112 attributes.setStops(stops);
113 }
114
115 if (isLinear) {
116 SVGLinearGradientElement& linear = downcast<SVGLinearGradientElement>(element);
117
118 if (!attributes.hasX1() && element.hasAttribute(SVGNames::x1Attr))
119 attributes.setX1(linear.x1());
120
121 if (!attributes.hasY1() && element.hasAttribute(SVGNames::y1Attr))
122 attributes.setY1(linear.y1());
123
124 if (!attributes.hasX2() && element.hasAttribute(SVGNames::x2Attr))
125 attributes.setX2(linear.x2());
126
127 if (!attributes.hasY2() && element.hasAttribute(SVGNames::y2Attr))
128 attributes.setY2(linear.y2());
129 }
130}
131
132bool SVGLinearGradientElement::collectGradientAttributes(LinearGradientAttributes& attributes)
133{
134 if (!renderer())
135 return false;
136
137 HashSet<Ref<SVGGradientElement>> processedGradients;
138 Ref<SVGGradientElement> current { *this };
139
140 setGradientAttributes(current.get(), attributes);
141 processedGradients.add(current.copyRef());
142
143 while (true) {
144 // Respect xlink:href, take attributes from referenced element
145 auto target = SVGURIReference::targetElementFromIRIString(current->href(), treeScope());
146 if (is<SVGGradientElement>(target.element)) {
147 current = downcast<SVGGradientElement>(*target.element);
148
149 // Cycle detection
150 if (processedGradients.contains(current))
151 return true;
152
153 if (!current->renderer())
154 return false;
155
156 setGradientAttributes(current.get(), attributes, current->hasTagName(SVGNames::linearGradientTag));
157 processedGradients.add(current.copyRef());
158 } else
159 return true;
160 }
161
162 ASSERT_NOT_REACHED();
163 return false;
164}
165
166bool SVGLinearGradientElement::selfHasRelativeLengths() const
167{
168 return x1().isRelative()
169 || y1().isRelative()
170 || x2().isRelative()
171 || y2().isRelative();
172}
173
174}
175