1/*
2 * Copyright (C) 2004, 2005, 2006, 2007, 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 "SVGPatternElement.h"
24
25#include "AffineTransform.h"
26#include "Attribute.h"
27#include "Document.h"
28#include "FloatConversion.h"
29#include "GraphicsContext.h"
30#include "ImageBuffer.h"
31#include "PatternAttributes.h"
32#include "RenderSVGContainer.h"
33#include "RenderSVGResourcePattern.h"
34#include "SVGElementInstance.h"
35#include "SVGFitToViewBox.h"
36#include "SVGGraphicsElement.h"
37#include "SVGNames.h"
38#include "SVGRenderSupport.h"
39#include "SVGSVGElement.h"
40#include "SVGTransformable.h"
41#include "XLinkNames.h"
42#include <wtf/NeverDestroyed.h>
43
44namespace WebCore {
45
46// Animated property definitions
47DEFINE_ANIMATED_LENGTH(SVGPatternElement, SVGNames::xAttr, X, x)
48DEFINE_ANIMATED_LENGTH(SVGPatternElement, SVGNames::yAttr, Y, y)
49DEFINE_ANIMATED_LENGTH(SVGPatternElement, SVGNames::widthAttr, Width, width)
50DEFINE_ANIMATED_LENGTH(SVGPatternElement, SVGNames::heightAttr, Height, height)
51DEFINE_ANIMATED_ENUMERATION(SVGPatternElement, SVGNames::patternUnitsAttr, PatternUnits, patternUnits, SVGUnitTypes::SVGUnitType)
52DEFINE_ANIMATED_ENUMERATION(SVGPatternElement, SVGNames::patternContentUnitsAttr, PatternContentUnits, patternContentUnits, SVGUnitTypes::SVGUnitType)
53DEFINE_ANIMATED_TRANSFORM_LIST(SVGPatternElement, SVGNames::patternTransformAttr, PatternTransform, patternTransform)
54DEFINE_ANIMATED_STRING(SVGPatternElement, XLinkNames::hrefAttr, Href, href)
55DEFINE_ANIMATED_BOOLEAN(SVGPatternElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
56DEFINE_ANIMATED_RECT(SVGPatternElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
57DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGPatternElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
58
59BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGPatternElement)
60    REGISTER_LOCAL_ANIMATED_PROPERTY(x)
61    REGISTER_LOCAL_ANIMATED_PROPERTY(y)
62    REGISTER_LOCAL_ANIMATED_PROPERTY(width)
63    REGISTER_LOCAL_ANIMATED_PROPERTY(height)
64    REGISTER_LOCAL_ANIMATED_PROPERTY(patternUnits)
65    REGISTER_LOCAL_ANIMATED_PROPERTY(patternContentUnits)
66    REGISTER_LOCAL_ANIMATED_PROPERTY(patternTransform)
67    REGISTER_LOCAL_ANIMATED_PROPERTY(href)
68    REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
69    REGISTER_LOCAL_ANIMATED_PROPERTY(viewBox)
70    REGISTER_LOCAL_ANIMATED_PROPERTY(preserveAspectRatio)
71    REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement)
72    REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
73END_REGISTER_ANIMATED_PROPERTIES
74
75inline SVGPatternElement::SVGPatternElement(const QualifiedName& tagName, Document& document)
76    : SVGElement(tagName, document)
77    , m_x(LengthModeWidth)
78    , m_y(LengthModeHeight)
79    , m_width(LengthModeWidth)
80    , m_height(LengthModeHeight)
81    , m_patternUnits(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
82    , m_patternContentUnits(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
83{
84    ASSERT(hasTagName(SVGNames::patternTag));
85    registerAnimatedPropertiesForSVGPatternElement();
86}
87
88PassRefPtr<SVGPatternElement> SVGPatternElement::create(const QualifiedName& tagName, Document& document)
89{
90    return adoptRef(new SVGPatternElement(tagName, document));
91}
92
93bool SVGPatternElement::isSupportedAttribute(const QualifiedName& attrName)
94{
95    static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes;
96    if (supportedAttributes.get().isEmpty()) {
97        SVGURIReference::addSupportedAttributes(supportedAttributes);
98        SVGTests::addSupportedAttributes(supportedAttributes);
99        SVGLangSpace::addSupportedAttributes(supportedAttributes);
100        SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
101        SVGFitToViewBox::addSupportedAttributes(supportedAttributes);
102        supportedAttributes.get().add(SVGNames::patternUnitsAttr);
103        supportedAttributes.get().add(SVGNames::patternContentUnitsAttr);
104        supportedAttributes.get().add(SVGNames::patternTransformAttr);
105        supportedAttributes.get().add(SVGNames::xAttr);
106        supportedAttributes.get().add(SVGNames::yAttr);
107        supportedAttributes.get().add(SVGNames::widthAttr);
108        supportedAttributes.get().add(SVGNames::heightAttr);
109    }
110    return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
111}
112
113void SVGPatternElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
114{
115    SVGParsingError parseError = NoError;
116
117    if (!isSupportedAttribute(name))
118        SVGElement::parseAttribute(name, value);
119    else if (name == SVGNames::patternUnitsAttr) {
120        SVGUnitTypes::SVGUnitType propertyValue = SVGPropertyTraits<SVGUnitTypes::SVGUnitType>::fromString(value);
121        if (propertyValue > 0)
122            setPatternUnitsBaseValue(propertyValue);
123        return;
124    } else if (name == SVGNames::patternContentUnitsAttr) {
125        SVGUnitTypes::SVGUnitType propertyValue = SVGPropertyTraits<SVGUnitTypes::SVGUnitType>::fromString(value);
126        if (propertyValue > 0)
127            setPatternContentUnitsBaseValue(propertyValue);
128        return;
129    } else if (name == SVGNames::patternTransformAttr) {
130        SVGTransformList newList;
131        newList.parse(value);
132        detachAnimatedPatternTransformListWrappers(newList.size());
133        setPatternTransformBaseValue(newList);
134        return;
135    } else if (name == SVGNames::xAttr)
136        setXBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
137    else if (name == SVGNames::yAttr)
138        setYBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
139    else if (name == SVGNames::widthAttr)
140        setWidthBaseValue(SVGLength::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths));
141    else if (name == SVGNames::heightAttr)
142        setHeightBaseValue(SVGLength::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths));
143    else if (SVGURIReference::parseAttribute(name, value)
144             || SVGTests::parseAttribute(name, value)
145             || SVGLangSpace::parseAttribute(name, value)
146             || SVGExternalResourcesRequired::parseAttribute(name, value)
147             || SVGFitToViewBox::parseAttribute(this, name, value)) {
148    } else
149        ASSERT_NOT_REACHED();
150
151    reportAttributeParsingError(parseError, name, value);
152}
153
154void SVGPatternElement::svgAttributeChanged(const QualifiedName& attrName)
155{
156    if (!isSupportedAttribute(attrName)) {
157        SVGElement::svgAttributeChanged(attrName);
158        return;
159    }
160
161    SVGElementInstance::InvalidationGuard invalidationGuard(this);
162
163    if (attrName == SVGNames::xAttr
164        || attrName == SVGNames::yAttr
165        || attrName == SVGNames::widthAttr
166        || attrName == SVGNames::heightAttr)
167        updateRelativeLengthsInformation();
168
169    if (RenderObject* object = renderer())
170        object->setNeedsLayout();
171}
172
173void SVGPatternElement::childrenChanged(const ChildChange& change)
174{
175    SVGElement::childrenChanged(change);
176
177    if (change.source == ChildChangeSourceParser)
178        return;
179
180    if (RenderObject* object = renderer())
181        object->setNeedsLayout();
182}
183
184RenderPtr<RenderElement> SVGPatternElement::createElementRenderer(PassRef<RenderStyle> style)
185{
186    return createRenderer<RenderSVGResourcePattern>(*this, WTF::move(style));
187}
188
189static void setPatternAttributes(const SVGPatternElement& element, PatternAttributes& attributes)
190{
191    if (!attributes.hasX() && element.hasAttribute(SVGNames::xAttr))
192        attributes.setX(element.x());
193
194    if (!attributes.hasY() && element.hasAttribute(SVGNames::yAttr))
195        attributes.setY(element.y());
196
197    if (!attributes.hasWidth() && element.hasAttribute(SVGNames::widthAttr))
198        attributes.setWidth(element.width());
199
200    if (!attributes.hasHeight() && element.hasAttribute(SVGNames::heightAttr))
201        attributes.setHeight(element.height());
202
203    if (!attributes.hasViewBox() && element.hasAttribute(SVGNames::viewBoxAttr) && element.viewBoxIsValid())
204        attributes.setViewBox(element.viewBox());
205
206    if (!attributes.hasPreserveAspectRatio() && element.hasAttribute(SVGNames::preserveAspectRatioAttr))
207        attributes.setPreserveAspectRatio(element.preserveAspectRatio());
208
209    if (!attributes.hasPatternUnits() && element.hasAttribute(SVGNames::patternUnitsAttr))
210        attributes.setPatternUnits(element.patternUnits());
211
212    if (!attributes.hasPatternContentUnits() && element.hasAttribute(SVGNames::patternContentUnitsAttr))
213        attributes.setPatternContentUnits(element.patternContentUnits());
214
215    if (!attributes.hasPatternTransform() && element.hasAttribute(SVGNames::patternTransformAttr)) {
216        AffineTransform transform;
217        element.patternTransform().concatenate(transform);
218        attributes.setPatternTransform(transform);
219    }
220
221    if (!attributes.hasPatternContentElement() && element.childElementCount())
222        attributes.setPatternContentElement(&element);
223}
224
225void SVGPatternElement::collectPatternAttributes(PatternAttributes& attributes) const
226{
227    HashSet<const SVGPatternElement*> processedPatterns;
228    const SVGPatternElement* current = this;
229
230    while (true) {
231        setPatternAttributes(*current, attributes);
232        processedPatterns.add(current);
233
234        // Respect xlink:href, take attributes from referenced element
235        Element* refElement = SVGURIReference::targetElementFromIRIString(current->href(), document());
236        if (refElement && isSVGPatternElement(refElement)) {
237            current = toSVGPatternElement(refElement);
238
239            // Cycle detection
240            if (processedPatterns.contains(current))
241                return;
242        } else
243            return;
244    }
245    ASSERT_NOT_REACHED();
246}
247
248AffineTransform SVGPatternElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope) const
249{
250    AffineTransform matrix;
251    patternTransform().concatenate(matrix);
252    return matrix;
253}
254
255bool SVGPatternElement::selfHasRelativeLengths() const
256{
257    return x().isRelative()
258        || y().isRelative()
259        || width().isRelative()
260        || height().isRelative();
261}
262
263}
264