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#if ENABLE(SVG)
25#include "SVGPatternElement.h"
26
27#include "AffineTransform.h"
28#include "Attribute.h"
29#include "Document.h"
30#include "FloatConversion.h"
31#include "GraphicsContext.h"
32#include "ImageBuffer.h"
33#include "PatternAttributes.h"
34#include "RenderSVGContainer.h"
35#include "RenderSVGResourcePattern.h"
36#include "SVGNames.h"
37#include "SVGRenderSupport.h"
38#include "SVGSVGElement.h"
39#include "SVGStyledTransformableElement.h"
40#include "SVGTransformable.h"
41#include "SVGUnitTypes.h"
42
43namespace WebCore {
44
45// Animated property definitions
46DEFINE_ANIMATED_LENGTH(SVGPatternElement, SVGNames::xAttr, X, x)
47DEFINE_ANIMATED_LENGTH(SVGPatternElement, SVGNames::yAttr, Y, y)
48DEFINE_ANIMATED_LENGTH(SVGPatternElement, SVGNames::widthAttr, Width, width)
49DEFINE_ANIMATED_LENGTH(SVGPatternElement, SVGNames::heightAttr, Height, height)
50DEFINE_ANIMATED_ENUMERATION(SVGPatternElement, SVGNames::patternUnitsAttr, PatternUnits, patternUnits)
51DEFINE_ANIMATED_ENUMERATION(SVGPatternElement, SVGNames::patternContentUnitsAttr, PatternContentUnits, patternContentUnits)
52DEFINE_ANIMATED_TRANSFORM_LIST(SVGPatternElement, SVGNames::patternTransformAttr, PatternTransform, patternTransform)
53DEFINE_ANIMATED_STRING(SVGPatternElement, XLinkNames::hrefAttr, Href, href)
54DEFINE_ANIMATED_BOOLEAN(SVGPatternElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
55DEFINE_ANIMATED_RECT(SVGPatternElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
56DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGPatternElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
57
58inline SVGPatternElement::SVGPatternElement(const QualifiedName& tagName, Document* document)
59    : SVGStyledElement(tagName, document)
60    , m_x(LengthModeWidth)
61    , m_y(LengthModeHeight)
62    , m_width(LengthModeWidth)
63    , m_height(LengthModeHeight)
64    , m_patternUnits(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
65    , m_patternContentUnits(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
66{
67}
68
69PassRefPtr<SVGPatternElement> SVGPatternElement::create(const QualifiedName& tagName, Document* document)
70{
71    return adoptRef(new SVGPatternElement(tagName, document));
72}
73
74void SVGPatternElement::parseMappedAttribute(Attribute* attr)
75{
76    if (attr->name() == SVGNames::patternUnitsAttr) {
77        if (attr->value() == "userSpaceOnUse")
78            setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
79        else if (attr->value() == "objectBoundingBox")
80            setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
81    } else if (attr->name() == SVGNames::patternContentUnitsAttr) {
82        if (attr->value() == "userSpaceOnUse")
83            setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
84        else if (attr->value() == "objectBoundingBox")
85            setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
86    } else if (attr->name() == SVGNames::patternTransformAttr) {
87        SVGTransformList newList;
88        if (!SVGTransformable::parseTransformAttribute(newList, attr->value()))
89            newList.clear();
90
91        detachAnimatedPatternTransformListWrappers(newList.size());
92        setPatternTransformBaseValue(newList);
93    } else if (attr->name() == SVGNames::xAttr)
94        setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
95    else if (attr->name() == SVGNames::yAttr)
96        setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
97    else if (attr->name() == SVGNames::widthAttr) {
98        setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
99        if (widthBaseValue().value(this) < 0.0)
100            document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <width> is not allowed");
101    } else if (attr->name() == SVGNames::heightAttr) {
102        setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
103        if (heightBaseValue().value(this) < 0.0)
104            document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <height> is not allowed");
105    } else {
106        if (SVGURIReference::parseMappedAttribute(attr))
107            return;
108        if (SVGTests::parseMappedAttribute(attr))
109            return;
110        if (SVGLangSpace::parseMappedAttribute(attr))
111            return;
112        if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
113            return;
114        if (SVGFitToViewBox::parseMappedAttribute(document(), attr))
115            return;
116
117        SVGStyledElement::parseMappedAttribute(attr);
118    }
119}
120
121void SVGPatternElement::svgAttributeChanged(const QualifiedName& attrName)
122{
123    SVGStyledElement::svgAttributeChanged(attrName);
124
125    bool invalidateClients = false;
126    if (attrName == SVGNames::xAttr
127        || attrName == SVGNames::yAttr
128        || attrName == SVGNames::widthAttr
129        || attrName == SVGNames::heightAttr) {
130        invalidateClients = true;
131        updateRelativeLengthsInformation();
132    }
133
134    RenderObject* object = renderer();
135    if (!object)
136        return;
137
138    if (invalidateClients
139        || attrName == SVGNames::patternUnitsAttr
140        || attrName == SVGNames::patternContentUnitsAttr
141        || attrName == SVGNames::patternTransformAttr
142        || SVGURIReference::isKnownAttribute(attrName)
143        || SVGTests::isKnownAttribute(attrName)
144        || SVGLangSpace::isKnownAttribute(attrName)
145        || SVGExternalResourcesRequired::isKnownAttribute(attrName)
146        || SVGFitToViewBox::isKnownAttribute(attrName)
147        || SVGStyledElement::isKnownAttribute(attrName))
148        object->setNeedsLayout(true);
149}
150
151void SVGPatternElement::synchronizeProperty(const QualifiedName& attrName)
152{
153    SVGStyledElement::synchronizeProperty(attrName);
154
155    if (attrName == anyQName()) {
156        synchronizePatternUnits();
157        synchronizePatternContentUnits();
158        synchronizePatternTransform();
159        synchronizeX();
160        synchronizeY();
161        synchronizeWidth();
162        synchronizeHeight();
163        synchronizeExternalResourcesRequired();
164        synchronizeViewBox();
165        synchronizePreserveAspectRatio();
166        synchronizeHref();
167        SVGTests::synchronizeProperties(this, attrName);
168        return;
169    }
170
171    if (attrName == SVGNames::patternUnitsAttr)
172        synchronizePatternUnits();
173    else if (attrName == SVGNames::patternContentUnitsAttr)
174        synchronizePatternContentUnits();
175    else if (attrName == SVGNames::patternTransformAttr)
176        synchronizePatternTransform();
177    else if (attrName == SVGNames::xAttr)
178        synchronizeX();
179    else if (attrName == SVGNames::yAttr)
180        synchronizeY();
181    else if (attrName == SVGNames::widthAttr)
182        synchronizeWidth();
183    else if (attrName == SVGNames::heightAttr)
184        synchronizeHeight();
185    else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
186        synchronizeExternalResourcesRequired();
187    else if (attrName == SVGNames::viewBoxAttr)
188        synchronizeViewBox();
189    else if (attrName == SVGNames::preserveAspectRatioAttr)
190        synchronizePreserveAspectRatio();
191    else if (SVGURIReference::isKnownAttribute(attrName))
192        synchronizeHref();
193    else if (SVGTests::isKnownAttribute(attrName))
194        SVGTests::synchronizeProperties(this, attrName);
195}
196
197AttributeToPropertyTypeMap& SVGPatternElement::attributeToPropertyTypeMap()
198{
199    DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
200    return s_attributeToPropertyTypeMap;
201}
202
203void SVGPatternElement::fillAttributeToPropertyTypeMap()
204{
205    AttributeToPropertyTypeMap& attributeToPropertyTypeMap = this->attributeToPropertyTypeMap();
206
207    SVGStyledElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap);
208    attributeToPropertyTypeMap.set(SVGNames::xAttr, AnimatedLength);
209    attributeToPropertyTypeMap.set(SVGNames::yAttr, AnimatedLength);
210    attributeToPropertyTypeMap.set(SVGNames::widthAttr, AnimatedLength);
211    attributeToPropertyTypeMap.set(SVGNames::heightAttr, AnimatedLength);
212    attributeToPropertyTypeMap.set(SVGNames::patternUnitsAttr, AnimatedEnumeration);
213    attributeToPropertyTypeMap.set(SVGNames::patternContentUnitsAttr, AnimatedEnumeration);
214    attributeToPropertyTypeMap.set(SVGNames::patternTransformAttr, AnimatedTransformList);
215    attributeToPropertyTypeMap.set(XLinkNames::hrefAttr, AnimatedString);
216    attributeToPropertyTypeMap.set(SVGNames::viewBoxAttr, AnimatedRect);
217    attributeToPropertyTypeMap.set(SVGNames::preserveAspectRatioAttr, AnimatedPreserveAspectRatio);
218}
219
220void SVGPatternElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
221{
222    SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
223
224    if (changedByParser)
225        return;
226
227    if (RenderObject* object = renderer())
228        object->setNeedsLayout(true);
229}
230
231RenderObject* SVGPatternElement::createRenderer(RenderArena* arena, RenderStyle*)
232{
233    return new (arena) RenderSVGResourcePattern(this);
234}
235
236void SVGPatternElement::collectPatternAttributes(PatternAttributes& attributes) const
237{
238    HashSet<const SVGPatternElement*> processedPatterns;
239
240    const SVGPatternElement* current = this;
241    while (current) {
242        if (!attributes.hasX() && current->hasAttribute(SVGNames::xAttr))
243            attributes.setX(current->x());
244
245        if (!attributes.hasY() && current->hasAttribute(SVGNames::yAttr))
246            attributes.setY(current->y());
247
248        if (!attributes.hasWidth() && current->hasAttribute(SVGNames::widthAttr))
249            attributes.setWidth(current->width());
250
251        if (!attributes.hasHeight() && current->hasAttribute(SVGNames::heightAttr))
252            attributes.setHeight(current->height());
253
254        if (!attributes.hasViewBox() && current->hasAttribute(SVGNames::viewBoxAttr))
255            attributes.setViewBox(current->viewBox());
256
257        if (!attributes.hasPreserveAspectRatio() && current->hasAttribute(SVGNames::preserveAspectRatioAttr))
258            attributes.setPreserveAspectRatio(current->preserveAspectRatio());
259
260        if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::patternUnitsAttr))
261            attributes.setBoundingBoxMode(current->patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
262
263        if (!attributes.hasBoundingBoxModeContent() && current->hasAttribute(SVGNames::patternContentUnitsAttr))
264            attributes.setBoundingBoxModeContent(current->patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
265
266        if (!attributes.hasPatternTransform() && current->hasAttribute(SVGNames::patternTransformAttr)) {
267            AffineTransform transform;
268            current->patternTransform().concatenate(transform);
269            attributes.setPatternTransform(transform);
270        }
271
272        if (!attributes.hasPatternContentElement() && current->hasChildNodes())
273            attributes.setPatternContentElement(current);
274
275        processedPatterns.add(current);
276
277        // Respect xlink:href, take attributes from referenced element
278        Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href()));
279        if (refNode && refNode->hasTagName(SVGNames::patternTag)) {
280            current = static_cast<const SVGPatternElement*>(const_cast<const Node*>(refNode));
281
282            // Cycle detection
283            if (processedPatterns.contains(current)) {
284                current = 0;
285                break;
286            }
287        } else
288            current = 0;
289    }
290}
291
292bool SVGPatternElement::selfHasRelativeLengths() const
293{
294    return x().isRelative()
295        || y().isRelative()
296        || width().isRelative()
297        || height().isRelative();
298}
299
300}
301
302#endif // ENABLE(SVG)
303