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