• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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     SVGElement::childrenChanged(/*changedByParser, beforeChange, afterChange, childCountDelta*/);
00155 
00156     if (!attached())
00157         return;
00158 
00159     buildPendingResource();
00160 
00161     if (m_shadowTreeRootElement)
00162         m_shadowTreeRootElement->setChanged();
00163 }
00164 
00165 void SVGUseElement::recalcStyle(StyleChange change)
00166 {
00167     SVGStyledElement::recalcStyle(change);
00168 
00169     // The shadow tree root element is NOT a direct child element of us.
00170     // So we have to take care it receives style updates, manually.
00171     if (!m_shadowTreeRootElement || !m_shadowTreeRootElement->attached())
00172         return;
00173 
00174     // Mimic Element::recalcStyle(). The main difference is that we don't call attach() on the
00175     // shadow tree root element, but call attachShadowTree() here. Calling attach() will crash
00176     // as the shadow tree root element has no (direct) parent node. Yes, shadow trees are tricky.
00177     if (change >= Inherit || m_shadowTreeRootElement->changed()) {
00178         RenderStyle* newStyle = document()->styleSelector()->styleForElement(m_shadowTreeRootElement.get());
00179         newStyle->ref();
00180         StyleChange ch = m_shadowTreeRootElement->diff((m_shadowTreeRootElement->renderer() ? m_shadowTreeRootElement->renderer()->style() : 0)/*renderStyle()*/, newStyle);
00181         if (ch == Detach) {
00182             ASSERT(m_shadowTreeRootElement->attached());
00183             m_shadowTreeRootElement->detach();
00184             attachShadowTree();
00185 
00186             // attach recalulates the style for all children. No need to do it twice.
00187             m_shadowTreeRootElement->setChanged(false);
00188             m_shadowTreeRootElement->setHasChangedChild(false);
00189             newStyle->deref();
00190             return;
00191         }
00192 
00193         newStyle->deref();
00194     }
00195 
00196     // Only change==Detach needs special treatment, for anything else recalcStyle() works.
00197     m_shadowTreeRootElement->recalcStyle(change);
00198 }
00199 
00200 #ifdef DUMP_INSTANCE_TREE
00201 void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance)
00202 {
00203     SVGElement* element = targetInstance->correspondingElement();
00204     ASSERT(element);
00205 
00206     String elementId = element->getIDAttribute();
00207     String elementNodeName = element->nodeName();
00208     String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null";
00209     String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null";
00210 
00211     for (unsigned int i = 0; i < depth; ++i)
00212         text += "  ";
00213 
00214     text += String::format("SVGElementInstance (parentNode=%s, firstChild=%s, correspondingElement=%s, id=%s)\n",
00215                            parentNodeName.latin1().data(), firstChildNodeName.latin1().data(), elementNodeName.latin1().data(), elementId.latin1().data());
00216  
00217     depth++;
00218 
00219     for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
00220         dumpInstanceTree(depth, text, instance);
00221 
00222     depth--;
00223 }
00224 #endif
00225 
00226 static bool isDisallowedElement(Node* element)
00227 {
00228 #if ENABLE(SVG_FOREIGN_OBJECT)
00229     // <foreignObject> should never be contained in a <use> tree. Too dangerous side effects possible.
00230     if (element->hasTagName(SVGNames::foreignObjectTag))
00231         return true;
00232 #endif
00233 #if ENABLE(SVG_ANIMATION)
00234     if (SVGSMILElement::isSMILElement(element))
00235         return true;
00236 #endif
00237 
00238     return false;
00239 }
00240 
00241 static bool subtreeContainsDisallowedElement(Node* start)
00242 {
00243     if (isDisallowedElement(start))
00244         return true;
00245 
00246     for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) {
00247         if (subtreeContainsDisallowedElement(cur))
00248             return true;
00249     }
00250 
00251     return false;
00252 }
00253 
00254 void SVGUseElement::buildPendingResource()
00255 {
00256     String id = SVGURIReference::getTarget(href());
00257     Element* targetElement = document()->getElementById(id);
00258 
00259     if (!targetElement) {
00260         // TODO: We want to deregister as pending resource, if our href() changed!
00261         // TODO: Move to svgAttributeChanged, once we're fixing use & the new dynamic update concept.
00262         document()->accessSVGExtensions()->addPendingResource(id, this);
00263         return;
00264     }
00265 
00266     // Do not build the shadow/instance tree for <use> elements living in a shadow tree.
00267     // The will be expanded soon anyway - see expandUseElementsInShadowTree().
00268     Node* parent = parentNode();
00269     while (parent) {
00270         if (parent->isShadowNode())
00271             return;
00272 
00273         parent = parent->parentNode();
00274     }
00275  
00276     SVGElement* target = 0;
00277     if (targetElement && targetElement->isSVGElement())
00278         target = static_cast<SVGElement*>(targetElement);
00279 
00280     // Do not allow self-referencing.
00281     // 'target' may be null, if it's a non SVG namespaced element.
00282     if (!target || target == this) {
00283         m_targetElementInstance = 0;
00284         m_shadowTreeRootElement = 0;
00285         return;
00286     }
00287 
00288     // Why a separated instance/shadow tree? SVG demands it:
00289     // The instance tree is accesable from JavaScript, and has to
00290     // expose a 1:1 copy of the referenced tree, whereas internally we need
00291     // to alter the tree for correct "use-on-symbol", "use-on-svg" support.  
00292  
00293     // Build instance tree. Create root SVGElementInstance object for the first sub-tree node.
00294     //
00295     // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a
00296     // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object
00297     // is the SVGRectElement that corresponds to the referenced 'rect' element.
00298     m_targetElementInstance = new SVGElementInstance(this, target);
00299 
00300     // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children
00301     bool foundProblem = false;
00302     buildInstanceTree(target, m_targetElementInstance.get(), foundProblem);
00303 
00304     // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it!
00305     // Non-appearing <use> content is easier to debug, then half-appearing content.
00306     if (foundProblem) {
00307         m_targetElementInstance = 0;
00308         m_shadowTreeRootElement = 0;
00309         return;
00310     }
00311 
00312     // Assure instance tree building was successful
00313     ASSERT(m_targetElementInstance);
00314     ASSERT(m_targetElementInstance->correspondingUseElement() == this);
00315 
00316     // Setup shadow tree root node
00317     m_shadowTreeRootElement = new SVGGElement(SVGNames::gTag, document());
00318     m_shadowTreeRootElement->setInDocument();
00319     m_shadowTreeRootElement->setShadowParentNode(this);
00320 
00321     // Spec: An additional transformation translate(x,y) is appended to the end
00322     // (i.e., right-side) of the transform attribute on the generated 'g', where x
00323     // and y represent the values of the x and y attributes on the 'use' element. 
00324     if (x().value() != 0.0 || y().value() != 0.0) {
00325         String transformString = String::format("translate(%f, %f)", x().value(), y().value());
00326         m_shadowTreeRootElement->setAttribute(SVGNames::transformAttr, transformString);
00327     }
00328 
00329     // Build shadow tree from instance tree
00330     // This also handles the special cases: <use> on <symbol>, <use> on <svg>.
00331     buildShadowTree(target, m_targetElementInstance.get());
00332 
00333 #if ENABLE(SVG) && ENABLE(SVG_USE)
00334     // Expand all <use> elements in the shadow tree.
00335     // Expand means: replace the actual <use> element by what it references.
00336     expandUseElementsInShadowTree(m_shadowTreeRootElement.get());
00337 
00338     // Expand all <symbol> elements in the shadow tree.
00339     // Expand means: replace the actual <symbol> element by the <svg> element.
00340     expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());
00341 
00342 #endif
00343 
00344     // Now that the shadow tree is completely expanded, we can associate
00345     // shadow tree elements <-> instances in the instance tree.
00346     associateInstancesWithShadowTreeElements(m_shadowTreeRootElement->firstChild(), m_targetElementInstance.get());
00347 
00348     // Eventually dump instance tree
00349 #ifdef DUMP_INSTANCE_TREE
00350     String text;
00351     unsigned int depth = 0;
00352 
00353     dumpInstanceTree(depth, text, m_targetElementInstance.get());
00354     fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data());
00355 #endif
00356 
00357     // Eventually dump shadow tree
00358 #ifdef DUMP_SHADOW_TREE
00359     ExceptionCode ec = 0;
00360 
00361     PassRefPtr<XMLSerializer> serializer = XMLSerializer::create();
00362 
00363     String markup = serializer->serializeToString(m_shadowTreeRootElement.get(), ec);
00364     ASSERT(ec == 0);
00365 
00366     fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data());
00367 #endif
00368 
00369     // The DOM side is setup properly. Now we have to attach the root shadow
00370     // tree element manually - using attach() won't work for "shadow nodes".
00371     attachShadowTree();
00372 }
00373 
00374 RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*)
00375 {
00376     return new (arena) RenderSVGTransformableContainer(this);
00377 }
00378 
00379 void SVGUseElement::attach()
00380 {
00381     SVGStyledTransformableElement::attach();
00382 
00383     // If we're a pending resource, this doesn't have any effect.
00384     attachShadowTree();
00385 }
00386 
00387 void SVGUseElement::detach()
00388 {
00389     if (m_shadowTreeRootElement)
00390         m_shadowTreeRootElement->detach();
00391     
00392     SVGStyledTransformableElement::detach();
00393 }
00394 
00395 static bool isDirectReference(Node* n)
00396 {
00397     return n->hasTagName(SVGNames::pathTag) ||
00398            n->hasTagName(SVGNames::rectTag) ||
00399            n->hasTagName(SVGNames::circleTag) ||
00400            n->hasTagName(SVGNames::ellipseTag) ||
00401            n->hasTagName(SVGNames::polygonTag) ||
00402            n->hasTagName(SVGNames::polylineTag) ||
00403            n->hasTagName(SVGNames::textTag);
00404 }
00405 
00406 Path SVGUseElement::toClipPath() const
00407 {
00408     if (!m_shadowTreeRootElement)
00409         const_cast<SVGUseElement*>(this)->buildPendingResource();
00410 
00411     Node* n = m_shadowTreeRootElement->firstChild();
00412     if (n->isSVGElement() && static_cast<SVGElement*>(n)->isStyledTransformable()) {
00413         if (!isDirectReference(n))
00414             // Spec: Indirect references are an error (14.3.5)
00415             document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>");
00416         else
00417             return static_cast<SVGStyledTransformableElement*>(n)->toClipPath();
00418     }
00419 
00420     return Path();
00421 }
00422 
00423 void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem)
00424 {
00425     ASSERT(target);
00426     ASSERT(targetInstance);
00427 
00428     // A general description from the SVG spec, describing what buildInstanceTree() actually does.
00429     //
00430     // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree
00431     // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement
00432     // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has
00433     // its correspondingElement that is an SVGRectElement object.
00434 
00435     for (Node* node = target->firstChild(); node; node = node->nextSibling()) {
00436         SVGElement* element = 0;
00437         if (node->isSVGElement())
00438             element = static_cast<SVGElement*>(node);
00439 
00440         // Skip any non-svg nodes or any disallowed element.
00441         if (!element || isDisallowedElement(element))
00442             continue;
00443 
00444         // Create SVGElementInstance object, for both container/non-container nodes.
00445         SVGElementInstance* instancePtr = new SVGElementInstance(this, element);
00446 
00447         RefPtr<SVGElementInstance> instance = instancePtr;
00448         targetInstance->appendChild(instance.release());
00449 
00450         // Enter recursion, appending new instance tree nodes to the "instance" object.
00451         if (element->hasChildNodes())
00452             buildInstanceTree(element, instancePtr, foundProblem);
00453 
00454         // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
00455         // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
00456         if (element->hasTagName(SVGNames::useTag))
00457             handleDeepUseReferencing(element, instancePtr, foundProblem);
00458     }
00459 
00460     // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
00461     // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
00462     if (target->hasTagName(SVGNames::useTag))
00463         handleDeepUseReferencing(target, targetInstance, foundProblem);
00464 }
00465 
00466 void SVGUseElement::handleDeepUseReferencing(SVGElement* use, SVGElementInstance* targetInstance, bool& foundProblem)
00467 {
00468     String id = SVGURIReference::getTarget(use->href());
00469     Element* targetElement = document()->getElementById(id); 
00470     SVGElement* target = 0;
00471     if (targetElement && targetElement->isSVGElement())
00472         target = static_cast<SVGElement*>(targetElement);
00473 
00474     if (!target)
00475         return;
00476 
00477     // Cycle detection first!
00478     foundProblem = (target == this);
00479 
00480     // Shortcut for self-references
00481     if (foundProblem)
00482         return;
00483 
00484     SVGElementInstance* instance = targetInstance->parentNode();
00485     while (instance) {
00486         SVGElement* element = instance->correspondingElement();
00487 
00488         if (element->getIDAttribute() == id) {
00489             foundProblem = true;
00490             return;
00491         }
00492     
00493         instance = instance->parentNode();
00494     }
00495 
00496     // Create an instance object, even if we're dealing with a cycle
00497     SVGElementInstance* newInstance = new SVGElementInstance(this, target);
00498     targetInstance->appendChild(newInstance);
00499 
00500     // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children
00501     buildInstanceTree(target, newInstance, foundProblem);
00502 }
00503 
00504 void SVGUseElement::alterShadowTreeForSVGTag(SVGElement* target)
00505 {
00506     String widthString = String::number(width().value());
00507     String heightString = String::number(height().value());
00508 
00509     if (hasAttribute(SVGNames::widthAttr))
00510         target->setAttribute(SVGNames::widthAttr, widthString);
00511 
00512     if (hasAttribute(SVGNames::heightAttr))
00513         target->setAttribute(SVGNames::heightAttr, heightString);
00514 }
00515 
00516 void SVGUseElement::removeDisallowedElementsFromSubtree(Node* subtree)
00517 {
00518     // Implement me: khtml, NodeImpl::traverseNextSibling
00519     /*ASSERT(!subtree->inDocument());
00520     ExceptionCode ec;
00521     Node* node = subtree->firstChild();
00522     while (node) {
00523         if (isDisallowedElement(node)) {
00524             Node* next = node->traverseNextSibling(subtree);
00525             // The subtree is not in document so this won't generate events that could mutate the tree.
00526             node->parent()->removeChild(node, ec);
00527             node = next;
00528         } else
00529             node = node->traverseNextNode(subtree);
00530     }*/
00531 }
00532 
00533 void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targetInstance)
00534 {
00535     // For instance <use> on <foreignObject> (direct case).
00536     if (isDisallowedElement(target))
00537         return;
00538 
00539     PassRefPtr<NodeImpl> newChild = targetInstance->correspondingElement()->cloneNode(true);
00540 
00541     // We don't walk the target tree element-by-element, and clone each element,
00542     // but instead use cloneNode(deep=true). This is an optimization for the common
00543     // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
00544     // Though if there are disallowed elements in the subtree, we have to remove them.
00545     // For instance: <use> on <g> containing <foreignObject> (indirect case).
00546     if (subtreeContainsDisallowedElement(newChild.get()))
00547         removeDisallowedElementsFromSubtree(newChild.get());
00548 
00549     SVGElement* newChildPtr = 0;
00550     if (newChild->isSVGElement())
00551         newChildPtr = static_cast<SVGElement*>(newChild.get());
00552     ASSERT(newChildPtr);
00553 
00554     /*ExceptionCode*//*khtml*/int ec = 0;
00555     m_shadowTreeRootElement->appendChild(newChild.releaseRef(), ec);
00556     ASSERT(ec == 0);
00557 
00558     // Handle use referencing <svg> special case
00559     if (target->hasTagName(SVGNames::svgTag))
00560         alterShadowTreeForSVGTag(newChildPtr);
00561 }
00562 
00563 #if ENABLE(SVG) && ENABLE(SVG_USE)
00564 void SVGUseElement::expandUseElementsInShadowTree(Node* element)
00565 {
00566     // Why expand the <use> elements in the shadow tree here, and not just
00567     // do this directly in buildShadowTree, if we encounter a <use> element?
00568     //
00569     // Short answer: Because we may miss to expand some elements. Ie. if a <symbol>
00570     // contains <use> tags, we'd miss them. So once we're done with settin' up the
00571     // actual shadow tree (after the special case modification for svg/symbol) we have
00572     // to walk it completely and expand all <use> elements.
00573     if (element->hasTagName(SVGNames::useTag)) {
00574         SVGUseElement* use = static_cast<SVGUseElement*>(element);
00575 
00576         String id = SVGURIReference::getTarget(use->href());
00577         Element* targetElement = document()->getElementById(id); 
00578         SVGElement* target = 0;
00579         if (targetElement && targetElement->isSVGElement())
00580             target = static_cast<SVGElement*>(targetElement);
00581 
00582         // Don't ASSERT(target) here, it may be "pending", too.
00583         if (target) {
00584             // Setup sub-shadow tree root node
00585             RefPtr<SVGElement> cloneParent = new SVGGElement(SVGNames::gTag, document());
00586 
00587             // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
00588             // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
00589             transferUseAttributesToReplacedElement(use, cloneParent.get());
00590 
00591             // Spec: An additional transformation translate(x,y) is appended to the end
00592             // (i.e., right-side) of the transform attribute on the generated 'g', where x
00593             // and y represent the values of the x and y attributes on the 'use' element.
00594             if (use->x().value() != 0.0 || use->y().value() != 0.0) {
00595                 if (!cloneParent->hasAttribute(SVGNames::transformAttr)) {
00596                     String transformString = String::format("translate(%f, %f)", use->x().value(), use->y().value());
00597                     cloneParent->setAttribute(SVGNames::transformAttr, transformString);
00598                 } else {
00599                     String transformString = String::format(" translate(%f, %f)", use->x().value(), use->y().value());
00600                     const AtomicString& transformAttribute = cloneParent->getAttribute(SVGNames::transformAttr);
00601                     cloneParent->setAttribute(SVGNames::transformAttr, transformAttribute + transformString); 
00602                 }
00603             }
00604 
00605             ExceptionCode ec = 0;
00606  
00607             // For instance <use> on <foreignObject> (direct case).
00608             if (isDisallowedElement(target)) {
00609                 // We still have to setup the <use> replacment (<g>). Otherwhise
00610                 // associateInstancesWithShadowTreeElements() makes wrong assumptions.
00611                 // Replace <use> with referenced content.
00612                 ASSERT(use->parentNode()); 
00613                 use->parentNode()->replaceChild(cloneParent.release(), use, ec);
00614                 ASSERT(ec == 0);
00615                 return;
00616             }
00617 
00618             RefPtr<Node> newChild = target->cloneNode(true);
00619 
00620             // We don't walk the target tree element-by-element, and clone each element,
00621             // but instead use cloneNode(deep=true). This is an optimization for the common
00622             // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
00623             // Though if there are disallowed elements in the subtree, we have to remove them.
00624             // For instance: <use> on <g> containing <foreignObject> (indirect case).
00625             if (subtreeContainsDisallowedElement(newChild.get()))
00626                 removeDisallowedElementsFromSubtree(newChild.get());
00627 
00628             SVGElement* newChildPtr = 0;
00629             if (newChild->isSVGElement())
00630                 newChildPtr = static_cast<SVGElement*>(newChild.get());
00631             ASSERT(newChildPtr);
00632 
00633             cloneParent->appendChild(newChild.release(), ec);
00634             ASSERT(ec == 0);
00635 
00636             // Replace <use> with referenced content.
00637             ASSERT(use->parentNode()); 
00638             use->parentNode()->replaceChild(cloneParent.release(), use, ec);
00639             ASSERT(ec == 0);
00640 
00641             // Handle use referencing <svg> special case
00642             if (target->hasTagName(SVGNames::svgTag))
00643                 alterShadowTreeForSVGTag(newChildPtr);
00644 
00645             // Immediately stop here, and restart expanding.
00646             expandUseElementsInShadowTree(m_shadowTreeRootElement.get());
00647             return;
00648         }
00649     }
00650 
00651     for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
00652         expandUseElementsInShadowTree(child.get());
00653 }
00654 
00655 void SVGUseElement::expandSymbolElementsInShadowTree(Node* element)
00656 {
00657     if (element->hasTagName(SVGNames::symbolTag)) {
00658         // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
00659         // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
00660         // always have explicit values for attributes width and height. If attributes width and/or
00661         // height are provided on the 'use' element, then these attributes will be transferred to
00662         // the generated 'svg'. If attributes width and/or height are not specified, the generated
00663         // 'svg' element will use values of 100% for these attributes.
00664         RefPtr<SVGSVGElement> svgElement = new SVGSVGElement(SVGNames::svgTag, document());
00665 
00666         // Transfer all attributes from <symbol> to the new <svg> element
00667         svgElement->attributes()->setAttributes(*element->attributes());
00668 
00669         // Explicitly re-set width/height values
00670         String widthString = String::number(width().value());
00671         String heightString = String::number(height().value()); 
00672 
00673         svgElement->setAttribute(SVGNames::widthAttr, hasAttribute(SVGNames::widthAttr) ? widthString : "100%");
00674         svgElement->setAttribute(SVGNames::heightAttr, hasAttribute(SVGNames::heightAttr) ? heightString : "100%");
00675 
00676         ExceptionCode ec = 0;
00677 
00678         // Only clone symbol children, and add them to the new <svg> element    
00679         for (Node* child = element->firstChild(); child; child = child->nextSibling()) {
00680             RefPtr<Node> newChild = child->cloneNode(true);
00681             svgElement->appendChild(newChild.release(), ec);
00682             ASSERT(ec == 0);
00683         }
00684     
00685         // We don't walk the target tree element-by-element, and clone each element,
00686         // but instead use cloneNode(deep=true). This is an optimization for the common
00687         // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
00688         // Though if there are disallowed elements in the subtree, we have to remove them.
00689         // For instance: <use> on <g> containing <foreignObject> (indirect case).
00690         if (subtreeContainsDisallowedElement(svgElement.get()))
00691             removeDisallowedElementsFromSubtree(svgElement.get());
00692 
00693         // Replace <symbol> with <svg>.
00694         ASSERT(element->parentNode()); 
00695         element->parentNode()->replaceChild(svgElement.release(), element, ec);
00696         ASSERT(ec == 0);
00697 
00698         // Immediately stop here, and restart expanding.
00699         expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());
00700         return;
00701     }
00702 
00703     for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
00704         expandSymbolElementsInShadowTree(child.get());
00705 }
00706 
00707 #endif
00708     
00709 void SVGUseElement::attachShadowTree()
00710 {
00711     if (!m_shadowTreeRootElement || m_shadowTreeRootElement->attached() || /*khtml !document()->shouldCreateRenderers() ||*/ !attached() || !renderer())
00712         return;
00713 
00714     // Inspired by RenderTextControl::createSubtreeIfNeeded(). 
00715     if (renderer()->childAllowed()/*canHaveChildren()*/ && childShouldCreateRenderer(m_shadowTreeRootElement.get())) {
00716         RenderStyle* style = m_shadowTreeRootElement->styleForRenderer(renderer());
00717         style->ref();
00718 
00719         if (m_shadowTreeRootElement->rendererIsNeeded(style)) {
00720             m_shadowTreeRootElement->setRenderer(m_shadowTreeRootElement->createRenderer(document()->renderArena(), style));
00721             if (RenderObject* shadowRenderer = m_shadowTreeRootElement->renderer()) {
00722                 shadowRenderer->setStyle(style);
00723                 renderer()->addChild(shadowRenderer, m_shadowTreeRootElement->nextRenderer());
00724                 m_shadowTreeRootElement->setAttached();
00725             }
00726         }
00727 
00728         style->deref();
00729 
00730         // This will take care of attaching all shadow tree child nodes.
00731         for (Node* child = m_shadowTreeRootElement->firstChild(); child; child = child->nextSibling())
00732             child->attach();
00733     }
00734 }
00735 
00736 void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance)
00737 {
00738     if (!target || !targetInstance)
00739         return;
00740 
00741     SVGElement* originalElement = targetInstance->correspondingElement();
00742 
00743     if (originalElement->hasTagName(SVGNames::useTag)) {
00744 #if ENABLE(SVG) && ENABLE(SVG_USE)
00745         // <use> gets replaced by <g>
00746         /*ASSERT(target->nodeName() == SVGNames::gTag);*/
00747 #else 
00748         /*ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag);*/
00749 #endif
00750     } else if (originalElement->hasTagName(SVGNames::symbolTag)) {
00751         // <symbol> gets replaced by <svg>
00752 #if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT)
00753         ASSERT(target->nodeName() == SVGNames::svgTag);
00754 #endif
00755     } else
00756         ASSERT(target->nodeName() == originalElement->nodeName());
00757 
00758     SVGElement* element = 0;
00759     if (target->isSVGElement())
00760         element = static_cast<SVGElement*>(target);
00761 
00762     ASSERT(!targetInstance->shadowTreeElement());
00763     targetInstance->setShadowTreeElement(element);
00764 
00765     Node* node = target->firstChild();
00766     for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) {
00767         // Skip any non-svg elements in shadow tree
00768         while (node && !node->isSVGElement())
00769            node = node->nextSibling();
00770 
00771         associateInstancesWithShadowTreeElements(node, instance);
00772         node = node->nextSibling();
00773     }
00774 }
00775 
00776 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const
00777 {
00778     return instanceForShadowTreeElement(element, m_targetElementInstance.get());
00779 }
00780 
00781 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const
00782 {
00783     ASSERT(element);
00784     ASSERT(instance);
00785     ASSERT(instance->shadowTreeElement());
00786 
00787     if (element == instance->shadowTreeElement())
00788         return instance;
00789 
00790     for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) {
00791         SVGElementInstance* search = instanceForShadowTreeElement(element, current);
00792         if (search)
00793             return search;
00794     }
00795 
00796     return 0;
00797 }
00798 
00799 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const
00800 {
00801     // Implement me: khtml
00802     /*ASSERT(from);
00803     ASSERT(to);
00804 
00805     to->attributes()->setAttributes(*from->attributes());
00806 
00807     ExceptionCode ec = 0;
00808 
00809     to->removeAttribute(SVGNames::xAttr, ec);
00810     ASSERT(ec == 0);
00811 
00812     to->removeAttribute(SVGNames::yAttr, ec);
00813     ASSERT(ec == 0);
00814 
00815     to->removeAttribute(SVGNames::widthAttr, ec);
00816     ASSERT(ec == 0);
00817 
00818     to->removeAttribute(SVGNames::heightAttr, ec);
00819     ASSERT(ec == 0);
00820 
00821     to->removeAttribute(XLinkNames::hrefAttr, ec);
00822     ASSERT(ec == 0);*/
00823 }
00824 
00825 }
00826 
00827 #endif // ENABLE(SVG)

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal