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