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