1 /* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2008 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2008 Apple Inc. All rights reserved. 5 * Copyright (C) 2008 Alp Toker <alp (at) atoker.com> 6 * Copyright (C) 2009 Cameron McCormack <cam (at) mcc.id.au> 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/SVGElement.h" 27 28 #include "HTMLNames.h" 29 #include "SVGNames.h" 30 #include "XLinkNames.h" 31 #include "XMLNames.h" 32 #include "bindings/v8/ScriptEventListener.h" 33 #include "core/css/CSSCursorImageValue.h" 34 #include "core/css/CSSParser.h" 35 #include "core/dom/DOMImplementation.h" 36 #include "core/dom/Document.h" 37 #include "core/dom/Event.h" 38 #include "core/dom/NodeRenderingContext.h" 39 #include "core/dom/NodeTraversal.h" 40 #include "core/dom/shadow/ShadowRoot.h" 41 #include "core/rendering/RenderObject.h" 42 #include "core/rendering/svg/RenderSVGResourceContainer.h" 43 #include "core/svg/SVGCursorElement.h" 44 #include "core/svg/SVGDocumentExtensions.h" 45 #include "core/svg/SVGElementInstance.h" 46 #include "core/svg/SVGElementRareData.h" 47 #include "core/svg/SVGGraphicsElement.h" 48 #include "core/svg/SVGSVGElement.h" 49 #include "core/svg/SVGUseElement.h" 50 51 namespace WebCore { 52 53 // Animated property definitions 54 DEFINE_ANIMATED_STRING(SVGElement, HTMLNames::classAttr, ClassName, className) 55 56 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGElement) 57 REGISTER_LOCAL_ANIMATED_PROPERTY(className) 58 END_REGISTER_ANIMATED_PROPERTIES 59 60 using namespace HTMLNames; 61 using namespace SVGNames; 62 63 void mapAttributeToCSSProperty(HashMap<StringImpl*, CSSPropertyID>* propertyNameToIdMap, const QualifiedName& attrName) 64 { 65 // FIXME: when CSS supports "transform-origin" the special case for transform_originAttr can be removed. 66 CSSPropertyID propertyId = cssPropertyID(attrName.localName()); 67 if (!propertyId && attrName == transform_originAttr) 68 propertyId = CSSPropertyWebkitTransformOrigin; // cssPropertyID("-webkit-transform-origin") 69 ASSERT(propertyId > 0); 70 propertyNameToIdMap->set(attrName.localName().impl(), propertyId); 71 } 72 73 SVGElement::SVGElement(const QualifiedName& tagName, Document* document, ConstructionType constructionType) 74 : Element(tagName, document, constructionType) 75 { 76 ScriptWrappable::init(this); 77 registerAnimatedPropertiesForSVGElement(); 78 setHasCustomStyleCallbacks(); 79 } 80 81 PassRefPtr<SVGElement> SVGElement::create(const QualifiedName& tagName, Document* document) 82 { 83 return adoptRef(new SVGElement(tagName, document)); 84 } 85 86 SVGElement::~SVGElement() 87 { 88 if (!hasSVGRareData()) 89 ASSERT(!SVGElementRareData::rareDataMap().contains(this)); 90 else { 91 ASSERT(document()); 92 SVGElementRareData::SVGElementRareDataMap& rareDataMap = SVGElementRareData::rareDataMap(); 93 SVGElementRareData::SVGElementRareDataMap::iterator it = rareDataMap.find(this); 94 ASSERT(it != rareDataMap.end()); 95 96 SVGElementRareData* rareData = it->value; 97 rareData->destroyAnimatedSMILStyleProperties(); 98 if (SVGCursorElement* cursorElement = rareData->cursorElement()) 99 cursorElement->removeClient(this); 100 if (CSSCursorImageValue* cursorImageValue = rareData->cursorImageValue()) 101 cursorImageValue->removeReferencedElement(this); 102 103 delete rareData; 104 105 // The rare data cleanup may have caused other SVG nodes to be deleted, 106 // modifying the rare data map. Do not rely on the existing iterator. 107 ASSERT(rareDataMap.contains(this)); 108 rareDataMap.remove(this); 109 // Clear HasSVGRareData flag now so that we are in a consistent state when 110 // calling rebuildAllElementReferencesForTarget() and 111 // removeAllElementReferencesForTarget() below. 112 clearHasSVGRareData(); 113 } 114 ASSERT(document()); 115 document()->accessSVGExtensions()->rebuildAllElementReferencesForTarget(this); 116 document()->accessSVGExtensions()->removeAllElementReferencesForTarget(this); 117 } 118 119 void SVGElement::willRecalcStyle(StyleChange change) 120 { 121 // FIXME: This assumes that when shouldNotifyRendererWithIdenticalStyles() is true 122 // the change came from a SMIL animation, but what if there were non-SMIL changes 123 // since then? I think we should remove the shouldNotifyRendererWithIdenticalStyles 124 // check. 125 if (!hasSVGRareData() || shouldNotifyRendererWithIdenticalStyles()) 126 return; 127 // If the style changes because of a regular property change (not induced by SMIL animations themselves) 128 // reset the "computed style without SMIL style properties", so the base value change gets reflected. 129 if (change > NoChange || needsStyleRecalc()) 130 svgRareData()->setNeedsOverrideComputedStyleUpdate(); 131 } 132 133 void SVGElement::buildPendingResourcesIfNeeded() 134 { 135 Document* document = this->document(); 136 if (!needsPendingResourceHandling() || !document || !inDocument() || isInShadowTree()) 137 return; 138 139 SVGDocumentExtensions* extensions = document->accessSVGExtensions(); 140 String resourceId = getIdAttribute(); 141 if (!extensions->hasPendingResource(resourceId)) 142 return; 143 144 // Mark pending resources as pending for removal. 145 extensions->markPendingResourcesForRemoval(resourceId); 146 147 // Rebuild pending resources for each client of a pending resource that is being removed. 148 while (Element* clientElement = extensions->removeElementFromPendingResourcesForRemoval(resourceId)) { 149 ASSERT(clientElement->hasPendingResources()); 150 if (clientElement->hasPendingResources()) { 151 clientElement->buildPendingResource(); 152 extensions->clearHasPendingResourcesIfPossible(clientElement); 153 } 154 } 155 } 156 157 bool SVGElement::rendererIsNeeded(const NodeRenderingContext& context) 158 { 159 // http://www.w3.org/TR/SVG/extend.html#PrivateData 160 // Prevent anything other than SVG renderers from appearing in our render tree 161 // Spec: SVG allows inclusion of elements from foreign namespaces anywhere 162 // with the SVG content. In general, the SVG user agent will include the unknown 163 // elements in the DOM but will otherwise ignore unknown elements. 164 if (!parentOrShadowHostElement() || parentOrShadowHostElement()->isSVGElement()) 165 return Element::rendererIsNeeded(context); 166 167 return false; 168 } 169 170 SVGElementRareData* SVGElement::svgRareData() const 171 { 172 ASSERT(hasSVGRareData()); 173 return SVGElementRareData::rareDataFromMap(this); 174 } 175 176 SVGElementRareData* SVGElement::ensureSVGRareData() 177 { 178 if (hasSVGRareData()) 179 return svgRareData(); 180 181 ASSERT(!SVGElementRareData::rareDataMap().contains(this)); 182 SVGElementRareData* data = new SVGElementRareData; 183 SVGElementRareData::rareDataMap().set(this, data); 184 setHasSVGRareData(); 185 return data; 186 } 187 188 bool SVGElement::isOutermostSVGSVGElement() const 189 { 190 if (!hasTagName(SVGNames::svgTag)) 191 return false; 192 193 // If we're living in a shadow tree, we're a <svg> element that got created as replacement 194 // for a <symbol> element or a cloned <svg> element in the referenced tree. In that case 195 // we're always an inner <svg> element. 196 if (isInShadowTree() && parentOrShadowHostElement() && parentOrShadowHostElement()->isSVGElement()) 197 return false; 198 199 // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc. 200 if (!parentNode()) 201 return true; 202 203 // We act like an outermost SVG element, if we're a direct child of a <foreignObject> element. 204 if (parentNode()->hasTagName(SVGNames::foreignObjectTag)) 205 return true; 206 207 // This is true whenever this is the outermost SVG, even if there are HTML elements outside it 208 return !parentNode()->isSVGElement(); 209 } 210 211 void SVGElement::reportAttributeParsingError(SVGParsingError error, const QualifiedName& name, const AtomicString& value) 212 { 213 if (error == NoError) 214 return; 215 216 String errorString = "<" + tagName() + "> attribute " + name.toString() + "=\"" + value + "\""; 217 SVGDocumentExtensions* extensions = document()->accessSVGExtensions(); 218 219 if (error == NegativeValueForbiddenError) { 220 extensions->reportError("Invalid negative value for " + errorString); 221 return; 222 } 223 224 if (error == ParsingAttributeFailedError) { 225 extensions->reportError("Invalid value for " + errorString); 226 return; 227 } 228 229 ASSERT_NOT_REACHED(); 230 } 231 232 233 bool SVGElement::isSupported(StringImpl* feature, StringImpl* version) const 234 { 235 return DOMImplementation::hasFeature(feature, version); 236 } 237 238 String SVGElement::title() const 239 { 240 // According to spec, we should not return titles when hovering over root <svg> elements (those 241 // <title> elements are the title of the document, not a tooltip) so we instantly return. 242 if (isOutermostSVGSVGElement()) 243 return String(); 244 245 // Walk up the tree, to find out whether we're inside a <use> shadow tree, to find the right title. 246 if (isInShadowTree()) { 247 Element* shadowHostElement = toShadowRoot(treeScope()->rootNode())->host(); 248 // At this time, SVG nodes are not allowed in non-<use> shadow trees, so any shadow root we do 249 // have should be a use. The assert and following test is here to catch future shadow DOM changes 250 // that do enable SVG in a shadow tree. 251 ASSERT(!shadowHostElement || shadowHostElement->hasTagName(SVGNames::useTag)); 252 if (shadowHostElement && shadowHostElement->hasTagName(SVGNames::useTag)) { 253 SVGUseElement* useElement = toSVGUseElement(shadowHostElement); 254 255 // If the <use> title is not empty we found the title to use. 256 String useTitle(useElement->title()); 257 if (!useTitle.isEmpty()) 258 return useTitle; 259 } 260 } 261 262 // If we aren't an instance in a <use> or the <use> title was not found, then find the first 263 // <title> child of this element. 264 Element* titleElement = ElementTraversal::firstWithin(this); 265 for (; titleElement; titleElement = ElementTraversal::nextSkippingChildren(titleElement, this)) { 266 if (titleElement->hasTagName(SVGNames::titleTag) && titleElement->isSVGElement()) 267 break; 268 } 269 270 // If a title child was found, return the text contents. 271 if (titleElement) 272 return titleElement->innerText(); 273 274 // Otherwise return a null/empty string. 275 return String(); 276 } 277 278 PassRefPtr<CSSValue> SVGElement::getPresentationAttribute(const String& name) 279 { 280 if (!hasAttributesWithoutUpdate()) 281 return 0; 282 283 QualifiedName attributeName(nullAtom, name, nullAtom); 284 const Attribute* attr = getAttributeItem(attributeName); 285 if (!attr) 286 return 0; 287 288 RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create(SVGAttributeMode); 289 CSSPropertyID propertyID = SVGElement::cssPropertyIdForSVGAttributeName(attr->name()); 290 style->setProperty(propertyID, attr->value()); 291 RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(propertyID); 292 return cssValue ? cssValue->cloneForCSSOM() : 0; 293 } 294 295 bool SVGElement::isKnownAttribute(const QualifiedName& attrName) 296 { 297 return isIdAttributeName(attrName); 298 } 299 300 bool SVGElement::instanceUpdatesBlocked() const 301 { 302 return hasSVGRareData() && svgRareData()->instanceUpdatesBlocked(); 303 } 304 305 void SVGElement::setInstanceUpdatesBlocked(bool value) 306 { 307 if (hasSVGRareData()) 308 svgRareData()->setInstanceUpdatesBlocked(value); 309 } 310 311 AffineTransform SVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope) const 312 { 313 // To be overriden by SVGGraphicsElement (or as special case SVGTextElement and SVGPatternElement) 314 return AffineTransform(); 315 } 316 317 String SVGElement::xmlbase() const 318 { 319 return fastGetAttribute(XMLNames::baseAttr); 320 } 321 322 void SVGElement::setXmlbase(const String& value) 323 { 324 setAttribute(XMLNames::baseAttr, value); 325 } 326 327 Node::InsertionNotificationRequest SVGElement::insertedInto(ContainerNode* rootParent) 328 { 329 Element::insertedInto(rootParent); 330 updateRelativeLengthsInformation(); 331 buildPendingResourcesIfNeeded(); 332 return InsertionDone; 333 } 334 335 void SVGElement::removedFrom(ContainerNode* rootParent) 336 { 337 bool wasInDocument = rootParent->inDocument(); 338 339 if (wasInDocument) 340 updateRelativeLengthsInformation(false, this); 341 342 Element::removedFrom(rootParent); 343 344 if (wasInDocument) { 345 document()->accessSVGExtensions()->rebuildAllElementReferencesForTarget(this); 346 document()->accessSVGExtensions()->removeAllElementReferencesForTarget(this); 347 } 348 349 SVGElementInstance::invalidateAllInstancesOfElement(this); 350 } 351 352 void SVGElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) 353 { 354 Element::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 355 356 // Invalidate all SVGElementInstances associated with us. 357 if (!changedByParser) 358 SVGElementInstance::invalidateAllInstancesOfElement(this); 359 } 360 361 CSSPropertyID SVGElement::cssPropertyIdForSVGAttributeName(const QualifiedName& attrName) 362 { 363 if (!attrName.namespaceURI().isNull()) 364 return CSSPropertyInvalid; 365 366 static HashMap<StringImpl*, CSSPropertyID>* propertyNameToIdMap = 0; 367 if (!propertyNameToIdMap) { 368 propertyNameToIdMap = new HashMap<StringImpl*, CSSPropertyID>; 369 // This is a list of all base CSS and SVG CSS properties which are exposed as SVG XML attributes 370 mapAttributeToCSSProperty(propertyNameToIdMap, alignment_baselineAttr); 371 mapAttributeToCSSProperty(propertyNameToIdMap, baseline_shiftAttr); 372 mapAttributeToCSSProperty(propertyNameToIdMap, buffered_renderingAttr); 373 mapAttributeToCSSProperty(propertyNameToIdMap, clipAttr); 374 mapAttributeToCSSProperty(propertyNameToIdMap, clip_pathAttr); 375 mapAttributeToCSSProperty(propertyNameToIdMap, clip_ruleAttr); 376 mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::colorAttr); 377 mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolationAttr); 378 mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolation_filtersAttr); 379 mapAttributeToCSSProperty(propertyNameToIdMap, color_profileAttr); 380 mapAttributeToCSSProperty(propertyNameToIdMap, color_renderingAttr); 381 mapAttributeToCSSProperty(propertyNameToIdMap, cursorAttr); 382 mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::directionAttr); 383 mapAttributeToCSSProperty(propertyNameToIdMap, displayAttr); 384 mapAttributeToCSSProperty(propertyNameToIdMap, dominant_baselineAttr); 385 mapAttributeToCSSProperty(propertyNameToIdMap, enable_backgroundAttr); 386 mapAttributeToCSSProperty(propertyNameToIdMap, fillAttr); 387 mapAttributeToCSSProperty(propertyNameToIdMap, fill_opacityAttr); 388 mapAttributeToCSSProperty(propertyNameToIdMap, fill_ruleAttr); 389 mapAttributeToCSSProperty(propertyNameToIdMap, filterAttr); 390 mapAttributeToCSSProperty(propertyNameToIdMap, flood_colorAttr); 391 mapAttributeToCSSProperty(propertyNameToIdMap, flood_opacityAttr); 392 mapAttributeToCSSProperty(propertyNameToIdMap, font_familyAttr); 393 mapAttributeToCSSProperty(propertyNameToIdMap, font_sizeAttr); 394 mapAttributeToCSSProperty(propertyNameToIdMap, font_stretchAttr); 395 mapAttributeToCSSProperty(propertyNameToIdMap, font_styleAttr); 396 mapAttributeToCSSProperty(propertyNameToIdMap, font_variantAttr); 397 mapAttributeToCSSProperty(propertyNameToIdMap, font_weightAttr); 398 mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_horizontalAttr); 399 mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_verticalAttr); 400 mapAttributeToCSSProperty(propertyNameToIdMap, image_renderingAttr); 401 mapAttributeToCSSProperty(propertyNameToIdMap, kerningAttr); 402 mapAttributeToCSSProperty(propertyNameToIdMap, letter_spacingAttr); 403 mapAttributeToCSSProperty(propertyNameToIdMap, lighting_colorAttr); 404 mapAttributeToCSSProperty(propertyNameToIdMap, marker_endAttr); 405 mapAttributeToCSSProperty(propertyNameToIdMap, marker_midAttr); 406 mapAttributeToCSSProperty(propertyNameToIdMap, marker_startAttr); 407 mapAttributeToCSSProperty(propertyNameToIdMap, maskAttr); 408 mapAttributeToCSSProperty(propertyNameToIdMap, mask_typeAttr); 409 mapAttributeToCSSProperty(propertyNameToIdMap, opacityAttr); 410 mapAttributeToCSSProperty(propertyNameToIdMap, overflowAttr); 411 mapAttributeToCSSProperty(propertyNameToIdMap, pointer_eventsAttr); 412 mapAttributeToCSSProperty(propertyNameToIdMap, shape_renderingAttr); 413 mapAttributeToCSSProperty(propertyNameToIdMap, stop_colorAttr); 414 mapAttributeToCSSProperty(propertyNameToIdMap, stop_opacityAttr); 415 mapAttributeToCSSProperty(propertyNameToIdMap, strokeAttr); 416 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dasharrayAttr); 417 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dashoffsetAttr); 418 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linecapAttr); 419 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linejoinAttr); 420 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_miterlimitAttr); 421 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_opacityAttr); 422 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_widthAttr); 423 mapAttributeToCSSProperty(propertyNameToIdMap, text_anchorAttr); 424 mapAttributeToCSSProperty(propertyNameToIdMap, text_decorationAttr); 425 mapAttributeToCSSProperty(propertyNameToIdMap, text_renderingAttr); 426 mapAttributeToCSSProperty(propertyNameToIdMap, transform_originAttr); 427 mapAttributeToCSSProperty(propertyNameToIdMap, unicode_bidiAttr); 428 mapAttributeToCSSProperty(propertyNameToIdMap, vector_effectAttr); 429 mapAttributeToCSSProperty(propertyNameToIdMap, visibilityAttr); 430 mapAttributeToCSSProperty(propertyNameToIdMap, word_spacingAttr); 431 mapAttributeToCSSProperty(propertyNameToIdMap, writing_modeAttr); 432 } 433 434 return propertyNameToIdMap->get(attrName.localName().impl()); 435 } 436 437 void SVGElement::updateRelativeLengthsInformation(bool hasRelativeLengths, SVGElement* element) 438 { 439 // If we're not yet in a document, this function will be called again from insertedInto(). Do nothing now. 440 if (!inDocument()) 441 return; 442 443 // An element wants to notify us that its own relative lengths state changed. 444 // Register it in the relative length map, and register us in the parent relative length map. 445 // Register the parent in the grandparents map, etc. Repeat procedure until the root of the SVG tree. 446 if (hasRelativeLengths) { 447 m_elementsWithRelativeLengths.add(element); 448 } else { 449 if (!m_elementsWithRelativeLengths.contains(element)) { 450 // We were never registered. Do nothing. 451 return; 452 } 453 454 m_elementsWithRelativeLengths.remove(element); 455 } 456 457 // Find first styled parent node, and notify it that we've changed our relative length state. 458 ContainerNode* node = parentNode(); 459 while (node) { 460 if (!node->isSVGElement()) 461 break; 462 463 SVGElement* element = toSVGElement(node); 464 465 // Register us in the parent element map. 466 element->updateRelativeLengthsInformation(hasRelativeLengths, this); 467 break; 468 } 469 } 470 471 SVGSVGElement* SVGElement::ownerSVGElement() const 472 { 473 ContainerNode* n = parentOrShadowHostNode(); 474 while (n) { 475 if (n->hasTagName(SVGNames::svgTag)) 476 return toSVGSVGElement(n); 477 478 n = n->parentOrShadowHostNode(); 479 } 480 481 return 0; 482 } 483 484 SVGElement* SVGElement::viewportElement() const 485 { 486 // This function needs shadow tree support - as RenderSVGContainer uses this function 487 // to determine the "overflow" property. <use> on <symbol> wouldn't work otherwhise. 488 ContainerNode* n = parentOrShadowHostNode(); 489 while (n) { 490 if (n->hasTagName(SVGNames::svgTag) || n->hasTagName(SVGNames::imageTag) || n->hasTagName(SVGNames::symbolTag)) 491 return toSVGElement(n); 492 493 n = n->parentOrShadowHostNode(); 494 } 495 496 return 0; 497 } 498 499 SVGDocumentExtensions* SVGElement::accessDocumentSVGExtensions() 500 { 501 // This function is provided for use by SVGAnimatedProperty to avoid 502 // global inclusion of core/dom/Document.h in SVG code. 503 return document() ? document()->accessSVGExtensions() : 0; 504 } 505 506 void SVGElement::mapInstanceToElement(SVGElementInstance* instance) 507 { 508 ASSERT(instance); 509 510 HashSet<SVGElementInstance*>& instances = ensureSVGRareData()->elementInstances(); 511 ASSERT(!instances.contains(instance)); 512 513 instances.add(instance); 514 } 515 516 void SVGElement::removeInstanceMapping(SVGElementInstance* instance) 517 { 518 ASSERT(instance); 519 ASSERT(hasSVGRareData()); 520 521 HashSet<SVGElementInstance*>& instances = svgRareData()->elementInstances(); 522 ASSERT(instances.contains(instance)); 523 524 instances.remove(instance); 525 } 526 527 const HashSet<SVGElementInstance*>& SVGElement::instancesForElement() const 528 { 529 if (!hasSVGRareData()) { 530 DEFINE_STATIC_LOCAL(HashSet<SVGElementInstance*>, emptyInstances, ()); 531 return emptyInstances; 532 } 533 return svgRareData()->elementInstances(); 534 } 535 536 bool SVGElement::getBoundingBox(FloatRect& rect, SVGLocatable::StyleUpdateStrategy styleUpdateStrategy) 537 { 538 if (isSVGGraphicsElement()) { 539 rect = toSVGGraphicsElement(this)->getBBox(styleUpdateStrategy); 540 return true; 541 } 542 return false; 543 } 544 545 void SVGElement::setCursorElement(SVGCursorElement* cursorElement) 546 { 547 SVGElementRareData* rareData = ensureSVGRareData(); 548 if (SVGCursorElement* oldCursorElement = rareData->cursorElement()) { 549 if (cursorElement == oldCursorElement) 550 return; 551 oldCursorElement->removeReferencedElement(this); 552 } 553 rareData->setCursorElement(cursorElement); 554 } 555 556 void SVGElement::cursorElementRemoved() 557 { 558 ASSERT(hasSVGRareData()); 559 svgRareData()->setCursorElement(0); 560 } 561 562 void SVGElement::setCursorImageValue(CSSCursorImageValue* cursorImageValue) 563 { 564 SVGElementRareData* rareData = ensureSVGRareData(); 565 if (CSSCursorImageValue* oldCursorImageValue = rareData->cursorImageValue()) { 566 if (cursorImageValue == oldCursorImageValue) 567 return; 568 oldCursorImageValue->removeReferencedElement(this); 569 } 570 rareData->setCursorImageValue(cursorImageValue); 571 } 572 573 void SVGElement::cursorImageValueRemoved() 574 { 575 ASSERT(hasSVGRareData()); 576 svgRareData()->setCursorImageValue(0); 577 } 578 579 SVGElement* SVGElement::correspondingElement() 580 { 581 ASSERT(!hasSVGRareData() || !svgRareData()->correspondingElement() || containingShadowRoot()); 582 return hasSVGRareData() ? svgRareData()->correspondingElement() : 0; 583 } 584 585 void SVGElement::setCorrespondingElement(SVGElement* correspondingElement) 586 { 587 ensureSVGRareData()->setCorrespondingElement(correspondingElement); 588 } 589 590 void SVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 591 { 592 // standard events 593 if (name == onloadAttr) 594 setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, name, value)); 595 else if (name == onclickAttr) 596 setAttributeEventListener(eventNames().clickEvent, createAttributeEventListener(this, name, value)); 597 else if (name == onmousedownAttr) 598 setAttributeEventListener(eventNames().mousedownEvent, createAttributeEventListener(this, name, value)); 599 else if (name == onmouseenterAttr) 600 setAttributeEventListener(eventNames().mouseenterEvent, createAttributeEventListener(this, name, value)); 601 else if (name == onmouseleaveAttr) 602 setAttributeEventListener(eventNames().mouseleaveEvent, createAttributeEventListener(this, name, value)); 603 else if (name == onmousemoveAttr) 604 setAttributeEventListener(eventNames().mousemoveEvent, createAttributeEventListener(this, name, value)); 605 else if (name == onmouseoutAttr) 606 setAttributeEventListener(eventNames().mouseoutEvent, createAttributeEventListener(this, name, value)); 607 else if (name == onmouseoverAttr) 608 setAttributeEventListener(eventNames().mouseoverEvent, createAttributeEventListener(this, name, value)); 609 else if (name == onmouseupAttr) 610 setAttributeEventListener(eventNames().mouseupEvent, createAttributeEventListener(this, name, value)); 611 else if (name == SVGNames::onfocusinAttr) 612 setAttributeEventListener(eventNames().focusinEvent, createAttributeEventListener(this, name, value)); 613 else if (name == SVGNames::onfocusoutAttr) 614 setAttributeEventListener(eventNames().focusoutEvent, createAttributeEventListener(this, name, value)); 615 else if (name == SVGNames::onactivateAttr) 616 setAttributeEventListener(eventNames().DOMActivateEvent, createAttributeEventListener(this, name, value)); 617 else if (name == HTMLNames::classAttr) { 618 // SVG animation has currently requires special storage of values so we set 619 // the className here. svgAttributeChanged actually causes the resulting 620 // style updates (instead of Element::parseAttribute). We don't 621 // tell Element about the change to avoid parsing the class list twice 622 setClassNameBaseValue(value); 623 } else if (SVGLangSpace::parseAttribute(name, value)) { 624 } else 625 Element::parseAttribute(name, value); 626 } 627 628 typedef HashMap<QualifiedName, AnimatedPropertyType> AttributeToPropertyTypeMap; 629 static inline AttributeToPropertyTypeMap& cssPropertyToTypeMap() 630 { 631 DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_cssPropertyMap, ()); 632 633 if (!s_cssPropertyMap.isEmpty()) 634 return s_cssPropertyMap; 635 636 // Fill the map for the first use. 637 s_cssPropertyMap.set(alignment_baselineAttr, AnimatedString); 638 s_cssPropertyMap.set(baseline_shiftAttr, AnimatedString); 639 s_cssPropertyMap.set(buffered_renderingAttr, AnimatedString); 640 s_cssPropertyMap.set(clipAttr, AnimatedRect); 641 s_cssPropertyMap.set(clip_pathAttr, AnimatedString); 642 s_cssPropertyMap.set(clip_ruleAttr, AnimatedString); 643 s_cssPropertyMap.set(SVGNames::colorAttr, AnimatedColor); 644 s_cssPropertyMap.set(color_interpolationAttr, AnimatedString); 645 s_cssPropertyMap.set(color_interpolation_filtersAttr, AnimatedString); 646 s_cssPropertyMap.set(color_profileAttr, AnimatedString); 647 s_cssPropertyMap.set(color_renderingAttr, AnimatedString); 648 s_cssPropertyMap.set(cursorAttr, AnimatedString); 649 s_cssPropertyMap.set(displayAttr, AnimatedString); 650 s_cssPropertyMap.set(dominant_baselineAttr, AnimatedString); 651 s_cssPropertyMap.set(fillAttr, AnimatedColor); 652 s_cssPropertyMap.set(fill_opacityAttr, AnimatedNumber); 653 s_cssPropertyMap.set(fill_ruleAttr, AnimatedString); 654 s_cssPropertyMap.set(filterAttr, AnimatedString); 655 s_cssPropertyMap.set(flood_colorAttr, AnimatedColor); 656 s_cssPropertyMap.set(flood_opacityAttr, AnimatedNumber); 657 s_cssPropertyMap.set(font_familyAttr, AnimatedString); 658 s_cssPropertyMap.set(font_sizeAttr, AnimatedLength); 659 s_cssPropertyMap.set(font_stretchAttr, AnimatedString); 660 s_cssPropertyMap.set(font_styleAttr, AnimatedString); 661 s_cssPropertyMap.set(font_variantAttr, AnimatedString); 662 s_cssPropertyMap.set(font_weightAttr, AnimatedString); 663 s_cssPropertyMap.set(image_renderingAttr, AnimatedString); 664 s_cssPropertyMap.set(kerningAttr, AnimatedLength); 665 s_cssPropertyMap.set(letter_spacingAttr, AnimatedLength); 666 s_cssPropertyMap.set(lighting_colorAttr, AnimatedColor); 667 s_cssPropertyMap.set(marker_endAttr, AnimatedString); 668 s_cssPropertyMap.set(marker_midAttr, AnimatedString); 669 s_cssPropertyMap.set(marker_startAttr, AnimatedString); 670 s_cssPropertyMap.set(maskAttr, AnimatedString); 671 s_cssPropertyMap.set(mask_typeAttr, AnimatedString); 672 s_cssPropertyMap.set(opacityAttr, AnimatedNumber); 673 s_cssPropertyMap.set(overflowAttr, AnimatedString); 674 s_cssPropertyMap.set(pointer_eventsAttr, AnimatedString); 675 s_cssPropertyMap.set(shape_renderingAttr, AnimatedString); 676 s_cssPropertyMap.set(stop_colorAttr, AnimatedColor); 677 s_cssPropertyMap.set(stop_opacityAttr, AnimatedNumber); 678 s_cssPropertyMap.set(strokeAttr, AnimatedColor); 679 s_cssPropertyMap.set(stroke_dasharrayAttr, AnimatedLengthList); 680 s_cssPropertyMap.set(stroke_dashoffsetAttr, AnimatedLength); 681 s_cssPropertyMap.set(stroke_linecapAttr, AnimatedString); 682 s_cssPropertyMap.set(stroke_linejoinAttr, AnimatedString); 683 s_cssPropertyMap.set(stroke_miterlimitAttr, AnimatedNumber); 684 s_cssPropertyMap.set(stroke_opacityAttr, AnimatedNumber); 685 s_cssPropertyMap.set(stroke_widthAttr, AnimatedLength); 686 s_cssPropertyMap.set(text_anchorAttr, AnimatedString); 687 s_cssPropertyMap.set(text_decorationAttr, AnimatedString); 688 s_cssPropertyMap.set(text_renderingAttr, AnimatedString); 689 s_cssPropertyMap.set(vector_effectAttr, AnimatedString); 690 s_cssPropertyMap.set(visibilityAttr, AnimatedString); 691 s_cssPropertyMap.set(word_spacingAttr, AnimatedLength); 692 return s_cssPropertyMap; 693 } 694 695 void SVGElement::animatedPropertyTypeForAttribute(const QualifiedName& attributeName, Vector<AnimatedPropertyType>& propertyTypes) 696 { 697 localAttributeToPropertyMap().animatedPropertyTypeForAttribute(attributeName, propertyTypes); 698 if (!propertyTypes.isEmpty()) 699 return; 700 701 AttributeToPropertyTypeMap& cssPropertyTypeMap = cssPropertyToTypeMap(); 702 if (cssPropertyTypeMap.contains(attributeName)) 703 propertyTypes.append(cssPropertyTypeMap.get(attributeName)); 704 } 705 706 bool SVGElement::isAnimatableCSSProperty(const QualifiedName& attrName) 707 { 708 return cssPropertyToTypeMap().contains(attrName); 709 } 710 711 bool SVGElement::isPresentationAttribute(const QualifiedName& name) const 712 { 713 return cssPropertyIdForSVGAttributeName(name) > 0; 714 } 715 716 void SVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 717 { 718 CSSPropertyID propertyID = cssPropertyIdForSVGAttributeName(name); 719 if (propertyID > 0) 720 addPropertyToPresentationAttributeStyle(style, propertyID, value); 721 } 722 723 bool SVGElement::haveLoadedRequiredResources() 724 { 725 Node* child = firstChild(); 726 while (child) { 727 if (child->isSVGElement() && !toSVGElement(child)->haveLoadedRequiredResources()) 728 return false; 729 child = child->nextSibling(); 730 } 731 return true; 732 } 733 734 static inline void collectInstancesForSVGElement(SVGElement* element, HashSet<SVGElementInstance*>& instances) 735 { 736 ASSERT(element); 737 if (element->containingShadowRoot()) 738 return; 739 740 ASSERT(!element->instanceUpdatesBlocked()); 741 742 instances = element->instancesForElement(); 743 } 744 745 bool SVGElement::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> prpListener, bool useCapture) 746 { 747 RefPtr<EventListener> listener = prpListener; 748 749 // Add event listener to regular DOM element 750 if (!Node::addEventListener(eventType, listener, useCapture)) 751 return false; 752 753 // Add event listener to all shadow tree DOM element instances 754 HashSet<SVGElementInstance*> instances; 755 collectInstancesForSVGElement(this, instances); 756 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); 757 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { 758 ASSERT((*it)->shadowTreeElement()); 759 ASSERT((*it)->correspondingElement() == this); 760 761 bool result = (*it)->shadowTreeElement()->Node::addEventListener(eventType, listener, useCapture); 762 ASSERT_UNUSED(result, result); 763 } 764 765 return true; 766 } 767 768 bool SVGElement::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) 769 { 770 HashSet<SVGElementInstance*> instances; 771 collectInstancesForSVGElement(this, instances); 772 if (instances.isEmpty()) 773 return Node::removeEventListener(eventType, listener, useCapture); 774 775 // EventTarget::removeEventListener creates a PassRefPtr around the given EventListener 776 // object when creating a temporary RegisteredEventListener object used to look up the 777 // event listener in a cache. If we want to be able to call removeEventListener() multiple 778 // times on different nodes, we have to delay its immediate destruction, which would happen 779 // after the first call below. 780 RefPtr<EventListener> protector(listener); 781 782 // Remove event listener from regular DOM element 783 if (!Node::removeEventListener(eventType, listener, useCapture)) 784 return false; 785 786 // Remove event listener from all shadow tree DOM element instances 787 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); 788 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { 789 ASSERT((*it)->correspondingElement() == this); 790 791 SVGElement* shadowTreeElement = (*it)->shadowTreeElement(); 792 ASSERT(shadowTreeElement); 793 794 if (shadowTreeElement->Node::removeEventListener(eventType, listener, useCapture)) 795 continue; 796 797 // This case can only be hit for event listeners created from markup 798 ASSERT(listener->wasCreatedFromMarkup()); 799 800 // If the event listener 'listener' has been created from markup and has been fired before 801 // then JSLazyEventListener::parseCode() has been called and m_jsFunction of that listener 802 // has been created (read: it's not 0 anymore). During shadow tree creation, the event 803 // listener DOM attribute has been cloned, and another event listener has been setup in 804 // the shadow tree. If that event listener has not been used yet, m_jsFunction is still 0, 805 // and tryRemoveEventListener() above will fail. Work around that very seldom problem. 806 EventTargetData* data = shadowTreeElement->eventTargetData(); 807 ASSERT(data); 808 809 data->eventListenerMap.removeFirstEventListenerCreatedFromMarkup(eventType); 810 } 811 812 return true; 813 } 814 815 static bool hasLoadListener(Element* element) 816 { 817 if (element->hasEventListeners(eventNames().loadEvent)) 818 return true; 819 820 for (element = element->parentOrShadowHostElement(); element; element = element->parentOrShadowHostElement()) { 821 const EventListenerVector& entry = element->getEventListeners(eventNames().loadEvent); 822 for (size_t i = 0; i < entry.size(); ++i) { 823 if (entry[i].useCapture) 824 return true; 825 } 826 } 827 828 return false; 829 } 830 831 bool SVGElement::shouldMoveToFlowThread(RenderStyle* styleToUse) const 832 { 833 // Allow only svg root elements to be directly collected by a render flow thread. 834 return parentNode() && !parentNode()->isSVGElement() && hasTagName(SVGNames::svgTag) && Element::shouldMoveToFlowThread(styleToUse); 835 } 836 837 void SVGElement::sendSVGLoadEventIfPossible(bool sendParentLoadEvents) 838 { 839 RefPtr<SVGElement> currentTarget = this; 840 while (currentTarget && currentTarget->haveLoadedRequiredResources()) { 841 RefPtr<Element> parent; 842 if (sendParentLoadEvents) 843 parent = currentTarget->parentOrShadowHostElement(); // save the next parent to dispatch too incase dispatching the event changes the tree 844 if (hasLoadListener(currentTarget.get())) 845 currentTarget->dispatchEvent(Event::create(eventNames().loadEvent, false, false)); 846 currentTarget = (parent && parent->isSVGElement()) ? static_pointer_cast<SVGElement>(parent) : RefPtr<SVGElement>(); 847 SVGElement* element = toSVGElement(currentTarget.get()); 848 if (!element || !element->isOutermostSVGSVGElement()) 849 continue; 850 851 // Consider <svg onload="foo()"><image xlink:href="foo.png" externalResourcesRequired="true"/></svg>. 852 // If foo.png is not yet loaded, the first SVGLoad event will go to the <svg> element, sent through 853 // Document::implicitClose(). Then the SVGLoad event will fire for <image>, once its loaded. 854 ASSERT(sendParentLoadEvents); 855 856 // If the load event was not sent yet by Document::implicitClose(), but the <image> from the example 857 // above, just appeared, don't send the SVGLoad event to the outermost <svg>, but wait for the document 858 // to be "ready to render", first. 859 if (!document()->loadEventFinished()) 860 break; 861 } 862 } 863 864 void SVGElement::sendSVGLoadEventIfPossibleAsynchronously() 865 { 866 svgLoadEventTimer()->startOneShot(0); 867 } 868 869 void SVGElement::svgLoadEventTimerFired(Timer<SVGElement>*) 870 { 871 sendSVGLoadEventIfPossible(); 872 } 873 874 Timer<SVGElement>* SVGElement::svgLoadEventTimer() 875 { 876 ASSERT_NOT_REACHED(); 877 return 0; 878 } 879 880 void SVGElement::finishParsingChildren() 881 { 882 Element::finishParsingChildren(); 883 884 // The outermost SVGSVGElement SVGLoad event is fired through Document::dispatchWindowLoadEvent. 885 if (isOutermostSVGSVGElement()) 886 return; 887 888 // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>) 889 // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish 890 sendSVGLoadEventIfPossible(); 891 } 892 893 bool SVGElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const 894 { 895 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, invalidTextContent, ()); 896 897 if (invalidTextContent.isEmpty()) { 898 invalidTextContent.add(SVGNames::textPathTag); 899 #if ENABLE(SVG_FONTS) 900 invalidTextContent.add(SVGNames::altGlyphTag); 901 #endif 902 invalidTextContent.add(SVGNames::trefTag); 903 invalidTextContent.add(SVGNames::tspanTag); 904 } 905 if (childContext.node()->isSVGElement()) { 906 SVGElement* svgChild = toSVGElement(childContext.node()); 907 if (invalidTextContent.contains(svgChild->tagQName())) 908 return false; 909 910 return svgChild->isValid(); 911 } 912 return false; 913 } 914 915 void SVGElement::attributeChanged(const QualifiedName& name, const AtomicString& newValue, AttributeModificationReason) 916 { 917 Element::attributeChanged(name, newValue); 918 919 if (isIdAttributeName(name)) 920 document()->accessSVGExtensions()->rebuildAllElementReferencesForTarget(this); 921 922 // Changes to the style attribute are processed lazily (see Element::getAttribute() and related methods), 923 // so we don't want changes to the style attribute to result in extra work here. 924 if (name != HTMLNames::styleAttr) 925 svgAttributeChanged(name); 926 } 927 928 void SVGElement::svgAttributeChanged(const QualifiedName& attrName) 929 { 930 CSSPropertyID propId = SVGElement::cssPropertyIdForSVGAttributeName(attrName); 931 if (propId > 0) { 932 SVGElementInstance::invalidateAllInstancesOfElement(this); 933 return; 934 } 935 936 if (attrName == HTMLNames::classAttr) { 937 classAttributeChanged(classNameCurrentValue()); 938 SVGElementInstance::invalidateAllInstancesOfElement(this); 939 return; 940 } 941 942 if (isIdAttributeName(attrName)) { 943 RenderObject* object = renderer(); 944 // Notify resources about id changes, this is important as we cache resources by id in SVGDocumentExtensions 945 if (object && object->isSVGResourceContainer()) 946 object->toRenderSVGResourceContainer()->idChanged(); 947 if (inDocument()) 948 buildPendingResourcesIfNeeded(); 949 SVGElementInstance::invalidateAllInstancesOfElement(this); 950 return; 951 } 952 } 953 954 void SVGElement::synchronizeAnimatedSVGAttribute(const QualifiedName& name) const 955 { 956 if (!elementData() || !elementData()->m_animatedSVGAttributesAreDirty) 957 return; 958 959 SVGElement* nonConstThis = const_cast<SVGElement*>(this); 960 if (name == anyQName()) { 961 nonConstThis->localAttributeToPropertyMap().synchronizeProperties(nonConstThis); 962 elementData()->m_animatedSVGAttributesAreDirty = false; 963 } else 964 nonConstThis->localAttributeToPropertyMap().synchronizeProperty(nonConstThis, name); 965 } 966 967 void SVGElement::synchronizeRequiredFeatures(SVGElement* contextElement) 968 { 969 ASSERT(contextElement); 970 contextElement->synchronizeRequiredFeatures(); 971 } 972 973 void SVGElement::synchronizeRequiredExtensions(SVGElement* contextElement) 974 { 975 ASSERT(contextElement); 976 contextElement->synchronizeRequiredExtensions(); 977 } 978 979 void SVGElement::synchronizeSystemLanguage(SVGElement* contextElement) 980 { 981 ASSERT(contextElement); 982 contextElement->synchronizeSystemLanguage(); 983 } 984 985 PassRefPtr<RenderStyle> SVGElement::customStyleForRenderer() 986 { 987 if (!correspondingElement()) 988 return document()->styleResolver()->styleForElement(this); 989 990 RenderStyle* style = 0; 991 if (Element* parent = parentOrShadowHostElement()) { 992 if (RenderObject* renderer = parent->renderer()) 993 style = renderer->style(); 994 } 995 996 return document()->styleResolver()->styleForElement(correspondingElement(), style, DisallowStyleSharing); 997 } 998 999 MutableStylePropertySet* SVGElement::animatedSMILStyleProperties() const 1000 { 1001 if (hasSVGRareData()) 1002 return svgRareData()->animatedSMILStyleProperties(); 1003 return 0; 1004 } 1005 1006 MutableStylePropertySet* SVGElement::ensureAnimatedSMILStyleProperties() 1007 { 1008 return ensureSVGRareData()->ensureAnimatedSMILStyleProperties(); 1009 } 1010 1011 void SVGElement::setUseOverrideComputedStyle(bool value) 1012 { 1013 if (hasSVGRareData()) 1014 svgRareData()->setUseOverrideComputedStyle(value); 1015 } 1016 1017 RenderStyle* SVGElement::computedStyle(PseudoId pseudoElementSpecifier) 1018 { 1019 if (!hasSVGRareData() || !svgRareData()->useOverrideComputedStyle()) 1020 return Element::computedStyle(pseudoElementSpecifier); 1021 1022 RenderStyle* parentStyle = 0; 1023 if (Element* parent = parentOrShadowHostElement()) { 1024 if (RenderObject* renderer = parent->renderer()) 1025 parentStyle = renderer->style(); 1026 } 1027 1028 return svgRareData()->overrideComputedStyle(this, parentStyle); 1029 } 1030 1031 bool SVGElement::hasFocusEventListeners() const 1032 { 1033 // FIXME: EventTarget::hasEventListeners should be const. 1034 SVGElement* mutableThis = const_cast<SVGElement*>(this); 1035 return mutableThis->hasEventListeners(eventNames().focusinEvent) || mutableThis->hasEventListeners(eventNames().focusoutEvent); 1036 } 1037 1038 bool SVGElement::isKeyboardFocusable() const 1039 { 1040 return isFocusable(); 1041 } 1042 1043 #ifndef NDEBUG 1044 bool SVGElement::isAnimatableAttribute(const QualifiedName& name) const 1045 { 1046 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, animatableAttributes, ()); 1047 1048 if (animatableAttributes.isEmpty()) { 1049 animatableAttributes.add(XLinkNames::hrefAttr); 1050 animatableAttributes.add(SVGNames::amplitudeAttr); 1051 animatableAttributes.add(SVGNames::azimuthAttr); 1052 animatableAttributes.add(SVGNames::baseFrequencyAttr); 1053 animatableAttributes.add(SVGNames::biasAttr); 1054 animatableAttributes.add(SVGNames::clipPathUnitsAttr); 1055 animatableAttributes.add(SVGNames::cxAttr); 1056 animatableAttributes.add(SVGNames::cyAttr); 1057 animatableAttributes.add(SVGNames::diffuseConstantAttr); 1058 animatableAttributes.add(SVGNames::divisorAttr); 1059 animatableAttributes.add(SVGNames::dxAttr); 1060 animatableAttributes.add(SVGNames::dyAttr); 1061 animatableAttributes.add(SVGNames::edgeModeAttr); 1062 animatableAttributes.add(SVGNames::elevationAttr); 1063 animatableAttributes.add(SVGNames::exponentAttr); 1064 animatableAttributes.add(SVGNames::externalResourcesRequiredAttr); 1065 animatableAttributes.add(SVGNames::filterResAttr); 1066 animatableAttributes.add(SVGNames::filterUnitsAttr); 1067 animatableAttributes.add(SVGNames::fxAttr); 1068 animatableAttributes.add(SVGNames::fyAttr); 1069 animatableAttributes.add(SVGNames::gradientTransformAttr); 1070 animatableAttributes.add(SVGNames::gradientUnitsAttr); 1071 animatableAttributes.add(SVGNames::heightAttr); 1072 animatableAttributes.add(SVGNames::in2Attr); 1073 animatableAttributes.add(SVGNames::inAttr); 1074 animatableAttributes.add(SVGNames::interceptAttr); 1075 animatableAttributes.add(SVGNames::k1Attr); 1076 animatableAttributes.add(SVGNames::k2Attr); 1077 animatableAttributes.add(SVGNames::k3Attr); 1078 animatableAttributes.add(SVGNames::k4Attr); 1079 animatableAttributes.add(SVGNames::kernelMatrixAttr); 1080 animatableAttributes.add(SVGNames::kernelUnitLengthAttr); 1081 animatableAttributes.add(SVGNames::lengthAdjustAttr); 1082 animatableAttributes.add(SVGNames::limitingConeAngleAttr); 1083 animatableAttributes.add(SVGNames::markerHeightAttr); 1084 animatableAttributes.add(SVGNames::markerUnitsAttr); 1085 animatableAttributes.add(SVGNames::markerWidthAttr); 1086 animatableAttributes.add(SVGNames::maskContentUnitsAttr); 1087 animatableAttributes.add(SVGNames::maskUnitsAttr); 1088 animatableAttributes.add(SVGNames::methodAttr); 1089 animatableAttributes.add(SVGNames::modeAttr); 1090 animatableAttributes.add(SVGNames::numOctavesAttr); 1091 animatableAttributes.add(SVGNames::offsetAttr); 1092 animatableAttributes.add(SVGNames::operatorAttr); 1093 animatableAttributes.add(SVGNames::orderAttr); 1094 animatableAttributes.add(SVGNames::orientAttr); 1095 animatableAttributes.add(SVGNames::pathLengthAttr); 1096 animatableAttributes.add(SVGNames::patternContentUnitsAttr); 1097 animatableAttributes.add(SVGNames::patternTransformAttr); 1098 animatableAttributes.add(SVGNames::patternUnitsAttr); 1099 animatableAttributes.add(SVGNames::pointsAtXAttr); 1100 animatableAttributes.add(SVGNames::pointsAtYAttr); 1101 animatableAttributes.add(SVGNames::pointsAtZAttr); 1102 animatableAttributes.add(SVGNames::preserveAlphaAttr); 1103 animatableAttributes.add(SVGNames::preserveAspectRatioAttr); 1104 animatableAttributes.add(SVGNames::primitiveUnitsAttr); 1105 animatableAttributes.add(SVGNames::radiusAttr); 1106 animatableAttributes.add(SVGNames::rAttr); 1107 animatableAttributes.add(SVGNames::refXAttr); 1108 animatableAttributes.add(SVGNames::refYAttr); 1109 animatableAttributes.add(SVGNames::resultAttr); 1110 animatableAttributes.add(SVGNames::rotateAttr); 1111 animatableAttributes.add(SVGNames::rxAttr); 1112 animatableAttributes.add(SVGNames::ryAttr); 1113 animatableAttributes.add(SVGNames::scaleAttr); 1114 animatableAttributes.add(SVGNames::seedAttr); 1115 animatableAttributes.add(SVGNames::slopeAttr); 1116 animatableAttributes.add(SVGNames::spacingAttr); 1117 animatableAttributes.add(SVGNames::specularConstantAttr); 1118 animatableAttributes.add(SVGNames::specularExponentAttr); 1119 animatableAttributes.add(SVGNames::spreadMethodAttr); 1120 animatableAttributes.add(SVGNames::startOffsetAttr); 1121 animatableAttributes.add(SVGNames::stdDeviationAttr); 1122 animatableAttributes.add(SVGNames::stitchTilesAttr); 1123 animatableAttributes.add(SVGNames::surfaceScaleAttr); 1124 animatableAttributes.add(SVGNames::tableValuesAttr); 1125 animatableAttributes.add(SVGNames::targetAttr); 1126 animatableAttributes.add(SVGNames::targetXAttr); 1127 animatableAttributes.add(SVGNames::targetYAttr); 1128 animatableAttributes.add(SVGNames::transformAttr); 1129 animatableAttributes.add(SVGNames::typeAttr); 1130 animatableAttributes.add(SVGNames::valuesAttr); 1131 animatableAttributes.add(SVGNames::viewBoxAttr); 1132 animatableAttributes.add(SVGNames::widthAttr); 1133 animatableAttributes.add(SVGNames::x1Attr); 1134 animatableAttributes.add(SVGNames::x2Attr); 1135 animatableAttributes.add(SVGNames::xAttr); 1136 animatableAttributes.add(SVGNames::xChannelSelectorAttr); 1137 animatableAttributes.add(SVGNames::y1Attr); 1138 animatableAttributes.add(SVGNames::y2Attr); 1139 animatableAttributes.add(SVGNames::yAttr); 1140 animatableAttributes.add(SVGNames::yChannelSelectorAttr); 1141 animatableAttributes.add(SVGNames::zAttr); 1142 } 1143 1144 if (name == classAttr) 1145 return true; 1146 1147 return animatableAttributes.contains(name); 1148 } 1149 #endif 1150 1151 } 1152