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