1 /* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 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 "Document.h" 30 #include "HTMLNames.h" 31 #include "PlatformString.h" 32 #include "RenderObject.h" 33 #include "RenderSVGResource.h" 34 #include "RenderSVGResourceClipper.h" 35 #include "RenderSVGResourceFilter.h" 36 #include "RenderSVGResourceMasker.h" 37 #include "SVGElement.h" 38 #include "SVGElementInstance.h" 39 #include "SVGElementRareData.h" 40 #include "SVGNames.h" 41 #include "SVGRenderStyle.h" 42 #include "SVGRenderSupport.h" 43 #include "SVGSVGElement.h" 44 #include "SVGUseElement.h" 45 #include <wtf/Assertions.h> 46 #include <wtf/HashMap.h> 47 48 namespace WebCore { 49 50 // Animated property definitions 51 DEFINE_ANIMATED_STRING(SVGStyledElement, HTMLNames::classAttr, ClassName, className) 52 53 using namespace SVGNames; 54 55 void mapAttributeToCSSProperty(HashMap<AtomicStringImpl*, int>* propertyNameToIdMap, const QualifiedName& attrName) 56 { 57 int propertyId = cssPropertyID(attrName.localName()); 58 ASSERT(propertyId > 0); 59 propertyNameToIdMap->set(attrName.localName().impl(), propertyId); 60 } 61 62 SVGStyledElement::SVGStyledElement(const QualifiedName& tagName, Document* document) 63 : SVGElement(tagName, document) 64 { 65 } 66 67 SVGStyledElement::~SVGStyledElement() 68 { 69 } 70 71 String SVGStyledElement::title() const 72 { 73 // According to spec, we should not return titles when hovering over root <svg> elements (those 74 // <title> elements are the title of the document, not a tooltip) so we instantly return. 75 if (hasTagName(SVGNames::svgTag)) { 76 const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(this); 77 if (svg->isOutermostSVG()) 78 return String(); 79 } 80 81 // Walk up the tree, to find out whether we're inside a <use> shadow tree, to find the right title. 82 Node* parent = const_cast<SVGStyledElement*>(this); 83 while (parent) { 84 if (!parent->isSVGShadowRoot()) { 85 parent = parent->parentNodeGuaranteedHostFree(); 86 continue; 87 } 88 89 // Get the <use> element. 90 Element* shadowParent = parent->svgShadowHost(); 91 if (shadowParent && shadowParent->isSVGElement() && shadowParent->hasTagName(SVGNames::useTag)) { 92 SVGUseElement* useElement = static_cast<SVGUseElement*>(shadowParent); 93 // If the <use> title is not empty we found the title to use. 94 String useTitle(useElement->title()); 95 if (useTitle.isEmpty()) 96 break; 97 return useTitle; 98 } 99 parent = parent->parentNode(); 100 } 101 102 // If we aren't an instance in a <use> or the <use> title was not found, then find the first 103 // <title> child of this element. 104 Element* titleElement = firstElementChild(); 105 for (; titleElement; titleElement = titleElement->nextElementSibling()) { 106 if (titleElement->hasTagName(SVGNames::titleTag) && titleElement->isSVGElement()) 107 break; 108 } 109 110 // If a title child was found, return the text contents. 111 if (titleElement) 112 return titleElement->innerText(); 113 114 // Otherwise return a null/empty string. 115 return String(); 116 } 117 118 bool SVGStyledElement::rendererIsNeeded(RenderStyle* style) 119 { 120 // http://www.w3.org/TR/SVG/extend.html#PrivateData 121 // Prevent anything other than SVG renderers from appearing in our render tree 122 // Spec: SVG allows inclusion of elements from foreign namespaces anywhere 123 // with the SVG content. In general, the SVG user agent will include the unknown 124 // elements in the DOM but will otherwise ignore unknown elements. 125 if (!parentNode() || parentNode()->isSVGElement()) 126 return StyledElement::rendererIsNeeded(style); 127 128 return false; 129 } 130 131 int SVGStyledElement::cssPropertyIdForSVGAttributeName(const QualifiedName& attrName) 132 { 133 if (!attrName.namespaceURI().isNull()) 134 return 0; 135 136 static HashMap<AtomicStringImpl*, int>* propertyNameToIdMap = 0; 137 if (!propertyNameToIdMap) { 138 propertyNameToIdMap = new HashMap<AtomicStringImpl*, int>; 139 // This is a list of all base CSS and SVG CSS properties which are exposed as SVG XML attributes 140 mapAttributeToCSSProperty(propertyNameToIdMap, alignment_baselineAttr); 141 mapAttributeToCSSProperty(propertyNameToIdMap, baseline_shiftAttr); 142 mapAttributeToCSSProperty(propertyNameToIdMap, clipAttr); 143 mapAttributeToCSSProperty(propertyNameToIdMap, clip_pathAttr); 144 mapAttributeToCSSProperty(propertyNameToIdMap, clip_ruleAttr); 145 mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::colorAttr); 146 mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolationAttr); 147 mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolation_filtersAttr); 148 mapAttributeToCSSProperty(propertyNameToIdMap, color_profileAttr); 149 mapAttributeToCSSProperty(propertyNameToIdMap, color_renderingAttr); 150 mapAttributeToCSSProperty(propertyNameToIdMap, cursorAttr); 151 mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::directionAttr); 152 mapAttributeToCSSProperty(propertyNameToIdMap, displayAttr); 153 mapAttributeToCSSProperty(propertyNameToIdMap, dominant_baselineAttr); 154 mapAttributeToCSSProperty(propertyNameToIdMap, enable_backgroundAttr); 155 mapAttributeToCSSProperty(propertyNameToIdMap, fillAttr); 156 mapAttributeToCSSProperty(propertyNameToIdMap, fill_opacityAttr); 157 mapAttributeToCSSProperty(propertyNameToIdMap, fill_ruleAttr); 158 mapAttributeToCSSProperty(propertyNameToIdMap, filterAttr); 159 mapAttributeToCSSProperty(propertyNameToIdMap, flood_colorAttr); 160 mapAttributeToCSSProperty(propertyNameToIdMap, flood_opacityAttr); 161 mapAttributeToCSSProperty(propertyNameToIdMap, font_familyAttr); 162 mapAttributeToCSSProperty(propertyNameToIdMap, font_sizeAttr); 163 mapAttributeToCSSProperty(propertyNameToIdMap, font_stretchAttr); 164 mapAttributeToCSSProperty(propertyNameToIdMap, font_styleAttr); 165 mapAttributeToCSSProperty(propertyNameToIdMap, font_variantAttr); 166 mapAttributeToCSSProperty(propertyNameToIdMap, font_weightAttr); 167 mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_horizontalAttr); 168 mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_verticalAttr); 169 mapAttributeToCSSProperty(propertyNameToIdMap, image_renderingAttr); 170 mapAttributeToCSSProperty(propertyNameToIdMap, kerningAttr); 171 mapAttributeToCSSProperty(propertyNameToIdMap, letter_spacingAttr); 172 mapAttributeToCSSProperty(propertyNameToIdMap, lighting_colorAttr); 173 mapAttributeToCSSProperty(propertyNameToIdMap, marker_endAttr); 174 mapAttributeToCSSProperty(propertyNameToIdMap, marker_midAttr); 175 mapAttributeToCSSProperty(propertyNameToIdMap, marker_startAttr); 176 mapAttributeToCSSProperty(propertyNameToIdMap, maskAttr); 177 mapAttributeToCSSProperty(propertyNameToIdMap, opacityAttr); 178 mapAttributeToCSSProperty(propertyNameToIdMap, overflowAttr); 179 mapAttributeToCSSProperty(propertyNameToIdMap, pointer_eventsAttr); 180 mapAttributeToCSSProperty(propertyNameToIdMap, shape_renderingAttr); 181 mapAttributeToCSSProperty(propertyNameToIdMap, stop_colorAttr); 182 mapAttributeToCSSProperty(propertyNameToIdMap, stop_opacityAttr); 183 mapAttributeToCSSProperty(propertyNameToIdMap, strokeAttr); 184 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dasharrayAttr); 185 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dashoffsetAttr); 186 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linecapAttr); 187 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linejoinAttr); 188 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_miterlimitAttr); 189 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_opacityAttr); 190 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_widthAttr); 191 mapAttributeToCSSProperty(propertyNameToIdMap, text_anchorAttr); 192 mapAttributeToCSSProperty(propertyNameToIdMap, text_decorationAttr); 193 mapAttributeToCSSProperty(propertyNameToIdMap, text_renderingAttr); 194 mapAttributeToCSSProperty(propertyNameToIdMap, unicode_bidiAttr); 195 mapAttributeToCSSProperty(propertyNameToIdMap, vector_effectAttr); 196 mapAttributeToCSSProperty(propertyNameToIdMap, visibilityAttr); 197 mapAttributeToCSSProperty(propertyNameToIdMap, word_spacingAttr); 198 mapAttributeToCSSProperty(propertyNameToIdMap, writing_modeAttr); 199 } 200 201 return propertyNameToIdMap->get(attrName.localName().impl()); 202 } 203 204 static inline AttributeToPropertyTypeMap& cssPropertyToTypeMap() 205 { 206 DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_cssPropertyMap, ()); 207 208 if (!s_cssPropertyMap.isEmpty()) 209 return s_cssPropertyMap; 210 211 // Fill the map for the first use. 212 s_cssPropertyMap.set(alignment_baselineAttr, AnimatedString); 213 s_cssPropertyMap.set(baseline_shiftAttr, AnimatedString); 214 s_cssPropertyMap.set(clipAttr, AnimatedRect); 215 s_cssPropertyMap.set(clip_pathAttr, AnimatedString); 216 s_cssPropertyMap.set(clip_ruleAttr, AnimatedString); 217 s_cssPropertyMap.set(SVGNames::colorAttr, AnimatedColor); 218 s_cssPropertyMap.set(color_interpolationAttr, AnimatedString); 219 s_cssPropertyMap.set(color_interpolation_filtersAttr, AnimatedString); 220 s_cssPropertyMap.set(color_profileAttr, AnimatedString); 221 s_cssPropertyMap.set(color_renderingAttr, AnimatedString); 222 s_cssPropertyMap.set(cursorAttr, AnimatedString); 223 s_cssPropertyMap.set(displayAttr, AnimatedString); 224 s_cssPropertyMap.set(dominant_baselineAttr, AnimatedString); 225 s_cssPropertyMap.set(fillAttr, AnimatedColor); 226 s_cssPropertyMap.set(fill_opacityAttr, AnimatedNumber); 227 s_cssPropertyMap.set(fill_ruleAttr, AnimatedString); 228 s_cssPropertyMap.set(filterAttr, AnimatedString); 229 s_cssPropertyMap.set(flood_colorAttr, AnimatedColor); 230 s_cssPropertyMap.set(flood_opacityAttr, AnimatedNumber); 231 s_cssPropertyMap.set(font_familyAttr, AnimatedString); 232 s_cssPropertyMap.set(font_sizeAttr, AnimatedLength); 233 s_cssPropertyMap.set(font_stretchAttr, AnimatedString); 234 s_cssPropertyMap.set(font_styleAttr, AnimatedString); 235 s_cssPropertyMap.set(font_variantAttr, AnimatedString); 236 s_cssPropertyMap.set(font_weightAttr, AnimatedString); 237 s_cssPropertyMap.set(image_renderingAttr, AnimatedString); 238 s_cssPropertyMap.set(kerningAttr, AnimatedLength); 239 s_cssPropertyMap.set(letter_spacingAttr, AnimatedLength); 240 s_cssPropertyMap.set(lighting_colorAttr, AnimatedColor); 241 s_cssPropertyMap.set(marker_endAttr, AnimatedString); 242 s_cssPropertyMap.set(marker_midAttr, AnimatedString); 243 s_cssPropertyMap.set(marker_startAttr, AnimatedString); 244 s_cssPropertyMap.set(maskAttr, AnimatedString); 245 s_cssPropertyMap.set(opacityAttr, AnimatedNumber); 246 s_cssPropertyMap.set(overflowAttr, AnimatedString); 247 s_cssPropertyMap.set(pointer_eventsAttr, AnimatedString); 248 s_cssPropertyMap.set(shape_renderingAttr, AnimatedString); 249 s_cssPropertyMap.set(stop_colorAttr, AnimatedColor); 250 s_cssPropertyMap.set(stop_opacityAttr, AnimatedNumber); 251 s_cssPropertyMap.set(strokeAttr, AnimatedColor); 252 s_cssPropertyMap.set(stroke_dasharrayAttr, AnimatedLengthList); 253 s_cssPropertyMap.set(stroke_dashoffsetAttr, AnimatedLength); 254 s_cssPropertyMap.set(stroke_linecapAttr, AnimatedString); 255 s_cssPropertyMap.set(stroke_linejoinAttr, AnimatedString); 256 s_cssPropertyMap.set(stroke_miterlimitAttr, AnimatedNumber); 257 s_cssPropertyMap.set(stroke_opacityAttr, AnimatedNumber); 258 s_cssPropertyMap.set(stroke_widthAttr, AnimatedLength); 259 s_cssPropertyMap.set(text_anchorAttr, AnimatedString); 260 s_cssPropertyMap.set(text_decorationAttr, AnimatedString); 261 s_cssPropertyMap.set(text_renderingAttr, AnimatedString); 262 s_cssPropertyMap.set(vector_effectAttr, AnimatedString); 263 s_cssPropertyMap.set(visibilityAttr, AnimatedString); 264 s_cssPropertyMap.set(word_spacingAttr, AnimatedLength); 265 return s_cssPropertyMap; 266 } 267 268 AnimatedAttributeType SVGStyledElement::animatedPropertyTypeForCSSProperty(const QualifiedName& attrName) 269 { 270 AttributeToPropertyTypeMap& cssPropertyTypeMap = cssPropertyToTypeMap(); 271 if (cssPropertyTypeMap.contains(attrName)) 272 return cssPropertyTypeMap.get(attrName); 273 return AnimatedUnknown; 274 } 275 276 bool SVGStyledElement::isAnimatableCSSProperty(const QualifiedName& attrName) 277 { 278 return cssPropertyToTypeMap().contains(attrName); 279 } 280 281 void SVGStyledElement::fillPassedAttributeToPropertyTypeMap(AttributeToPropertyTypeMap& attributeToPropertyTypeMap) 282 { 283 attributeToPropertyTypeMap.set(HTMLNames::classAttr, AnimatedString); 284 } 285 286 bool SVGStyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const 287 { 288 if (SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName) > 0) { 289 result = eSVG; 290 return false; 291 } 292 return SVGElement::mapToEntry(attrName, result); 293 } 294 295 void SVGStyledElement::parseMappedAttribute(Attribute* attr) 296 { 297 const QualifiedName& attrName = attr->name(); 298 // NOTE: Any subclass which overrides parseMappedAttribute for a property handled by 299 // cssPropertyIdForSVGAttributeName will also have to override mapToEntry to disable the default eSVG mapping 300 int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName); 301 if (propId > 0) { 302 addCSSProperty(attr, propId, attr->value()); 303 setNeedsStyleRecalc(); 304 return; 305 } 306 307 // SVG animation has currently requires special storage of values so we set 308 // the className here. svgAttributeChanged actually causes the resulting 309 // style updates (instead of StyledElement::parseMappedAttribute). We don't 310 // tell StyledElement about the change to avoid parsing the class list twice 311 if (attrName.matches(HTMLNames::classAttr)) 312 setClassNameBaseValue(attr->value()); 313 else 314 // id is handled by StyledElement which SVGElement inherits from 315 SVGElement::parseMappedAttribute(attr); 316 } 317 318 bool SVGStyledElement::isKnownAttribute(const QualifiedName& attrName) 319 { 320 return isIdAttributeName(attrName); 321 } 322 323 void SVGStyledElement::svgAttributeChanged(const QualifiedName& attrName) 324 { 325 SVGElement::svgAttributeChanged(attrName); 326 327 if (attrName.matches(HTMLNames::classAttr)) 328 classAttributeChanged(className()); 329 330 RenderObject* object = renderer(); 331 332 if (isIdAttributeName(attrName)) { 333 // Notify resources about id changes, this is important as we cache resources by id in SVGDocumentExtensions 334 if (object && object->isSVGResourceContainer()) 335 object->toRenderSVGResourceContainer()->idChanged(); 336 } 337 338 // Invalidate all SVGElementInstances associated with us 339 SVGElementInstance::invalidateAllInstancesOfElement(this); 340 } 341 342 void SVGStyledElement::synchronizeProperty(const QualifiedName& attrName) 343 { 344 SVGElement::synchronizeProperty(attrName); 345 346 if (attrName == anyQName() || attrName.matches(HTMLNames::classAttr)) 347 synchronizeClassName(); 348 } 349 350 void SVGStyledElement::attach() 351 { 352 SVGElement::attach(); 353 354 if (RenderObject* object = renderer()) 355 object->updateFromElement(); 356 } 357 358 void SVGStyledElement::insertedIntoDocument() 359 { 360 SVGElement::insertedIntoDocument(); 361 updateRelativeLengthsInformation(); 362 } 363 364 void SVGStyledElement::removedFromDocument() 365 { 366 updateRelativeLengthsInformation(false, this); 367 SVGElement::removedFromDocument(); 368 } 369 370 void SVGStyledElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) 371 { 372 SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 373 374 // Invalidate all SVGElementInstances associated with us 375 if (!changedByParser) 376 SVGElementInstance::invalidateAllInstancesOfElement(this); 377 } 378 379 PassRefPtr<RenderStyle> SVGStyledElement::resolveStyle(RenderStyle* parentStyle) 380 { 381 if (renderer()) 382 return renderer()->style(); 383 return document()->styleSelector()->styleForElement(this, parentStyle); 384 } 385 386 PassRefPtr<CSSValue> SVGStyledElement::getPresentationAttribute(const String& name) 387 { 388 if (!attributeMap()) 389 return 0; 390 391 QualifiedName attributeName(nullAtom, name, nullAtom); 392 Attribute* attr = attributeMap()->getAttributeItem(attributeName); 393 if (!attr || !attr->isMappedAttribute() || !attr->style()) 394 return 0; 395 396 Attribute* cssSVGAttr = attr; 397 // This function returns a pointer to a CSSValue which can be mutated from JavaScript. 398 // If the associated MappedAttribute uses the same CSSMappedAttributeDeclaration 399 // as StyledElement's mappedAttributeDecls cache, create a new CSSMappedAttributeDeclaration 400 // before returning so that any modifications to the CSSValue will not affect other attributes. 401 MappedAttributeEntry entry; 402 mapToEntry(attributeName, entry); 403 if (getMappedAttributeDecl(entry, cssSVGAttr) == cssSVGAttr->decl()) { 404 cssSVGAttr->setDecl(0); 405 int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(cssSVGAttr->name()); 406 addCSSProperty(cssSVGAttr, propId, cssSVGAttr->value()); 407 } 408 return cssSVGAttr->style()->getPropertyCSSValue(name); 409 } 410 411 bool SVGStyledElement::instanceUpdatesBlocked() const 412 { 413 return hasRareSVGData() && rareSVGData()->instanceUpdatesBlocked(); 414 } 415 416 void SVGStyledElement::setInstanceUpdatesBlocked(bool value) 417 { 418 if (hasRareSVGData()) 419 rareSVGData()->setInstanceUpdatesBlocked(value); 420 } 421 422 AffineTransform SVGStyledElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope) const 423 { 424 // To be overriden by SVGStyledLocatableElement/SVGStyledTransformableElement (or as special case SVGTextElement) 425 ASSERT_NOT_REACHED(); 426 return AffineTransform(); 427 } 428 429 void SVGStyledElement::updateRelativeLengthsInformation(bool hasRelativeLengths, SVGStyledElement* element) 430 { 431 // If we're not yet in a document, this function will be called again from insertedIntoDocument(). Do nothing now. 432 if (!inDocument()) 433 return; 434 435 // An element wants to notify us that its own relative lengths state changed. 436 // Register it in the relative length map, and register us in the parent relative length map. 437 // Register the parent in the grandparents map, etc. Repeat procedure until the root of the SVG tree. 438 439 if (hasRelativeLengths) 440 m_elementsWithRelativeLengths.add(element); 441 else { 442 if (!m_elementsWithRelativeLengths.contains(element)) { 443 // We were never registered. Do nothing. 444 return; 445 } 446 447 m_elementsWithRelativeLengths.remove(element); 448 } 449 450 // Find first styled parent node, and notify it that we've changed our relative length state. 451 ContainerNode* node = parentNode(); 452 while (node) { 453 if (!node->isSVGElement()) 454 break; 455 456 SVGElement* element = static_cast<SVGElement*>(node); 457 if (!element->isStyled()) { 458 node = node->parentNode(); 459 continue; 460 } 461 462 // Register us in the parent element map. 463 static_cast<SVGStyledElement*>(element)->updateRelativeLengthsInformation(hasRelativeLengths, this); 464 break; 465 } 466 } 467 468 } 469 470 #endif // ENABLE(SVG) 471