1 /* 2 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 2004, 2005, 2007, 2008 Rob Buis <buis (at) kde.org> 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Library General Public 7 License as published by the Free Software Foundation; either 8 version 2 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public License 16 along with this library; see the file COPYING.LIB. If not, write to 17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 21 #include "config.h" 22 23 #if ENABLE(SVG) 24 #include "SVGStyledElement.h" 25 26 #include "Attr.h" 27 #include "CSSParser.h" 28 #include "CSSStyleSelector.h" 29 #include "CString.h" 30 #include "Document.h" 31 #include "HTMLNames.h" 32 #include "MappedAttribute.h" 33 #include "PlatformString.h" 34 #include "RenderObject.h" 35 #include "SVGElement.h" 36 #include "SVGElementInstance.h" 37 #include "SVGElementRareData.h" 38 #include "SVGNames.h" 39 #include "SVGRenderStyle.h" 40 #include "SVGResourceClipper.h" 41 #include "SVGResourceFilter.h" 42 #include "SVGResourceMasker.h" 43 #include "SVGSVGElement.h" 44 #include <wtf/Assertions.h> 45 46 namespace WebCore { 47 48 using namespace SVGNames; 49 50 void mapAttributeToCSSProperty(HashMap<AtomicStringImpl*, int>* propertyNameToIdMap, const QualifiedName& attrName) 51 { 52 int propertyId = cssPropertyID(attrName.localName()); 53 ASSERT(propertyId > 0); 54 propertyNameToIdMap->set(attrName.localName().impl(), propertyId); 55 } 56 57 SVGStyledElement::SVGStyledElement(const QualifiedName& tagName, Document* doc) 58 : SVGElement(tagName, doc) 59 { 60 } 61 62 SVGStyledElement::~SVGStyledElement() 63 { 64 SVGResource::removeClient(this); 65 } 66 67 bool SVGStyledElement::rendererIsNeeded(RenderStyle* style) 68 { 69 // http://www.w3.org/TR/SVG/extend.html#PrivateData 70 // Prevent anything other than SVG renderers from appearing in our render tree 71 // Spec: SVG allows inclusion of elements from foreign namespaces anywhere 72 // with the SVG content. In general, the SVG user agent will include the unknown 73 // elements in the DOM but will otherwise ignore unknown elements. 74 if (!parentNode() || parentNode()->isSVGElement()) 75 return StyledElement::rendererIsNeeded(style); 76 77 return false; 78 } 79 80 int SVGStyledElement::cssPropertyIdForSVGAttributeName(const QualifiedName& attrName) 81 { 82 if (!attrName.namespaceURI().isNull()) 83 return 0; 84 85 static HashMap<AtomicStringImpl*, int>* propertyNameToIdMap = 0; 86 if (!propertyNameToIdMap) { 87 propertyNameToIdMap = new HashMap<AtomicStringImpl*, int>; 88 // This is a list of all base CSS and SVG CSS properties which are exposed as SVG XML attributes 89 mapAttributeToCSSProperty(propertyNameToIdMap, alignment_baselineAttr); 90 mapAttributeToCSSProperty(propertyNameToIdMap, baseline_shiftAttr); 91 mapAttributeToCSSProperty(propertyNameToIdMap, clipAttr); 92 mapAttributeToCSSProperty(propertyNameToIdMap, clip_pathAttr); 93 mapAttributeToCSSProperty(propertyNameToIdMap, clip_ruleAttr); 94 mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::colorAttr); 95 mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolationAttr); 96 mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolation_filtersAttr); 97 mapAttributeToCSSProperty(propertyNameToIdMap, color_profileAttr); 98 mapAttributeToCSSProperty(propertyNameToIdMap, color_renderingAttr); 99 mapAttributeToCSSProperty(propertyNameToIdMap, cursorAttr); 100 mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::directionAttr); 101 mapAttributeToCSSProperty(propertyNameToIdMap, displayAttr); 102 mapAttributeToCSSProperty(propertyNameToIdMap, dominant_baselineAttr); 103 mapAttributeToCSSProperty(propertyNameToIdMap, enable_backgroundAttr); 104 mapAttributeToCSSProperty(propertyNameToIdMap, fillAttr); 105 mapAttributeToCSSProperty(propertyNameToIdMap, fill_opacityAttr); 106 mapAttributeToCSSProperty(propertyNameToIdMap, fill_ruleAttr); 107 mapAttributeToCSSProperty(propertyNameToIdMap, filterAttr); 108 mapAttributeToCSSProperty(propertyNameToIdMap, flood_colorAttr); 109 mapAttributeToCSSProperty(propertyNameToIdMap, flood_opacityAttr); 110 mapAttributeToCSSProperty(propertyNameToIdMap, font_familyAttr); 111 mapAttributeToCSSProperty(propertyNameToIdMap, font_sizeAttr); 112 mapAttributeToCSSProperty(propertyNameToIdMap, font_stretchAttr); 113 mapAttributeToCSSProperty(propertyNameToIdMap, font_styleAttr); 114 mapAttributeToCSSProperty(propertyNameToIdMap, font_variantAttr); 115 mapAttributeToCSSProperty(propertyNameToIdMap, font_weightAttr); 116 mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_horizontalAttr); 117 mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_verticalAttr); 118 mapAttributeToCSSProperty(propertyNameToIdMap, image_renderingAttr); 119 mapAttributeToCSSProperty(propertyNameToIdMap, kerningAttr); 120 mapAttributeToCSSProperty(propertyNameToIdMap, letter_spacingAttr); 121 mapAttributeToCSSProperty(propertyNameToIdMap, lighting_colorAttr); 122 mapAttributeToCSSProperty(propertyNameToIdMap, marker_endAttr); 123 mapAttributeToCSSProperty(propertyNameToIdMap, marker_midAttr); 124 mapAttributeToCSSProperty(propertyNameToIdMap, marker_startAttr); 125 mapAttributeToCSSProperty(propertyNameToIdMap, maskAttr); 126 mapAttributeToCSSProperty(propertyNameToIdMap, opacityAttr); 127 mapAttributeToCSSProperty(propertyNameToIdMap, overflowAttr); 128 mapAttributeToCSSProperty(propertyNameToIdMap, pointer_eventsAttr); 129 mapAttributeToCSSProperty(propertyNameToIdMap, shape_renderingAttr); 130 mapAttributeToCSSProperty(propertyNameToIdMap, stop_colorAttr); 131 mapAttributeToCSSProperty(propertyNameToIdMap, stop_opacityAttr); 132 mapAttributeToCSSProperty(propertyNameToIdMap, strokeAttr); 133 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dasharrayAttr); 134 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dashoffsetAttr); 135 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linecapAttr); 136 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linejoinAttr); 137 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_miterlimitAttr); 138 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_opacityAttr); 139 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_widthAttr); 140 mapAttributeToCSSProperty(propertyNameToIdMap, text_anchorAttr); 141 mapAttributeToCSSProperty(propertyNameToIdMap, text_decorationAttr); 142 mapAttributeToCSSProperty(propertyNameToIdMap, text_renderingAttr); 143 mapAttributeToCSSProperty(propertyNameToIdMap, unicode_bidiAttr); 144 mapAttributeToCSSProperty(propertyNameToIdMap, visibilityAttr); 145 mapAttributeToCSSProperty(propertyNameToIdMap, word_spacingAttr); 146 mapAttributeToCSSProperty(propertyNameToIdMap, writing_modeAttr); 147 } 148 149 return propertyNameToIdMap->get(attrName.localName().impl()); 150 } 151 152 bool SVGStyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const 153 { 154 if (SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName) > 0) { 155 result = eSVG; 156 return false; 157 } 158 return SVGElement::mapToEntry(attrName, result); 159 } 160 161 void SVGStyledElement::parseMappedAttribute(MappedAttribute* attr) 162 { 163 const QualifiedName& attrName = attr->name(); 164 // NOTE: Any subclass which overrides parseMappedAttribute for a property handled by 165 // cssPropertyIdForSVGAttributeName will also have to override mapToEntry to disable the default eSVG mapping 166 int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName); 167 if (propId > 0) { 168 addCSSProperty(attr, propId, attr->value()); 169 setNeedsStyleRecalc(); 170 return; 171 } 172 173 // SVG animation has currently requires special storage of values so we set 174 // the className here. svgAttributeChanged actually causes the resulting 175 // style updates (instead of StyledElement::parseMappedAttribute). We don't 176 // tell StyledElement about the change to avoid parsing the class list twice 177 if (attrName.matches(HTMLNames::classAttr)) 178 setClassNameBaseValue(attr->value()); 179 else 180 // id is handled by StyledElement which SVGElement inherits from 181 SVGElement::parseMappedAttribute(attr); 182 } 183 184 bool SVGStyledElement::isKnownAttribute(const QualifiedName& attrName) 185 { 186 // Recognize all style related SVG CSS properties 187 int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName); 188 if (propId > 0) 189 return true; 190 191 return (attrName == idAttributeName() || attrName == HTMLNames::styleAttr); 192 } 193 194 void SVGStyledElement::svgAttributeChanged(const QualifiedName& attrName) 195 { 196 SVGElement::svgAttributeChanged(attrName); 197 198 if (attrName.matches(HTMLNames::classAttr)) 199 classAttributeChanged(className()); 200 201 // If we're the child of a resource element, be sure to invalidate it. 202 invalidateResourcesInAncestorChain(); 203 204 // If the element is using resources, invalidate them. 205 invalidateResources(); 206 207 // Invalidate all SVGElementInstances associated with us 208 SVGElementInstance::invalidateAllInstancesOfElement(this); 209 } 210 211 void SVGStyledElement::synchronizeProperty(const QualifiedName& attrName) 212 { 213 SVGElement::synchronizeProperty(attrName); 214 215 if (attrName == anyQName() || attrName.matches(HTMLNames::classAttr)) 216 synchronizeClassName(); 217 } 218 219 void SVGStyledElement::invalidateResources() 220 { 221 RenderObject* object = renderer(); 222 if (!object) 223 return; 224 225 const SVGRenderStyle* svgStyle = object->style()->svgStyle(); 226 Document* document = this->document(); 227 228 if (document->parsing()) 229 return; 230 231 #if ENABLE(FILTERS) 232 SVGResourceFilter* filter = getFilterById(document, svgStyle->filter(), object); 233 if (filter) 234 filter->invalidate(); 235 #endif 236 237 SVGResourceMasker* masker = getMaskerById(document, svgStyle->maskElement(), object); 238 if (masker) 239 masker->invalidate(); 240 241 SVGResourceClipper* clipper = getClipperById(document, svgStyle->clipPath(), object); 242 if (clipper) 243 clipper->invalidate(); 244 } 245 246 void SVGStyledElement::invalidateResourcesInAncestorChain() const 247 { 248 Node* node = parentNode(); 249 while (node) { 250 if (!node->isSVGElement()) 251 break; 252 253 SVGElement* element = static_cast<SVGElement*>(node); 254 if (SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(element->isStyled() ? element : 0)) 255 styledElement->invalidateCanvasResources(); 256 257 node = node->parentNode(); 258 } 259 } 260 261 void SVGStyledElement::invalidateCanvasResources() 262 { 263 if (SVGResource* resource = canvasResource(renderer())) 264 resource->invalidate(); 265 } 266 267 void SVGStyledElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) 268 { 269 SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 270 271 // Invalidate all SVGElementInstances associated with us 272 SVGElementInstance::invalidateAllInstancesOfElement(this); 273 } 274 275 PassRefPtr<RenderStyle> SVGStyledElement::resolveStyle(RenderStyle* parentStyle) 276 { 277 if (renderer()) 278 return renderer()->style(); 279 return document()->styleSelector()->styleForElement(this, parentStyle); 280 } 281 282 PassRefPtr<CSSValue> SVGStyledElement::getPresentationAttribute(const String& name) 283 { 284 if (!mappedAttributes()) 285 return 0; 286 287 QualifiedName attributeName(nullAtom, name, nullAtom); 288 Attribute* attr = mappedAttributes()->getAttributeItem(attributeName); 289 if (!attr || !attr->isMappedAttribute() || !attr->style()) 290 return 0; 291 292 MappedAttribute* cssSVGAttr = static_cast<MappedAttribute*>(attr); 293 // This function returns a pointer to a CSSValue which can be mutated from JavaScript. 294 // If the associated MappedAttribute uses the same CSSMappedAttributeDeclaration 295 // as StyledElement's mappedAttributeDecls cache, create a new CSSMappedAttributeDeclaration 296 // before returning so that any modifications to the CSSValue will not affect other attributes. 297 MappedAttributeEntry entry; 298 mapToEntry(attributeName, entry); 299 if (getMappedAttributeDecl(entry, cssSVGAttr) == cssSVGAttr->decl()) { 300 cssSVGAttr->setDecl(0); 301 int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(cssSVGAttr->name()); 302 addCSSProperty(cssSVGAttr, propId, cssSVGAttr->value()); 303 } 304 return cssSVGAttr->style()->getPropertyCSSValue(name); 305 } 306 307 void SVGStyledElement::detach() 308 { 309 SVGResource::removeClient(this); 310 SVGElement::detach(); 311 } 312 313 bool SVGStyledElement::instanceUpdatesBlocked() const 314 { 315 return hasRareSVGData() && rareSVGData()->instanceUpdatesBlocked(); 316 } 317 318 void SVGStyledElement::setInstanceUpdatesBlocked(bool value) 319 { 320 if (hasRareSVGData()) 321 rareSVGData()->setInstanceUpdatesBlocked(value); 322 } 323 324 } 325 326 #endif // ENABLE(SVG) 327