1 /* 2 * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2008 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2008 Dirk Schulze <krit (at) webkit.org> 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 */ 23 24 #include "config.h" 25 26 #include "core/svg/SVGRadialGradientElement.h" 27 28 #include "SVGNames.h" 29 #include "core/rendering/svg/RenderSVGResourceRadialGradient.h" 30 #include "core/svg/RadialGradientAttributes.h" 31 #include "core/svg/SVGElementInstance.h" 32 #include "core/svg/SVGTransformList.h" 33 34 namespace WebCore { 35 36 // Animated property definitions 37 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cxAttr, Cx, cx) 38 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cyAttr, Cy, cy) 39 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::rAttr, R, r) 40 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fxAttr, Fx, fx) 41 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fyAttr, Fy, fy) 42 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::frAttr, Fr, fr) 43 44 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGRadialGradientElement) 45 REGISTER_LOCAL_ANIMATED_PROPERTY(cx) 46 REGISTER_LOCAL_ANIMATED_PROPERTY(cy) 47 REGISTER_LOCAL_ANIMATED_PROPERTY(r) 48 REGISTER_LOCAL_ANIMATED_PROPERTY(fx) 49 REGISTER_LOCAL_ANIMATED_PROPERTY(fy) 50 REGISTER_LOCAL_ANIMATED_PROPERTY(fr) 51 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGradientElement) 52 END_REGISTER_ANIMATED_PROPERTIES 53 54 inline SVGRadialGradientElement::SVGRadialGradientElement(const QualifiedName& tagName, Document* document) 55 : SVGGradientElement(tagName, document) 56 , m_cx(LengthModeWidth, "50%") 57 , m_cy(LengthModeHeight, "50%") 58 , m_r(LengthModeOther, "50%") 59 , m_fx(LengthModeWidth) 60 , m_fy(LengthModeHeight) 61 , m_fr(LengthModeOther, "0%") 62 { 63 // Spec: If the cx/cy/r/fr attribute is not specified, the effect is as if a value of "50%" were specified. 64 ASSERT(hasTagName(SVGNames::radialGradientTag)); 65 ScriptWrappable::init(this); 66 registerAnimatedPropertiesForSVGRadialGradientElement(); 67 } 68 69 PassRefPtr<SVGRadialGradientElement> SVGRadialGradientElement::create(const QualifiedName& tagName, Document* document) 70 { 71 return adoptRef(new SVGRadialGradientElement(tagName, document)); 72 } 73 74 bool SVGRadialGradientElement::isSupportedAttribute(const QualifiedName& attrName) 75 { 76 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 77 if (supportedAttributes.isEmpty()) { 78 supportedAttributes.add(SVGNames::cxAttr); 79 supportedAttributes.add(SVGNames::cyAttr); 80 supportedAttributes.add(SVGNames::fxAttr); 81 supportedAttributes.add(SVGNames::fyAttr); 82 supportedAttributes.add(SVGNames::rAttr); 83 supportedAttributes.add(SVGNames::frAttr); 84 } 85 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); 86 } 87 88 void SVGRadialGradientElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 89 { 90 SVGParsingError parseError = NoError; 91 92 if (!isSupportedAttribute(name)) 93 SVGGradientElement::parseAttribute(name, value); 94 else if (name == SVGNames::cxAttr) 95 setCxBaseValue(SVGLength::construct(LengthModeWidth, value, parseError)); 96 else if (name == SVGNames::cyAttr) 97 setCyBaseValue(SVGLength::construct(LengthModeHeight, value, parseError)); 98 else if (name == SVGNames::rAttr) 99 setRBaseValue(SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths)); 100 else if (name == SVGNames::fxAttr) 101 setFxBaseValue(SVGLength::construct(LengthModeWidth, value, parseError)); 102 else if (name == SVGNames::fyAttr) 103 setFyBaseValue(SVGLength::construct(LengthModeHeight, value, parseError)); 104 else if (name == SVGNames::frAttr) 105 setFrBaseValue(SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths)); 106 else 107 ASSERT_NOT_REACHED(); 108 109 reportAttributeParsingError(parseError, name, value); 110 } 111 112 void SVGRadialGradientElement::svgAttributeChanged(const QualifiedName& attrName) 113 { 114 if (!isSupportedAttribute(attrName)) { 115 SVGGradientElement::svgAttributeChanged(attrName); 116 return; 117 } 118 119 SVGElementInstance::InvalidationGuard invalidationGuard(this); 120 121 updateRelativeLengthsInformation(); 122 123 if (RenderObject* object = renderer()) 124 object->setNeedsLayout(); 125 } 126 127 RenderObject* SVGRadialGradientElement::createRenderer(RenderStyle*) 128 { 129 return new RenderSVGResourceRadialGradient(this); 130 } 131 132 bool SVGRadialGradientElement::collectGradientAttributes(RadialGradientAttributes& attributes) 133 { 134 HashSet<SVGGradientElement*> processedGradients; 135 136 bool isRadial = true; 137 SVGGradientElement* current = this; 138 139 while (current) { 140 if (!current->renderer()) 141 return false; 142 143 if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr)) 144 attributes.setSpreadMethod(current->spreadMethodCurrentValue()); 145 146 if (!attributes.hasGradientUnits() && current->hasAttribute(SVGNames::gradientUnitsAttr)) 147 attributes.setGradientUnits(current->gradientUnitsCurrentValue()); 148 149 if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) { 150 AffineTransform transform; 151 current->gradientTransformCurrentValue().concatenate(transform); 152 attributes.setGradientTransform(transform); 153 } 154 155 if (!attributes.hasStops()) { 156 const Vector<Gradient::ColorStop>& stops(current->buildStops()); 157 if (!stops.isEmpty()) 158 attributes.setStops(stops); 159 } 160 161 if (isRadial) { 162 SVGRadialGradientElement* radial = static_cast<SVGRadialGradientElement*>(current); 163 164 if (!attributes.hasCx() && current->hasAttribute(SVGNames::cxAttr)) 165 attributes.setCx(radial->cxCurrentValue()); 166 167 if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr)) 168 attributes.setCy(radial->cyCurrentValue()); 169 170 if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr)) 171 attributes.setR(radial->rCurrentValue()); 172 173 if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr)) 174 attributes.setFx(radial->fxCurrentValue()); 175 176 if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr)) 177 attributes.setFy(radial->fyCurrentValue()); 178 179 if (!attributes.hasFr() && current->hasAttribute(SVGNames::frAttr)) 180 attributes.setFr(radial->frCurrentValue()); 181 } 182 183 processedGradients.add(current); 184 185 // Respect xlink:href, take attributes from referenced element 186 Node* refNode = SVGURIReference::targetElementFromIRIString(current->hrefCurrentValue(), document()); 187 if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) { 188 current = toSVGGradientElement(refNode); 189 190 // Cycle detection 191 if (processedGradients.contains(current)) { 192 current = 0; 193 break; 194 } 195 196 isRadial = current->hasTagName(SVGNames::radialGradientTag); 197 } else 198 current = 0; 199 } 200 201 // Handle default values for fx/fy 202 if (!attributes.hasFx()) 203 attributes.setFx(attributes.cx()); 204 205 if (!attributes.hasFy()) 206 attributes.setFy(attributes.cy()); 207 return true; 208 } 209 210 bool SVGRadialGradientElement::selfHasRelativeLengths() const 211 { 212 return cxCurrentValue().isRelative() 213 || cyCurrentValue().isRelative() 214 || rCurrentValue().isRelative() 215 || fxCurrentValue().isRelative() 216 || fyCurrentValue().isRelative() 217 || frCurrentValue().isRelative(); 218 } 219 220 } 221