File indexing completed on 2024-04-28 15:24:43
0001 /* 0002 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 0003 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> 0004 0005 This file is part of the KDE project 0006 0007 This library is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU Library General Public 0009 License as published by the Free Software Foundation; either 0010 version 2 of the License, or (at your option) any later version. 0011 0012 This library is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 Library General Public License for more details. 0016 0017 You should have received a copy of the GNU Library General Public License 0018 along with this library; see the file COPYING.LIB. If not, write to 0019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 Boston, MA 02110-1301, USA. 0021 */ 0022 0023 #include "wtf/Platform.h" 0024 0025 // Dump SVGElementInstance object tree - useful to debug instanceRoot problems 0026 // #define DUMP_INSTANCE_TREE 0027 0028 // Dump the deep-expanded shadow tree (where the renderers are built from) 0029 // #define DUMP_SHADOW_TREE 0030 0031 #if ENABLE(SVG) 0032 #include "SVGUseElement.h" 0033 0034 #include "css/cssstyleselector.h" 0035 /*#include "CString.h"*/ 0036 #include "Document.h" 0037 /*#include "Event.h" 0038 #include "HTMLNames.h"*/ 0039 #include "RenderSVGTransformableContainer.h" 0040 #include "SVGElementInstance.h" 0041 #include "SVGElementInstanceList.h" 0042 #include "SVGGElement.h" 0043 #include "SVGLength.h" 0044 #include "SVGNames.h" 0045 #include "SVGPreserveAspectRatio.h" 0046 /*#include "SVGSMILElement.h"*/ 0047 #include "SVGSVGElement.h" 0048 #include "SVGSymbolElement.h" 0049 #include "XLinkNames.h" 0050 /*#include "XMLSerializer.h"*/ 0051 #include <wtf/OwnPtr.h> 0052 0053 namespace WebCore 0054 { 0055 0056 SVGUseElement::SVGUseElement(const QualifiedName &tagName, Document *doc) 0057 : SVGStyledTransformableElement(tagName, doc) 0058 , SVGTests() 0059 , SVGLangSpace() 0060 , SVGExternalResourcesRequired() 0061 , SVGURIReference() 0062 , m_x(this, LengthModeWidth) 0063 , m_y(this, LengthModeHeight) 0064 , m_width(this, LengthModeWidth) 0065 , m_height(this, LengthModeHeight) 0066 { 0067 } 0068 0069 SVGUseElement::~SVGUseElement() 0070 { 0071 } 0072 0073 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, X, x, SVGNames::xAttr, m_x) 0074 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Y, y, SVGNames::yAttr, m_y) 0075 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Width, width, SVGNames::widthAttr, m_width) 0076 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Height, height, SVGNames::heightAttr, m_height) 0077 0078 SVGElementInstance *SVGUseElement::instanceRoot() const 0079 { 0080 return m_targetElementInstance.get(); 0081 } 0082 0083 SVGElementInstance *SVGUseElement::animatedInstanceRoot() const 0084 { 0085 // FIXME: Implement me. 0086 return nullptr; 0087 } 0088 0089 void SVGUseElement::parseMappedAttribute(MappedAttribute *attr) 0090 { 0091 if (attr->name() == SVGNames::xAttr) { 0092 setXBaseValue(SVGLength(this, LengthModeWidth, attr->value())); 0093 } else if (attr->name() == SVGNames::yAttr) { 0094 setYBaseValue(SVGLength(this, LengthModeHeight, attr->value())); 0095 } else if (attr->name() == SVGNames::widthAttr) { 0096 setWidthBaseValue(SVGLength(this, LengthModeWidth, attr->value())); 0097 if (width().value() < 0.0) { 0098 document()->accessSVGExtensions()->reportError("A negative value for use attribute <width> is not allowed"); 0099 } 0100 } else if (attr->name() == SVGNames::heightAttr) { 0101 setHeightBaseValue(SVGLength(this, LengthModeHeight, attr->value())); 0102 if (height().value() < 0.0) { 0103 document()->accessSVGExtensions()->reportError("A negative value for use attribute <height> is not allowed"); 0104 } 0105 } else { 0106 if (SVGTests::parseMappedAttribute(attr)) { 0107 return; 0108 } 0109 if (SVGLangSpace::parseMappedAttribute(attr)) { 0110 return; 0111 } 0112 if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) { 0113 return; 0114 } 0115 if (SVGURIReference::parseMappedAttribute(attr)) { 0116 return; 0117 } 0118 SVGStyledTransformableElement::parseMappedAttribute(attr); 0119 } 0120 } 0121 0122 void SVGUseElement::insertedIntoDocument() 0123 { 0124 SVGElement::insertedIntoDocument(); 0125 buildPendingResource(); 0126 } 0127 0128 void SVGUseElement::removedFromDocument() 0129 { 0130 m_targetElementInstance = nullptr; 0131 m_shadowTreeRootElement = nullptr; 0132 SVGElement::removedFromDocument(); 0133 } 0134 0135 void SVGUseElement::svgAttributeChanged(const QualifiedName &attrName) 0136 { 0137 SVGStyledTransformableElement::svgAttributeChanged(attrName); 0138 0139 if (!attached()) { 0140 return; 0141 } 0142 0143 if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || 0144 attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr || 0145 SVGTests::isKnownAttribute(attrName) || 0146 SVGLangSpace::isKnownAttribute(attrName) || 0147 SVGExternalResourcesRequired::isKnownAttribute(attrName) || 0148 SVGURIReference::isKnownAttribute(attrName) || 0149 SVGStyledTransformableElement::isKnownAttribute(attrName)) { 0150 // TODO: Now that we're aware of the attribute name, we can finally optimize 0151 // updating <use> attributes - to not reclone every time. 0152 buildPendingResource(); 0153 0154 if (m_shadowTreeRootElement) { 0155 m_shadowTreeRootElement->setChanged(); 0156 } 0157 } 0158 } 0159 0160 void SVGUseElement::childrenChanged(bool changedByParser, Node *beforeChange, Node *afterChange, int childCountDelta) 0161 { 0162 Q_UNUSED(changedByParser); 0163 Q_UNUSED(beforeChange); 0164 Q_UNUSED(afterChange); 0165 Q_UNUSED(childCountDelta); 0166 SVGElement::childrenChanged(/*changedByParser, beforeChange, afterChange, childCountDelta*/); 0167 0168 if (!attached()) { 0169 return; 0170 } 0171 0172 buildPendingResource(); 0173 0174 if (m_shadowTreeRootElement) { 0175 m_shadowTreeRootElement->setChanged(); 0176 } 0177 } 0178 0179 void SVGUseElement::recalcStyle(StyleChange change) 0180 { 0181 SVGStyledElement::recalcStyle(change); 0182 0183 // The shadow tree root element is NOT a direct child element of us. 0184 // So we have to take care it receives style updates, manually. 0185 if (!m_shadowTreeRootElement || !m_shadowTreeRootElement->attached()) { 0186 return; 0187 } 0188 0189 // Mimic Element::recalcStyle(). The main difference is that we don't call attach() on the 0190 // shadow tree root element, but call attachShadowTree() here. Calling attach() will crash 0191 // as the shadow tree root element has no (direct) parent node. Yes, shadow trees are tricky. 0192 if (change >= Inherit || m_shadowTreeRootElement->changed()) { 0193 RenderStyle *newStyle = document()->styleSelector()->styleForElement(m_shadowTreeRootElement.get()); 0194 newStyle->ref(); 0195 StyleChange ch = m_shadowTreeRootElement->diff((m_shadowTreeRootElement->renderer() ? m_shadowTreeRootElement->renderer()->style() : nullptr)/*renderStyle()*/, newStyle); 0196 if (ch == Detach) { 0197 ASSERT(m_shadowTreeRootElement->attached()); 0198 m_shadowTreeRootElement->detach(); 0199 attachShadowTree(); 0200 0201 // attach recalulates the style for all children. No need to do it twice. 0202 m_shadowTreeRootElement->setChanged(false); 0203 m_shadowTreeRootElement->setHasChangedChild(false); 0204 newStyle->deref(); 0205 return; 0206 } 0207 0208 newStyle->deref(); 0209 } 0210 0211 // Only change==Detach needs special treatment, for anything else recalcStyle() works. 0212 m_shadowTreeRootElement->recalcStyle(change); 0213 } 0214 0215 #ifdef DUMP_INSTANCE_TREE 0216 void dumpInstanceTree(unsigned int &depth, String &text, SVGElementInstance *targetInstance) 0217 { 0218 SVGElement *element = targetInstance->correspondingElement(); 0219 ASSERT(element); 0220 0221 String elementId = element->getIDAttribute(); 0222 String elementNodeName = element->nodeName(); 0223 String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null"; 0224 String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null"; 0225 0226 for (unsigned int i = 0; i < depth; ++i) { 0227 text += " "; 0228 } 0229 0230 text += String::format("SVGElementInstance (parentNode=%s, firstChild=%s, correspondingElement=%s, id=%s)\n", 0231 parentNodeName.latin1().data(), firstChildNodeName.latin1().data(), elementNodeName.latin1().data(), elementId.latin1().data()); 0232 0233 depth++; 0234 0235 for (SVGElementInstance *instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) { 0236 dumpInstanceTree(depth, text, instance); 0237 } 0238 0239 depth--; 0240 } 0241 #endif 0242 0243 static bool isDisallowedElement(Node *element) 0244 { 0245 Q_UNUSED(element); 0246 #if ENABLE(SVG_FOREIGN_OBJECT) 0247 // <foreignObject> should never be contained in a <use> tree. Too dangerous side effects possible. 0248 if (element->hasTagName(SVGNames::foreignObjectTag)) { 0249 return true; 0250 } 0251 #endif 0252 #if ENABLE(SVG_ANIMATION) 0253 if (SVGSMILElement::isSMILElement(element)) { 0254 return true; 0255 } 0256 #endif 0257 0258 return false; 0259 } 0260 0261 static bool subtreeContainsDisallowedElement(Node *start) 0262 { 0263 if (isDisallowedElement(start)) { 0264 return true; 0265 } 0266 0267 for (Node *cur = start->firstChild(); cur; cur = cur->nextSibling()) { 0268 if (subtreeContainsDisallowedElement(cur)) { 0269 return true; 0270 } 0271 } 0272 0273 return false; 0274 } 0275 0276 void SVGUseElement::buildPendingResource() 0277 { 0278 String id = SVGURIReference::getTarget(href()); 0279 Element *targetElement = document()->getElementById(id); 0280 0281 if (!targetElement) { 0282 // TODO: We want to deregister as pending resource, if our href() changed! 0283 // TODO: Move to svgAttributeChanged, once we're fixing use & the new dynamic update concept. 0284 document()->accessSVGExtensions()->addPendingResource(id, this); 0285 return; 0286 } 0287 0288 // Do not build the shadow/instance tree for <use> elements living in a shadow tree. 0289 // The will be expanded soon anyway - see expandUseElementsInShadowTree(). 0290 Node *parent = parentNode(); 0291 while (parent) { 0292 if (parent->isShadowNode()) { 0293 return; 0294 } 0295 0296 parent = parent->parentNode(); 0297 } 0298 0299 SVGElement *target = nullptr; 0300 if (targetElement && targetElement->isSVGElement()) { 0301 target = static_cast<SVGElement *>(targetElement); 0302 } 0303 0304 // Do not allow self-referencing. 0305 // 'target' may be null, if it's a non SVG namespaced element. 0306 if (!target || target == this) { 0307 m_targetElementInstance = nullptr; 0308 m_shadowTreeRootElement = nullptr; 0309 return; 0310 } 0311 0312 // Why a separated instance/shadow tree? SVG demands it: 0313 // The instance tree is accesable from JavaScript, and has to 0314 // expose a 1:1 copy of the referenced tree, whereas internally we need 0315 // to alter the tree for correct "use-on-symbol", "use-on-svg" support. 0316 0317 // Build instance tree. Create root SVGElementInstance object for the first sub-tree node. 0318 // 0319 // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a 0320 // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object 0321 // is the SVGRectElement that corresponds to the referenced 'rect' element. 0322 m_targetElementInstance = new SVGElementInstance(this, target); 0323 0324 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children 0325 bool foundProblem = false; 0326 buildInstanceTree(target, m_targetElementInstance.get(), foundProblem); 0327 0328 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it! 0329 // Non-appearing <use> content is easier to debug, then half-appearing content. 0330 if (foundProblem) { 0331 m_targetElementInstance = nullptr; 0332 m_shadowTreeRootElement = nullptr; 0333 return; 0334 } 0335 0336 // Assure instance tree building was successful 0337 ASSERT(m_targetElementInstance); 0338 ASSERT(m_targetElementInstance->correspondingUseElement() == this); 0339 0340 // Setup shadow tree root node 0341 m_shadowTreeRootElement = new SVGGElement(SVGNames::gTag, document()); 0342 m_shadowTreeRootElement->setInDocument(); 0343 m_shadowTreeRootElement->setShadowParentNode(this); 0344 0345 // Spec: An additional transformation translate(x,y) is appended to the end 0346 // (i.e., right-side) of the transform attribute on the generated 'g', where x 0347 // and y represent the values of the x and y attributes on the 'use' element. 0348 if (x().value() != 0.0 || y().value() != 0.0) { 0349 String transformString = String::format("translate(%f, %f)", x().value(), y().value()); 0350 m_shadowTreeRootElement->setAttribute(SVGNames::transformAttr, transformString); 0351 } 0352 0353 // Build shadow tree from instance tree 0354 // This also handles the special cases: <use> on <symbol>, <use> on <svg>. 0355 buildShadowTree(target, m_targetElementInstance.get()); 0356 0357 #if ENABLE(SVG) && ENABLE(SVG_USE) 0358 // Expand all <use> elements in the shadow tree. 0359 // Expand means: replace the actual <use> element by what it references. 0360 expandUseElementsInShadowTree(m_shadowTreeRootElement.get()); 0361 0362 // Expand all <symbol> elements in the shadow tree. 0363 // Expand means: replace the actual <symbol> element by the <svg> element. 0364 expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get()); 0365 0366 #endif 0367 0368 // Now that the shadow tree is completely expanded, we can associate 0369 // shadow tree elements <-> instances in the instance tree. 0370 associateInstancesWithShadowTreeElements(m_shadowTreeRootElement->firstChild(), m_targetElementInstance.get()); 0371 0372 // Eventually dump instance tree 0373 #ifdef DUMP_INSTANCE_TREE 0374 String text; 0375 unsigned int depth = 0; 0376 0377 dumpInstanceTree(depth, text, m_targetElementInstance.get()); 0378 fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data()); 0379 #endif 0380 0381 // Eventually dump shadow tree 0382 #ifdef DUMP_SHADOW_TREE 0383 ExceptionCode ec = 0; 0384 0385 PassRefPtr<XMLSerializer> serializer = XMLSerializer::create(); 0386 0387 String markup = serializer->serializeToString(m_shadowTreeRootElement.get(), ec); 0388 ASSERT(ec == 0); 0389 0390 fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data()); 0391 #endif 0392 0393 // The DOM side is setup properly. Now we have to attach the root shadow 0394 // tree element manually - using attach() won't work for "shadow nodes". 0395 attachShadowTree(); 0396 } 0397 0398 RenderObject *SVGUseElement::createRenderer(RenderArena *arena, RenderStyle *) 0399 { 0400 return new(arena) RenderSVGTransformableContainer(this); 0401 } 0402 0403 void SVGUseElement::attach() 0404 { 0405 SVGStyledTransformableElement::attach(); 0406 0407 // If we're a pending resource, this doesn't have any effect. 0408 attachShadowTree(); 0409 } 0410 0411 void SVGUseElement::detach() 0412 { 0413 if (m_shadowTreeRootElement) { 0414 m_shadowTreeRootElement->detach(); 0415 } 0416 0417 SVGStyledTransformableElement::detach(); 0418 } 0419 0420 static bool isDirectReference(Node *n) 0421 { 0422 return n->hasTagName(SVGNames::pathTag) || 0423 n->hasTagName(SVGNames::rectTag) || 0424 n->hasTagName(SVGNames::circleTag) || 0425 n->hasTagName(SVGNames::ellipseTag) || 0426 n->hasTagName(SVGNames::polygonTag) || 0427 n->hasTagName(SVGNames::polylineTag) || 0428 n->hasTagName(SVGNames::textTag); 0429 } 0430 0431 Path SVGUseElement::toClipPath() const 0432 { 0433 if (!m_shadowTreeRootElement) { 0434 const_cast<SVGUseElement *>(this)->buildPendingResource(); 0435 } 0436 0437 if (!m_shadowTreeRootElement) { 0438 return Path(); 0439 } 0440 0441 Node *n = m_shadowTreeRootElement->firstChild(); 0442 if (n->isSVGElement() && static_cast<SVGElement *>(n)->isStyledTransformable()) { 0443 if (!isDirectReference(n)) 0444 // Spec: Indirect references are an error (14.3.5) 0445 { 0446 document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>"); 0447 } else { 0448 return static_cast<SVGStyledTransformableElement *>(n)->toClipPath(); 0449 } 0450 } 0451 0452 return Path(); 0453 } 0454 0455 void SVGUseElement::buildInstanceTree(SVGElement *target, SVGElementInstance *targetInstance, bool &foundProblem) 0456 { 0457 ASSERT(target); 0458 ASSERT(targetInstance); 0459 0460 // A general description from the SVG spec, describing what buildInstanceTree() actually does. 0461 // 0462 // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree 0463 // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement 0464 // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has 0465 // its correspondingElement that is an SVGRectElement object. 0466 0467 for (Node *node = target->firstChild(); node; node = node->nextSibling()) { 0468 SVGElement *element = nullptr; 0469 if (node->isSVGElement()) { 0470 element = static_cast<SVGElement *>(node); 0471 } 0472 0473 // Skip any non-svg nodes or any disallowed element. 0474 if (!element || isDisallowedElement(element)) { 0475 continue; 0476 } 0477 0478 // Create SVGElementInstance object, for both container/non-container nodes. 0479 SVGElementInstance *instancePtr = new SVGElementInstance(this, element); 0480 0481 RefPtr<SVGElementInstance> instance = instancePtr; 0482 targetInstance->appendChild(instance.release()); 0483 0484 // Enter recursion, appending new instance tree nodes to the "instance" object. 0485 if (element->hasChildNodes()) { 0486 buildInstanceTree(element, instancePtr, foundProblem); 0487 } 0488 0489 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced 0490 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. 0491 if (element->hasTagName(SVGNames::useTag)) { 0492 handleDeepUseReferencing(element, instancePtr, foundProblem); 0493 } 0494 } 0495 0496 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced 0497 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. 0498 if (target->hasTagName(SVGNames::useTag)) { 0499 handleDeepUseReferencing(target, targetInstance, foundProblem); 0500 } 0501 } 0502 0503 void SVGUseElement::handleDeepUseReferencing(SVGElement *use, SVGElementInstance *targetInstance, bool &foundProblem) 0504 { 0505 String id = SVGURIReference::getTarget(use->href()); 0506 Element *targetElement = document()->getElementById(id); 0507 SVGElement *target = nullptr; 0508 if (targetElement && targetElement->isSVGElement()) { 0509 target = static_cast<SVGElement *>(targetElement); 0510 } 0511 0512 if (!target) { 0513 return; 0514 } 0515 0516 // Cycle detection first! 0517 foundProblem = (target == this); 0518 0519 // Shortcut for self-references 0520 if (foundProblem) { 0521 return; 0522 } 0523 0524 SVGElementInstance *instance = targetInstance->parentNode(); 0525 while (instance) { 0526 SVGElement *element = instance->correspondingElement(); 0527 0528 if (element->getIDAttribute() == id) { 0529 foundProblem = true; 0530 return; 0531 } 0532 0533 instance = instance->parentNode(); 0534 } 0535 0536 // Create an instance object, even if we're dealing with a cycle 0537 SVGElementInstance *newInstance = new SVGElementInstance(this, target); 0538 targetInstance->appendChild(newInstance); 0539 0540 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children 0541 buildInstanceTree(target, newInstance, foundProblem); 0542 } 0543 0544 void SVGUseElement::alterShadowTreeForSVGTag(SVGElement *target) 0545 { 0546 String widthString = String::number(width().value()); 0547 String heightString = String::number(height().value()); 0548 0549 if (hasAttribute(SVGNames::widthAttr)) { 0550 target->setAttribute(SVGNames::widthAttr, widthString); 0551 } 0552 0553 if (hasAttribute(SVGNames::heightAttr)) { 0554 target->setAttribute(SVGNames::heightAttr, heightString); 0555 } 0556 } 0557 0558 void SVGUseElement::removeDisallowedElementsFromSubtree(Node *subtree) 0559 { 0560 Q_UNUSED(subtree); 0561 // Implement me: khtml, NodeImpl::traverseNextSibling 0562 /*ASSERT(!subtree->inDocument()); 0563 ExceptionCode ec; 0564 Node* node = subtree->firstChild(); 0565 while (node) { 0566 if (isDisallowedElement(node)) { 0567 Node* next = node->traverseNextSibling(subtree); 0568 // The subtree is not in document so this won't generate events that could mutate the tree. 0569 node->parent()->removeChild(node, ec); 0570 node = next; 0571 } else 0572 node = node->traverseNextNode(subtree); 0573 }*/ 0574 } 0575 0576 void SVGUseElement::buildShadowTree(SVGElement *target, SVGElementInstance *targetInstance) 0577 { 0578 // For instance <use> on <foreignObject> (direct case). 0579 if (isDisallowedElement(target)) { 0580 return; 0581 } 0582 0583 PassRefPtr<NodeImpl> newChild = targetInstance->correspondingElement()->cloneNode(true); 0584 0585 // We don't walk the target tree element-by-element, and clone each element, 0586 // but instead use cloneNode(deep=true). This is an optimization for the common 0587 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 0588 // Though if there are disallowed elements in the subtree, we have to remove them. 0589 // For instance: <use> on <g> containing <foreignObject> (indirect case). 0590 if (subtreeContainsDisallowedElement(newChild.get())) { 0591 removeDisallowedElementsFromSubtree(newChild.get()); 0592 } 0593 0594 SVGElement *newChildPtr = nullptr; 0595 if (newChild->isSVGElement()) { 0596 newChildPtr = static_cast<SVGElement *>(newChild.get()); 0597 } 0598 ASSERT(newChildPtr); 0599 0600 /*ExceptionCode*//*khtml*/int ec = 0; 0601 m_shadowTreeRootElement->appendChild(newChild.releaseRef(), ec); 0602 ASSERT(ec == 0); 0603 0604 // Handle use referencing <svg> special case 0605 if (target->hasTagName(SVGNames::svgTag)) { 0606 alterShadowTreeForSVGTag(newChildPtr); 0607 } 0608 } 0609 0610 #if ENABLE(SVG) && ENABLE(SVG_USE) 0611 void SVGUseElement::expandUseElementsInShadowTree(Node *element) 0612 { 0613 // Why expand the <use> elements in the shadow tree here, and not just 0614 // do this directly in buildShadowTree, if we encounter a <use> element? 0615 // 0616 // Short answer: Because we may miss to expand some elements. Ie. if a <symbol> 0617 // contains <use> tags, we'd miss them. So once we're done with settin' up the 0618 // actual shadow tree (after the special case modification for svg/symbol) we have 0619 // to walk it completely and expand all <use> elements. 0620 if (element->hasTagName(SVGNames::useTag)) { 0621 SVGUseElement *use = static_cast<SVGUseElement *>(element); 0622 0623 String id = SVGURIReference::getTarget(use->href()); 0624 Element *targetElement = document()->getElementById(id); 0625 SVGElement *target = 0; 0626 if (targetElement && targetElement->isSVGElement()) { 0627 target = static_cast<SVGElement *>(targetElement); 0628 } 0629 0630 // Don't ASSERT(target) here, it may be "pending", too. 0631 if (target) { 0632 // Setup sub-shadow tree root node 0633 RefPtr<SVGElement> cloneParent = new SVGGElement(SVGNames::gTag, document()); 0634 0635 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the 0636 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element. 0637 transferUseAttributesToReplacedElement(use, cloneParent.get()); 0638 0639 // Spec: An additional transformation translate(x,y) is appended to the end 0640 // (i.e., right-side) of the transform attribute on the generated 'g', where x 0641 // and y represent the values of the x and y attributes on the 'use' element. 0642 if (use->x().value() != 0.0 || use->y().value() != 0.0) { 0643 if (!cloneParent->hasAttribute(SVGNames::transformAttr)) { 0644 String transformString = String::format("translate(%f, %f)", use->x().value(), use->y().value()); 0645 cloneParent->setAttribute(SVGNames::transformAttr, transformString); 0646 } else { 0647 String transformString = String::format(" translate(%f, %f)", use->x().value(), use->y().value()); 0648 const AtomicString &transformAttribute = cloneParent->getAttribute(SVGNames::transformAttr); 0649 cloneParent->setAttribute(SVGNames::transformAttr, transformAttribute + transformString); 0650 } 0651 } 0652 0653 ExceptionCode ec = 0; 0654 0655 // For instance <use> on <foreignObject> (direct case). 0656 if (isDisallowedElement(target)) { 0657 // We still have to setup the <use> replacment (<g>). Otherwhise 0658 // associateInstancesWithShadowTreeElements() makes wrong assumptions. 0659 // Replace <use> with referenced content. 0660 ASSERT(use->parentNode()); 0661 use->parentNode()->replaceChild(cloneParent.release(), use, ec); 0662 ASSERT(ec == 0); 0663 return; 0664 } 0665 0666 RefPtr<Node> newChild = target->cloneNode(true); 0667 0668 // We don't walk the target tree element-by-element, and clone each element, 0669 // but instead use cloneNode(deep=true). This is an optimization for the common 0670 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 0671 // Though if there are disallowed elements in the subtree, we have to remove them. 0672 // For instance: <use> on <g> containing <foreignObject> (indirect case). 0673 if (subtreeContainsDisallowedElement(newChild.get())) { 0674 removeDisallowedElementsFromSubtree(newChild.get()); 0675 } 0676 0677 SVGElement *newChildPtr = 0; 0678 if (newChild->isSVGElement()) { 0679 newChildPtr = static_cast<SVGElement *>(newChild.get()); 0680 } 0681 ASSERT(newChildPtr); 0682 0683 cloneParent->appendChild(newChild.release(), ec); 0684 ASSERT(ec == 0); 0685 0686 // Replace <use> with referenced content. 0687 ASSERT(use->parentNode()); 0688 use->parentNode()->replaceChild(cloneParent.release(), use, ec); 0689 ASSERT(ec == 0); 0690 0691 // Handle use referencing <svg> special case 0692 if (target->hasTagName(SVGNames::svgTag)) { 0693 alterShadowTreeForSVGTag(newChildPtr); 0694 } 0695 0696 // Immediately stop here, and restart expanding. 0697 expandUseElementsInShadowTree(m_shadowTreeRootElement.get()); 0698 return; 0699 } 0700 } 0701 0702 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) { 0703 expandUseElementsInShadowTree(child.get()); 0704 } 0705 } 0706 0707 void SVGUseElement::expandSymbolElementsInShadowTree(Node *element) 0708 { 0709 if (element->hasTagName(SVGNames::symbolTag)) { 0710 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree, 0711 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will 0712 // always have explicit values for attributes width and height. If attributes width and/or 0713 // height are provided on the 'use' element, then these attributes will be transferred to 0714 // the generated 'svg'. If attributes width and/or height are not specified, the generated 0715 // 'svg' element will use values of 100% for these attributes. 0716 RefPtr<SVGSVGElement> svgElement = new SVGSVGElement(SVGNames::svgTag, document()); 0717 0718 // Transfer all attributes from <symbol> to the new <svg> element 0719 svgElement->attributes()->setAttributes(*element->attributes()); 0720 0721 // Explicitly re-set width/height values 0722 String widthString = String::number(width().value()); 0723 String heightString = String::number(height().value()); 0724 0725 svgElement->setAttribute(SVGNames::widthAttr, hasAttribute(SVGNames::widthAttr) ? widthString : "100%"); 0726 svgElement->setAttribute(SVGNames::heightAttr, hasAttribute(SVGNames::heightAttr) ? heightString : "100%"); 0727 0728 ExceptionCode ec = 0; 0729 0730 // Only clone symbol children, and add them to the new <svg> element 0731 for (Node *child = element->firstChild(); child; child = child->nextSibling()) { 0732 RefPtr<Node> newChild = child->cloneNode(true); 0733 svgElement->appendChild(newChild.release(), ec); 0734 ASSERT(ec == 0); 0735 } 0736 0737 // We don't walk the target tree element-by-element, and clone each element, 0738 // but instead use cloneNode(deep=true). This is an optimization for the common 0739 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 0740 // Though if there are disallowed elements in the subtree, we have to remove them. 0741 // For instance: <use> on <g> containing <foreignObject> (indirect case). 0742 if (subtreeContainsDisallowedElement(svgElement.get())) { 0743 removeDisallowedElementsFromSubtree(svgElement.get()); 0744 } 0745 0746 // Replace <symbol> with <svg>. 0747 ASSERT(element->parentNode()); 0748 element->parentNode()->replaceChild(svgElement.release(), element, ec); 0749 ASSERT(ec == 0); 0750 0751 // Immediately stop here, and restart expanding. 0752 expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get()); 0753 return; 0754 } 0755 0756 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) { 0757 expandSymbolElementsInShadowTree(child.get()); 0758 } 0759 } 0760 0761 #endif 0762 0763 void SVGUseElement::attachShadowTree() 0764 { 0765 if (!m_shadowTreeRootElement || m_shadowTreeRootElement->attached() || /*khtml !document()->shouldCreateRenderers() ||*/ !attached() || !renderer()) { 0766 return; 0767 } 0768 0769 // Inspired by RenderTextControl::createSubtreeIfNeeded(). 0770 if (renderer()->childAllowed()/*canHaveChildren()*/ && childShouldCreateRenderer(m_shadowTreeRootElement.get())) { 0771 RenderStyle *style = m_shadowTreeRootElement->styleForRenderer(renderer()); 0772 style->ref(); 0773 0774 if (m_shadowTreeRootElement->rendererIsNeeded(style)) { 0775 m_shadowTreeRootElement->setRenderer(m_shadowTreeRootElement->createRenderer(document()->renderArena(), style)); 0776 if (RenderObject *shadowRenderer = m_shadowTreeRootElement->renderer()) { 0777 shadowRenderer->setStyle(style); 0778 renderer()->addChild(shadowRenderer, m_shadowTreeRootElement->nextRenderer()); 0779 m_shadowTreeRootElement->setAttached(); 0780 } 0781 } 0782 0783 style->deref(); 0784 0785 // This will take care of attaching all shadow tree child nodes. 0786 for (Node *child = m_shadowTreeRootElement->firstChild(); child; child = child->nextSibling()) { 0787 child->attach(); 0788 } 0789 } 0790 } 0791 0792 void SVGUseElement::associateInstancesWithShadowTreeElements(Node *target, SVGElementInstance *targetInstance) 0793 { 0794 if (!target || !targetInstance) { 0795 return; 0796 } 0797 0798 SVGElement *originalElement = targetInstance->correspondingElement(); 0799 0800 if (originalElement->hasTagName(SVGNames::useTag)) { 0801 #if ENABLE(SVG) && ENABLE(SVG_USE) 0802 // <use> gets replaced by <g> 0803 /*ASSERT(target->nodeName() == SVGNames::gTag);*/ 0804 #else 0805 /*ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag);*/ 0806 #endif 0807 } else if (originalElement->hasTagName(SVGNames::symbolTag)) { 0808 // <symbol> gets replaced by <svg> 0809 #if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT) 0810 ASSERT(target->nodeName() == SVGNames::svgTag); 0811 #endif 0812 } else { 0813 ASSERT(target->nodeName() == originalElement->nodeName()); 0814 } 0815 0816 SVGElement *element = nullptr; 0817 if (target->isSVGElement()) { 0818 element = static_cast<SVGElement *>(target); 0819 } 0820 0821 ASSERT(!targetInstance->shadowTreeElement()); 0822 targetInstance->setShadowTreeElement(element); 0823 0824 Node *node = target->firstChild(); 0825 for (SVGElementInstance *instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) { 0826 // Skip any non-svg elements in shadow tree 0827 while (node && !node->isSVGElement()) { 0828 node = node->nextSibling(); 0829 } 0830 0831 associateInstancesWithShadowTreeElements(node, instance); 0832 node = node->nextSibling(); 0833 } 0834 } 0835 0836 SVGElementInstance *SVGUseElement::instanceForShadowTreeElement(Node *element) const 0837 { 0838 return instanceForShadowTreeElement(element, m_targetElementInstance.get()); 0839 } 0840 0841 SVGElementInstance *SVGUseElement::instanceForShadowTreeElement(Node *element, SVGElementInstance *instance) const 0842 { 0843 ASSERT(element); 0844 ASSERT(instance); 0845 ASSERT(instance->shadowTreeElement()); 0846 0847 if (element == instance->shadowTreeElement()) { 0848 return instance; 0849 } 0850 0851 for (SVGElementInstance *current = instance->firstChild(); current; current = current->nextSibling()) { 0852 SVGElementInstance *search = instanceForShadowTreeElement(element, current); 0853 if (search) { 0854 return search; 0855 } 0856 } 0857 0858 return nullptr; 0859 } 0860 0861 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement *from, SVGElement *to) const 0862 { 0863 Q_UNUSED(from); 0864 Q_UNUSED(to); 0865 // Implement me: khtml 0866 /*ASSERT(from); 0867 ASSERT(to); 0868 0869 to->attributes()->setAttributes(*from->attributes()); 0870 0871 ExceptionCode ec = 0; 0872 0873 to->removeAttribute(SVGNames::xAttr, ec); 0874 ASSERT(ec == 0); 0875 0876 to->removeAttribute(SVGNames::yAttr, ec); 0877 ASSERT(ec == 0); 0878 0879 to->removeAttribute(SVGNames::widthAttr, ec); 0880 ASSERT(ec == 0); 0881 0882 to->removeAttribute(SVGNames::heightAttr, ec); 0883 ASSERT(ec == 0); 0884 0885 to->removeAttribute(XLinkNames::hrefAttr, ec); 0886 ASSERT(ec == 0);*/ 0887 } 0888 0889 } 0890 0891 #endif // ENABLE(SVG)