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 #if ENABLE(SVG) 27 #include "SVGElement.h" 28 29 #include "Attribute.h" 30 #include "CSSCursorImageValue.h" 31 #include "DOMImplementation.h" 32 #include "Document.h" 33 #include "Event.h" 34 #include "EventListener.h" 35 #include "EventNames.h" 36 #include "FrameView.h" 37 #include "HTMLNames.h" 38 #include "RegisteredEventListener.h" 39 #include "RenderObject.h" 40 #include "SVGCursorElement.h" 41 #include "SVGDocumentExtensions.h" 42 #include "SVGElementInstance.h" 43 #include "SVGElementRareData.h" 44 #include "SVGNames.h" 45 #include "SVGSVGElement.h" 46 #include "SVGStyledLocatableElement.h" 47 #include "SVGTextElement.h" 48 #include "SVGURIReference.h" 49 #include "SVGUseElement.h" 50 #include "ScriptEventListener.h" 51 #include "XMLNames.h" 52 53 namespace WebCore { 54 55 using namespace HTMLNames; 56 57 SVGElement::SVGElement(const QualifiedName& tagName, Document* document) 58 : StyledElement(tagName, document, CreateSVGElement) 59 { 60 } 61 62 PassRefPtr<SVGElement> SVGElement::create(const QualifiedName& tagName, Document* document) 63 { 64 return adoptRef(new SVGElement(tagName, document)); 65 } 66 67 SVGElement::~SVGElement() 68 { 69 if (!hasRareSVGData()) 70 ASSERT(!SVGElementRareData::rareDataMap().contains(this)); 71 else { 72 SVGElementRareData::SVGElementRareDataMap& rareDataMap = SVGElementRareData::rareDataMap(); 73 SVGElementRareData::SVGElementRareDataMap::iterator it = rareDataMap.find(this); 74 ASSERT(it != rareDataMap.end()); 75 76 SVGElementRareData* rareData = it->second; 77 if (SVGCursorElement* cursorElement = rareData->cursorElement()) 78 cursorElement->removeClient(this); 79 if (CSSCursorImageValue* cursorImageValue = rareData->cursorImageValue()) 80 cursorImageValue->removeReferencedElement(this); 81 82 delete rareData; 83 rareDataMap.remove(it); 84 } 85 document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this); 86 } 87 88 SVGElementRareData* SVGElement::rareSVGData() const 89 { 90 ASSERT(hasRareSVGData()); 91 return SVGElementRareData::rareDataFromMap(this); 92 } 93 94 SVGElementRareData* SVGElement::ensureRareSVGData() 95 { 96 if (hasRareSVGData()) 97 return rareSVGData(); 98 99 ASSERT(!SVGElementRareData::rareDataMap().contains(this)); 100 SVGElementRareData* data = new SVGElementRareData; 101 SVGElementRareData::rareDataMap().set(this, data); 102 setHasRareSVGData(); 103 return data; 104 } 105 106 bool SVGElement::isSupported(StringImpl* feature, StringImpl* version) const 107 { 108 return DOMImplementation::hasFeature(feature, version); 109 } 110 111 String SVGElement::xmlbase() const 112 { 113 return getAttribute(XMLNames::baseAttr); 114 } 115 116 void SVGElement::setXmlbase(const String& value, ExceptionCode&) 117 { 118 setAttribute(XMLNames::baseAttr, value); 119 } 120 121 void SVGElement::removedFromDocument() 122 { 123 document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this); 124 StyledElement::removedFromDocument(); 125 } 126 127 SVGSVGElement* SVGElement::ownerSVGElement() const 128 { 129 ContainerNode* n = parentOrHostNode(); 130 while (n) { 131 if (n->hasTagName(SVGNames::svgTag)) 132 return static_cast<SVGSVGElement*>(n); 133 134 n = n->parentOrHostNode(); 135 } 136 137 return 0; 138 } 139 140 SVGElement* SVGElement::viewportElement() const 141 { 142 // This function needs shadow tree support - as RenderSVGContainer uses this function 143 // to determine the "overflow" property. <use> on <symbol> wouldn't work otherwhise. 144 ContainerNode* n = parentOrHostNode(); 145 while (n) { 146 if (n->hasTagName(SVGNames::svgTag) || n->hasTagName(SVGNames::imageTag) || n->hasTagName(SVGNames::symbolTag)) 147 return static_cast<SVGElement*>(n); 148 149 n = n->parentOrHostNode(); 150 } 151 152 return 0; 153 } 154 155 SVGDocumentExtensions* SVGElement::accessDocumentSVGExtensions() const 156 { 157 // This function is provided for use by SVGAnimatedProperty to avoid 158 // global inclusion of Document.h in SVG code. 159 return document() ? document()->accessSVGExtensions() : 0; 160 } 161 162 void SVGElement::mapInstanceToElement(SVGElementInstance* instance) 163 { 164 ASSERT(instance); 165 166 HashSet<SVGElementInstance*>& instances = ensureRareSVGData()->elementInstances(); 167 ASSERT(!instances.contains(instance)); 168 169 instances.add(instance); 170 } 171 172 void SVGElement::removeInstanceMapping(SVGElementInstance* instance) 173 { 174 ASSERT(instance); 175 ASSERT(hasRareSVGData()); 176 177 HashSet<SVGElementInstance*>& instances = rareSVGData()->elementInstances(); 178 ASSERT(instances.contains(instance)); 179 180 instances.remove(instance); 181 } 182 183 const HashSet<SVGElementInstance*>& SVGElement::instancesForElement() const 184 { 185 if (!hasRareSVGData()) { 186 DEFINE_STATIC_LOCAL(HashSet<SVGElementInstance*>, emptyInstances, ()); 187 return emptyInstances; 188 } 189 return rareSVGData()->elementInstances(); 190 } 191 192 bool SVGElement::boundingBox(FloatRect& rect, SVGLocatable::StyleUpdateStrategy styleUpdateStrategy) const 193 { 194 if (isStyledLocatable()) { 195 rect = static_cast<const SVGStyledLocatableElement*>(this)->getBBox(styleUpdateStrategy); 196 return true; 197 } 198 if (hasTagName(SVGNames::textTag)) { 199 rect = static_cast<const SVGTextElement*>(this)->getBBox(styleUpdateStrategy); 200 return true; 201 } 202 return false; 203 } 204 205 void SVGElement::setCursorElement(SVGCursorElement* cursorElement) 206 { 207 SVGElementRareData* rareData = ensureRareSVGData(); 208 if (SVGCursorElement* oldCursorElement = rareData->cursorElement()) { 209 if (cursorElement == oldCursorElement) 210 return; 211 oldCursorElement->removeReferencedElement(this); 212 } 213 rareData->setCursorElement(cursorElement); 214 } 215 216 void SVGElement::cursorElementRemoved() 217 { 218 ASSERT(hasRareSVGData()); 219 rareSVGData()->setCursorElement(0); 220 } 221 222 void SVGElement::setCursorImageValue(CSSCursorImageValue* cursorImageValue) 223 { 224 SVGElementRareData* rareData = ensureRareSVGData(); 225 if (CSSCursorImageValue* oldCursorImageValue = rareData->cursorImageValue()) { 226 if (cursorImageValue == oldCursorImageValue) 227 return; 228 oldCursorImageValue->removeReferencedElement(this); 229 } 230 rareData->setCursorImageValue(cursorImageValue); 231 } 232 233 void SVGElement::cursorImageValueRemoved() 234 { 235 ASSERT(hasRareSVGData()); 236 rareSVGData()->setCursorImageValue(0); 237 } 238 239 void SVGElement::parseMappedAttribute(Attribute* attr) 240 { 241 // standard events 242 if (attr->name() == onloadAttr) 243 setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); 244 else if (attr->name() == onclickAttr) 245 setAttributeEventListener(eventNames().clickEvent, createAttributeEventListener(this, attr)); 246 else if (attr->name() == onmousedownAttr) 247 setAttributeEventListener(eventNames().mousedownEvent, createAttributeEventListener(this, attr)); 248 else if (attr->name() == onmousemoveAttr) 249 setAttributeEventListener(eventNames().mousemoveEvent, createAttributeEventListener(this, attr)); 250 else if (attr->name() == onmouseoutAttr) 251 setAttributeEventListener(eventNames().mouseoutEvent, createAttributeEventListener(this, attr)); 252 else if (attr->name() == onmouseoverAttr) 253 setAttributeEventListener(eventNames().mouseoverEvent, createAttributeEventListener(this, attr)); 254 else if (attr->name() == onmouseupAttr) 255 setAttributeEventListener(eventNames().mouseupEvent, createAttributeEventListener(this, attr)); 256 else if (attr->name() == SVGNames::onfocusinAttr) 257 setAttributeEventListener(eventNames().focusinEvent, createAttributeEventListener(this, attr)); 258 else if (attr->name() == SVGNames::onfocusoutAttr) 259 setAttributeEventListener(eventNames().focusoutEvent, createAttributeEventListener(this, attr)); 260 else if (attr->name() == SVGNames::onactivateAttr) 261 setAttributeEventListener(eventNames().DOMActivateEvent, createAttributeEventListener(this, attr)); 262 else 263 StyledElement::parseMappedAttribute(attr); 264 } 265 266 AttributeToPropertyTypeMap& SVGElement::attributeToPropertyTypeMap() 267 { 268 DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ()); 269 return s_attributeToPropertyTypeMap; 270 } 271 272 AnimatedAttributeType SVGElement::animatedPropertyTypeForAttribute(const QualifiedName& attrName) 273 { 274 AttributeToPropertyTypeMap& animatedAttributeMap = attributeToPropertyTypeMap(); 275 if (animatedAttributeMap.isEmpty()) 276 fillAttributeToPropertyTypeMap(); 277 if (animatedAttributeMap.contains(attrName)) 278 return animatedAttributeMap.get(attrName); 279 if (isStyled()) 280 return static_cast<SVGStyledElement*>(this)->animatedPropertyTypeForCSSProperty(attrName); 281 282 return AnimatedUnknown; 283 } 284 285 bool SVGElement::haveLoadedRequiredResources() 286 { 287 Node* child = firstChild(); 288 while (child) { 289 if (child->isSVGElement() && !static_cast<SVGElement*>(child)->haveLoadedRequiredResources()) 290 return false; 291 child = child->nextSibling(); 292 } 293 return true; 294 } 295 296 static bool hasLoadListener(Node* node) 297 { 298 if (node->hasEventListeners(eventNames().loadEvent)) 299 return true; 300 301 for (node = node->parentNode(); node && node->isElementNode(); node = node->parentNode()) { 302 const EventListenerVector& entry = node->getEventListeners(eventNames().loadEvent); 303 for (size_t i = 0; i < entry.size(); ++i) { 304 if (entry[i].useCapture) 305 return true; 306 } 307 } 308 309 return false; 310 } 311 312 void SVGElement::sendSVGLoadEventIfPossible(bool sendParentLoadEvents) 313 { 314 RefPtr<SVGElement> currentTarget = this; 315 while (currentTarget && currentTarget->haveLoadedRequiredResources()) { 316 RefPtr<Node> parent; 317 if (sendParentLoadEvents) 318 parent = currentTarget->parentNode(); // save the next parent to dispatch too incase dispatching the event changes the tree 319 if (hasLoadListener(currentTarget.get())) 320 currentTarget->dispatchEvent(Event::create(eventNames().loadEvent, false, false)); 321 currentTarget = (parent && parent->isSVGElement()) ? static_pointer_cast<SVGElement>(parent) : RefPtr<SVGElement>(); 322 } 323 } 324 325 void SVGElement::finishParsingChildren() 326 { 327 StyledElement::finishParsingChildren(); 328 329 // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>) 330 // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish 331 sendSVGLoadEventIfPossible(); 332 } 333 334 bool SVGElement::childShouldCreateRenderer(Node* child) const 335 { 336 if (child->isSVGElement()) 337 return static_cast<SVGElement*>(child)->isValid(); 338 return false; 339 } 340 341 void SVGElement::insertedIntoDocument() 342 { 343 StyledElement::insertedIntoDocument(); 344 345 if (!needsPendingResourceHandling()) 346 return; 347 348 SVGDocumentExtensions* extensions = document()->accessSVGExtensions(); 349 String resourceId = getIdAttribute(); 350 if (!extensions->isPendingResource(resourceId)) 351 return; 352 353 OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions->removePendingResource(resourceId)); 354 if (clients->isEmpty()) 355 return; 356 357 const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end(); 358 for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) 359 (*it)->buildPendingResource(); 360 } 361 362 void SVGElement::attributeChanged(Attribute* attr, bool preserveDecls) 363 { 364 ASSERT(attr); 365 if (!attr) 366 return; 367 368 StyledElement::attributeChanged(attr, preserveDecls); 369 370 // When an animated SVG property changes through SVG DOM, svgAttributeChanged() is called, not attributeChanged(). 371 // Next time someone tries to access the XML attributes, the synchronization code starts. During that synchronization 372 // SVGAnimatedPropertySynchronizer may call NamedNodeMap::removeAttribute(), which in turn calls attributeChanged(). 373 // At this point we're not allowed to call svgAttributeChanged() again - it may lead to extra work being done, or crashes 374 // see bug https://bugs.webkit.org/show_bug.cgi?id=40994. 375 if (isSynchronizingSVGAttributes()) 376 return; 377 378 if (isIdAttributeName(attr->name())) 379 document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this); 380 381 // Changes to the style attribute are processed lazily (see Element::getAttribute() and related methods), 382 // so we don't want changes to the style attribute to result in extra work here. 383 if (attr->name() != HTMLNames::styleAttr) 384 svgAttributeChanged(attr->name()); 385 } 386 387 void SVGElement::updateAnimatedSVGAttribute(const QualifiedName& name) const 388 { 389 if (isSynchronizingSVGAttributes() || areSVGAttributesValid()) 390 return; 391 392 setIsSynchronizingSVGAttributes(); 393 394 const_cast<SVGElement*>(this)->synchronizeProperty(name); 395 if (name == anyQName()) 396 setAreSVGAttributesValid(); 397 398 clearIsSynchronizingSVGAttributes(); 399 } 400 401 } 402 403 #endif // ENABLE(SVG) 404