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#include "core/svg/SVGUseElement.h" 28 29#include "bindings/core/v8/ExceptionStatePlaceholder.h" 30#include "core/XLinkNames.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/SVGGElement.h" 41#include "core/svg/SVGLengthContext.h" 42#include "core/svg/SVGSVGElement.h" 43#include "core/xml/parser/XMLDocumentParser.h" 44 45namespace blink { 46 47inline SVGUseElement::SVGUseElement(Document& document) 48 : SVGGraphicsElement(SVGNames::useTag, document) 49 , SVGURIReference(this) 50 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths)) 51 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths)) 52 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths)) 53 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths)) 54 , m_haveFiredLoadEvent(false) 55 , m_needsShadowTreeRecreation(false) 56 , m_svgLoadEventTimer(this, &SVGElement::svgLoadEventTimerFired) 57{ 58 ASSERT(hasCustomStyleCallbacks()); 59 60 addToPropertyMap(m_x); 61 addToPropertyMap(m_y); 62 addToPropertyMap(m_width); 63 addToPropertyMap(m_height); 64} 65 66PassRefPtrWillBeRawPtr<SVGUseElement> SVGUseElement::create(Document& document) 67{ 68 // Always build a user agent #shadow-root for SVGUseElement. 69 RefPtrWillBeRawPtr<SVGUseElement> use = adoptRefWillBeNoop(new SVGUseElement(document)); 70 use->ensureUserAgentShadowRoot(); 71 return use.release(); 72} 73 74SVGUseElement::~SVGUseElement() 75{ 76 setDocumentResource(0); 77#if !ENABLE(OILPAN) 78 clearResourceReferences(); 79#endif 80} 81 82bool SVGUseElement::isSupportedAttribute(const QualifiedName& attrName) 83{ 84 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ()); 85 if (supportedAttributes.isEmpty()) { 86 SVGURIReference::addSupportedAttributes(supportedAttributes); 87 supportedAttributes.add(SVGNames::xAttr); 88 supportedAttributes.add(SVGNames::yAttr); 89 supportedAttributes.add(SVGNames::widthAttr); 90 supportedAttributes.add(SVGNames::heightAttr); 91 } 92 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName); 93} 94 95void SVGUseElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 96{ 97 parseAttributeNew(name, value); 98} 99 100#if ENABLE(ASSERT) 101static inline bool isWellFormedDocument(Document* document) 102{ 103 if (document->isXMLDocument()) 104 return static_cast<XMLDocumentParser*>(document->parser())->wellFormed(); 105 return true; 106} 107#endif 108 109Node::InsertionNotificationRequest SVGUseElement::insertedInto(ContainerNode* rootParent) 110{ 111 // This functions exists to assure assumptions made in the code regarding SVGElementInstance creation/destruction are satisfied. 112 SVGGraphicsElement::insertedInto(rootParent); 113 if (!rootParent->inDocument()) 114 return InsertionDone; 115 ASSERT(!m_targetElementInstance || !isWellFormedDocument(&document())); 116 ASSERT(!hasPendingResources() || !isWellFormedDocument(&document())); 117 invalidateShadowTree(); 118 if (!isStructurallyExternal()) 119 sendSVGLoadEventIfPossibleAsynchronously(); 120 return InsertionDone; 121} 122 123void SVGUseElement::removedFrom(ContainerNode* rootParent) 124{ 125 SVGGraphicsElement::removedFrom(rootParent); 126 if (rootParent->inDocument()) 127 clearResourceReferences(); 128} 129 130TreeScope* SVGUseElement::referencedScope() const 131{ 132 if (!isExternalURIReference(hrefString(), document())) 133 return &treeScope(); 134 return externalDocument(); 135} 136 137Document* SVGUseElement::externalDocument() const 138{ 139 if (m_resource && m_resource->isLoaded()) { 140 // Gracefully handle error condition. 141 if (m_resource->errorOccurred()) 142 return 0; 143 ASSERT(m_resource->document()); 144 return m_resource->document(); 145 } 146 return 0; 147} 148 149void transferUseWidthAndHeightIfNeeded(const SVGUseElement& use, SVGElement* shadowElement, const SVGElement& originalElement) 150{ 151 DEFINE_STATIC_LOCAL(const AtomicString, hundredPercentString, ("100%", AtomicString::ConstructFromLiteral)); 152 ASSERT(shadowElement); 153 if (isSVGSymbolElement(*shadowElement)) { 154 // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height. 155 // If attributes width and/or height are provided on the 'use' element, then these attributes 156 // will be transferred to the generated 'svg'. If attributes width and/or height are not specified, 157 // the generated 'svg' element will use values of 100% for these attributes. 158 shadowElement->setAttribute(SVGNames::widthAttr, use.width()->isSpecified() ? AtomicString(use.width()->currentValue()->valueAsString()) : hundredPercentString); 159 shadowElement->setAttribute(SVGNames::heightAttr, use.height()->isSpecified() ? AtomicString(use.height()->currentValue()->valueAsString()) : hundredPercentString); 160 } else if (isSVGSVGElement(*shadowElement)) { 161 // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these 162 // values will override the corresponding attributes on the 'svg' in the generated tree. 163 if (use.width()->isSpecified()) 164 shadowElement->setAttribute(SVGNames::widthAttr, AtomicString(use.width()->currentValue()->valueAsString())); 165 else 166 shadowElement->setAttribute(SVGNames::widthAttr, originalElement.getAttribute(SVGNames::widthAttr)); 167 if (use.height()->isSpecified()) 168 shadowElement->setAttribute(SVGNames::heightAttr, AtomicString(use.height()->currentValue()->valueAsString())); 169 else 170 shadowElement->setAttribute(SVGNames::heightAttr, originalElement.getAttribute(SVGNames::heightAttr)); 171 } 172} 173 174void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) 175{ 176 if (!isSupportedAttribute(attrName)) { 177 SVGGraphicsElement::svgAttributeChanged(attrName); 178 return; 179 } 180 181 SVGElement::InvalidationGuard invalidationGuard(this); 182 183 RenderObject* renderer = this->renderer(); 184 if (attrName == SVGNames::xAttr 185 || attrName == SVGNames::yAttr 186 || attrName == SVGNames::widthAttr 187 || attrName == SVGNames::heightAttr) { 188 updateRelativeLengthsInformation(); 189 if (m_targetElementInstance) { 190 ASSERT(m_targetElementInstance->correspondingElement()); 191 transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement()); 192 } 193 if (renderer) 194 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 195 return; 196 } 197 198 if (SVGURIReference::isKnownAttribute(attrName)) { 199 bool isExternalReference = isExternalURIReference(hrefString(), document()); 200 if (isExternalReference) { 201 KURL url = document().completeURL(hrefString()); 202 if (url.hasFragmentIdentifier()) { 203 FetchRequest request(ResourceRequest(url), localName()); 204 setDocumentResource(document().fetcher()->fetchSVGDocument(request)); 205 } 206 } else { 207 setDocumentResource(0); 208 } 209 210 invalidateShadowTree(); 211 212 return; 213 } 214 215 if (!renderer) 216 return; 217 218 ASSERT_NOT_REACHED(); 219} 220 221static bool isDisallowedElement(Node* node) 222{ 223 // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used 224 // (i.e., "instanced") in the SVG document via a 'use' element." 225 // "Graphics Element" is defined as 'circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text' 226 // Excluded are anything that is used by reference or that only make sense to appear once in a document. 227 // We must also allow the shadow roots of other use elements. 228 if (node->isShadowRoot() || node->isTextNode()) 229 return false; 230 231 if (!node->isSVGElement()) 232 return true; 233 234 Element* element = toElement(node); 235 236 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, allowedElementTags, ()); 237 if (allowedElementTags.isEmpty()) { 238 allowedElementTags.add(SVGNames::aTag); 239 allowedElementTags.add(SVGNames::circleTag); 240 allowedElementTags.add(SVGNames::descTag); 241 allowedElementTags.add(SVGNames::ellipseTag); 242 allowedElementTags.add(SVGNames::gTag); 243 allowedElementTags.add(SVGNames::imageTag); 244 allowedElementTags.add(SVGNames::lineTag); 245 allowedElementTags.add(SVGNames::metadataTag); 246 allowedElementTags.add(SVGNames::pathTag); 247 allowedElementTags.add(SVGNames::polygonTag); 248 allowedElementTags.add(SVGNames::polylineTag); 249 allowedElementTags.add(SVGNames::rectTag); 250 allowedElementTags.add(SVGNames::svgTag); 251 allowedElementTags.add(SVGNames::switchTag); 252 allowedElementTags.add(SVGNames::symbolTag); 253 allowedElementTags.add(SVGNames::textTag); 254 allowedElementTags.add(SVGNames::textPathTag); 255 allowedElementTags.add(SVGNames::titleTag); 256 allowedElementTags.add(SVGNames::tspanTag); 257 allowedElementTags.add(SVGNames::useTag); 258 } 259 return !allowedElementTags.contains<SVGAttributeHashTranslator>(element->tagQName()); 260} 261 262static bool subtreeContainsDisallowedElement(Node* start) 263{ 264 if (isDisallowedElement(start)) 265 return true; 266 267 for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) { 268 if (subtreeContainsDisallowedElement(cur)) 269 return true; 270 } 271 272 return false; 273} 274 275void SVGUseElement::scheduleShadowTreeRecreation() 276{ 277 if (!referencedScope() || inUseShadowTree()) 278 return; 279 m_needsShadowTreeRecreation = true; 280 document().scheduleUseShadowTreeUpdate(*this); 281} 282 283void SVGUseElement::clearResourceReferences() 284{ 285 if (m_targetElementInstance) 286 m_targetElementInstance = nullptr; 287 288 // FIXME: We should try to optimize this, to at least allow partial reclones. 289 if (ShadowRoot* shadowTreeRootElement = userAgentShadowRoot()) 290 shadowTreeRootElement->removeChildren(); 291 292 m_needsShadowTreeRecreation = false; 293 document().unscheduleUseShadowTreeUpdate(*this); 294 295 removeAllOutgoingReferences(); 296} 297 298void SVGUseElement::buildPendingResource() 299{ 300 if (!referencedScope() || inUseShadowTree()) 301 return; 302 clearResourceReferences(); 303 if (!inDocument()) 304 return; 305 306 AtomicString id; 307 Element* target = SVGURIReference::targetElementFromIRIString(hrefString(), treeScope(), &id, externalDocument()); 308 if (!target || !target->inDocument()) { 309 // If we can't find the target of an external element, just give up. 310 // We can't observe if the target somewhen enters the external document, nor should we do it. 311 if (externalDocument()) 312 return; 313 if (id.isEmpty()) 314 return; 315 316 referencedScope()->document().accessSVGExtensions().addPendingResource(id, this); 317 ASSERT(hasPendingResources()); 318 return; 319 } 320 321 if (target->isSVGElement()) { 322 buildShadowAndInstanceTree(toSVGElement(target)); 323 invalidateDependentShadowTrees(); 324 } 325 326 ASSERT(!m_needsShadowTreeRecreation); 327} 328 329static PassRefPtrWillBeRawPtr<Node> cloneNodeAndAssociate(Node& toClone) 330{ 331 RefPtrWillBeRawPtr<Node> clone = toClone.cloneNode(false); 332 if (!clone->isSVGElement()) 333 return clone.release(); 334 335 SVGElement& svgElement = toSVGElement(toClone); 336 ASSERT(!svgElement.correspondingElement()); 337 toSVGElement(clone.get())->setCorrespondingElement(&svgElement); 338 if (EventTargetData* data = toClone.eventTargetData()) 339 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(clone.get()); 340 TrackExceptionState exceptionState; 341 for (Node* node = toClone.firstChild(); node && !exceptionState.hadException(); node = node->nextSibling()) 342 clone->appendChild(cloneNodeAndAssociate(*node), exceptionState); 343 return clone.release(); 344} 345 346void SVGUseElement::buildShadowAndInstanceTree(SVGElement* target) 347{ 348 ASSERT(!m_targetElementInstance); 349 350 // <use> creates a "user agent" shadow root. Do not build the shadow/instance tree for <use> 351 // elements living in a user agent shadow tree because they will get expanded in a second 352 // pass -- see expandUseElementsInShadowTree(). 353 if (inUseShadowTree()) 354 return; 355 356 // Do not allow self-referencing. 357 // 'target' may be null, if it's a non SVG namespaced element. 358 if (!target || target == this) 359 return; 360 361 // Set up root SVG element in shadow tree. 362 RefPtrWillBeRawPtr<Element> newChild = target->cloneElementWithoutChildren(); 363 m_targetElementInstance = toSVGElement(newChild.get()); 364 ShadowRoot* shadowTreeRootElement = userAgentShadowRoot(); 365 shadowTreeRootElement->appendChild(newChild.release()); 366 367 // Clone the target subtree into the shadow tree, not handling <use> and <symbol> yet. 368 369 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it! 370 // Non-appearing <use> content is easier to debug, then half-appearing content. 371 if (!buildShadowTree(target, m_targetElementInstance.get(), false)) { 372 clearResourceReferences(); 373 return; 374 } 375 376 if (instanceTreeIsLoading(m_targetElementInstance.get())) 377 return; 378 379 // Assure shadow tree building was successfull 380 ASSERT(m_targetElementInstance); 381 ASSERT(m_targetElementInstance->correspondingUseElement() == this); 382 ASSERT(m_targetElementInstance->correspondingElement() == target); 383 384 // Expand all <use> elements in the shadow tree. 385 // Expand means: replace the actual <use> element by what it references. 386 if (!expandUseElementsInShadowTree(m_targetElementInstance.get())) { 387 clearResourceReferences(); 388 return; 389 } 390 391 // Expand all <symbol> elements in the shadow tree. 392 // Expand means: replace the actual <symbol> element by the <svg> element. 393 expandSymbolElementsInShadowTree(toSVGElement(shadowTreeRootElement->firstChild())); 394 395 m_targetElementInstance = toSVGElement(shadowTreeRootElement->firstChild()); 396 transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement()); 397 398 ASSERT(m_targetElementInstance->parentNode() == shadowTreeRootElement); 399 400 // Update relative length information. 401 updateRelativeLengthsInformation(); 402} 403 404RenderObject* SVGUseElement::createRenderer(RenderStyle*) 405{ 406 return new RenderSVGTransformableContainer(this); 407} 408 409static bool isDirectReference(const SVGElement& element) 410{ 411 return isSVGPathElement(element) 412 || isSVGRectElement(element) 413 || isSVGCircleElement(element) 414 || isSVGEllipseElement(element) 415 || isSVGPolygonElement(element) 416 || isSVGPolylineElement(element) 417 || isSVGTextElement(element); 418} 419 420void SVGUseElement::toClipPath(Path& path) 421{ 422 ASSERT(path.isEmpty()); 423 424 Node* n = userAgentShadowRoot()->firstChild(); 425 if (!n || !n->isSVGElement()) 426 return; 427 SVGElement& element = toSVGElement(*n); 428 429 if (element.isSVGGraphicsElement()) { 430 if (!isDirectReference(element)) { 431 // Spec: Indirect references are an error (14.3.5) 432 document().accessSVGExtensions().reportError("Not allowed to use indirect reference in <clip-path>"); 433 } else { 434 toSVGGraphicsElement(element).toClipPath(path); 435 // FIXME: Avoid manual resolution of x/y here. Its potentially harmful. 436 SVGLengthContext lengthContext(this); 437 path.translate(FloatSize(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext))); 438 path.transform(animatedLocalTransform()); 439 } 440 } 441} 442 443RenderObject* SVGUseElement::rendererClipChild() const 444{ 445 if (Node* n = userAgentShadowRoot()->firstChild()) { 446 if (n->isSVGElement() && isDirectReference(toSVGElement(*n))) 447 return n->renderer(); 448 } 449 450 return 0; 451} 452 453bool SVGUseElement::buildShadowTree(SVGElement* target, SVGElement* targetInstance, bool foundUse) 454{ 455 ASSERT(target); 456 ASSERT(targetInstance); 457 458 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced 459 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. 460 if (isSVGUseElement(*target)) { 461 // We only need to track first degree <use> dependencies. Indirect references are handled 462 // as the invalidation bubbles up the dependency chain. 463 if (!foundUse && !isStructurallyExternal()) { 464 addReferenceTo(target); 465 foundUse = true; 466 } 467 } else if (isDisallowedElement(target)) { 468 return false; 469 } 470 471 targetInstance->setCorrespondingElement(target); 472 if (EventTargetData* data = target->eventTargetData()) 473 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(targetInstance); 474 475 for (Node* child = target->firstChild(); child; child = child->nextSibling()) { 476 // Skip any disallowed element. 477 if (isDisallowedElement(child)) 478 continue; 479 480 RefPtrWillBeRawPtr<Node> newChild = child->cloneNode(false); 481 targetInstance->appendChild(newChild.get()); 482 if (newChild->isSVGElement()) { 483 // Enter recursion, appending new instance tree nodes to the "instance" object. 484 if (!buildShadowTree(toSVGElement(child), toSVGElement(newChild), foundUse)) 485 return false; 486 } 487 } 488 return true; 489} 490 491bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, ContainerNode* targetInstance, SVGElement*& newTarget) 492{ 493 ASSERT(referencedScope()); 494 Element* targetElement = SVGURIReference::targetElementFromIRIString(use->hrefString(), *referencedScope()); 495 newTarget = 0; 496 if (targetElement && targetElement->isSVGElement()) 497 newTarget = toSVGElement(targetElement); 498 499 if (!newTarget) 500 return false; 501 502 // Shortcut for self-references 503 if (newTarget == this) 504 return true; 505 506 AtomicString targetId = newTarget->getIdAttribute(); 507 ContainerNode* instance = targetInstance->parentNode(); 508 while (instance && instance->isSVGElement()) { 509 SVGElement* element = toSVGElement(instance); 510 if (element->hasID() && element->getIdAttribute() == targetId && element->document() == newTarget->document()) 511 return true; 512 513 instance = instance->parentNode(); 514 } 515 return false; 516} 517 518static inline void removeDisallowedElementsFromSubtree(Element& subtree) 519{ 520 ASSERT(!subtree.inDocument()); 521 Element* element = ElementTraversal::firstWithin(subtree); 522 while (element) { 523 if (isDisallowedElement(element)) { 524 Element* next = ElementTraversal::nextSkippingChildren(*element, &subtree); 525 // The subtree is not in document so this won't generate events that could mutate the tree. 526 element->parentNode()->removeChild(element); 527 element = next; 528 } else { 529 element = ElementTraversal::next(*element, &subtree); 530 } 531 } 532} 533 534bool SVGUseElement::expandUseElementsInShadowTree(SVGElement* element) 535{ 536 ASSERT(element); 537 // Why expand the <use> elements in the shadow tree here, and not just 538 // do this directly in buildShadowTree, if we encounter a <use> element? 539 // 540 // Short answer: Because we may miss to expand some elements. For example, if a <symbol> 541 // contains <use> tags, we'd miss them. So once we're done with setting up the 542 // actual shadow tree (after the special case modification for svg/symbol) we have 543 // to walk it completely and expand all <use> elements. 544 if (isSVGUseElement(*element)) { 545 SVGUseElement* use = toSVGUseElement(element); 546 ASSERT(!use->resourceIsStillLoading()); 547 548 SVGElement* target = 0; 549 if (hasCycleUseReferencing(toSVGUseElement(use->correspondingElement()), use, target)) 550 return false; 551 552 if (target && isDisallowedElement(target)) 553 return false; 554 // Don't ASSERT(target) here, it may be "pending", too. 555 // Setup sub-shadow tree root node 556 RefPtrWillBeRawPtr<SVGGElement> cloneParent = SVGGElement::create(referencedScope()->document()); 557 cloneParent->setCorrespondingElement(use->correspondingElement()); 558 559 // Move already cloned elements to the new <g> element 560 for (Node* child = use->firstChild(); child; ) { 561 Node* nextChild = child->nextSibling(); 562 cloneParent->appendChild(child); 563 child = nextChild; 564 } 565 566 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the 567 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element. 568 transferUseAttributesToReplacedElement(use, cloneParent.get()); 569 570 if (target) { 571 RefPtrWillBeRawPtr<Node> newChild = cloneNodeAndAssociate(*target); 572 ASSERT(newChild->isSVGElement()); 573 transferUseWidthAndHeightIfNeeded(*use, toSVGElement(newChild.get()), *target); 574 cloneParent->appendChild(newChild.release()); 575 } 576 577 // We don't walk the target tree element-by-element, and clone each element, 578 // but instead use cloneElementWithChildren(). This is an optimization for the common 579 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 580 // Though if there are disallowed elements in the subtree, we have to remove them. 581 // For instance: <use> on <g> containing <foreignObject> (indirect case). 582 if (subtreeContainsDisallowedElement(cloneParent.get())) 583 removeDisallowedElementsFromSubtree(*cloneParent); 584 585 RefPtrWillBeRawPtr<SVGElement> replacingElement(cloneParent.get()); 586 587 // Replace <use> with referenced content. 588 ASSERT(use->parentNode()); 589 use->parentNode()->replaceChild(cloneParent.release(), use); 590 591 // Expand the siblings because the *element* is replaced and we will 592 // lose the sibling chain when we are back from recursion. 593 element = replacingElement.get(); 594 for (RefPtrWillBeRawPtr<SVGElement> sibling = Traversal<SVGElement>::nextSibling(*element); sibling; sibling = Traversal<SVGElement>::nextSibling(*sibling)) { 595 if (!expandUseElementsInShadowTree(sibling.get())) 596 return false; 597 } 598 } 599 600 for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child)) { 601 if (!expandUseElementsInShadowTree(child.get())) 602 return false; 603 } 604 return true; 605} 606 607void SVGUseElement::expandSymbolElementsInShadowTree(SVGElement* element) 608{ 609 ASSERT(element); 610 if (isSVGSymbolElement(*element)) { 611 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree, 612 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will 613 // always have explicit values for attributes width and height. If attributes width and/or 614 // height are provided on the 'use' element, then these attributes will be transferred to 615 // the generated 'svg'. If attributes width and/or height are not specified, the generated 616 // 'svg' element will use values of 100% for these attributes. 617 ASSERT(referencedScope()); 618 RefPtrWillBeRawPtr<SVGSVGElement> svgElement = SVGSVGElement::create(referencedScope()->document()); 619 // Transfer all data (attributes, etc.) from <symbol> to the new <svg> element. 620 svgElement->cloneDataFromElement(*element); 621 svgElement->setCorrespondingElement(element->correspondingElement()); 622 623 // Move already cloned elements to the new <svg> element 624 for (Node* child = element->firstChild(); child; ) { 625 Node* nextChild = child->nextSibling(); 626 svgElement->appendChild(child); 627 child = nextChild; 628 } 629 630 // We don't walk the target tree element-by-element, and clone each element, 631 // but instead use cloneNode(deep=true). This is an optimization for the common 632 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 633 // Though if there are disallowed elements in the subtree, we have to remove them. 634 // For instance: <use> on <g> containing <foreignObject> (indirect case). 635 if (subtreeContainsDisallowedElement(svgElement.get())) 636 removeDisallowedElementsFromSubtree(*svgElement); 637 638 RefPtrWillBeRawPtr<SVGElement> replacingElement(svgElement.get()); 639 640 // Replace <symbol> with <svg>. 641 ASSERT(element->parentNode()); 642 element->parentNode()->replaceChild(svgElement.release(), element); 643 644 // Expand the siblings because the *element* is replaced and we will 645 // lose the sibling chain when we are back from recursion. 646 element = replacingElement.get(); 647 } 648 649 for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child)) 650 expandSymbolElementsInShadowTree(child.get()); 651} 652 653void SVGUseElement::invalidateShadowTree() 654{ 655 if (!inActiveDocument() || m_needsShadowTreeRecreation) 656 return; 657 scheduleShadowTreeRecreation(); 658 invalidateDependentShadowTrees(); 659} 660 661void SVGUseElement::invalidateDependentShadowTrees() 662{ 663 // Recursively invalidate dependent <use> shadow trees 664 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >& instances = instancesForElement(); 665 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator end = instances.end(); 666 for (WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement> >::const_iterator it = instances.begin(); it != end; ++it) { 667 if (SVGUseElement* element = (*it)->correspondingUseElement()) { 668 ASSERT(element->inDocument()); 669 element->invalidateShadowTree(); 670 } 671 } 672} 673 674void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const 675{ 676 ASSERT(from); 677 ASSERT(to); 678 679 to->cloneDataFromElement(*from); 680 681 to->removeAttribute(SVGNames::xAttr); 682 to->removeAttribute(SVGNames::yAttr); 683 to->removeAttribute(SVGNames::widthAttr); 684 to->removeAttribute(SVGNames::heightAttr); 685 to->removeAttribute(XLinkNames::hrefAttr); 686} 687 688bool SVGUseElement::selfHasRelativeLengths() const 689{ 690 if (m_x->currentValue()->isRelative() 691 || m_y->currentValue()->isRelative() 692 || m_width->currentValue()->isRelative() 693 || m_height->currentValue()->isRelative()) 694 return true; 695 696 if (!m_targetElementInstance) 697 return false; 698 699 return m_targetElementInstance->hasRelativeLengths(); 700} 701 702void SVGUseElement::notifyFinished(Resource* resource) 703{ 704 if (!inDocument()) 705 return; 706 707 invalidateShadowTree(); 708 if (resource->errorOccurred()) 709 dispatchEvent(Event::create(EventTypeNames::error)); 710 else if (!resource->wasCanceled()) { 711 if (m_haveFiredLoadEvent) 712 return; 713 if (!isStructurallyExternal()) 714 return; 715 ASSERT(!m_haveFiredLoadEvent); 716 m_haveFiredLoadEvent = true; 717 sendSVGLoadEventIfPossibleAsynchronously(); 718 } 719} 720 721bool SVGUseElement::resourceIsStillLoading() 722{ 723 if (m_resource && m_resource->isLoading()) 724 return true; 725 return false; 726} 727 728bool SVGUseElement::instanceTreeIsLoading(SVGElement* targetInstance) 729{ 730 for (SVGElement* element = Traversal<SVGElement>::firstChild(*targetInstance); element; element = Traversal<SVGElement>::nextSibling(*element)) { 731 if (SVGUseElement* use = element->correspondingUseElement()) { 732 if (use->resourceIsStillLoading()) 733 return true; 734 } 735 if (element->hasChildren() && instanceTreeIsLoading(element)) 736 return true; 737 } 738 return false; 739} 740 741void SVGUseElement::setDocumentResource(ResourcePtr<DocumentResource> resource) 742{ 743 if (m_resource == resource) 744 return; 745 746 if (m_resource) 747 m_resource->removeClient(this); 748 749 m_resource = resource; 750 if (m_resource) 751 m_resource->addClient(this); 752} 753 754void SVGUseElement::trace(Visitor* visitor) 755{ 756 visitor->trace(m_targetElementInstance); 757 SVGGraphicsElement::trace(visitor); 758} 759 760} 761