KHTML
SVGUseElement.cpp
Go to the documentation of this file.
00001 /* 00002 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 00003 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> 00004 00005 This file is part of the KDE project 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00022 00023 #include "config.h" 00024 #include "wtf/Platform.h" 00025 00026 // Dump SVGElementInstance object tree - useful to debug instanceRoot problems 00027 // #define DUMP_INSTANCE_TREE 00028 00029 // Dump the deep-expanded shadow tree (where the renderers are built from) 00030 // #define DUMP_SHADOW_TREE 00031 00032 #if ENABLE(SVG) 00033 #include "SVGUseElement.h" 00034 00035 #include "css/cssstyleselector.h" 00036 /*#include "CString.h"*/ 00037 #include "Document.h" 00038 /*#include "Event.h" 00039 #include "HTMLNames.h"*/ 00040 #include "RenderSVGTransformableContainer.h" 00041 #include "SVGElementInstance.h" 00042 #include "SVGElementInstanceList.h" 00043 #include "SVGGElement.h" 00044 #include "SVGLength.h" 00045 #include "SVGNames.h" 00046 #include "SVGPreserveAspectRatio.h" 00047 /*#include "SVGSMILElement.h"*/ 00048 #include "SVGSVGElement.h" 00049 #include "SVGSymbolElement.h" 00050 #include "XLinkNames.h" 00051 /*#include "XMLSerializer.h"*/ 00052 #include <wtf/OwnPtr.h> 00053 00054 namespace WebCore { 00055 00056 SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* doc) 00057 : SVGStyledTransformableElement(tagName, doc) 00058 , SVGTests() 00059 , SVGLangSpace() 00060 , SVGExternalResourcesRequired() 00061 , SVGURIReference() 00062 , m_x(this, LengthModeWidth) 00063 , m_y(this, LengthModeHeight) 00064 , m_width(this, LengthModeWidth) 00065 , m_height(this, LengthModeHeight) 00066 { 00067 } 00068 00069 SVGUseElement::~SVGUseElement() 00070 { 00071 } 00072 00073 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, X, x, SVGNames::xAttr, m_x) 00074 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Y, y, SVGNames::yAttr, m_y) 00075 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Width, width, SVGNames::widthAttr, m_width) 00076 ANIMATED_PROPERTY_DEFINITIONS(SVGUseElement, SVGLength, Length, length, Height, height, SVGNames::heightAttr, m_height) 00077 00078 SVGElementInstance* SVGUseElement::instanceRoot() const 00079 { 00080 return m_targetElementInstance.get(); 00081 } 00082 00083 SVGElementInstance* SVGUseElement::animatedInstanceRoot() const 00084 { 00085 // FIXME: Implement me. 00086 return 0; 00087 } 00088 00089 void SVGUseElement::parseMappedAttribute(MappedAttribute* attr) 00090 { 00091 if (attr->name() == SVGNames::xAttr) 00092 setXBaseValue(SVGLength(this, LengthModeWidth, attr->value())); 00093 else if (attr->name() == SVGNames::yAttr) 00094 setYBaseValue(SVGLength(this, LengthModeHeight, attr->value())); 00095 else if (attr->name() == SVGNames::widthAttr) { 00096 setWidthBaseValue(SVGLength(this, LengthModeWidth, attr->value())); 00097 if (width().value() < 0.0) 00098 document()->accessSVGExtensions()->reportError("A negative value for use attribute <width> is not allowed"); 00099 } else if (attr->name() == SVGNames::heightAttr) { 00100 setHeightBaseValue(SVGLength(this, LengthModeHeight, attr->value())); 00101 if (height().value() < 0.0) 00102 document()->accessSVGExtensions()->reportError("A negative value for use attribute <height> is not allowed"); 00103 } else { 00104 if (SVGTests::parseMappedAttribute(attr)) 00105 return; 00106 if (SVGLangSpace::parseMappedAttribute(attr)) 00107 return; 00108 if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) 00109 return; 00110 if (SVGURIReference::parseMappedAttribute(attr)) 00111 return; 00112 SVGStyledTransformableElement::parseMappedAttribute(attr); 00113 } 00114 } 00115 00116 void SVGUseElement::insertedIntoDocument() 00117 { 00118 SVGElement::insertedIntoDocument(); 00119 buildPendingResource(); 00120 } 00121 00122 void SVGUseElement::removedFromDocument() 00123 { 00124 m_targetElementInstance = 0; 00125 m_shadowTreeRootElement = 0; 00126 SVGElement::removedFromDocument(); 00127 } 00128 00129 void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName) 00130 { 00131 SVGStyledTransformableElement::svgAttributeChanged(attrName); 00132 00133 if (!attached()) 00134 return; 00135 00136 if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || 00137 attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr || 00138 SVGTests::isKnownAttribute(attrName) || 00139 SVGLangSpace::isKnownAttribute(attrName) || 00140 SVGExternalResourcesRequired::isKnownAttribute(attrName) || 00141 SVGURIReference::isKnownAttribute(attrName) || 00142 SVGStyledTransformableElement::isKnownAttribute(attrName)) { 00143 // TODO: Now that we're aware of the attribute name, we can finally optimize 00144 // updating <use> attributes - to not reclone every time. 00145 buildPendingResource(); 00146 00147 if (m_shadowTreeRootElement) 00148 m_shadowTreeRootElement->setChanged(); 00149 } 00150 } 00151 00152 void SVGUseElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) 00153 { 00154 Q_UNUSED(changedByParser); 00155 Q_UNUSED(beforeChange); 00156 Q_UNUSED(afterChange); 00157 Q_UNUSED(childCountDelta); 00158 SVGElement::childrenChanged(/*changedByParser, beforeChange, afterChange, childCountDelta*/); 00159 00160 if (!attached()) 00161 return; 00162 00163 buildPendingResource(); 00164 00165 if (m_shadowTreeRootElement) 00166 m_shadowTreeRootElement->setChanged(); 00167 } 00168 00169 void SVGUseElement::recalcStyle(StyleChange change) 00170 { 00171 SVGStyledElement::recalcStyle(change); 00172 00173 // The shadow tree root element is NOT a direct child element of us. 00174 // So we have to take care it receives style updates, manually. 00175 if (!m_shadowTreeRootElement || !m_shadowTreeRootElement->attached()) 00176 return; 00177 00178 // Mimic Element::recalcStyle(). The main difference is that we don't call attach() on the 00179 // shadow tree root element, but call attachShadowTree() here. Calling attach() will crash 00180 // as the shadow tree root element has no (direct) parent node. Yes, shadow trees are tricky. 00181 if (change >= Inherit || m_shadowTreeRootElement->changed()) { 00182 RenderStyle* newStyle = document()->styleSelector()->styleForElement(m_shadowTreeRootElement.get()); 00183 newStyle->ref(); 00184 StyleChange ch = m_shadowTreeRootElement->diff((m_shadowTreeRootElement->renderer() ? m_shadowTreeRootElement->renderer()->style() : 0)/*renderStyle()*/, newStyle); 00185 if (ch == Detach) { 00186 ASSERT(m_shadowTreeRootElement->attached()); 00187 m_shadowTreeRootElement->detach(); 00188 attachShadowTree(); 00189 00190 // attach recalulates the style for all children. No need to do it twice. 00191 m_shadowTreeRootElement->setChanged(false); 00192 m_shadowTreeRootElement->setHasChangedChild(false); 00193 newStyle->deref(); 00194 return; 00195 } 00196 00197 newStyle->deref(); 00198 } 00199 00200 // Only change==Detach needs special treatment, for anything else recalcStyle() works. 00201 m_shadowTreeRootElement->recalcStyle(change); 00202 } 00203 00204 #ifdef DUMP_INSTANCE_TREE 00205 void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance) 00206 { 00207 SVGElement* element = targetInstance->correspondingElement(); 00208 ASSERT(element); 00209 00210 String elementId = element->getIDAttribute(); 00211 String elementNodeName = element->nodeName(); 00212 String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null"; 00213 String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null"; 00214 00215 for (unsigned int i = 0; i < depth; ++i) 00216 text += " "; 00217 00218 text += String::format("SVGElementInstance (parentNode=%s, firstChild=%s, correspondingElement=%s, id=%s)\n", 00219 parentNodeName.latin1().data(), firstChildNodeName.latin1().data(), elementNodeName.latin1().data(), elementId.latin1().data()); 00220 00221 depth++; 00222 00223 for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) 00224 dumpInstanceTree(depth, text, instance); 00225 00226 depth--; 00227 } 00228 #endif 00229 00230 static bool isDisallowedElement(Node* element) 00231 { 00232 Q_UNUSED(element); 00233 #if ENABLE(SVG_FOREIGN_OBJECT) 00234 // <foreignObject> should never be contained in a <use> tree. Too dangerous side effects possible. 00235 if (element->hasTagName(SVGNames::foreignObjectTag)) 00236 return true; 00237 #endif 00238 #if ENABLE(SVG_ANIMATION) 00239 if (SVGSMILElement::isSMILElement(element)) 00240 return true; 00241 #endif 00242 00243 return false; 00244 } 00245 00246 static bool subtreeContainsDisallowedElement(Node* start) 00247 { 00248 if (isDisallowedElement(start)) 00249 return true; 00250 00251 for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) { 00252 if (subtreeContainsDisallowedElement(cur)) 00253 return true; 00254 } 00255 00256 return false; 00257 } 00258 00259 void SVGUseElement::buildPendingResource() 00260 { 00261 String id = SVGURIReference::getTarget(href()); 00262 Element* targetElement = document()->getElementById(id); 00263 00264 if (!targetElement) { 00265 // TODO: We want to deregister as pending resource, if our href() changed! 00266 // TODO: Move to svgAttributeChanged, once we're fixing use & the new dynamic update concept. 00267 document()->accessSVGExtensions()->addPendingResource(id, this); 00268 return; 00269 } 00270 00271 // Do not build the shadow/instance tree for <use> elements living in a shadow tree. 00272 // The will be expanded soon anyway - see expandUseElementsInShadowTree(). 00273 Node* parent = parentNode(); 00274 while (parent) { 00275 if (parent->isShadowNode()) 00276 return; 00277 00278 parent = parent->parentNode(); 00279 } 00280 00281 SVGElement* target = 0; 00282 if (targetElement && targetElement->isSVGElement()) 00283 target = static_cast<SVGElement*>(targetElement); 00284 00285 // Do not allow self-referencing. 00286 // 'target' may be null, if it's a non SVG namespaced element. 00287 if (!target || target == this) { 00288 m_targetElementInstance = 0; 00289 m_shadowTreeRootElement = 0; 00290 return; 00291 } 00292 00293 // Why a separated instance/shadow tree? SVG demands it: 00294 // The instance tree is accesable from JavaScript, and has to 00295 // expose a 1:1 copy of the referenced tree, whereas internally we need 00296 // to alter the tree for correct "use-on-symbol", "use-on-svg" support. 00297 00298 // Build instance tree. Create root SVGElementInstance object for the first sub-tree node. 00299 // 00300 // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a 00301 // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object 00302 // is the SVGRectElement that corresponds to the referenced 'rect' element. 00303 m_targetElementInstance = new SVGElementInstance(this, target); 00304 00305 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children 00306 bool foundProblem = false; 00307 buildInstanceTree(target, m_targetElementInstance.get(), foundProblem); 00308 00309 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it! 00310 // Non-appearing <use> content is easier to debug, then half-appearing content. 00311 if (foundProblem) { 00312 m_targetElementInstance = 0; 00313 m_shadowTreeRootElement = 0; 00314 return; 00315 } 00316 00317 // Assure instance tree building was successful 00318 ASSERT(m_targetElementInstance); 00319 ASSERT(m_targetElementInstance->correspondingUseElement() == this); 00320 00321 // Setup shadow tree root node 00322 m_shadowTreeRootElement = new SVGGElement(SVGNames::gTag, document()); 00323 m_shadowTreeRootElement->setInDocument(); 00324 m_shadowTreeRootElement->setShadowParentNode(this); 00325 00326 // Spec: An additional transformation translate(x,y) is appended to the end 00327 // (i.e., right-side) of the transform attribute on the generated 'g', where x 00328 // and y represent the values of the x and y attributes on the 'use' element. 00329 if (x().value() != 0.0 || y().value() != 0.0) { 00330 String transformString = String::format("translate(%f, %f)", x().value(), y().value()); 00331 m_shadowTreeRootElement->setAttribute(SVGNames::transformAttr, transformString); 00332 } 00333 00334 // Build shadow tree from instance tree 00335 // This also handles the special cases: <use> on <symbol>, <use> on <svg>. 00336 buildShadowTree(target, m_targetElementInstance.get()); 00337 00338 #if ENABLE(SVG) && ENABLE(SVG_USE) 00339 // Expand all <use> elements in the shadow tree. 00340 // Expand means: replace the actual <use> element by what it references. 00341 expandUseElementsInShadowTree(m_shadowTreeRootElement.get()); 00342 00343 // Expand all <symbol> elements in the shadow tree. 00344 // Expand means: replace the actual <symbol> element by the <svg> element. 00345 expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get()); 00346 00347 #endif 00348 00349 // Now that the shadow tree is completely expanded, we can associate 00350 // shadow tree elements <-> instances in the instance tree. 00351 associateInstancesWithShadowTreeElements(m_shadowTreeRootElement->firstChild(), m_targetElementInstance.get()); 00352 00353 // Eventually dump instance tree 00354 #ifdef DUMP_INSTANCE_TREE 00355 String text; 00356 unsigned int depth = 0; 00357 00358 dumpInstanceTree(depth, text, m_targetElementInstance.get()); 00359 fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data()); 00360 #endif 00361 00362 // Eventually dump shadow tree 00363 #ifdef DUMP_SHADOW_TREE 00364 ExceptionCode ec = 0; 00365 00366 PassRefPtr<XMLSerializer> serializer = XMLSerializer::create(); 00367 00368 String markup = serializer->serializeToString(m_shadowTreeRootElement.get(), ec); 00369 ASSERT(ec == 0); 00370 00371 fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data()); 00372 #endif 00373 00374 // The DOM side is setup properly. Now we have to attach the root shadow 00375 // tree element manually - using attach() won't work for "shadow nodes". 00376 attachShadowTree(); 00377 } 00378 00379 RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*) 00380 { 00381 return new (arena) RenderSVGTransformableContainer(this); 00382 } 00383 00384 void SVGUseElement::attach() 00385 { 00386 SVGStyledTransformableElement::attach(); 00387 00388 // If we're a pending resource, this doesn't have any effect. 00389 attachShadowTree(); 00390 } 00391 00392 void SVGUseElement::detach() 00393 { 00394 if (m_shadowTreeRootElement) 00395 m_shadowTreeRootElement->detach(); 00396 00397 SVGStyledTransformableElement::detach(); 00398 } 00399 00400 static bool isDirectReference(Node* n) 00401 { 00402 return n->hasTagName(SVGNames::pathTag) || 00403 n->hasTagName(SVGNames::rectTag) || 00404 n->hasTagName(SVGNames::circleTag) || 00405 n->hasTagName(SVGNames::ellipseTag) || 00406 n->hasTagName(SVGNames::polygonTag) || 00407 n->hasTagName(SVGNames::polylineTag) || 00408 n->hasTagName(SVGNames::textTag); 00409 } 00410 00411 Path SVGUseElement::toClipPath() const 00412 { 00413 if (!m_shadowTreeRootElement) 00414 const_cast<SVGUseElement*>(this)->buildPendingResource(); 00415 00416 if (!m_shadowTreeRootElement) 00417 return Path(); 00418 00419 Node* n = m_shadowTreeRootElement->firstChild(); 00420 if (n->isSVGElement() && static_cast<SVGElement*>(n)->isStyledTransformable()) { 00421 if (!isDirectReference(n)) 00422 // Spec: Indirect references are an error (14.3.5) 00423 document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>"); 00424 else 00425 return static_cast<SVGStyledTransformableElement*>(n)->toClipPath(); 00426 } 00427 00428 return Path(); 00429 } 00430 00431 void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem) 00432 { 00433 ASSERT(target); 00434 ASSERT(targetInstance); 00435 00436 // A general description from the SVG spec, describing what buildInstanceTree() actually does. 00437 // 00438 // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree 00439 // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement 00440 // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has 00441 // its correspondingElement that is an SVGRectElement object. 00442 00443 for (Node* node = target->firstChild(); node; node = node->nextSibling()) { 00444 SVGElement* element = 0; 00445 if (node->isSVGElement()) 00446 element = static_cast<SVGElement*>(node); 00447 00448 // Skip any non-svg nodes or any disallowed element. 00449 if (!element || isDisallowedElement(element)) 00450 continue; 00451 00452 // Create SVGElementInstance object, for both container/non-container nodes. 00453 SVGElementInstance* instancePtr = new SVGElementInstance(this, element); 00454 00455 RefPtr<SVGElementInstance> instance = instancePtr; 00456 targetInstance->appendChild(instance.release()); 00457 00458 // Enter recursion, appending new instance tree nodes to the "instance" object. 00459 if (element->hasChildNodes()) 00460 buildInstanceTree(element, instancePtr, foundProblem); 00461 00462 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced 00463 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. 00464 if (element->hasTagName(SVGNames::useTag)) 00465 handleDeepUseReferencing(element, instancePtr, foundProblem); 00466 } 00467 00468 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced 00469 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. 00470 if (target->hasTagName(SVGNames::useTag)) 00471 handleDeepUseReferencing(target, targetInstance, foundProblem); 00472 } 00473 00474 void SVGUseElement::handleDeepUseReferencing(SVGElement* use, SVGElementInstance* targetInstance, bool& foundProblem) 00475 { 00476 String id = SVGURIReference::getTarget(use->href()); 00477 Element* targetElement = document()->getElementById(id); 00478 SVGElement* target = 0; 00479 if (targetElement && targetElement->isSVGElement()) 00480 target = static_cast<SVGElement*>(targetElement); 00481 00482 if (!target) 00483 return; 00484 00485 // Cycle detection first! 00486 foundProblem = (target == this); 00487 00488 // Shortcut for self-references 00489 if (foundProblem) 00490 return; 00491 00492 SVGElementInstance* instance = targetInstance->parentNode(); 00493 while (instance) { 00494 SVGElement* element = instance->correspondingElement(); 00495 00496 if (element->getIDAttribute() == id) { 00497 foundProblem = true; 00498 return; 00499 } 00500 00501 instance = instance->parentNode(); 00502 } 00503 00504 // Create an instance object, even if we're dealing with a cycle 00505 SVGElementInstance* newInstance = new SVGElementInstance(this, target); 00506 targetInstance->appendChild(newInstance); 00507 00508 // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children 00509 buildInstanceTree(target, newInstance, foundProblem); 00510 } 00511 00512 void SVGUseElement::alterShadowTreeForSVGTag(SVGElement* target) 00513 { 00514 String widthString = String::number(width().value()); 00515 String heightString = String::number(height().value()); 00516 00517 if (hasAttribute(SVGNames::widthAttr)) 00518 target->setAttribute(SVGNames::widthAttr, widthString); 00519 00520 if (hasAttribute(SVGNames::heightAttr)) 00521 target->setAttribute(SVGNames::heightAttr, heightString); 00522 } 00523 00524 void SVGUseElement::removeDisallowedElementsFromSubtree(Node* subtree) 00525 { 00526 Q_UNUSED(subtree); 00527 // Implement me: khtml, NodeImpl::traverseNextSibling 00528 /*ASSERT(!subtree->inDocument()); 00529 ExceptionCode ec; 00530 Node* node = subtree->firstChild(); 00531 while (node) { 00532 if (isDisallowedElement(node)) { 00533 Node* next = node->traverseNextSibling(subtree); 00534 // The subtree is not in document so this won't generate events that could mutate the tree. 00535 node->parent()->removeChild(node, ec); 00536 node = next; 00537 } else 00538 node = node->traverseNextNode(subtree); 00539 }*/ 00540 } 00541 00542 void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targetInstance) 00543 { 00544 // For instance <use> on <foreignObject> (direct case). 00545 if (isDisallowedElement(target)) 00546 return; 00547 00548 PassRefPtr<NodeImpl> newChild = targetInstance->correspondingElement()->cloneNode(true); 00549 00550 // We don't walk the target tree element-by-element, and clone each element, 00551 // but instead use cloneNode(deep=true). This is an optimization for the common 00552 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 00553 // Though if there are disallowed elements in the subtree, we have to remove them. 00554 // For instance: <use> on <g> containing <foreignObject> (indirect case). 00555 if (subtreeContainsDisallowedElement(newChild.get())) 00556 removeDisallowedElementsFromSubtree(newChild.get()); 00557 00558 SVGElement* newChildPtr = 0; 00559 if (newChild->isSVGElement()) 00560 newChildPtr = static_cast<SVGElement*>(newChild.get()); 00561 ASSERT(newChildPtr); 00562 00563 /*ExceptionCode*//*khtml*/int ec = 0; 00564 m_shadowTreeRootElement->appendChild(newChild.releaseRef(), ec); 00565 ASSERT(ec == 0); 00566 00567 // Handle use referencing <svg> special case 00568 if (target->hasTagName(SVGNames::svgTag)) 00569 alterShadowTreeForSVGTag(newChildPtr); 00570 } 00571 00572 #if ENABLE(SVG) && ENABLE(SVG_USE) 00573 void SVGUseElement::expandUseElementsInShadowTree(Node* element) 00574 { 00575 // Why expand the <use> elements in the shadow tree here, and not just 00576 // do this directly in buildShadowTree, if we encounter a <use> element? 00577 // 00578 // Short answer: Because we may miss to expand some elements. Ie. if a <symbol> 00579 // contains <use> tags, we'd miss them. So once we're done with settin' up the 00580 // actual shadow tree (after the special case modification for svg/symbol) we have 00581 // to walk it completely and expand all <use> elements. 00582 if (element->hasTagName(SVGNames::useTag)) { 00583 SVGUseElement* use = static_cast<SVGUseElement*>(element); 00584 00585 String id = SVGURIReference::getTarget(use->href()); 00586 Element* targetElement = document()->getElementById(id); 00587 SVGElement* target = 0; 00588 if (targetElement && targetElement->isSVGElement()) 00589 target = static_cast<SVGElement*>(targetElement); 00590 00591 // Don't ASSERT(target) here, it may be "pending", too. 00592 if (target) { 00593 // Setup sub-shadow tree root node 00594 RefPtr<SVGElement> cloneParent = new SVGGElement(SVGNames::gTag, document()); 00595 00596 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the 00597 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element. 00598 transferUseAttributesToReplacedElement(use, cloneParent.get()); 00599 00600 // Spec: An additional transformation translate(x,y) is appended to the end 00601 // (i.e., right-side) of the transform attribute on the generated 'g', where x 00602 // and y represent the values of the x and y attributes on the 'use' element. 00603 if (use->x().value() != 0.0 || use->y().value() != 0.0) { 00604 if (!cloneParent->hasAttribute(SVGNames::transformAttr)) { 00605 String transformString = String::format("translate(%f, %f)", use->x().value(), use->y().value()); 00606 cloneParent->setAttribute(SVGNames::transformAttr, transformString); 00607 } else { 00608 String transformString = String::format(" translate(%f, %f)", use->x().value(), use->y().value()); 00609 const AtomicString& transformAttribute = cloneParent->getAttribute(SVGNames::transformAttr); 00610 cloneParent->setAttribute(SVGNames::transformAttr, transformAttribute + transformString); 00611 } 00612 } 00613 00614 ExceptionCode ec = 0; 00615 00616 // For instance <use> on <foreignObject> (direct case). 00617 if (isDisallowedElement(target)) { 00618 // We still have to setup the <use> replacment (<g>). Otherwhise 00619 // associateInstancesWithShadowTreeElements() makes wrong assumptions. 00620 // Replace <use> with referenced content. 00621 ASSERT(use->parentNode()); 00622 use->parentNode()->replaceChild(cloneParent.release(), use, ec); 00623 ASSERT(ec == 0); 00624 return; 00625 } 00626 00627 RefPtr<Node> newChild = target->cloneNode(true); 00628 00629 // We don't walk the target tree element-by-element, and clone each element, 00630 // but instead use cloneNode(deep=true). This is an optimization for the common 00631 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 00632 // Though if there are disallowed elements in the subtree, we have to remove them. 00633 // For instance: <use> on <g> containing <foreignObject> (indirect case). 00634 if (subtreeContainsDisallowedElement(newChild.get())) 00635 removeDisallowedElementsFromSubtree(newChild.get()); 00636 00637 SVGElement* newChildPtr = 0; 00638 if (newChild->isSVGElement()) 00639 newChildPtr = static_cast<SVGElement*>(newChild.get()); 00640 ASSERT(newChildPtr); 00641 00642 cloneParent->appendChild(newChild.release(), ec); 00643 ASSERT(ec == 0); 00644 00645 // Replace <use> with referenced content. 00646 ASSERT(use->parentNode()); 00647 use->parentNode()->replaceChild(cloneParent.release(), use, ec); 00648 ASSERT(ec == 0); 00649 00650 // Handle use referencing <svg> special case 00651 if (target->hasTagName(SVGNames::svgTag)) 00652 alterShadowTreeForSVGTag(newChildPtr); 00653 00654 // Immediately stop here, and restart expanding. 00655 expandUseElementsInShadowTree(m_shadowTreeRootElement.get()); 00656 return; 00657 } 00658 } 00659 00660 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) 00661 expandUseElementsInShadowTree(child.get()); 00662 } 00663 00664 void SVGUseElement::expandSymbolElementsInShadowTree(Node* element) 00665 { 00666 if (element->hasTagName(SVGNames::symbolTag)) { 00667 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree, 00668 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will 00669 // always have explicit values for attributes width and height. If attributes width and/or 00670 // height are provided on the 'use' element, then these attributes will be transferred to 00671 // the generated 'svg'. If attributes width and/or height are not specified, the generated 00672 // 'svg' element will use values of 100% for these attributes. 00673 RefPtr<SVGSVGElement> svgElement = new SVGSVGElement(SVGNames::svgTag, document()); 00674 00675 // Transfer all attributes from <symbol> to the new <svg> element 00676 svgElement->attributes()->setAttributes(*element->attributes()); 00677 00678 // Explicitly re-set width/height values 00679 String widthString = String::number(width().value()); 00680 String heightString = String::number(height().value()); 00681 00682 svgElement->setAttribute(SVGNames::widthAttr, hasAttribute(SVGNames::widthAttr) ? widthString : "100%"); 00683 svgElement->setAttribute(SVGNames::heightAttr, hasAttribute(SVGNames::heightAttr) ? heightString : "100%"); 00684 00685 ExceptionCode ec = 0; 00686 00687 // Only clone symbol children, and add them to the new <svg> element 00688 for (Node* child = element->firstChild(); child; child = child->nextSibling()) { 00689 RefPtr<Node> newChild = child->cloneNode(true); 00690 svgElement->appendChild(newChild.release(), ec); 00691 ASSERT(ec == 0); 00692 } 00693 00694 // We don't walk the target tree element-by-element, and clone each element, 00695 // but instead use cloneNode(deep=true). This is an optimization for the common 00696 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). 00697 // Though if there are disallowed elements in the subtree, we have to remove them. 00698 // For instance: <use> on <g> containing <foreignObject> (indirect case). 00699 if (subtreeContainsDisallowedElement(svgElement.get())) 00700 removeDisallowedElementsFromSubtree(svgElement.get()); 00701 00702 // Replace <symbol> with <svg>. 00703 ASSERT(element->parentNode()); 00704 element->parentNode()->replaceChild(svgElement.release(), element, ec); 00705 ASSERT(ec == 0); 00706 00707 // Immediately stop here, and restart expanding. 00708 expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get()); 00709 return; 00710 } 00711 00712 for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) 00713 expandSymbolElementsInShadowTree(child.get()); 00714 } 00715 00716 #endif 00717 00718 void SVGUseElement::attachShadowTree() 00719 { 00720 if (!m_shadowTreeRootElement || m_shadowTreeRootElement->attached() || /*khtml !document()->shouldCreateRenderers() ||*/ !attached() || !renderer()) 00721 return; 00722 00723 // Inspired by RenderTextControl::createSubtreeIfNeeded(). 00724 if (renderer()->childAllowed()/*canHaveChildren()*/ && childShouldCreateRenderer(m_shadowTreeRootElement.get())) { 00725 RenderStyle* style = m_shadowTreeRootElement->styleForRenderer(renderer()); 00726 style->ref(); 00727 00728 if (m_shadowTreeRootElement->rendererIsNeeded(style)) { 00729 m_shadowTreeRootElement->setRenderer(m_shadowTreeRootElement->createRenderer(document()->renderArena(), style)); 00730 if (RenderObject* shadowRenderer = m_shadowTreeRootElement->renderer()) { 00731 shadowRenderer->setStyle(style); 00732 renderer()->addChild(shadowRenderer, m_shadowTreeRootElement->nextRenderer()); 00733 m_shadowTreeRootElement->setAttached(); 00734 } 00735 } 00736 00737 style->deref(); 00738 00739 // This will take care of attaching all shadow tree child nodes. 00740 for (Node* child = m_shadowTreeRootElement->firstChild(); child; child = child->nextSibling()) 00741 child->attach(); 00742 } 00743 } 00744 00745 void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance) 00746 { 00747 if (!target || !targetInstance) 00748 return; 00749 00750 SVGElement* originalElement = targetInstance->correspondingElement(); 00751 00752 if (originalElement->hasTagName(SVGNames::useTag)) { 00753 #if ENABLE(SVG) && ENABLE(SVG_USE) 00754 // <use> gets replaced by <g> 00755 /*ASSERT(target->nodeName() == SVGNames::gTag);*/ 00756 #else 00757 /*ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag);*/ 00758 #endif 00759 } else if (originalElement->hasTagName(SVGNames::symbolTag)) { 00760 // <symbol> gets replaced by <svg> 00761 #if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT) 00762 ASSERT(target->nodeName() == SVGNames::svgTag); 00763 #endif 00764 } else 00765 ASSERT(target->nodeName() == originalElement->nodeName()); 00766 00767 SVGElement* element = 0; 00768 if (target->isSVGElement()) 00769 element = static_cast<SVGElement*>(target); 00770 00771 ASSERT(!targetInstance->shadowTreeElement()); 00772 targetInstance->setShadowTreeElement(element); 00773 00774 Node* node = target->firstChild(); 00775 for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) { 00776 // Skip any non-svg elements in shadow tree 00777 while (node && !node->isSVGElement()) 00778 node = node->nextSibling(); 00779 00780 associateInstancesWithShadowTreeElements(node, instance); 00781 node = node->nextSibling(); 00782 } 00783 } 00784 00785 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const 00786 { 00787 return instanceForShadowTreeElement(element, m_targetElementInstance.get()); 00788 } 00789 00790 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const 00791 { 00792 ASSERT(element); 00793 ASSERT(instance); 00794 ASSERT(instance->shadowTreeElement()); 00795 00796 if (element == instance->shadowTreeElement()) 00797 return instance; 00798 00799 for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) { 00800 SVGElementInstance* search = instanceForShadowTreeElement(element, current); 00801 if (search) 00802 return search; 00803 } 00804 00805 return 0; 00806 } 00807 00808 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const 00809 { 00810 Q_UNUSED(from); 00811 Q_UNUSED(to); 00812 // Implement me: khtml 00813 /*ASSERT(from); 00814 ASSERT(to); 00815 00816 to->attributes()->setAttributes(*from->attributes()); 00817 00818 ExceptionCode ec = 0; 00819 00820 to->removeAttribute(SVGNames::xAttr, ec); 00821 ASSERT(ec == 0); 00822 00823 to->removeAttribute(SVGNames::yAttr, ec); 00824 ASSERT(ec == 0); 00825 00826 to->removeAttribute(SVGNames::widthAttr, ec); 00827 ASSERT(ec == 0); 00828 00829 to->removeAttribute(SVGNames::heightAttr, ec); 00830 ASSERT(ec == 0); 00831 00832 to->removeAttribute(XLinkNames::hrefAttr, ec); 00833 ASSERT(ec == 0);*/ 00834 } 00835 00836 } 00837 00838 #endif // ENABLE(SVG)
KDE 4.6 API Reference