SVGElement.cpp revision 2bde8e466a4451c7319e3a072d118917957d6554
1/* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2008 Rob Buis <buis@kde.org> 4 * Copyright (C) 2008 Apple Inc. All rights reserved. 5 * Copyright (C) 2008 Alp Toker <alp@atoker.com> 6 * Copyright (C) 2009 Cameron McCormack <cam@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 53namespace WebCore { 54 55using namespace HTMLNames; 56 57SVGElement::SVGElement(const QualifiedName& tagName, Document* document) 58 : StyledElement(tagName, document, CreateSVGElement) 59{ 60} 61 62PassRefPtr<SVGElement> SVGElement::create(const QualifiedName& tagName, Document* document) 63{ 64 return adoptRef(new SVGElement(tagName, document)); 65} 66 67SVGElement::~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 88SVGElementRareData* SVGElement::rareSVGData() const 89{ 90 ASSERT(hasRareSVGData()); 91 return SVGElementRareData::rareDataFromMap(this); 92} 93 94SVGElementRareData* 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 106bool SVGElement::isSupported(StringImpl* feature, StringImpl* version) const 107{ 108 return DOMImplementation::hasFeature(feature, version); 109} 110 111String SVGElement::xmlbase() const 112{ 113 return getAttribute(XMLNames::baseAttr); 114} 115 116void SVGElement::setXmlbase(const String& value, ExceptionCode&) 117{ 118 setAttribute(XMLNames::baseAttr, value); 119} 120 121void SVGElement::removedFromDocument() 122{ 123 document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this); 124 StyledElement::removedFromDocument(); 125} 126 127SVGSVGElement* 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 140SVGElement* 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 155SVGDocumentExtensions* 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 162void 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 172void 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 183const 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 192bool 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 205void 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 216void SVGElement::cursorElementRemoved() 217{ 218 ASSERT(hasRareSVGData()); 219 rareSVGData()->setCursorElement(0); 220} 221 222void 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 233void SVGElement::cursorImageValueRemoved() 234{ 235 ASSERT(hasRareSVGData()); 236 rareSVGData()->setCursorImageValue(0); 237} 238 239void 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 266AttributeToPropertyTypeMap& SVGElement::attributeToPropertyTypeMap() 267{ 268 DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ()); 269 return s_attributeToPropertyTypeMap; 270} 271 272AnimatedAttributeType 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 285bool 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 296static 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 312void 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 325void 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 334bool SVGElement::childShouldCreateRenderer(Node* child) const 335{ 336 if (child->isSVGElement()) 337 return static_cast<SVGElement*>(child)->isValid(); 338 return false; 339} 340 341void 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 362void 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 387void 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