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
24#include "core/svg/SVGPatternElement.h"
25
26#include "core/XLinkNames.h"
27#include "core/dom/ElementTraversal.h"
28#include "core/rendering/svg/RenderSVGResourcePattern.h"
29#include "core/svg/PatternAttributes.h"
30#include "platform/transforms/AffineTransform.h"
31
32namespace blink {
33
34inline SVGPatternElement::SVGPatternElement(Document& document)
35    : SVGElement(SVGNames::patternTag, document)
36    , SVGURIReference(this)
37    , SVGTests(this)
38    , SVGFitToViewBox(this)
39    , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths))
40    , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths))
41    , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths))
42    , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths))
43    , m_patternTransform(SVGAnimatedTransformList::create(this, SVGNames::patternTransformAttr, SVGTransformList::create()))
44    , m_patternUnits(SVGAnimatedEnumeration<SVGUnitTypes::SVGUnitType>::create(this, SVGNames::patternUnitsAttr, SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX))
45    , m_patternContentUnits(SVGAnimatedEnumeration<SVGUnitTypes::SVGUnitType>::create(this, SVGNames::patternContentUnitsAttr, SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE))
46{
47    addToPropertyMap(m_x);
48    addToPropertyMap(m_y);
49    addToPropertyMap(m_width);
50    addToPropertyMap(m_height);
51    addToPropertyMap(m_patternTransform);
52    addToPropertyMap(m_patternUnits);
53    addToPropertyMap(m_patternContentUnits);
54}
55
56DEFINE_NODE_FACTORY(SVGPatternElement)
57
58bool SVGPatternElement::isSupportedAttribute(const QualifiedName& attrName)
59{
60    DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
61    if (supportedAttributes.isEmpty()) {
62        SVGURIReference::addSupportedAttributes(supportedAttributes);
63        SVGTests::addSupportedAttributes(supportedAttributes);
64        SVGFitToViewBox::addSupportedAttributes(supportedAttributes);
65        supportedAttributes.add(SVGNames::patternUnitsAttr);
66        supportedAttributes.add(SVGNames::patternContentUnitsAttr);
67        supportedAttributes.add(SVGNames::patternTransformAttr);
68        supportedAttributes.add(SVGNames::xAttr);
69        supportedAttributes.add(SVGNames::yAttr);
70        supportedAttributes.add(SVGNames::widthAttr);
71        supportedAttributes.add(SVGNames::heightAttr);
72    }
73    return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
74}
75
76void SVGPatternElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
77{
78    SVGParsingError parseError = NoError;
79
80    if (!isSupportedAttribute(name)) {
81        SVGElement::parseAttribute(name, value);
82    } else if (name == SVGNames::patternUnitsAttr) {
83        m_patternUnits->setBaseValueAsString(value, parseError);
84    } else if (name == SVGNames::patternContentUnitsAttr) {
85        m_patternContentUnits->setBaseValueAsString(value, parseError);
86    } else if (name == SVGNames::patternTransformAttr) {
87        m_patternTransform->setBaseValueAsString(value, parseError);
88    } else if (name == SVGNames::xAttr) {
89        m_x->setBaseValueAsString(value, parseError);
90    } else if (name == SVGNames::yAttr) {
91        m_y->setBaseValueAsString(value, parseError);
92    } else if (name == SVGNames::widthAttr) {
93        m_width->setBaseValueAsString(value, parseError);
94    } else if (name == SVGNames::heightAttr) {
95        m_height->setBaseValueAsString(value, parseError);
96    } else if (SVGURIReference::parseAttribute(name, value, parseError)) {
97    } else if (SVGTests::parseAttribute(name, value)) {
98    } else if (SVGFitToViewBox::parseAttribute(name, value, document(), parseError)) {
99    } else {
100        ASSERT_NOT_REACHED();
101    }
102
103    reportAttributeParsingError(parseError, name, value);
104}
105
106void SVGPatternElement::svgAttributeChanged(const QualifiedName& attrName)
107{
108    if (!isSupportedAttribute(attrName)) {
109        SVGElement::svgAttributeChanged(attrName);
110        return;
111    }
112
113    SVGElement::InvalidationGuard invalidationGuard(this);
114
115    if (attrName == SVGNames::xAttr
116        || attrName == SVGNames::yAttr
117        || attrName == SVGNames::widthAttr
118        || attrName == SVGNames::heightAttr)
119        updateRelativeLengthsInformation();
120
121    RenderSVGResourceContainer* renderer = toRenderSVGResourceContainer(this->renderer());
122    if (renderer)
123        renderer->invalidateCacheAndMarkForLayout();
124}
125
126void SVGPatternElement::childrenChanged(const ChildrenChange& change)
127{
128    SVGElement::childrenChanged(change);
129
130    if (change.byParser)
131        return;
132
133    if (RenderObject* object = renderer())
134        object->setNeedsLayoutAndFullPaintInvalidation();
135}
136
137RenderObject* SVGPatternElement::createRenderer(RenderStyle*)
138{
139    return new RenderSVGResourcePattern(this);
140}
141
142static void setPatternAttributes(const SVGPatternElement* element, PatternAttributes& attributes)
143{
144    if (!attributes.hasX() && element->x()->isSpecified())
145        attributes.setX(element->x()->currentValue());
146
147    if (!attributes.hasY() && element->y()->isSpecified())
148        attributes.setY(element->y()->currentValue());
149
150    if (!attributes.hasWidth() && element->width()->isSpecified())
151        attributes.setWidth(element->width()->currentValue());
152
153    if (!attributes.hasHeight() && element->height()->isSpecified())
154        attributes.setHeight(element->height()->currentValue());
155
156    if (!attributes.hasViewBox() && element->viewBox()->isSpecified() && element->viewBox()->currentValue()->isValid())
157        attributes.setViewBox(element->viewBox()->currentValue()->value());
158
159    if (!attributes.hasPreserveAspectRatio() && element->preserveAspectRatio()->isSpecified())
160        attributes.setPreserveAspectRatio(element->preserveAspectRatio()->currentValue());
161
162    if (!attributes.hasPatternUnits() && element->patternUnits()->isSpecified())
163        attributes.setPatternUnits(element->patternUnits()->currentValue()->enumValue());
164
165    if (!attributes.hasPatternContentUnits() && element->patternContentUnits()->isSpecified())
166        attributes.setPatternContentUnits(element->patternContentUnits()->currentValue()->enumValue());
167
168    if (!attributes.hasPatternTransform() && element->patternTransform()->isSpecified()) {
169        AffineTransform transform;
170        element->patternTransform()->currentValue()->concatenate(transform);
171        attributes.setPatternTransform(transform);
172    }
173
174    if (!attributes.hasPatternContentElement() && ElementTraversal::firstWithin(*element))
175        attributes.setPatternContentElement(element);
176}
177
178void SVGPatternElement::collectPatternAttributes(PatternAttributes& attributes) const
179{
180    WillBeHeapHashSet<RawPtrWillBeMember<const SVGPatternElement> > processedPatterns;
181    const SVGPatternElement* current = this;
182
183    while (true) {
184        setPatternAttributes(current, attributes);
185        processedPatterns.add(current);
186
187        // Respect xlink:href, take attributes from referenced element
188        Node* refNode = SVGURIReference::targetElementFromIRIString(current->hrefString(), treeScope());
189        if (isSVGPatternElement(refNode)) {
190            current = toSVGPatternElement(refNode);
191
192            // Cycle detection
193            if (processedPatterns.contains(current))
194                return;
195        } else {
196            return;
197        }
198    }
199
200    ASSERT_NOT_REACHED();
201}
202
203AffineTransform SVGPatternElement::localCoordinateSpaceTransform(SVGElement::CTMScope) const
204{
205    AffineTransform matrix;
206    m_patternTransform->currentValue()->concatenate(matrix);
207    return matrix;
208}
209
210bool SVGPatternElement::selfHasRelativeLengths() const
211{
212    return m_x->currentValue()->isRelative()
213        || m_y->currentValue()->isRelative()
214        || m_width->currentValue()->isRelative()
215        || m_height->currentValue()->isRelative();
216}
217
218} // namespace blink
219