1/* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> 4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. 5 * Copyright (C) 2011 Torch Mobile (Beijing) Co. Ltd. All rights reserved. 6 * Copyright (C) 2012 University of Szeged 7 * Copyright (C) 2012 Renata Hodovan <reni@webkit.org> 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 */ 24 25#include "config.h" 26 27#if ENABLE(SVG) 28#include "SVGUseElement.h" 29 30#include "Attribute.h" 31#include "CachedResourceLoader.h" 32#include "CachedResourceRequest.h" 33#include "CachedSVGDocument.h" 34#include "Document.h" 35#include "ElementShadow.h" 36#include "Event.h" 37#include "EventListener.h" 38#include "HTMLNames.h" 39#include "NodeRenderStyle.h" 40#include "NodeTraversal.h" 41#include "RegisteredEventListener.h" 42#include "RenderSVGResource.h" 43#include "RenderSVGTransformableContainer.h" 44#include "ShadowRoot.h" 45#include "SVGElementInstance.h" 46#include "SVGElementRareData.h" 47#include "SVGElementInstanceList.h" 48#include "SVGGElement.h" 49#include "SVGLengthContext.h" 50#include "SVGNames.h" 51#include "SVGSMILElement.h" 52#include "SVGSVGElement.h" 53#include "SVGSymbolElement.h" 54#include "StyleResolver.h" 55#include "XLinkNames.h" 56#include "XMLDocumentParser.h" 57#include "XMLSerializer.h" 58 59// Dump SVGElementInstance object tree - useful to debug instanceRoot problems 60// #define DUMP_INSTANCE_TREE 61 62// Dump the deep-expanded shadow tree (where the renderers are built from) 63// #define DUMP_SHADOW_TREE 64 65namespace WebCore { 66 67// Animated property definitions 68DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::xAttr, X, x) 69DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::yAttr, Y, y) 70DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::widthAttr, Width, width) 71DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::heightAttr, Height, height) 72DEFINE_ANIMATED_STRING(SVGUseElement, XLinkNames::hrefAttr, Href, href) 73DEFINE_ANIMATED_BOOLEAN(SVGUseElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 74 75BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGUseElement) 76 REGISTER_LOCAL_ANIMATED_PROPERTY(x) 77 REGISTER_LOCAL_ANIMATED_PROPERTY(y) 78 REGISTER_LOCAL_ANIMATED_PROPERTY(width) 79 REGISTER_LOCAL_ANIMATED_PROPERTY(height) 80 REGISTER_LOCAL_ANIMATED_PROPERTY(href) 81 REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired) 82 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGStyledTransformableElement) 83 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests) 84END_REGISTER_ANIMATED_PROPERTIES 85 86inline SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser) 87 : SVGStyledTransformableElement(tagName, document) 88 , m_x(LengthModeWidth) 89 , m_y(LengthModeHeight) 90 , m_width(LengthModeWidth) 91 , m_height(LengthModeHeight) 92 , m_wasInsertedByParser(wasInsertedByParser) 93 , m_haveFiredLoadEvent(false) 94 , m_needsShadowTreeRecreation(false) 95 , m_svgLoadEventTimer(this, &SVGElement::svgLoadEventTimerFired) 96{ 97 ASSERT(hasCustomStyleCallbacks()); 98 ASSERT(hasTagName(SVGNames::useTag)); 99 registerAnimatedPropertiesForSVGUseElement(); 100} 101 102PassRefPtr<SVGUseElement> SVGUseElement::create(const QualifiedName& tagName, Document* document, bool wasInsertedByParser) 103{ 104 // Always build a #shadow-root for SVGUseElement. 105 RefPtr<SVGUseElement> use = adoptRef(new SVGUseElement(tagName, document, wasInsertedByParser)); 106 use->ensureUserAgentShadowRoot(); 107 return use.release(); 108} 109 110SVGUseElement::~SVGUseElement() 111{ 112 setCachedDocument(0); 113 114 clearResourceReferences(); 115} 116 117SVGElementInstance* SVGUseElement::instanceRoot() 118{ 119 // If there is no element instance tree, force immediate SVGElementInstance tree 120 // creation by asking the document to invoke our recalcStyle function - as we can't 121 // wait for the lazy creation to happen if e.g. JS wants to access the instanceRoot 122 // object right after creating the element on-the-fly 123 if (!m_targetElementInstance) 124 document()->updateLayoutIgnorePendingStylesheets(); 125 126 return m_targetElementInstance.get(); 127} 128 129SVGElementInstance* SVGUseElement::animatedInstanceRoot() const 130{ 131 // FIXME: Implement me. 132 return 0; 133} 134 135bool SVGUseElement::isSupportedAttribute(const QualifiedName& attrName) 136{ 137 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 138 if (supportedAttributes.isEmpty()) { 139 SVGTests::addSupportedAttributes(supportedAttributes); 140 SVGLangSpace::addSupportedAttributes(supportedAttributes); 141 SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes); 142 SVGURIReference::addSupportedAttributes(supportedAttributes); 143 supportedAttributes.add(SVGNames::xAttr); 144 supportedAttributes.add(SVGNames::yAttr); 145 supportedAttributes.add(SVGNames::widthAttr); 146 supportedAttributes.add(SVGNames::heightAttr); 147 } 148 return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName); 149} 150 151void SVGUseElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 152{ 153 SVGParsingError parseError = NoError; 154 155 if (!isSupportedAttribute(name)) 156 SVGStyledTransformableElement::parseAttribute(name, value); 157 else if (name == SVGNames::xAttr) 158 setXBaseValue(SVGLength::construct(LengthModeWidth, value, parseError)); 159 else if (name == SVGNames::yAttr) 160 setYBaseValue(SVGLength::construct(LengthModeHeight, value, parseError)); 161 else if (name == SVGNames::widthAttr) 162 setWidthBaseValue(SVGLength::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths)); 163 else if (name == SVGNames::heightAttr) 164 setHeightBaseValue(SVGLength::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths)); 165 else if (SVGTests::parseAttribute(name, value) 166 || SVGLangSpace::parseAttribute(name, value) 167 || SVGExternalResourcesRequired::parseAttribute(name, value) 168 || SVGURIReference::parseAttribute(name, value)) { 169 } else 170 ASSERT_NOT_REACHED(); 171 172 reportAttributeParsingError(parseError, name, value); 173} 174 175static inline bool isWellFormedDocument(Document* document) 176{ 177 if (document->isSVGDocument() || document->isXHTMLDocument()) 178 return static_cast<XMLDocumentParser*>(document->parser())->wellFormed(); 179 return true; 180} 181 182Node::InsertionNotificationRequest SVGUseElement::insertedInto(ContainerNode* rootParent) 183{ 184 // This functions exists to assure assumptions made in the code regarding SVGElementInstance creation/destruction are satisfied. 185 SVGStyledTransformableElement::insertedInto(rootParent); 186 if (!rootParent->inDocument()) 187 return InsertionDone; 188 ASSERT(!m_targetElementInstance || !isWellFormedDocument(document())); 189 ASSERT(!hasPendingResources() || !isWellFormedDocument(document())); 190 SVGExternalResourcesRequired::insertedIntoDocument(this); 191 if (!m_wasInsertedByParser) 192 return InsertionShouldCallDidNotifySubtreeInsertions; 193 return InsertionDone; 194} 195 196void SVGUseElement::didNotifySubtreeInsertions(ContainerNode*) 197{ 198 buildPendingResource(); 199} 200 201void SVGUseElement::removedFrom(ContainerNode* rootParent) 202{ 203 SVGStyledTransformableElement::removedFrom(rootParent); 204 if (rootParent->inDocument()) 205 clearResourceReferences(); 206} 207 208Document* SVGUseElement::referencedDocument() const 209{ 210 if (!isExternalURIReference(href(), document())) 211 return document(); 212 return externalDocument(); 213} 214 215Document* SVGUseElement::externalDocument() const 216{ 217 if (m_cachedDocument && m_cachedDocument->isLoaded()) { 218 // Gracefully handle error condition. 219 if (m_cachedDocument->errorOccurred()) 220 return 0; 221 ASSERT(m_cachedDocument->document()); 222 return m_cachedDocument->document(); 223 } 224 return 0; 225} 226 227void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) 228{ 229 if (!isSupportedAttribute(attrName)) { 230 SVGStyledTransformableElement::svgAttributeChanged(attrName); 231 return; 232 } 233 234 SVGElementInstance::InvalidationGuard invalidationGuard(this); 235 236 RenderObject* renderer = this->renderer(); 237 if (attrName == SVGNames::xAttr 238 || attrName == SVGNames::yAttr 239 || attrName == SVGNames::widthAttr 240 || attrName == SVGNames::heightAttr) { 241 updateRelativeLengthsInformation(); 242 if (renderer) 243 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 244 return; 245 } 246 247 if (SVGTests::handleAttributeChange(this, attrName)) 248 return; 249 250 if (SVGExternalResourcesRequired::handleAttributeChange(this, attrName)) 251 return; 252 253 if (SVGURIReference::isKnownAttribute(attrName)) { 254 bool isExternalReference = isExternalURIReference(href(), document()); 255 if (isExternalReference) { 256 KURL url = document()->completeURL(href()); 257 if (url.hasFragmentIdentifier()) { 258 CachedResourceRequest request(ResourceRequest(url.string())); 259 request.setInitiator(this); 260 setCachedDocument(document()->cachedResourceLoader()->requestSVGDocument(request)); 261 } 262 } else 263 setCachedDocument(0); 264 265 if (!m_wasInsertedByParser) 266 buildPendingResource(); 267 268 return; 269 } 270 271 if (!renderer) 272 return; 273 274 if (SVGLangSpace::isKnownAttribute(attrName) 275 || SVGExternalResourcesRequired::isKnownAttribute(attrName)) { 276 invalidateShadowTree(); 277 return; 278 } 279 280 ASSERT_NOT_REACHED(); 281} 282 283bool SVGUseElement::willRecalcStyle(StyleChange) 284{ 285 if (!m_wasInsertedByParser && m_needsShadowTreeRecreation && renderer() && needsStyleRecalc()) 286 buildPendingResource(); 287 return true; 288} 289 290#ifdef DUMP_INSTANCE_TREE 291static void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance) 292{ 293 SVGElement* element = targetInstance->correspondingElement(); 294 ASSERT(element); 295 296 if (element->hasTagName(SVGNames::useTag)) { 297 if (static_cast<SVGUseElement*>(element)->cachedDocumentIsStillLoading()) 298 return; 299 } 300 301 SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); 302 ASSERT(shadowTreeElement); 303 304 SVGUseElement* directUseElement = targetInstance->directUseElement(); 305 String directUseElementName = directUseElement ? directUseElement->nodeName() : "null"; 306 307 String elementId = element->getIdAttribute(); 308 String elementNodeName = element->nodeName(); 309 String shadowTreeElementNodeName = shadowTreeElement->nodeName(); 310 String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null"; 311 String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null"; 312 313 for (unsigned int i = 0; i < depth; ++i) 314 text += " "; 315 316 text += String::format("SVGElementInstance this=%p, (parentNode=%s (%p), firstChild=%s (%p), correspondingElement=%s (%p), directUseElement=%s (%p), shadowTreeElement=%s (%p), id=%s)\n", 317 targetInstance, parentNodeName.latin1().data(), element->parentNode(), firstChildNodeName.latin1().data(), element->firstChild(), 318 elementNodeName.latin1().data(), element, directUseElementName.latin1().data(), directUseElement, shadowTreeElementNodeName.latin1().data(), shadowTreeElement, elementId.latin1().data()); 319 320 for (unsigned int i = 0; i < depth; ++i) 321 text += " "; 322 323 const HashSet<SVGElementInstance*>& elementInstances = element->instancesForElement(); 324 text += "Corresponding element is associated with " + String::number(elementInstances.size()) + " instance(s):\n"; 325 326 const HashSet<SVGElementInstance*>::const_iterator end = elementInstances.end(); 327 for (HashSet<SVGElementInstance*>::const_iterator it = elementInstances.begin(); it != end; ++it) { 328 for (unsigned int i = 0; i < depth; ++i) 329 text += " "; 330 331 text += String::format(" -> SVGElementInstance this=%p, (refCount: %i, shadowTreeElement in document? %i)\n", 332 *it, (*it)->refCount(), (*it)->shadowTreeElement()->inDocument()); 333 } 334 335 ++depth; 336 337 for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) 338 dumpInstanceTree(depth, text, instance); 339 340 --depth; 341} 342#endif 343 344static bool isDisallowedElement(Node* node) 345{ 346 // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used 347 // (i.e., "instanced") in the SVG document via a 'use' element." 348 // "Graphics Element" is defined as 'circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text' 349 // Excluded are anything that is used by reference or that only make sense to appear once in a document. 350 // We must also allow the shadow roots of other use elements. 351 if (node->isShadowRoot() || node->isTextNode()) 352 return false; 353 354 if (!node->isSVGElement()) 355 return true; 356 357 Element* element = toElement(node); 358 359 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, allowedElementTags, ()); 360 if (allowedElementTags.isEmpty()) { 361 allowedElementTags.add(SVGNames::aTag); 362 allowedElementTags.add(SVGNames::circleTag); 363 allowedElementTags.add(SVGNames::descTag); 364 allowedElementTags.add(SVGNames::ellipseTag); 365 allowedElementTags.add(SVGNames::gTag); 366 allowedElementTags.add(SVGNames::imageTag); 367 allowedElementTags.add(SVGNames::lineTag); 368 allowedElementTags.add(SVGNames::metadataTag); 369 allowedElementTags.add(SVGNames::pathTag); 370 allowedElementTags.add(SVGNames::polygonTag); 371 allowedElementTags.add(SVGNames::polylineTag); 372 allowedElementTags.add(SVGNames::rectTag); 373 allowedElementTags.add(SVGNames::svgTag); 374 allowedElementTags.add(SVGNames::switchTag); 375 allowedElementTags.add(SVGNames::symbolTag); 376 allowedElementTags.add(SVGNames::textTag); 377 allowedElementTags.add(SVGNames::textPathTag); 378 allowedElementTags.add(SVGNames::titleTag); 379 allowedElementTags.add(SVGNames::trefTag); 380 allowedElementTags.add(SVGNames::tspanTag); 381 allowedElementTags.add(SVGNames::useTag); 382 } 383 return !allowedElementTags.contains<QualifiedName, SVGAttributeHashTranslator>(element->tagQName()); 384} 385 386static bool subtreeContainsDisallowedElement(Node* start) 387{ 388 if (isDisallowedElement(start)) 389 return true; 390 391 for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) { 392 if (subtreeContainsDisallowedElement(cur)) 393 return true; 394 } 395 396 return false; 397} 398 399void SVGUseElement::clearResourceReferences() 400{ 401 // FIXME: We should try to optimize this, to at least allow partial reclones. 402 if (ShadowRoot* shadowTreeRootElement = shadow()->shadowRoot()) 403 shadowTreeRootElement->removeChildren(); 404 405 if (m_targetElementInstance) { 406 m_targetElementInstance->detach(); 407 m_targetElementInstance = 0; 408 } 409 410 m_needsShadowTreeRecreation = false; 411 412 ASSERT(document()); 413 document()->accessSVGExtensions()->removeAllTargetReferencesForElement(this); 414} 415 416void SVGUseElement::buildPendingResource() 417{ 418 if (!referencedDocument() || isInShadowTree()) 419 return; 420 clearResourceReferences(); 421 if (!inDocument()) 422 return; 423 424 String id; 425 Element* target = SVGURIReference::targetElementFromIRIString(href(), document(), &id, externalDocument()); 426 if (!target || !target->inDocument()) { 427 // If we can't find the target of an external element, just give up. 428 // We can't observe if the target somewhen enters the external document, nor should we do it. 429 if (externalDocument()) 430 return; 431 if (id.isEmpty()) 432 return; 433 434 referencedDocument()->accessSVGExtensions()->addPendingResource(id, this); 435 ASSERT(hasPendingResources()); 436 return; 437 } 438 439 if (target->isSVGElement()) { 440 buildShadowAndInstanceTree(toSVGElement(target)); 441 invalidateDependentShadowTrees(); 442 } 443 444 ASSERT(!m_needsShadowTreeRecreation); 445} 446 447void SVGUseElement::buildShadowAndInstanceTree(SVGElement* target) 448{ 449 ASSERT(!m_targetElementInstance); 450 451 // Do not build the shadow/instance tree for <use> elements living in a shadow tree. 452 // The will be expanded soon anyway - see expandUseElementsInShadowTree(). 453 if (isInShadowTree()) 454 return; 455 456 // Do not allow self-referencing. 457 // 'target' may be null, if it's a non SVG namespaced element. 458 if (!target || target == this) 459 return; 460 461 // Why a seperated instance/shadow tree? SVG demands it: 462 // The instance tree is accesable from JavaScript, and has to 463 // expose a 1:1 copy of the referenced tree, whereas internally we need 464 // to alter the tree for correct "use-on-symbol", "use-on-svg" support. 465 466 // Build instance tree. Create root SVGElementInstance object for the first sub-tree node. 467 // 468 // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a 469 // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object 470 // is the SVGRectElement that corresponds to the referenced 'rect' element. 471 m_targetElementInstance = SVGElementInstance::create(this, this, target); 472 473 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children 474 bool foundProblem = false; 475 buildInstanceTree(target, m_targetElementInstance.get(), foundProblem, false); 476 477 if (instanceTreeIsLoading(m_targetElementInstance.get())) 478 return; 479 480 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it! 481 // Non-appearing <use> content is easier to debug, then half-appearing content. 482 if (foundProblem) { 483 clearResourceReferences(); 484 return; 485 } 486 487 // Assure instance tree building was successfull 488 ASSERT(m_targetElementInstance); 489 ASSERT(!m_targetElementInstance->shadowTreeElement()); 490 ASSERT(m_targetElementInstance->correspondingUseElement() == this); 491 ASSERT(m_targetElementInstance->directUseElement() == this); 492 ASSERT(m_targetElementInstance->correspondingElement() == target); 493 494 ShadowRoot* shadowTreeRootElement = shadow()->shadowRoot(); 495 ASSERT(shadowTreeRootElement); 496 497 // Build shadow tree from instance tree 498 // This also handles the special cases: <use> on <symbol>, <use> on <svg>. 499 buildShadowTree(target, m_targetElementInstance.get()); 500 501 // Expand all <use> elements in the shadow tree. 502 // Expand means: replace the actual <use> element by what it references. 503 expandUseElementsInShadowTree(shadowTreeRootElement); 504 505 // Expand all <symbol> elements in the shadow tree. 506 // Expand means: replace the actual <symbol> element by the <svg> element. 507 expandSymbolElementsInShadowTree(shadowTreeRootElement); 508 509 // Now that the shadow tree is completly expanded, we can associate 510 // shadow tree elements <-> instances in the instance tree. 511 associateInstancesWithShadowTreeElements(shadowTreeRootElement->firstChild(), m_targetElementInstance.get()); 512 513 // If no shadow tree element is present, this means that the reference root 514 // element was removed, as it is disallowed (ie. <use> on <foreignObject>) 515 // Do NOT leave an inconsistent instance tree around, instead destruct it. 516 if (!m_targetElementInstance->shadowTreeElement()) { 517 clearResourceReferences(); 518 return; 519 } 520 521 ASSERT(m_targetElementInstance->shadowTreeElement()->parentNode() == shadowTreeRootElement); 522 523 // Transfer event listeners assigned to the referenced element to our shadow tree elements. 524 transferEventListenersToShadowTree(m_targetElementInstance.get()); 525 526 // Update relative length information. 527 updateRelativeLengthsInformation(); 528 529 // Eventually dump instance tree 530#ifdef DUMP_INSTANCE_TREE 531 String text; 532 unsigned int depth = 0; 533 534 dumpInstanceTree(depth, text, m_targetElementInstance.get()); 535 fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data()); 536#endif 537 538 // Eventually dump shadow tree 539#ifdef DUMP_SHADOW_TREE 540 RefPtr<XMLSerializer> serializer = XMLSerializer::create(); 541 String markup = serializer->serializeToString(shadowTreeRootElement, ASSERT_NO_EXCEPTION); 542 fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data()); 543#endif 544} 545 546RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*) 547{ 548 return new (arena) RenderSVGTransformableContainer(this); 549} 550 551static bool isDirectReference(const Node* node) 552{ 553 return node->hasTagName(SVGNames::pathTag) 554 || node->hasTagName(SVGNames::rectTag) 555 || node->hasTagName(SVGNames::circleTag) 556 || node->hasTagName(SVGNames::ellipseTag) 557 || node->hasTagName(SVGNames::polygonTag) 558 || node->hasTagName(SVGNames::polylineTag) 559 || node->hasTagName(SVGNames::textTag); 560} 561 562void SVGUseElement::toClipPath(Path& path) 563{ 564 ASSERT(path.isEmpty()); 565 566 Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0; 567 if (!n) 568 return; 569 570 if (n->isSVGElement() && toSVGElement(n)->isStyledTransformable()) { 571 if (!isDirectReference(n)) 572 // Spec: Indirect references are an error (14.3.5) 573 document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>"); 574 else { 575 toSVGStyledTransformableElement(n)->toClipPath(path); 576 // FIXME: Avoid manual resolution of x/y here. Its potentially harmful. 577 SVGLengthContext lengthContext(this); 578 path.translate(FloatSize(x().value(lengthContext), y().value(lengthContext))); 579 path.transform(animatedLocalTransform()); 580 } 581 } 582} 583 584RenderObject* SVGUseElement::rendererClipChild() const 585{ 586 Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0; 587 if (!n) 588 return 0; 589 590 if (n->isSVGElement() && isDirectReference(n)) 591 return toSVGElement(n)->renderer(); 592 593 return 0; 594} 595 596void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem, bool foundUse) 597{ 598 ASSERT(target); 599 ASSERT(targetInstance); 600 601 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced 602 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. 603 bool targetHasUseTag = target->hasTagName(SVGNames::useTag); 604 SVGElement* newTarget = 0; 605 if (targetHasUseTag) { 606 foundProblem = hasCycleUseReferencing(static_cast<SVGUseElement*>(target), targetInstance, newTarget); 607 if (foundProblem) 608 return; 609 610 // We only need to track first degree <use> dependencies. Indirect references are handled 611 // as the invalidation bubbles up the dependency chain. 612 if (!foundUse) { 613 ASSERT(document()); 614 document()->accessSVGExtensions()->addElementReferencingTarget(this, target); 615 foundUse = true; 616 } 617 } else if (isDisallowedElement(target)) { 618 foundProblem = true; 619 return; 620 } 621 622 // A general description from the SVG spec, describing what buildInstanceTree() actually does. 623 // 624 // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree 625 // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement 626 // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has 627 // its correspondingElement that is an SVGRectElement object. 628 629 for (Node* node = target->firstChild(); node; node = node->nextSibling()) { 630 SVGElement* element = 0; 631 if (node->isSVGElement()) 632 element = toSVGElement(node); 633 634 // Skip any non-svg nodes or any disallowed element. 635 if (!element || isDisallowedElement(element)) 636 continue; 637 638 // Create SVGElementInstance object, for both container/non-container nodes. 639 RefPtr<SVGElementInstance> instance = SVGElementInstance::create(this, 0, element); 640 SVGElementInstance* instancePtr = instance.get(); 641 targetInstance->appendChild(instance.release()); 642 643 // Enter recursion, appending new instance tree nodes to the "instance" object. 644 buildInstanceTree(element, instancePtr, foundProblem, foundUse); 645 if (foundProblem) 646 return; 647 } 648 649 if (!targetHasUseTag || !newTarget) 650 return; 651 652 RefPtr<SVGElementInstance> newInstance = SVGElementInstance::create(this, static_cast<SVGUseElement*>(target), newTarget); 653 SVGElementInstance* newInstancePtr = newInstance.get(); 654 targetInstance->appendChild(newInstance.release()); 655 buildInstanceTree(newTarget, newInstancePtr, foundProblem, foundUse); 656} 657 658bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, SVGElementInstance* targetInstance, SVGElement*& newTarget) 659{ 660 Element* targetElement = SVGURIReference::targetElementFromIRIString(use->href(), referencedDocument()); 661 newTarget = 0; 662 if (targetElement && targetElement->isSVGElement()) 663 newTarget = toSVGElement(targetElement); 664 665 if (!newTarget) 666 return false; 667 668 // Shortcut for self-references 669 if (newTarget == this) 670 return true; 671 672 AtomicString targetId = newTarget->getIdAttribute(); 673 SVGElementInstance* instance = targetInstance->parentNode(); 674 while (instance) { 675 SVGElement* element = instance->correspondingElement(); 676 677 if (element->hasID() && element->getIdAttribute() == targetId && element->document() == newTarget->document()) 678 return true; 679 680 instance = instance->parentNode(); 681 } 682 return false; 683} 684 685static inline void removeDisallowedElementsFromSubtree(Element* subtree) 686{ 687 ASSERT(!subtree->inDocument()); 688 Element* element = ElementTraversal::firstWithin(subtree); 689 while (element) { 690 if (isDisallowedElement(element)) { 691 Element* next = ElementTraversal::nextSkippingChildren(element, subtree); 692 // The subtree is not in document so this won't generate events that could mutate the tree. 693 element->parentNode()->removeChild(element); 694 element = next; 695 } else 696 element = ElementTraversal::next(element, subtree); 697 } 698} 699 700void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targetInstance) 701{ 702 // For instance <use> on <foreignObject> (direct case). 703 if (isDisallowedElement(target)) 704 return; 705 706 RefPtr<Element> newChild = targetInstance->correspondingElement()->cloneElementWithChildren(); 707 708 // We don't walk the target tree element-by-element, and clone each element, 709 // but instead use cloneElementWithChildren(). This is an optimization for the common 710 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 711 // Though if there are disallowed elements in the subtree, we have to remove them. 712 // For instance: <use> on <g> containing <foreignObject> (indirect case). 713 if (subtreeContainsDisallowedElement(newChild.get())) 714 removeDisallowedElementsFromSubtree(newChild.get()); 715 716 shadow()->shadowRoot()->appendChild(newChild.release()); 717} 718 719void SVGUseElement::expandUseElementsInShadowTree(Node* element) 720{ 721 // Why expand the <use> elements in the shadow tree here, and not just 722 // do this directly in buildShadowTree, if we encounter a <use> element? 723 // 724 // Short answer: Because we may miss to expand some elements. Ie. if a <symbol> 725 // contains <use> tags, we'd miss them. So once we're done with settin' up the 726 // actual shadow tree (after the special case modification for svg/symbol) we have 727 // to walk it completely and expand all <use> elements. 728 if (element->hasTagName(SVGNames::useTag)) { 729 SVGUseElement* use = static_cast<SVGUseElement*>(element); 730 ASSERT(!use->cachedDocumentIsStillLoading()); 731 732 Element* targetElement = SVGURIReference::targetElementFromIRIString(use->href(), referencedDocument()); 733 SVGElement* target = 0; 734 if (targetElement && targetElement->isSVGElement()) 735 target = toSVGElement(targetElement); 736 737 // Don't ASSERT(target) here, it may be "pending", too. 738 // Setup sub-shadow tree root node 739 RefPtr<SVGGElement> cloneParent = SVGGElement::create(SVGNames::gTag, referencedDocument()); 740 use->cloneChildNodes(cloneParent.get()); 741 742 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the 743 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element. 744 transferUseAttributesToReplacedElement(use, cloneParent.get()); 745 746 if (target && !isDisallowedElement(target)) { 747 RefPtr<Element> newChild = target->cloneElementWithChildren(); 748 ASSERT(newChild->isSVGElement()); 749 cloneParent->appendChild(newChild.release()); 750 } 751 752 // We don't walk the target tree element-by-element, and clone each element, 753 // but instead use cloneElementWithChildren(). This is an optimization for the common 754 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 755 // Though if there are disallowed elements in the subtree, we have to remove them. 756 // For instance: <use> on <g> containing <foreignObject> (indirect case). 757 if (subtreeContainsDisallowedElement(cloneParent.get())) 758 removeDisallowedElementsFromSubtree(cloneParent.get()); 759 760 RefPtr<Node> replacingElement(cloneParent.get()); 761 762 // Replace <use> with referenced content. 763 ASSERT(use->parentNode()); 764 use->parentNode()->replaceChild(cloneParent.release(), use); 765 766 // Expand the siblings because the *element* is replaced and we will 767 // lose the sibling chain when we are back from recursion. 768 element = replacingElement.get(); 769 for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling()) 770 expandUseElementsInShadowTree(sibling.get()); 771 } 772 773 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) 774 expandUseElementsInShadowTree(child.get()); 775} 776 777void SVGUseElement::expandSymbolElementsInShadowTree(Node* element) 778{ 779 if (element->hasTagName(SVGNames::symbolTag)) { 780 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree, 781 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will 782 // always have explicit values for attributes width and height. If attributes width and/or 783 // height are provided on the 'use' element, then these attributes will be transferred to 784 // the generated 'svg'. If attributes width and/or height are not specified, the generated 785 // 'svg' element will use values of 100% for these attributes. 786 RefPtr<SVGSVGElement> svgElement = SVGSVGElement::create(SVGNames::svgTag, referencedDocument()); 787 788 // Transfer all data (attributes, etc.) from <symbol> to the new <svg> element. 789 svgElement->cloneDataFromElement(*toElement(element)); 790 791 // Only clone symbol children, and add them to the new <svg> element 792 for (Node* child = element->firstChild(); child; child = child->nextSibling()) { 793 RefPtr<Node> newChild = child->cloneNode(true); 794 svgElement->appendChild(newChild.release()); 795 } 796 797 // We don't walk the target tree element-by-element, and clone each element, 798 // but instead use cloneNode(deep=true). This is an optimization for the common 799 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 800 // Though if there are disallowed elements in the subtree, we have to remove them. 801 // For instance: <use> on <g> containing <foreignObject> (indirect case). 802 if (subtreeContainsDisallowedElement(svgElement.get())) 803 removeDisallowedElementsFromSubtree(svgElement.get()); 804 805 RefPtr<Node> replacingElement(svgElement.get()); 806 807 // Replace <symbol> with <svg>. 808 element->parentNode()->replaceChild(svgElement.release(), element); 809 810 // Expand the siblings because the *element* is replaced and we will 811 // lose the sibling chain when we are back from recursion. 812 element = replacingElement.get(); 813 for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling()) 814 expandSymbolElementsInShadowTree(sibling.get()); 815 } 816 817 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) 818 expandSymbolElementsInShadowTree(child.get()); 819} 820 821void SVGUseElement::transferEventListenersToShadowTree(SVGElementInstance* target) 822{ 823 if (!target) 824 return; 825 826 SVGElement* originalElement = target->correspondingElement(); 827 ASSERT(originalElement); 828 829 if (SVGElement* shadowTreeElement = target->shadowTreeElement()) { 830 if (EventTargetData* data = originalElement->eventTargetData()) 831 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(shadowTreeElement); 832 } 833 834 for (SVGElementInstance* instance = target->firstChild(); instance; instance = instance->nextSibling()) 835 transferEventListenersToShadowTree(instance); 836} 837 838void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance) 839{ 840 if (!target || !targetInstance) 841 return; 842 843 SVGElement* originalElement = targetInstance->correspondingElement(); 844 845 if (originalElement->hasTagName(SVGNames::useTag)) { 846 // <use> gets replaced by <g> 847 ASSERT(target->nodeName() == SVGNames::gTag); 848 } else if (originalElement->hasTagName(SVGNames::symbolTag)) { 849 // <symbol> gets replaced by <svg> 850 ASSERT(target->nodeName() == SVGNames::svgTag); 851 } else 852 ASSERT(target->nodeName() == originalElement->nodeName()); 853 854 SVGElement* element = 0; 855 if (target->isSVGElement()) 856 element = toSVGElement(target); 857 858 ASSERT(!targetInstance->shadowTreeElement()); 859 targetInstance->setShadowTreeElement(element); 860 element->setCorrespondingElement(originalElement); 861 862 Node* node = target->firstChild(); 863 for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) { 864 // Skip any non-svg elements in shadow tree 865 while (node && !node->isSVGElement()) 866 node = node->nextSibling(); 867 868 if (!node) 869 break; 870 871 associateInstancesWithShadowTreeElements(node, instance); 872 node = node->nextSibling(); 873 } 874} 875 876SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const 877{ 878 if (!m_targetElementInstance) { 879 ASSERT(!inDocument()); 880 return 0; 881 } 882 883 return instanceForShadowTreeElement(element, m_targetElementInstance.get()); 884} 885 886SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const 887{ 888 ASSERT(element); 889 ASSERT(instance); 890 891 // We're dispatching a mutation event during shadow tree construction 892 // this instance hasn't yet been associated to a shadowTree element. 893 if (!instance->shadowTreeElement()) 894 return 0; 895 896 if (element == instance->shadowTreeElement()) 897 return instance; 898 899 for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) { 900 if (SVGElementInstance* search = instanceForShadowTreeElement(element, current)) 901 return search; 902 } 903 904 return 0; 905} 906 907void SVGUseElement::invalidateShadowTree() 908{ 909 if (!renderer() || m_needsShadowTreeRecreation) 910 return; 911 m_needsShadowTreeRecreation = true; 912 setNeedsStyleRecalc(); 913 invalidateDependentShadowTrees(); 914} 915 916void SVGUseElement::invalidateDependentShadowTrees() 917{ 918 // Recursively invalidate dependent <use> shadow trees 919 const HashSet<SVGElementInstance*>& instances = instancesForElement(); 920 const HashSet<SVGElementInstance*>::const_iterator end = instances.end(); 921 for (HashSet<SVGElementInstance*>::const_iterator it = instances.begin(); it != end; ++it) { 922 if (SVGUseElement* element = (*it)->correspondingUseElement()) { 923 ASSERT(element->inDocument()); 924 element->invalidateShadowTree(); 925 } 926 } 927} 928 929void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const 930{ 931 ASSERT(from); 932 ASSERT(to); 933 934 to->cloneDataFromElement(*from); 935 936 to->removeAttribute(SVGNames::xAttr); 937 to->removeAttribute(SVGNames::yAttr); 938 to->removeAttribute(SVGNames::widthAttr); 939 to->removeAttribute(SVGNames::heightAttr); 940 to->removeAttribute(XLinkNames::hrefAttr); 941} 942 943bool SVGUseElement::selfHasRelativeLengths() const 944{ 945 if (x().isRelative() 946 || y().isRelative() 947 || width().isRelative() 948 || height().isRelative()) 949 return true; 950 951 if (!m_targetElementInstance) 952 return false; 953 954 SVGElement* element = m_targetElementInstance->correspondingElement(); 955 if (!element || !element->isSVGStyledElement()) 956 return false; 957 958 return toSVGStyledElement(element)->hasRelativeLengths(); 959} 960 961void SVGUseElement::notifyFinished(CachedResource* resource) 962{ 963 if (!inDocument()) 964 return; 965 966 invalidateShadowTree(); 967 if (resource->errorOccurred()) 968 dispatchEvent(Event::create(eventNames().errorEvent, false, false)); 969 else if (!resource->wasCanceled()) 970 SVGExternalResourcesRequired::dispatchLoadEvent(this); 971} 972 973bool SVGUseElement::cachedDocumentIsStillLoading() 974{ 975 if (m_cachedDocument && m_cachedDocument->isLoading()) 976 return true; 977 return false; 978} 979 980bool SVGUseElement::instanceTreeIsLoading(SVGElementInstance* targetElementInstance) 981{ 982 for (SVGElementInstance* instance = targetElementInstance->firstChild(); instance; instance = instance->nextSibling()) { 983 if (SVGUseElement* use = instance->correspondingUseElement()) { 984 if (use->cachedDocumentIsStillLoading()) 985 return true; 986 } 987 if (instance->hasChildNodes()) 988 instanceTreeIsLoading(instance); 989 } 990 return false; 991} 992 993void SVGUseElement::finishParsingChildren() 994{ 995 SVGStyledTransformableElement::finishParsingChildren(); 996 SVGExternalResourcesRequired::finishParsingChildren(); 997 if (m_wasInsertedByParser) { 998 buildPendingResource(); 999 m_wasInsertedByParser = false; 1000 } 1001} 1002 1003void SVGUseElement::setCachedDocument(CachedResourceHandle<CachedSVGDocument> cachedDocument) 1004{ 1005 if (m_cachedDocument == cachedDocument) 1006 return; 1007 1008 if (m_cachedDocument) 1009 m_cachedDocument->removeClient(this); 1010 1011 m_cachedDocument = cachedDocument; 1012 if (m_cachedDocument) 1013 m_cachedDocument->addClient(this); 1014} 1015 1016} 1017 1018#endif // ENABLE(SVG) 1019