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