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 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 */ 22 23 #include "config.h" 24 25 #if ENABLE(SVG) 26 #include "SVGUseElement.h" 27 28 #include "Attribute.h" 29 #include "CSSStyleSelector.h" 30 #include "Document.h" 31 #include "Event.h" 32 #include "EventListener.h" 33 #include "HTMLNames.h" 34 #include "NodeRenderStyle.h" 35 #include "RegisteredEventListener.h" 36 #include "RenderSVGResource.h" 37 #include "RenderSVGShadowTreeRootContainer.h" 38 #include "SVGElementInstance.h" 39 #include "SVGElementInstanceList.h" 40 #include "SVGGElement.h" 41 #include "SVGNames.h" 42 #include "SVGSMILElement.h" 43 #include "SVGSVGElement.h" 44 #include "SVGShadowTreeElements.h" 45 #include "SVGSymbolElement.h" 46 #include "XLinkNames.h" 47 #include "XMLDocumentParser.h" 48 #include "XMLSerializer.h" 49 50 #include <wtf/text/StringConcatenate.h> 51 52 // Dump SVGElementInstance object tree - useful to debug instanceRoot problems 53 // #define DUMP_INSTANCE_TREE 54 55 // Dump the deep-expanded shadow tree (where the renderers are built from) 56 // #define DUMP_SHADOW_TREE 57 58 namespace WebCore { 59 60 // Animated property definitions 61 DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::xAttr, X, x) 62 DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::yAttr, Y, y) 63 DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::widthAttr, Width, width) 64 DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::heightAttr, Height, height) 65 DEFINE_ANIMATED_STRING(SVGUseElement, XLinkNames::hrefAttr, Href, href) 66 DEFINE_ANIMATED_BOOLEAN(SVGUseElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 67 68 inline SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* document) 69 : SVGStyledTransformableElement(tagName, document) 70 , m_x(LengthModeWidth) 71 , m_y(LengthModeHeight) 72 , m_width(LengthModeWidth) 73 , m_height(LengthModeHeight) 74 , m_updatesBlocked(false) 75 , m_isPendingResource(false) 76 , m_needsShadowTreeRecreation(false) 77 { 78 } 79 80 PassRefPtr<SVGUseElement> SVGUseElement::create(const QualifiedName& tagName, Document* document) 81 { 82 return adoptRef(new SVGUseElement(tagName, document)); 83 } 84 85 SVGElementInstance* SVGUseElement::instanceRoot() const 86 { 87 // If there is no element instance tree, force immediate SVGElementInstance tree 88 // creation by asking the document to invoke our recalcStyle function - as we can't 89 // wait for the lazy creation to happen if e.g. JS wants to access the instanceRoot 90 // object right after creating the element on-the-fly 91 if (!m_targetElementInstance) 92 document()->updateLayoutIgnorePendingStylesheets(); 93 94 return m_targetElementInstance.get(); 95 } 96 97 SVGElementInstance* SVGUseElement::animatedInstanceRoot() const 98 { 99 // FIXME: Implement me. 100 return 0; 101 } 102 103 void SVGUseElement::parseMappedAttribute(Attribute* attr) 104 { 105 if (attr->name() == SVGNames::xAttr) 106 setXBaseValue(SVGLength(LengthModeWidth, attr->value())); 107 else if (attr->name() == SVGNames::yAttr) 108 setYBaseValue(SVGLength(LengthModeHeight, attr->value())); 109 else if (attr->name() == SVGNames::widthAttr) { 110 setWidthBaseValue(SVGLength(LengthModeWidth, attr->value())); 111 if (widthBaseValue().value(this) < 0.0) 112 document()->accessSVGExtensions()->reportError("A negative value for use attribute <width> is not allowed"); 113 } else if (attr->name() == SVGNames::heightAttr) { 114 setHeightBaseValue(SVGLength(LengthModeHeight, attr->value())); 115 if (heightBaseValue().value(this) < 0.0) 116 document()->accessSVGExtensions()->reportError("A negative value for use attribute <height> is not allowed"); 117 } else { 118 if (SVGTests::parseMappedAttribute(attr)) 119 return; 120 if (SVGLangSpace::parseMappedAttribute(attr)) 121 return; 122 if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) 123 return; 124 if (SVGURIReference::parseMappedAttribute(attr)) 125 return; 126 SVGStyledTransformableElement::parseMappedAttribute(attr); 127 } 128 } 129 130 void SVGUseElement::insertedIntoDocument() 131 { 132 // This functions exists to assure assumptions made in the code regarding SVGElementInstance creation/destruction are satisfied. 133 SVGStyledTransformableElement::insertedIntoDocument(); 134 ASSERT(!m_targetElementInstance || ((document()->isSVGDocument() || document()->isXHTMLDocument()) && !static_cast<XMLDocumentParser*>(document()->parser())->wellFormed())); 135 ASSERT(!m_isPendingResource); 136 } 137 138 void SVGUseElement::removedFromDocument() 139 { 140 SVGStyledTransformableElement::removedFromDocument(); 141 detachInstance(); 142 } 143 144 void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) 145 { 146 SVGStyledTransformableElement::svgAttributeChanged(attrName); 147 148 bool isXYAttribute = attrName == SVGNames::xAttr || attrName == SVGNames::yAttr; 149 bool isWidthHeightAttribute = attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr; 150 151 if (isXYAttribute || isWidthHeightAttribute) 152 updateRelativeLengthsInformation(); 153 154 if (SVGTests::handleAttributeChange(this, attrName)) 155 return; 156 157 RenderObject* object = renderer(); 158 if (!object) 159 return; 160 161 if (SVGURIReference::isKnownAttribute(attrName)) { 162 if (m_isPendingResource) { 163 document()->accessSVGExtensions()->removePendingResource(m_resourceId); 164 m_resourceId = String(); 165 m_isPendingResource = false; 166 } 167 168 invalidateShadowTree(); 169 return; 170 } 171 172 if (isXYAttribute) { 173 updateContainerOffsets(); 174 return; 175 } 176 177 if (isWidthHeightAttribute) { 178 updateContainerSizes(); 179 return; 180 } 181 182 // Be very careful here, if svgAttributeChanged() has been called because a SVG CSS property changed, we do NOT want to reclone the tree! 183 if (SVGStyledElement::isKnownAttribute(attrName)) { 184 setNeedsStyleRecalc(); 185 return; 186 } 187 188 if (SVGLangSpace::isKnownAttribute(attrName) 189 || SVGExternalResourcesRequired::isKnownAttribute(attrName)) 190 invalidateShadowTree(); 191 } 192 193 void SVGUseElement::synchronizeProperty(const QualifiedName& attrName) 194 { 195 SVGStyledTransformableElement::synchronizeProperty(attrName); 196 197 if (attrName == anyQName()) { 198 synchronizeX(); 199 synchronizeY(); 200 synchronizeWidth(); 201 synchronizeHeight(); 202 synchronizeExternalResourcesRequired(); 203 synchronizeHref(); 204 SVGTests::synchronizeProperties(this, attrName); 205 return; 206 } 207 208 if (attrName == SVGNames::xAttr) 209 synchronizeX(); 210 else if (attrName == SVGNames::yAttr) 211 synchronizeY(); 212 else if (attrName == SVGNames::widthAttr) 213 synchronizeWidth(); 214 else if (attrName == SVGNames::heightAttr) 215 synchronizeHeight(); 216 else if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) 217 synchronizeExternalResourcesRequired(); 218 else if (SVGURIReference::isKnownAttribute(attrName)) 219 synchronizeHref(); 220 else if (SVGTests::isKnownAttribute(attrName)) 221 SVGTests::synchronizeProperties(this, attrName); 222 } 223 224 AttributeToPropertyTypeMap& SVGUseElement::attributeToPropertyTypeMap() 225 { 226 DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ()); 227 return s_attributeToPropertyTypeMap; 228 } 229 230 void SVGUseElement::fillAttributeToPropertyTypeMap() 231 { 232 AttributeToPropertyTypeMap& attributeToPropertyTypeMap = this->attributeToPropertyTypeMap(); 233 234 SVGStyledTransformableElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap); 235 attributeToPropertyTypeMap.set(SVGNames::xAttr, AnimatedLength); 236 attributeToPropertyTypeMap.set(SVGNames::yAttr, AnimatedLength); 237 attributeToPropertyTypeMap.set(SVGNames::widthAttr, AnimatedLength); 238 attributeToPropertyTypeMap.set(SVGNames::heightAttr, AnimatedLength); 239 attributeToPropertyTypeMap.set(XLinkNames::hrefAttr, AnimatedString); 240 } 241 242 static void updateContainerSize(SVGElementInstance* targetInstance) 243 { 244 // Depth-first used to write the method in early exit style, no particular other reason. 245 for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) 246 updateContainerSize(instance); 247 248 SVGUseElement* useElement = targetInstance->directUseElement(); 249 if (!useElement) 250 return; 251 252 SVGElement* correspondingElement = targetInstance->correspondingElement(); 253 ASSERT(correspondingElement); 254 255 bool isSymbolTag = correspondingElement->hasTagName(SVGNames::symbolTag); 256 if (!correspondingElement->hasTagName(SVGNames::svgTag) && !isSymbolTag) 257 return; 258 259 SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); 260 ASSERT(shadowTreeElement); 261 ASSERT(shadowTreeElement->hasTagName(SVGNames::svgTag)); 262 263 // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height. 264 // If attributes width and/or height are provided on the 'use' element, then these attributes 265 // will be transferred to the generated 'svg'. If attributes width and/or height are not specified, 266 // the generated 'svg' element will use values of 100% for these attributes. 267 268 // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these 269 // values will override the corresponding attributes on the 'svg' in the generated tree. 270 271 if (useElement->hasAttribute(SVGNames::widthAttr)) 272 shadowTreeElement->setAttribute(SVGNames::widthAttr, useElement->getAttribute(SVGNames::widthAttr)); 273 else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::widthAttr)) 274 shadowTreeElement->setAttribute(SVGNames::widthAttr, "100%"); 275 276 if (useElement->hasAttribute(SVGNames::heightAttr)) 277 shadowTreeElement->setAttribute(SVGNames::heightAttr, useElement->getAttribute(SVGNames::heightAttr)); 278 else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::heightAttr)) 279 shadowTreeElement->setAttribute(SVGNames::heightAttr, "100%"); 280 } 281 282 void SVGUseElement::updateContainerSizes() 283 { 284 if (!m_targetElementInstance) 285 return; 286 287 // Update whole subtree, scanning for shadow container elements, that correspond to <svg>/<symbol> tags 288 ASSERT(m_targetElementInstance->directUseElement() == this); 289 updateContainerSize(m_targetElementInstance.get()); 290 291 if (RenderObject* object = renderer()) 292 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object); 293 } 294 295 static void updateContainerOffset(SVGElementInstance* targetInstance) 296 { 297 // Depth-first used to write the method in early exit style, no particular other reason. 298 for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) 299 updateContainerOffset(instance); 300 301 SVGElement* correspondingElement = targetInstance->correspondingElement(); 302 ASSERT(correspondingElement); 303 304 if (!correspondingElement->hasTagName(SVGNames::useTag)) 305 return; 306 307 SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); 308 ASSERT(shadowTreeElement); 309 ASSERT(shadowTreeElement->hasTagName(SVGNames::gTag)); 310 311 if (!static_cast<SVGGElement*>(shadowTreeElement)->isShadowTreeContainerElement()) 312 return; 313 314 // Spec: An additional transformation translate(x,y) is appended to the end 315 // (i.e., right-side) of the transform attribute on the generated 'g', where x 316 // and y represent the values of the x and y attributes on the 'use' element. 317 SVGUseElement* useElement = static_cast<SVGUseElement*>(correspondingElement); 318 SVGShadowTreeContainerElement* containerElement = static_cast<SVGShadowTreeContainerElement*>(shadowTreeElement); 319 containerElement->setContainerOffset(useElement->x(), useElement->y()); 320 } 321 322 void SVGUseElement::updateContainerOffsets() 323 { 324 if (!m_targetElementInstance) 325 return; 326 327 // Update root container offset (not reachable through instance tree) 328 SVGElement* shadowRoot = m_targetElementInstance->shadowTreeElement(); 329 ASSERT(shadowRoot); 330 331 ContainerNode* parentNode = shadowRoot->parentNode(); 332 ASSERT(parentNode); 333 ASSERT(parentNode->isSVGElement()); 334 ASSERT(parentNode->hasTagName(SVGNames::gTag)); 335 ASSERT(static_cast<SVGGElement*>(parentNode)->isShadowTreeContainerElement()); 336 337 SVGShadowTreeContainerElement* containerElement = static_cast<SVGShadowTreeContainerElement*>(parentNode); 338 containerElement->setContainerOffset(x(), y()); 339 340 // Update whole subtree, scanning for shadow container elements, marking a cloned use subtree 341 updateContainerOffset(m_targetElementInstance.get()); 342 343 if (RenderObject* object = renderer()) 344 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object); 345 } 346 347 void SVGUseElement::recalcStyle(StyleChange change) 348 { 349 // Eventually mark shadow root element needing style recalc 350 if ((change >= Inherit || needsStyleRecalc() || childNeedsStyleRecalc()) && m_targetElementInstance && !m_updatesBlocked) { 351 if (SVGElement* shadowRoot = m_targetElementInstance->shadowTreeElement()) 352 shadowRoot->setNeedsStyleRecalc(); 353 } 354 355 SVGStyledTransformableElement::recalcStyle(change); 356 357 // Assure that the shadow tree has not been marked for recreation, while we're building it. 358 if (m_updatesBlocked) 359 ASSERT(!m_needsShadowTreeRecreation); 360 361 RenderSVGShadowTreeRootContainer* shadowRoot = static_cast<RenderSVGShadowTreeRootContainer*>(renderer()); 362 if (!shadowRoot) 363 return; 364 365 bool needsStyleUpdate = !m_needsShadowTreeRecreation; 366 if (m_needsShadowTreeRecreation) { 367 shadowRoot->markShadowTreeForRecreation(); 368 m_needsShadowTreeRecreation = false; 369 } 370 371 shadowRoot->updateFromElement(); 372 373 if (!needsStyleUpdate) 374 return; 375 376 shadowRoot->updateStyle(change); 377 } 378 379 #ifdef DUMP_INSTANCE_TREE 380 void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance) 381 { 382 SVGElement* element = targetInstance->correspondingElement(); 383 ASSERT(element); 384 385 SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); 386 ASSERT(shadowTreeElement); 387 388 SVGUseElement* directUseElement = targetInstance->directUseElement(); 389 String directUseElementName = directUseElement ? directUseElement->nodeName() : "null"; 390 391 String elementId = element->getIdAttribute(); 392 String elementNodeName = element->nodeName(); 393 String shadowTreeElementNodeName = shadowTreeElement->nodeName(); 394 String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null"; 395 String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null"; 396 397 for (unsigned int i = 0; i < depth; ++i) 398 text += " "; 399 400 text += String::format("SVGElementInstance this=%p, (parentNode=%s (%p), firstChild=%s (%p), correspondingElement=%s (%p), directUseElement=%s (%p), shadowTreeElement=%s (%p), id=%s)\n", 401 targetInstance, parentNodeName.latin1().data(), element->parentNode(), firstChildNodeName.latin1().data(), element->firstChild(), 402 elementNodeName.latin1().data(), element, directUseElementName.latin1().data(), directUseElement, shadowTreeElementNodeName.latin1().data(), shadowTreeElement, elementId.latin1().data()); 403 404 for (unsigned int i = 0; i < depth; ++i) 405 text += " "; 406 407 const HashSet<SVGElementInstance*>& elementInstances = element->instancesForElement(); 408 text += makeString("Corresponding element is associated with ", String::number(elementInstances.size()), " instance(s):\n"); 409 410 const HashSet<SVGElementInstance*>::const_iterator end = elementInstances.end(); 411 for (HashSet<SVGElementInstance*>::const_iterator it = elementInstances.begin(); it != end; ++it) { 412 for (unsigned int i = 0; i < depth; ++i) 413 text += " "; 414 415 text += String::format(" -> SVGElementInstance this=%p, (refCount: %i, shadowTreeElement in document? %i)\n", 416 *it, (*it)->refCount(), (*it)->shadowTreeElement()->inDocument()); 417 } 418 419 ++depth; 420 421 for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) 422 dumpInstanceTree(depth, text, instance); 423 424 --depth; 425 } 426 #endif 427 428 static bool isDisallowedElement(Node* element) 429 { 430 #if ENABLE(SVG_FOREIGN_OBJECT) 431 // <foreignObject> should never be contained in a <use> tree. Too dangerous side effects possible. 432 if (element->hasTagName(SVGNames::foreignObjectTag)) 433 return true; 434 #endif 435 #if ENABLE(SVG_ANIMATION) 436 if (SVGSMILElement::isSMILElement(element)) 437 return true; 438 #endif 439 440 return false; 441 } 442 443 static bool subtreeContainsDisallowedElement(Node* start) 444 { 445 if (isDisallowedElement(start)) 446 return true; 447 448 for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) { 449 if (subtreeContainsDisallowedElement(cur)) 450 return true; 451 } 452 453 return false; 454 } 455 456 void SVGUseElement::buildPendingResource() 457 { 458 // If we're called the first time (during shadow tree root creation from RenderSVGShadowTreeRootContainer) 459 // we either determine that our target is available or not - then we add ourselves to the pending resource list 460 // Once the pending resource appears, it will call buildPendingResource(), so we're called a second time. 461 String id = SVGURIReference::getTarget(href()); 462 Element* targetElement = document()->getElementById(id); 463 ASSERT(!m_targetElementInstance); 464 465 if (!targetElement) { 466 if (m_isPendingResource || id.isEmpty()) 467 return; 468 469 m_isPendingResource = true; 470 m_resourceId = id; 471 document()->accessSVGExtensions()->addPendingResource(id, this); 472 return; 473 } 474 475 if (m_isPendingResource) { 476 ASSERT(!m_targetElementInstance); 477 m_isPendingResource = false; 478 m_resourceId = String(); 479 invalidateShadowTree(); 480 } 481 } 482 483 void SVGUseElement::buildShadowAndInstanceTree(SVGShadowTreeRootElement* shadowRoot) 484 { 485 struct ShadowTreeUpdateBlocker { 486 ShadowTreeUpdateBlocker(SVGUseElement* currentUseElement) 487 : useElement(currentUseElement) 488 { 489 useElement->setUpdatesBlocked(true); 490 } 491 492 ~ShadowTreeUpdateBlocker() 493 { 494 useElement->setUpdatesBlocked(false); 495 } 496 497 SVGUseElement* useElement; 498 }; 499 500 // When cloning the target nodes, they may decide to synchronize style and/or animated SVG attributes. 501 // That causes calls to SVGElementInstance::updateAllInstancesOfElement(), which mark the shadow tree for recreation. 502 // Solution: block any updates to the shadow tree while we're building it. 503 ShadowTreeUpdateBlocker blocker(this); 504 505 String id = SVGURIReference::getTarget(href()); 506 Element* targetElement = document()->getElementById(id); 507 if (!targetElement) { 508 // The only time we should get here is when the use element has not been 509 // given a resource to target. 510 ASSERT(m_resourceId.isEmpty()); 511 return; 512 } 513 514 // Do not build the shadow/instance tree for <use> elements living in a shadow tree. 515 // The will be expanded soon anyway - see expandUseElementsInShadowTree(). 516 ContainerNode* parent = parentNode(); 517 while (parent) { 518 if (parent->isShadowRoot()) 519 return; 520 521 parent = parent->parentNodeGuaranteedHostFree(); 522 } 523 524 SVGElement* target = 0; 525 if (targetElement && targetElement->isSVGElement()) 526 target = static_cast<SVGElement*>(targetElement); 527 528 detachInstance(); 529 530 // Do not allow self-referencing. 531 // 'target' may be null, if it's a non SVG namespaced element. 532 if (!target || target == this) 533 return; 534 535 // Why a seperated instance/shadow tree? SVG demands it: 536 // The instance tree is accesable from JavaScript, and has to 537 // expose a 1:1 copy of the referenced tree, whereas internally we need 538 // to alter the tree for correct "use-on-symbol", "use-on-svg" support. 539 540 // Build instance tree. Create root SVGElementInstance object for the first sub-tree node. 541 // 542 // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a 543 // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object 544 // is the SVGRectElement that corresponds to the referenced 'rect' element. 545 m_targetElementInstance = SVGElementInstance::create(this, this, target); 546 547 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children 548 bool foundProblem = false; 549 buildInstanceTree(target, m_targetElementInstance.get(), foundProblem); 550 551 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it! 552 // Non-appearing <use> content is easier to debug, then half-appearing content. 553 if (foundProblem) { 554 detachInstance(); 555 return; 556 } 557 558 // Assure instance tree building was successfull 559 ASSERT(m_targetElementInstance); 560 ASSERT(!m_targetElementInstance->shadowTreeElement()); 561 ASSERT(m_targetElementInstance->correspondingUseElement() == this); 562 ASSERT(m_targetElementInstance->directUseElement() == this); 563 ASSERT(m_targetElementInstance->correspondingElement() == target); 564 565 // Build shadow tree from instance tree 566 // This also handles the special cases: <use> on <symbol>, <use> on <svg>. 567 buildShadowTree(shadowRoot, target, m_targetElementInstance.get()); 568 569 #if ENABLE(SVG) && ENABLE(SVG_USE) 570 // Expand all <use> elements in the shadow tree. 571 // Expand means: replace the actual <use> element by what it references. 572 expandUseElementsInShadowTree(shadowRoot); 573 574 // Expand all <symbol> elements in the shadow tree. 575 // Expand means: replace the actual <symbol> element by the <svg> element. 576 expandSymbolElementsInShadowTree(shadowRoot); 577 #endif 578 579 // Now that the shadow tree is completly expanded, we can associate 580 // shadow tree elements <-> instances in the instance tree. 581 associateInstancesWithShadowTreeElements(shadowRoot->firstChild(), m_targetElementInstance.get()); 582 583 // If no shadow tree element is present, this means that the reference root 584 // element was removed, as it is disallowed (ie. <use> on <foreignObject>) 585 // Do NOT leave an inconsistent instance tree around, instead destruct it. 586 if (!m_targetElementInstance->shadowTreeElement()) { 587 shadowRoot->removeAllChildren(); 588 detachInstance(); 589 return; 590 } 591 592 // Consistency checks - this is assumed in updateContainerOffset(). 593 ASSERT(m_targetElementInstance->shadowTreeElement()->parentNode() == shadowRoot); 594 595 // Transfer event listeners assigned to the referenced element to our shadow tree elements. 596 transferEventListenersToShadowTree(m_targetElementInstance.get()); 597 598 // Update container offset/size 599 updateContainerOffsets(); 600 updateContainerSizes(); 601 602 // Update relative length information 603 updateRelativeLengthsInformation(); 604 605 // Eventually dump instance tree 606 #ifdef DUMP_INSTANCE_TREE 607 String text; 608 unsigned int depth = 0; 609 610 dumpInstanceTree(depth, text, m_targetElementInstance.get()); 611 fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data()); 612 #endif 613 614 // Eventually dump shadow tree 615 #ifdef DUMP_SHADOW_TREE 616 ExceptionCode ec = 0; 617 618 RefPtr<XMLSerializer> serializer = XMLSerializer::create(); 619 620 String markup = serializer->serializeToString(shadowRoot, ec); 621 ASSERT(!ec); 622 623 fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data()); 624 #endif 625 } 626 627 void SVGUseElement::detachInstance() 628 { 629 if (!m_targetElementInstance) 630 return; 631 m_targetElementInstance->clearUseElements(); 632 m_targetElementInstance = 0; 633 } 634 635 RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*) 636 { 637 return new (arena) RenderSVGShadowTreeRootContainer(this); 638 } 639 640 static void updateFromElementCallback(Node* node) 641 { 642 if (RenderObject* renderer = node->renderer()) 643 renderer->updateFromElement(); 644 } 645 646 void SVGUseElement::attach() 647 { 648 SVGStyledTransformableElement::attach(); 649 650 if (renderer()) 651 queuePostAttachCallback(updateFromElementCallback, this); 652 } 653 654 void SVGUseElement::detach() 655 { 656 SVGStyledTransformableElement::detach(); 657 detachInstance(); 658 } 659 660 static bool isDirectReference(Node* node) 661 { 662 return node->hasTagName(SVGNames::pathTag) 663 || node->hasTagName(SVGNames::rectTag) 664 || node->hasTagName(SVGNames::circleTag) 665 || node->hasTagName(SVGNames::ellipseTag) 666 || node->hasTagName(SVGNames::polygonTag) 667 || node->hasTagName(SVGNames::polylineTag) 668 || node->hasTagName(SVGNames::textTag); 669 } 670 671 void SVGUseElement::toClipPath(Path& path) const 672 { 673 ASSERT(path.isEmpty()); 674 675 Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0; 676 if (!n) 677 return; 678 679 if (n->isSVGElement() && static_cast<SVGElement*>(n)->isStyledTransformable()) { 680 if (!isDirectReference(n)) 681 // Spec: Indirect references are an error (14.3.5) 682 document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>"); 683 else { 684 static_cast<SVGStyledTransformableElement*>(n)->toClipPath(path); 685 path.translate(FloatSize(x().value(this), y().value(this))); 686 path.transform(animatedLocalTransform()); 687 } 688 } 689 } 690 691 RenderObject* SVGUseElement::rendererClipChild() const 692 { 693 Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0; 694 if (!n) 695 return 0; 696 697 if (n->isSVGElement() && isDirectReference(n)) 698 return static_cast<SVGElement*>(n)->renderer(); 699 700 return 0; 701 } 702 703 void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem) 704 { 705 ASSERT(target); 706 ASSERT(targetInstance); 707 708 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced 709 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. 710 bool targetHasUseTag = target->hasTagName(SVGNames::useTag); 711 SVGElement* newTarget = 0; 712 if (targetHasUseTag) { 713 foundProblem = hasCycleUseReferencing(static_cast<SVGUseElement*>(target), targetInstance, newTarget); 714 if (foundProblem) 715 return; 716 } 717 718 // A general description from the SVG spec, describing what buildInstanceTree() actually does. 719 // 720 // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree 721 // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement 722 // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has 723 // its correspondingElement that is an SVGRectElement object. 724 725 for (Node* node = target->firstChild(); node; node = node->nextSibling()) { 726 SVGElement* element = 0; 727 if (node->isSVGElement()) 728 element = static_cast<SVGElement*>(node); 729 730 // Skip any non-svg nodes or any disallowed element. 731 if (!element || isDisallowedElement(element)) 732 continue; 733 734 // Create SVGElementInstance object, for both container/non-container nodes. 735 RefPtr<SVGElementInstance> instance = SVGElementInstance::create(this, 0, element); 736 SVGElementInstance* instancePtr = instance.get(); 737 targetInstance->appendChild(instance.release()); 738 739 // Enter recursion, appending new instance tree nodes to the "instance" object. 740 buildInstanceTree(element, instancePtr, foundProblem); 741 if (foundProblem) 742 return; 743 } 744 745 if (!targetHasUseTag || !newTarget) 746 return; 747 748 RefPtr<SVGElementInstance> newInstance = SVGElementInstance::create(this, static_cast<SVGUseElement*>(target), newTarget); 749 SVGElementInstance* newInstancePtr = newInstance.get(); 750 targetInstance->appendChild(newInstance.release()); 751 buildInstanceTree(newTarget, newInstancePtr, foundProblem); 752 } 753 754 bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, SVGElementInstance* targetInstance, SVGElement*& newTarget) 755 { 756 String id = SVGURIReference::getTarget(use->href()); 757 Element* targetElement = document()->getElementById(id); 758 newTarget = 0; 759 if (targetElement && targetElement->isSVGElement()) 760 newTarget = static_cast<SVGElement*>(targetElement); 761 762 if (!newTarget) 763 return false; 764 765 // Shortcut for self-references 766 if (newTarget == this) 767 return true; 768 769 SVGElementInstance* instance = targetInstance->parentNode(); 770 while (instance) { 771 SVGElement* element = instance->correspondingElement(); 772 773 // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution. 774 if (element->hasID() && element->idForStyleResolution() == id) 775 return true; 776 777 instance = instance->parentNode(); 778 } 779 return false; 780 } 781 782 void SVGUseElement::removeDisallowedElementsFromSubtree(Node* subtree) 783 { 784 ASSERT(!subtree->inDocument()); 785 ExceptionCode ec; 786 Node* node = subtree->firstChild(); 787 while (node) { 788 if (isDisallowedElement(node)) { 789 Node* next = node->traverseNextSibling(subtree); 790 // The subtree is not in document so this won't generate events that could mutate the tree. 791 node->parentNode()->removeChild(node, ec); 792 node = next; 793 } else 794 node = node->traverseNextNode(subtree); 795 } 796 } 797 798 void SVGUseElement::buildShadowTree(SVGShadowTreeRootElement* shadowRoot, SVGElement* target, SVGElementInstance* targetInstance) 799 { 800 // For instance <use> on <foreignObject> (direct case). 801 if (isDisallowedElement(target)) 802 return; 803 804 RefPtr<Element> newChild = targetInstance->correspondingElement()->cloneElementWithChildren(); 805 806 // We don't walk the target tree element-by-element, and clone each element, 807 // but instead use cloneElementWithChildren(). This is an optimization for the common 808 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 809 // Though if there are disallowed elements in the subtree, we have to remove them. 810 // For instance: <use> on <g> containing <foreignObject> (indirect case). 811 if (subtreeContainsDisallowedElement(newChild.get())) 812 removeDisallowedElementsFromSubtree(newChild.get()); 813 814 SVGElement* newChildPtr = 0; 815 if (newChild->isSVGElement()) 816 newChildPtr = static_cast<SVGElement*>(newChild.get()); 817 ASSERT(newChildPtr); 818 819 ExceptionCode ec = 0; 820 shadowRoot->appendChild(newChild.release(), ec); 821 ASSERT(!ec); 822 } 823 824 #if ENABLE(SVG) && ENABLE(SVG_USE) 825 void SVGUseElement::expandUseElementsInShadowTree(Node* element) 826 { 827 // Why expand the <use> elements in the shadow tree here, and not just 828 // do this directly in buildShadowTree, if we encounter a <use> element? 829 // 830 // Short answer: Because we may miss to expand some elements. Ie. if a <symbol> 831 // contains <use> tags, we'd miss them. So once we're done with settin' up the 832 // actual shadow tree (after the special case modification for svg/symbol) we have 833 // to walk it completely and expand all <use> elements. 834 if (element->hasTagName(SVGNames::useTag)) { 835 SVGUseElement* use = static_cast<SVGUseElement*>(element); 836 837 String id = SVGURIReference::getTarget(use->href()); 838 Element* targetElement = document()->getElementById(id); 839 SVGElement* target = 0; 840 if (targetElement && targetElement->isSVGElement()) 841 target = static_cast<SVGElement*>(targetElement); 842 843 // Don't ASSERT(target) here, it may be "pending", too. 844 // Setup sub-shadow tree root node 845 RefPtr<SVGShadowTreeContainerElement> cloneParent = SVGShadowTreeContainerElement::create(document()); 846 use->cloneChildNodes(cloneParent.get()); 847 848 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the 849 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element. 850 transferUseAttributesToReplacedElement(use, cloneParent.get()); 851 852 ExceptionCode ec = 0; 853 if (target && !isDisallowedElement(target)) { 854 RefPtr<Element> newChild = target->cloneElementWithChildren(); 855 856 SVGElement* newChildPtr = 0; 857 if (newChild->isSVGElement()) 858 newChildPtr = static_cast<SVGElement*>(newChild.get()); 859 ASSERT(newChildPtr); 860 861 cloneParent->appendChild(newChild.release(), ec); 862 ASSERT(!ec); 863 } 864 865 // We don't walk the target tree element-by-element, and clone each element, 866 // but instead use cloneElementWithChildren(). This is an optimization for the common 867 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 868 // Though if there are disallowed elements in the subtree, we have to remove them. 869 // For instance: <use> on <g> containing <foreignObject> (indirect case). 870 if (subtreeContainsDisallowedElement(cloneParent.get())) 871 removeDisallowedElementsFromSubtree(cloneParent.get()); 872 873 RefPtr<Node> replacingElement(cloneParent.get()); 874 875 // Replace <use> with referenced content. 876 ASSERT(use->parentNode()); 877 use->parentNode()->replaceChild(cloneParent.release(), use, ec); 878 ASSERT(!ec); 879 880 // Expand the siblings because the *element* is replaced and we will 881 // lose the sibling chain when we are back from recursion. 882 element = replacingElement.get(); 883 for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling()) 884 expandUseElementsInShadowTree(sibling.get()); 885 } 886 887 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) 888 expandUseElementsInShadowTree(child.get()); 889 } 890 891 void SVGUseElement::expandSymbolElementsInShadowTree(Node* element) 892 { 893 if (element->hasTagName(SVGNames::symbolTag)) { 894 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree, 895 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will 896 // always have explicit values for attributes width and height. If attributes width and/or 897 // height are provided on the 'use' element, then these attributes will be transferred to 898 // the generated 'svg'. If attributes width and/or height are not specified, the generated 899 // 'svg' element will use values of 100% for these attributes. 900 RefPtr<SVGSVGElement> svgElement = SVGSVGElement::create(SVGNames::svgTag, document()); 901 902 // Transfer all attributes from <symbol> to the new <svg> element 903 svgElement->attributes()->setAttributes(*element->attributes()); 904 905 // Only clone symbol children, and add them to the new <svg> element 906 ExceptionCode ec = 0; 907 for (Node* child = element->firstChild(); child; child = child->nextSibling()) { 908 RefPtr<Node> newChild = child->cloneNode(true); 909 svgElement->appendChild(newChild.release(), ec); 910 ASSERT(!ec); 911 } 912 913 // We don't walk the target tree element-by-element, and clone each element, 914 // but instead use cloneNode(deep=true). This is an optimization for the common 915 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 916 // Though if there are disallowed elements in the subtree, we have to remove them. 917 // For instance: <use> on <g> containing <foreignObject> (indirect case). 918 if (subtreeContainsDisallowedElement(svgElement.get())) 919 removeDisallowedElementsFromSubtree(svgElement.get()); 920 921 RefPtr<Node> replacingElement(svgElement.get()); 922 923 // Replace <symbol> with <svg>. 924 ASSERT(element->parentNode()); 925 element->parentNode()->replaceChild(svgElement.release(), element, ec); 926 ASSERT(!ec); 927 928 // Expand the siblings because the *element* is replaced and we will 929 // lose the sibling chain when we are back from recursion. 930 element = replacingElement.get(); 931 for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling()) 932 expandSymbolElementsInShadowTree(sibling.get()); 933 } 934 935 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) 936 expandSymbolElementsInShadowTree(child.get()); 937 } 938 939 #endif 940 941 void SVGUseElement::transferEventListenersToShadowTree(SVGElementInstance* target) 942 { 943 if (!target) 944 return; 945 946 SVGElement* originalElement = target->correspondingElement(); 947 ASSERT(originalElement); 948 949 if (SVGElement* shadowTreeElement = target->shadowTreeElement()) { 950 if (EventTargetData* d = originalElement->eventTargetData()) { 951 EventListenerMap& map = d->eventListenerMap; 952 EventListenerMap::iterator end = map.end(); 953 for (EventListenerMap::iterator it = map.begin(); it != end; ++it) { 954 EventListenerVector& entry = *it->second; 955 for (size_t i = 0; i < entry.size(); ++i) { 956 // Event listeners created from markup have already been transfered to the shadow tree during cloning. 957 if (entry[i].listener->wasCreatedFromMarkup()) 958 continue; 959 shadowTreeElement->addEventListener(it->first, entry[i].listener, entry[i].useCapture); 960 } 961 } 962 } 963 } 964 965 for (SVGElementInstance* instance = target->firstChild(); instance; instance = instance->nextSibling()) 966 transferEventListenersToShadowTree(instance); 967 } 968 969 void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance) 970 { 971 if (!target || !targetInstance) 972 return; 973 974 SVGElement* originalElement = targetInstance->correspondingElement(); 975 976 if (originalElement->hasTagName(SVGNames::useTag)) { 977 #if ENABLE(SVG) && ENABLE(SVG_USE) 978 // <use> gets replaced by <g> 979 ASSERT(target->nodeName() == SVGNames::gTag); 980 #else 981 ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag); 982 #endif 983 } else if (originalElement->hasTagName(SVGNames::symbolTag)) { 984 // <symbol> gets replaced by <svg> 985 #if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT) 986 ASSERT(target->nodeName() == SVGNames::svgTag); 987 #endif 988 } else 989 ASSERT(target->nodeName() == originalElement->nodeName()); 990 991 SVGElement* element = 0; 992 if (target->isSVGElement()) 993 element = static_cast<SVGElement*>(target); 994 995 ASSERT(!targetInstance->shadowTreeElement()); 996 targetInstance->setShadowTreeElement(element); 997 998 Node* node = target->firstChild(); 999 for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) { 1000 // Skip any non-svg elements in shadow tree 1001 while (node && !node->isSVGElement()) 1002 node = node->nextSibling(); 1003 1004 if (!node) 1005 break; 1006 1007 associateInstancesWithShadowTreeElements(node, instance); 1008 node = node->nextSibling(); 1009 } 1010 } 1011 1012 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const 1013 { 1014 if (!m_targetElementInstance) { 1015 ASSERT(!inDocument()); 1016 return 0; 1017 } 1018 1019 return instanceForShadowTreeElement(element, m_targetElementInstance.get()); 1020 } 1021 1022 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const 1023 { 1024 ASSERT(element); 1025 ASSERT(instance); 1026 1027 // We're dispatching a mutation event during shadow tree construction 1028 // this instance hasn't yet been associated to a shadowTree element. 1029 if (!instance->shadowTreeElement()) 1030 return 0; 1031 1032 if (element == instance->shadowTreeElement()) 1033 return instance; 1034 1035 for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) { 1036 if (SVGElementInstance* search = instanceForShadowTreeElement(element, current)) 1037 return search; 1038 } 1039 1040 return 0; 1041 } 1042 1043 void SVGUseElement::invalidateShadowTree() 1044 { 1045 // Don't mutate the shadow tree while we're building it. 1046 if (m_updatesBlocked) 1047 return; 1048 1049 m_needsShadowTreeRecreation = true; 1050 setNeedsStyleRecalc(); 1051 } 1052 1053 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const 1054 { 1055 ASSERT(from); 1056 ASSERT(to); 1057 1058 to->attributes()->setAttributes(*from->attributes()); 1059 1060 ExceptionCode ec = 0; 1061 1062 to->removeAttribute(SVGNames::xAttr, ec); 1063 ASSERT(!ec); 1064 1065 to->removeAttribute(SVGNames::yAttr, ec); 1066 ASSERT(!ec); 1067 1068 to->removeAttribute(SVGNames::widthAttr, ec); 1069 ASSERT(!ec); 1070 1071 to->removeAttribute(SVGNames::heightAttr, ec); 1072 ASSERT(!ec); 1073 1074 to->removeAttribute(XLinkNames::hrefAttr, ec); 1075 ASSERT(!ec); 1076 } 1077 1078 bool SVGUseElement::selfHasRelativeLengths() const 1079 { 1080 if (x().isRelative() 1081 || y().isRelative() 1082 || width().isRelative() 1083 || height().isRelative()) 1084 return true; 1085 1086 if (!m_targetElementInstance) 1087 return false; 1088 1089 SVGElement* element = m_targetElementInstance->correspondingElement(); 1090 if (!element || !element->isStyled()) 1091 return false; 1092 1093 return static_cast<SVGStyledElement*>(element)->hasRelativeLengths(); 1094 } 1095 1096 } 1097 1098 #endif // ENABLE(SVG) 1099