1/* 2 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 3 2004, 2005, 2007, 2008 Rob Buis <buis@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 46namespace WebCore { 47 48using namespace SVGNames; 49 50void 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 57SVGStyledElement::SVGStyledElement(const QualifiedName& tagName, Document* doc) 58 : SVGElement(tagName, doc) 59{ 60} 61 62SVGStyledElement::~SVGStyledElement() 63{ 64 SVGResource::removeClient(this); 65} 66 67bool 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 80int 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 152bool 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 161void 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 184bool 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 194void 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 211void SVGStyledElement::synchronizeProperty(const QualifiedName& attrName) 212{ 213 SVGElement::synchronizeProperty(attrName); 214 215 if (attrName == anyQName() || attrName.matches(HTMLNames::classAttr)) 216 synchronizeClassName(); 217} 218 219void 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 246void 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 261void SVGStyledElement::invalidateCanvasResources() 262{ 263 if (SVGResource* resource = canvasResource(renderer())) 264 resource->invalidate(); 265} 266 267void 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 275PassRefPtr<RenderStyle> SVGStyledElement::resolveStyle(RenderStyle* parentStyle) 276{ 277 if (renderer()) 278 return renderer()->style(); 279 return document()->styleSelector()->styleForElement(this, parentStyle); 280} 281 282PassRefPtr<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 307void SVGStyledElement::detach() 308{ 309 SVGResource::removeClient(this); 310 SVGElement::detach(); 311} 312 313bool SVGStyledElement::instanceUpdatesBlocked() const 314{ 315 return hasRareSVGData() && rareSVGData()->instanceUpdatesBlocked(); 316} 317 318void SVGStyledElement::setInstanceUpdatesBlocked(bool value) 319{ 320 if (hasRareSVGData()) 321 rareSVGData()->setInstanceUpdatesBlocked(value); 322} 323 324} 325 326#endif // ENABLE(SVG) 327