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