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) Research In Motion Limited 2010. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "SVGGradientElement.h"
24
25#include "ElementIterator.h"
26#include "RenderSVGHiddenContainer.h"
27#include "RenderSVGPath.h"
28#include "RenderSVGResourceLinearGradient.h"
29#include "RenderSVGResourceRadialGradient.h"
30#include "SVGElementInstance.h"
31#include "SVGNames.h"
32#include "SVGStopElement.h"
33#include "SVGTransformList.h"
34#include "SVGTransformable.h"
35#include "StyleResolver.h"
36#include "XLinkNames.h"
37#include <wtf/NeverDestroyed.h>
38
39namespace WebCore {
40
41// Animated property definitions
42DEFINE_ANIMATED_ENUMERATION(SVGGradientElement, SVGNames::spreadMethodAttr, SpreadMethod, spreadMethod, SVGSpreadMethodType)
43DEFINE_ANIMATED_ENUMERATION(SVGGradientElement, SVGNames::gradientUnitsAttr, GradientUnits, gradientUnits, SVGUnitTypes::SVGUnitType)
44DEFINE_ANIMATED_TRANSFORM_LIST(SVGGradientElement, SVGNames::gradientTransformAttr, GradientTransform, gradientTransform)
45DEFINE_ANIMATED_STRING(SVGGradientElement, XLinkNames::hrefAttr, Href, href)
46DEFINE_ANIMATED_BOOLEAN(SVGGradientElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
47
48BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGGradientElement)
49    REGISTER_LOCAL_ANIMATED_PROPERTY(spreadMethod)
50    REGISTER_LOCAL_ANIMATED_PROPERTY(gradientUnits)
51    REGISTER_LOCAL_ANIMATED_PROPERTY(gradientTransform)
52    REGISTER_LOCAL_ANIMATED_PROPERTY(href)
53    REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
54    REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement)
55END_REGISTER_ANIMATED_PROPERTIES
56
57SVGGradientElement::SVGGradientElement(const QualifiedName& tagName, Document& document)
58    : SVGElement(tagName, document)
59    , m_spreadMethod(SVGSpreadMethodPad)
60    , m_gradientUnits(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
61{
62    registerAnimatedPropertiesForSVGGradientElement();
63}
64
65bool SVGGradientElement::isSupportedAttribute(const QualifiedName& attrName)
66{
67    static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes;
68    if (supportedAttributes.get().isEmpty()) {
69        SVGURIReference::addSupportedAttributes(supportedAttributes);
70        SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
71        supportedAttributes.get().add(SVGNames::gradientUnitsAttr);
72        supportedAttributes.get().add(SVGNames::gradientTransformAttr);
73        supportedAttributes.get().add(SVGNames::spreadMethodAttr);
74    }
75    return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
76}
77
78void SVGGradientElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
79{
80    if (!isSupportedAttribute(name)) {
81        SVGElement::parseAttribute(name, value);
82        return;
83    }
84
85    if (name == SVGNames::gradientUnitsAttr) {
86        SVGUnitTypes::SVGUnitType propertyValue = SVGPropertyTraits<SVGUnitTypes::SVGUnitType>::fromString(value);
87        if (propertyValue > 0)
88            setGradientUnitsBaseValue(propertyValue);
89        return;
90    }
91
92    if (name == SVGNames::gradientTransformAttr) {
93        SVGTransformList newList;
94        newList.parse(value);
95        detachAnimatedGradientTransformListWrappers(newList.size());
96        setGradientTransformBaseValue(newList);
97        return;
98    }
99
100    if (name == SVGNames::spreadMethodAttr) {
101        SVGSpreadMethodType propertyValue = SVGPropertyTraits<SVGSpreadMethodType>::fromString(value);
102        if (propertyValue > 0)
103            setSpreadMethodBaseValue(propertyValue);
104        return;
105    }
106
107    if (SVGURIReference::parseAttribute(name, value))
108        return;
109    if (SVGExternalResourcesRequired::parseAttribute(name, value))
110        return;
111
112    ASSERT_NOT_REACHED();
113}
114
115void SVGGradientElement::svgAttributeChanged(const QualifiedName& attrName)
116{
117    if (!isSupportedAttribute(attrName)) {
118        SVGElement::svgAttributeChanged(attrName);
119        return;
120    }
121
122    SVGElementInstance::InvalidationGuard invalidationGuard(this);
123
124    if (RenderObject* object = renderer())
125        object->setNeedsLayout();
126}
127
128void SVGGradientElement::childrenChanged(const ChildChange& change)
129{
130    SVGElement::childrenChanged(change);
131
132    if (change.source == ChildChangeSourceParser)
133        return;
134
135    if (RenderObject* object = renderer())
136        object->setNeedsLayout();
137}
138
139Vector<Gradient::ColorStop> SVGGradientElement::buildStops()
140{
141    Vector<Gradient::ColorStop> stops;
142    float previousOffset = 0.0f;
143
144    for (auto& stop : childrenOfType<SVGStopElement>(*this)) {
145        Color color = stop.stopColorIncludingOpacity();
146
147        // Figure out right monotonic offset
148        float offset = stop.offset();
149        offset = std::min(std::max(previousOffset, offset), 1.0f);
150        previousOffset = offset;
151
152        // Extract individual channel values
153        // FIXME: Why doesn't ColorStop take a Color and an offset??
154        float r, g, b, a;
155        color.getRGBA(r, g, b, a);
156
157        stops.append(Gradient::ColorStop(offset, r, g, b, a));
158    }
159
160    return stops;
161}
162
163bool isSVGGradientElement(const Node& node)
164{
165    return node.hasTagName(SVGNames::radialGradientTag) || node.hasTagName(SVGNames::linearGradientTag);
166}
167
168}
169