• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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     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)

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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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