• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
5  * Copyright (C) 2011 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include "config.h"
24 
25 #if ENABLE(SVG)
26 #include "SVGUseElement.h"
27 
28 #include "Attribute.h"
29 #include "CSSStyleSelector.h"
30 #include "Document.h"
31 #include "Event.h"
32 #include "EventListener.h"
33 #include "HTMLNames.h"
34 #include "NodeRenderStyle.h"
35 #include "RegisteredEventListener.h"
36 #include "RenderSVGResource.h"
37 #include "RenderSVGShadowTreeRootContainer.h"
38 #include "SVGElementInstance.h"
39 #include "SVGElementInstanceList.h"
40 #include "SVGGElement.h"
41 #include "SVGNames.h"
42 #include "SVGSMILElement.h"
43 #include "SVGSVGElement.h"
44 #include "SVGShadowTreeElements.h"
45 #include "SVGSymbolElement.h"
46 #include "XLinkNames.h"
47 #include "XMLDocumentParser.h"
48 #include "XMLSerializer.h"
49 
50 #include <wtf/text/StringConcatenate.h>
51 
52 // Dump SVGElementInstance object tree - useful to debug instanceRoot problems
53 // #define DUMP_INSTANCE_TREE
54 
55 // Dump the deep-expanded shadow tree (where the renderers are built from)
56 // #define DUMP_SHADOW_TREE
57 
58 namespace WebCore {
59 
60 // Animated property definitions
DEFINE_ANIMATED_LENGTH(SVGUseElement,SVGNames::xAttr,X,x)61 DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::xAttr, X, x)
62 DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::yAttr, Y, y)
63 DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::widthAttr, Width, width)
64 DEFINE_ANIMATED_LENGTH(SVGUseElement, SVGNames::heightAttr, Height, height)
65 DEFINE_ANIMATED_STRING(SVGUseElement, XLinkNames::hrefAttr, Href, href)
66 DEFINE_ANIMATED_BOOLEAN(SVGUseElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
67 
68 inline SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document* document)
69     : SVGStyledTransformableElement(tagName, document)
70     , m_x(LengthModeWidth)
71     , m_y(LengthModeHeight)
72     , m_width(LengthModeWidth)
73     , m_height(LengthModeHeight)
74     , m_updatesBlocked(false)
75     , m_isPendingResource(false)
76     , m_needsShadowTreeRecreation(false)
77 {
78 }
79 
create(const QualifiedName & tagName,Document * document)80 PassRefPtr<SVGUseElement> SVGUseElement::create(const QualifiedName& tagName, Document* document)
81 {
82     return adoptRef(new SVGUseElement(tagName, document));
83 }
84 
instanceRoot() const85 SVGElementInstance* SVGUseElement::instanceRoot() const
86 {
87     // If there is no element instance tree, force immediate SVGElementInstance tree
88     // creation by asking the document to invoke our recalcStyle function - as we can't
89     // wait for the lazy creation to happen if e.g. JS wants to access the instanceRoot
90     // object right after creating the element on-the-fly
91     if (!m_targetElementInstance)
92         document()->updateLayoutIgnorePendingStylesheets();
93 
94     return m_targetElementInstance.get();
95 }
96 
animatedInstanceRoot() const97 SVGElementInstance* SVGUseElement::animatedInstanceRoot() const
98 {
99     // FIXME: Implement me.
100     return 0;
101 }
102 
parseMappedAttribute(Attribute * attr)103 void SVGUseElement::parseMappedAttribute(Attribute* attr)
104 {
105     if (attr->name() == SVGNames::xAttr)
106         setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
107     else if (attr->name() == SVGNames::yAttr)
108         setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
109     else if (attr->name() == SVGNames::widthAttr) {
110         setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
111         if (widthBaseValue().value(this) < 0.0)
112             document()->accessSVGExtensions()->reportError("A negative value for use attribute <width> is not allowed");
113     } else if (attr->name() == SVGNames::heightAttr) {
114         setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
115         if (heightBaseValue().value(this) < 0.0)
116             document()->accessSVGExtensions()->reportError("A negative value for use attribute <height> is not allowed");
117     } else {
118         if (SVGTests::parseMappedAttribute(attr))
119             return;
120         if (SVGLangSpace::parseMappedAttribute(attr))
121             return;
122         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
123             return;
124         if (SVGURIReference::parseMappedAttribute(attr))
125             return;
126         SVGStyledTransformableElement::parseMappedAttribute(attr);
127     }
128 }
129 
insertedIntoDocument()130 void SVGUseElement::insertedIntoDocument()
131 {
132     // This functions exists to assure assumptions made in the code regarding SVGElementInstance creation/destruction are satisfied.
133     SVGStyledTransformableElement::insertedIntoDocument();
134     ASSERT(!m_targetElementInstance || ((document()->isSVGDocument() || document()->isXHTMLDocument()) && !static_cast<XMLDocumentParser*>(document()->parser())->wellFormed()));
135     ASSERT(!m_isPendingResource);
136 }
137 
removedFromDocument()138 void SVGUseElement::removedFromDocument()
139 {
140     SVGStyledTransformableElement::removedFromDocument();
141     detachInstance();
142 }
143 
svgAttributeChanged(const QualifiedName & attrName)144 void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
145 {
146     SVGStyledTransformableElement::svgAttributeChanged(attrName);
147 
148     bool isXYAttribute = attrName == SVGNames::xAttr || attrName == SVGNames::yAttr;
149     bool isWidthHeightAttribute = attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr;
150 
151     if (isXYAttribute || isWidthHeightAttribute)
152         updateRelativeLengthsInformation();
153 
154     if (SVGTests::handleAttributeChange(this, attrName))
155         return;
156 
157     RenderObject* object = renderer();
158     if (!object)
159         return;
160 
161     if (SVGURIReference::isKnownAttribute(attrName)) {
162         if (m_isPendingResource) {
163             document()->accessSVGExtensions()->removePendingResource(m_resourceId);
164             m_resourceId = String();
165             m_isPendingResource = false;
166         }
167 
168         invalidateShadowTree();
169         return;
170     }
171 
172     if (isXYAttribute) {
173         updateContainerOffsets();
174         return;
175     }
176 
177     if (isWidthHeightAttribute) {
178         updateContainerSizes();
179         return;
180     }
181 
182     // Be very careful here, if svgAttributeChanged() has been called because a SVG CSS property changed, we do NOT want to reclone the tree!
183     if (SVGStyledElement::isKnownAttribute(attrName)) {
184         setNeedsStyleRecalc();
185         return;
186     }
187 
188     if (SVGLangSpace::isKnownAttribute(attrName)
189         || SVGExternalResourcesRequired::isKnownAttribute(attrName))
190         invalidateShadowTree();
191 }
192 
synchronizeProperty(const QualifiedName & attrName)193 void SVGUseElement::synchronizeProperty(const QualifiedName& attrName)
194 {
195     SVGStyledTransformableElement::synchronizeProperty(attrName);
196 
197     if (attrName == anyQName()) {
198         synchronizeX();
199         synchronizeY();
200         synchronizeWidth();
201         synchronizeHeight();
202         synchronizeExternalResourcesRequired();
203         synchronizeHref();
204         SVGTests::synchronizeProperties(this, attrName);
205         return;
206     }
207 
208     if (attrName == SVGNames::xAttr)
209         synchronizeX();
210     else if (attrName == SVGNames::yAttr)
211         synchronizeY();
212     else if (attrName == SVGNames::widthAttr)
213         synchronizeWidth();
214     else if (attrName == SVGNames::heightAttr)
215         synchronizeHeight();
216     else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
217         synchronizeExternalResourcesRequired();
218     else if (SVGURIReference::isKnownAttribute(attrName))
219         synchronizeHref();
220     else if (SVGTests::isKnownAttribute(attrName))
221         SVGTests::synchronizeProperties(this, attrName);
222 }
223 
attributeToPropertyTypeMap()224 AttributeToPropertyTypeMap& SVGUseElement::attributeToPropertyTypeMap()
225 {
226     DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
227     return s_attributeToPropertyTypeMap;
228 }
229 
fillAttributeToPropertyTypeMap()230 void SVGUseElement::fillAttributeToPropertyTypeMap()
231 {
232     AttributeToPropertyTypeMap& attributeToPropertyTypeMap = this->attributeToPropertyTypeMap();
233 
234     SVGStyledTransformableElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap);
235     attributeToPropertyTypeMap.set(SVGNames::xAttr, AnimatedLength);
236     attributeToPropertyTypeMap.set(SVGNames::yAttr, AnimatedLength);
237     attributeToPropertyTypeMap.set(SVGNames::widthAttr, AnimatedLength);
238     attributeToPropertyTypeMap.set(SVGNames::heightAttr, AnimatedLength);
239     attributeToPropertyTypeMap.set(XLinkNames::hrefAttr, AnimatedString);
240 }
241 
updateContainerSize(SVGElementInstance * targetInstance)242 static void updateContainerSize(SVGElementInstance* targetInstance)
243 {
244     // Depth-first used to write the method in early exit style, no particular other reason.
245     for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
246         updateContainerSize(instance);
247 
248     SVGUseElement* useElement = targetInstance->directUseElement();
249     if (!useElement)
250         return;
251 
252     SVGElement* correspondingElement = targetInstance->correspondingElement();
253     ASSERT(correspondingElement);
254 
255     bool isSymbolTag = correspondingElement->hasTagName(SVGNames::symbolTag);
256     if (!correspondingElement->hasTagName(SVGNames::svgTag) && !isSymbolTag)
257         return;
258 
259     SVGElement* shadowTreeElement = targetInstance->shadowTreeElement();
260     ASSERT(shadowTreeElement);
261     ASSERT(shadowTreeElement->hasTagName(SVGNames::svgTag));
262 
263     // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height.
264     // If attributes width and/or height are provided on the 'use' element, then these attributes
265     // will be transferred to the generated 'svg'. If attributes width and/or height are not specified,
266     // the generated 'svg' element will use values of 100% for these attributes.
267 
268     // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these
269     // values will override the corresponding attributes on the 'svg' in the generated tree.
270 
271     if (useElement->hasAttribute(SVGNames::widthAttr))
272         shadowTreeElement->setAttribute(SVGNames::widthAttr, useElement->getAttribute(SVGNames::widthAttr));
273     else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::widthAttr))
274         shadowTreeElement->setAttribute(SVGNames::widthAttr, "100%");
275 
276     if (useElement->hasAttribute(SVGNames::heightAttr))
277         shadowTreeElement->setAttribute(SVGNames::heightAttr, useElement->getAttribute(SVGNames::heightAttr));
278     else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::heightAttr))
279         shadowTreeElement->setAttribute(SVGNames::heightAttr, "100%");
280 }
281 
updateContainerSizes()282 void SVGUseElement::updateContainerSizes()
283 {
284     if (!m_targetElementInstance)
285         return;
286 
287     // Update whole subtree, scanning for shadow container elements, that correspond to <svg>/<symbol> tags
288     ASSERT(m_targetElementInstance->directUseElement() == this);
289     updateContainerSize(m_targetElementInstance.get());
290 
291     if (RenderObject* object = renderer())
292         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
293 }
294 
updateContainerOffset(SVGElementInstance * targetInstance)295 static void updateContainerOffset(SVGElementInstance* targetInstance)
296 {
297     // Depth-first used to write the method in early exit style, no particular other reason.
298     for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
299         updateContainerOffset(instance);
300 
301     SVGElement* correspondingElement = targetInstance->correspondingElement();
302     ASSERT(correspondingElement);
303 
304     if (!correspondingElement->hasTagName(SVGNames::useTag))
305         return;
306 
307     SVGElement* shadowTreeElement = targetInstance->shadowTreeElement();
308     ASSERT(shadowTreeElement);
309     ASSERT(shadowTreeElement->hasTagName(SVGNames::gTag));
310 
311     if (!static_cast<SVGGElement*>(shadowTreeElement)->isShadowTreeContainerElement())
312         return;
313 
314     // Spec: An additional transformation translate(x,y) is appended to the end
315     // (i.e., right-side) of the transform attribute on the generated 'g', where x
316     // and y represent the values of the x and y attributes on the 'use' element.
317     SVGUseElement* useElement = static_cast<SVGUseElement*>(correspondingElement);
318     SVGShadowTreeContainerElement* containerElement = static_cast<SVGShadowTreeContainerElement*>(shadowTreeElement);
319     containerElement->setContainerOffset(useElement->x(), useElement->y());
320 }
321 
updateContainerOffsets()322 void SVGUseElement::updateContainerOffsets()
323 {
324     if (!m_targetElementInstance)
325         return;
326 
327     // Update root container offset (not reachable through instance tree)
328     SVGElement* shadowRoot = m_targetElementInstance->shadowTreeElement();
329     ASSERT(shadowRoot);
330 
331     ContainerNode* parentNode = shadowRoot->parentNode();
332     ASSERT(parentNode);
333     ASSERT(parentNode->isSVGElement());
334     ASSERT(parentNode->hasTagName(SVGNames::gTag));
335     ASSERT(static_cast<SVGGElement*>(parentNode)->isShadowTreeContainerElement());
336 
337     SVGShadowTreeContainerElement* containerElement = static_cast<SVGShadowTreeContainerElement*>(parentNode);
338     containerElement->setContainerOffset(x(), y());
339 
340     // Update whole subtree, scanning for shadow container elements, marking a cloned use subtree
341     updateContainerOffset(m_targetElementInstance.get());
342 
343     if (RenderObject* object = renderer())
344         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
345 }
346 
recalcStyle(StyleChange change)347 void SVGUseElement::recalcStyle(StyleChange change)
348 {
349     // Eventually mark shadow root element needing style recalc
350     if ((change >= Inherit || needsStyleRecalc() || childNeedsStyleRecalc()) && m_targetElementInstance && !m_updatesBlocked) {
351         if (SVGElement* shadowRoot = m_targetElementInstance->shadowTreeElement())
352             shadowRoot->setNeedsStyleRecalc();
353     }
354 
355     SVGStyledTransformableElement::recalcStyle(change);
356 
357     // Assure that the shadow tree has not been marked for recreation, while we're building it.
358     if (m_updatesBlocked)
359         ASSERT(!m_needsShadowTreeRecreation);
360 
361     RenderSVGShadowTreeRootContainer* shadowRoot = static_cast<RenderSVGShadowTreeRootContainer*>(renderer());
362     if (!shadowRoot)
363         return;
364 
365     bool needsStyleUpdate = !m_needsShadowTreeRecreation;
366     if (m_needsShadowTreeRecreation) {
367         shadowRoot->markShadowTreeForRecreation();
368         m_needsShadowTreeRecreation = false;
369     }
370 
371     shadowRoot->updateFromElement();
372 
373     if (!needsStyleUpdate)
374         return;
375 
376     shadowRoot->updateStyle(change);
377 }
378 
379 #ifdef DUMP_INSTANCE_TREE
dumpInstanceTree(unsigned int & depth,String & text,SVGElementInstance * targetInstance)380 void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance)
381 {
382     SVGElement* element = targetInstance->correspondingElement();
383     ASSERT(element);
384 
385     SVGElement* shadowTreeElement = targetInstance->shadowTreeElement();
386     ASSERT(shadowTreeElement);
387 
388     SVGUseElement* directUseElement = targetInstance->directUseElement();
389     String directUseElementName = directUseElement ? directUseElement->nodeName() : "null";
390 
391     String elementId = element->getIdAttribute();
392     String elementNodeName = element->nodeName();
393     String shadowTreeElementNodeName = shadowTreeElement->nodeName();
394     String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null";
395     String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null";
396 
397     for (unsigned int i = 0; i < depth; ++i)
398         text += "  ";
399 
400     text += String::format("SVGElementInstance this=%p, (parentNode=%s (%p), firstChild=%s (%p), correspondingElement=%s (%p), directUseElement=%s (%p), shadowTreeElement=%s (%p), id=%s)\n",
401                            targetInstance, parentNodeName.latin1().data(), element->parentNode(), firstChildNodeName.latin1().data(), element->firstChild(),
402                            elementNodeName.latin1().data(), element, directUseElementName.latin1().data(), directUseElement, shadowTreeElementNodeName.latin1().data(), shadowTreeElement, elementId.latin1().data());
403 
404     for (unsigned int i = 0; i < depth; ++i)
405         text += "  ";
406 
407     const HashSet<SVGElementInstance*>& elementInstances = element->instancesForElement();
408     text += makeString("Corresponding element is associated with ", String::number(elementInstances.size()), " instance(s):\n");
409 
410     const HashSet<SVGElementInstance*>::const_iterator end = elementInstances.end();
411     for (HashSet<SVGElementInstance*>::const_iterator it = elementInstances.begin(); it != end; ++it) {
412         for (unsigned int i = 0; i < depth; ++i)
413             text += "  ";
414 
415         text += String::format(" -> SVGElementInstance this=%p, (refCount: %i, shadowTreeElement in document? %i)\n",
416                                *it, (*it)->refCount(), (*it)->shadowTreeElement()->inDocument());
417     }
418 
419     ++depth;
420 
421     for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling())
422         dumpInstanceTree(depth, text, instance);
423 
424     --depth;
425 }
426 #endif
427 
isDisallowedElement(Node * element)428 static bool isDisallowedElement(Node* element)
429 {
430 #if ENABLE(SVG_FOREIGN_OBJECT)
431     // <foreignObject> should never be contained in a <use> tree. Too dangerous side effects possible.
432     if (element->hasTagName(SVGNames::foreignObjectTag))
433         return true;
434 #endif
435 #if ENABLE(SVG_ANIMATION)
436     if (SVGSMILElement::isSMILElement(element))
437         return true;
438 #endif
439 
440     return false;
441 }
442 
subtreeContainsDisallowedElement(Node * start)443 static bool subtreeContainsDisallowedElement(Node* start)
444 {
445     if (isDisallowedElement(start))
446         return true;
447 
448     for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) {
449         if (subtreeContainsDisallowedElement(cur))
450             return true;
451     }
452 
453     return false;
454 }
455 
buildPendingResource()456 void SVGUseElement::buildPendingResource()
457 {
458     // If we're called the first time (during shadow tree root creation from RenderSVGShadowTreeRootContainer)
459     // we either determine that our target is available or not - then we add ourselves to the pending resource list
460     // Once the pending resource appears, it will call buildPendingResource(), so we're called a second time.
461     String id = SVGURIReference::getTarget(href());
462     Element* targetElement = document()->getElementById(id);
463     ASSERT(!m_targetElementInstance);
464 
465     if (!targetElement) {
466         if (m_isPendingResource || id.isEmpty())
467             return;
468 
469         m_isPendingResource = true;
470         m_resourceId = id;
471         document()->accessSVGExtensions()->addPendingResource(id, this);
472         return;
473     }
474 
475     if (m_isPendingResource) {
476         ASSERT(!m_targetElementInstance);
477         m_isPendingResource = false;
478         m_resourceId = String();
479         invalidateShadowTree();
480     }
481 }
482 
buildShadowAndInstanceTree(SVGShadowTreeRootElement * shadowRoot)483 void SVGUseElement::buildShadowAndInstanceTree(SVGShadowTreeRootElement* shadowRoot)
484 {
485     struct ShadowTreeUpdateBlocker {
486         ShadowTreeUpdateBlocker(SVGUseElement* currentUseElement)
487             : useElement(currentUseElement)
488         {
489             useElement->setUpdatesBlocked(true);
490         }
491 
492         ~ShadowTreeUpdateBlocker()
493         {
494             useElement->setUpdatesBlocked(false);
495         }
496 
497         SVGUseElement* useElement;
498     };
499 
500     // When cloning the target nodes, they may decide to synchronize style and/or animated SVG attributes.
501     // That causes calls to SVGElementInstance::updateAllInstancesOfElement(), which mark the shadow tree for recreation.
502     // Solution: block any updates to the shadow tree while we're building it.
503     ShadowTreeUpdateBlocker blocker(this);
504 
505     String id = SVGURIReference::getTarget(href());
506     Element* targetElement = document()->getElementById(id);
507     if (!targetElement) {
508         // The only time we should get here is when the use element has not been
509         // given a resource to target.
510         ASSERT(m_resourceId.isEmpty());
511         return;
512     }
513 
514     // Do not build the shadow/instance tree for <use> elements living in a shadow tree.
515     // The will be expanded soon anyway - see expandUseElementsInShadowTree().
516     ContainerNode* parent = parentNode();
517     while (parent) {
518         if (parent->isShadowRoot())
519             return;
520 
521         parent = parent->parentNodeGuaranteedHostFree();
522     }
523 
524     SVGElement* target = 0;
525     if (targetElement && targetElement->isSVGElement())
526         target = static_cast<SVGElement*>(targetElement);
527 
528     detachInstance();
529 
530     // Do not allow self-referencing.
531     // 'target' may be null, if it's a non SVG namespaced element.
532     if (!target || target == this)
533         return;
534 
535     // Why a seperated instance/shadow tree? SVG demands it:
536     // The instance tree is accesable from JavaScript, and has to
537     // expose a 1:1 copy of the referenced tree, whereas internally we need
538     // to alter the tree for correct "use-on-symbol", "use-on-svg" support.
539 
540     // Build instance tree. Create root SVGElementInstance object for the first sub-tree node.
541     //
542     // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a
543     // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object
544     // is the SVGRectElement that corresponds to the referenced 'rect' element.
545     m_targetElementInstance = SVGElementInstance::create(this, this, target);
546 
547     // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children
548     bool foundProblem = false;
549     buildInstanceTree(target, m_targetElementInstance.get(), foundProblem);
550 
551     // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it!
552     // Non-appearing <use> content is easier to debug, then half-appearing content.
553     if (foundProblem) {
554         detachInstance();
555         return;
556     }
557 
558     // Assure instance tree building was successfull
559     ASSERT(m_targetElementInstance);
560     ASSERT(!m_targetElementInstance->shadowTreeElement());
561     ASSERT(m_targetElementInstance->correspondingUseElement() == this);
562     ASSERT(m_targetElementInstance->directUseElement() == this);
563     ASSERT(m_targetElementInstance->correspondingElement() == target);
564 
565     // Build shadow tree from instance tree
566     // This also handles the special cases: <use> on <symbol>, <use> on <svg>.
567     buildShadowTree(shadowRoot, target, m_targetElementInstance.get());
568 
569 #if ENABLE(SVG) && ENABLE(SVG_USE)
570     // Expand all <use> elements in the shadow tree.
571     // Expand means: replace the actual <use> element by what it references.
572     expandUseElementsInShadowTree(shadowRoot);
573 
574     // Expand all <symbol> elements in the shadow tree.
575     // Expand means: replace the actual <symbol> element by the <svg> element.
576     expandSymbolElementsInShadowTree(shadowRoot);
577 #endif
578 
579     // Now that the shadow tree is completly expanded, we can associate
580     // shadow tree elements <-> instances in the instance tree.
581     associateInstancesWithShadowTreeElements(shadowRoot->firstChild(), m_targetElementInstance.get());
582 
583     // If no shadow tree element is present, this means that the reference root
584     // element was removed, as it is disallowed (ie. <use> on <foreignObject>)
585     // Do NOT leave an inconsistent instance tree around, instead destruct it.
586     if (!m_targetElementInstance->shadowTreeElement()) {
587         shadowRoot->removeAllChildren();
588         detachInstance();
589         return;
590     }
591 
592     // Consistency checks - this is assumed in updateContainerOffset().
593     ASSERT(m_targetElementInstance->shadowTreeElement()->parentNode() == shadowRoot);
594 
595     // Transfer event listeners assigned to the referenced element to our shadow tree elements.
596     transferEventListenersToShadowTree(m_targetElementInstance.get());
597 
598     // Update container offset/size
599     updateContainerOffsets();
600     updateContainerSizes();
601 
602     // Update relative length information
603     updateRelativeLengthsInformation();
604 
605     // Eventually dump instance tree
606 #ifdef DUMP_INSTANCE_TREE
607     String text;
608     unsigned int depth = 0;
609 
610     dumpInstanceTree(depth, text, m_targetElementInstance.get());
611     fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data());
612 #endif
613 
614     // Eventually dump shadow tree
615 #ifdef DUMP_SHADOW_TREE
616     ExceptionCode ec = 0;
617 
618     RefPtr<XMLSerializer> serializer = XMLSerializer::create();
619 
620     String markup = serializer->serializeToString(shadowRoot, ec);
621     ASSERT(!ec);
622 
623     fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data());
624 #endif
625 }
626 
detachInstance()627 void SVGUseElement::detachInstance()
628 {
629     if (!m_targetElementInstance)
630         return;
631     m_targetElementInstance->clearUseElements();
632     m_targetElementInstance = 0;
633 }
634 
createRenderer(RenderArena * arena,RenderStyle *)635 RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*)
636 {
637     return new (arena) RenderSVGShadowTreeRootContainer(this);
638 }
639 
updateFromElementCallback(Node * node)640 static void updateFromElementCallback(Node* node)
641 {
642     if (RenderObject* renderer = node->renderer())
643         renderer->updateFromElement();
644 }
645 
attach()646 void SVGUseElement::attach()
647 {
648     SVGStyledTransformableElement::attach();
649 
650     if (renderer())
651         queuePostAttachCallback(updateFromElementCallback, this);
652 }
653 
detach()654 void SVGUseElement::detach()
655 {
656     SVGStyledTransformableElement::detach();
657     detachInstance();
658 }
659 
isDirectReference(Node * node)660 static bool isDirectReference(Node* node)
661 {
662     return node->hasTagName(SVGNames::pathTag)
663            || node->hasTagName(SVGNames::rectTag)
664            || node->hasTagName(SVGNames::circleTag)
665            || node->hasTagName(SVGNames::ellipseTag)
666            || node->hasTagName(SVGNames::polygonTag)
667            || node->hasTagName(SVGNames::polylineTag)
668            || node->hasTagName(SVGNames::textTag);
669 }
670 
toClipPath(Path & path) const671 void SVGUseElement::toClipPath(Path& path) const
672 {
673     ASSERT(path.isEmpty());
674 
675     Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0;
676     if (!n)
677         return;
678 
679     if (n->isSVGElement() && static_cast<SVGElement*>(n)->isStyledTransformable()) {
680         if (!isDirectReference(n))
681             // Spec: Indirect references are an error (14.3.5)
682             document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>");
683         else {
684             static_cast<SVGStyledTransformableElement*>(n)->toClipPath(path);
685             path.translate(FloatSize(x().value(this), y().value(this)));
686             path.transform(animatedLocalTransform());
687         }
688     }
689 }
690 
rendererClipChild() const691 RenderObject* SVGUseElement::rendererClipChild() const
692 {
693     Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0;
694     if (!n)
695         return 0;
696 
697     if (n->isSVGElement() && isDirectReference(n))
698         return static_cast<SVGElement*>(n)->renderer();
699 
700     return 0;
701 }
702 
buildInstanceTree(SVGElement * target,SVGElementInstance * targetInstance,bool & foundProblem)703 void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem)
704 {
705     ASSERT(target);
706     ASSERT(targetInstance);
707 
708     // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
709     // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
710     bool targetHasUseTag = target->hasTagName(SVGNames::useTag);
711     SVGElement* newTarget = 0;
712     if (targetHasUseTag) {
713         foundProblem = hasCycleUseReferencing(static_cast<SVGUseElement*>(target), targetInstance, newTarget);
714         if (foundProblem)
715             return;
716     }
717 
718     // A general description from the SVG spec, describing what buildInstanceTree() actually does.
719     //
720     // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree
721     // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement
722     // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has
723     // its correspondingElement that is an SVGRectElement object.
724 
725     for (Node* node = target->firstChild(); node; node = node->nextSibling()) {
726         SVGElement* element = 0;
727         if (node->isSVGElement())
728             element = static_cast<SVGElement*>(node);
729 
730         // Skip any non-svg nodes or any disallowed element.
731         if (!element || isDisallowedElement(element))
732             continue;
733 
734         // Create SVGElementInstance object, for both container/non-container nodes.
735         RefPtr<SVGElementInstance> instance = SVGElementInstance::create(this, 0, element);
736         SVGElementInstance* instancePtr = instance.get();
737         targetInstance->appendChild(instance.release());
738 
739         // Enter recursion, appending new instance tree nodes to the "instance" object.
740         buildInstanceTree(element, instancePtr, foundProblem);
741         if (foundProblem)
742             return;
743     }
744 
745     if (!targetHasUseTag || !newTarget)
746         return;
747 
748     RefPtr<SVGElementInstance> newInstance = SVGElementInstance::create(this, static_cast<SVGUseElement*>(target), newTarget);
749     SVGElementInstance* newInstancePtr = newInstance.get();
750     targetInstance->appendChild(newInstance.release());
751     buildInstanceTree(newTarget, newInstancePtr, foundProblem);
752 }
753 
hasCycleUseReferencing(SVGUseElement * use,SVGElementInstance * targetInstance,SVGElement * & newTarget)754 bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, SVGElementInstance* targetInstance, SVGElement*& newTarget)
755 {
756     String id = SVGURIReference::getTarget(use->href());
757     Element* targetElement = document()->getElementById(id);
758     newTarget = 0;
759     if (targetElement && targetElement->isSVGElement())
760         newTarget = static_cast<SVGElement*>(targetElement);
761 
762     if (!newTarget)
763         return false;
764 
765     // Shortcut for self-references
766     if (newTarget == this)
767         return true;
768 
769     SVGElementInstance* instance = targetInstance->parentNode();
770     while (instance) {
771         SVGElement* element = instance->correspondingElement();
772 
773         // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution.
774         if (element->hasID() && element->idForStyleResolution() == id)
775             return true;
776 
777         instance = instance->parentNode();
778     }
779     return false;
780 }
781 
removeDisallowedElementsFromSubtree(Node * subtree)782 void SVGUseElement::removeDisallowedElementsFromSubtree(Node* subtree)
783 {
784     ASSERT(!subtree->inDocument());
785     ExceptionCode ec;
786     Node* node = subtree->firstChild();
787     while (node) {
788         if (isDisallowedElement(node)) {
789             Node* next = node->traverseNextSibling(subtree);
790             // The subtree is not in document so this won't generate events that could mutate the tree.
791             node->parentNode()->removeChild(node, ec);
792             node = next;
793         } else
794             node = node->traverseNextNode(subtree);
795     }
796 }
797 
buildShadowTree(SVGShadowTreeRootElement * shadowRoot,SVGElement * target,SVGElementInstance * targetInstance)798 void SVGUseElement::buildShadowTree(SVGShadowTreeRootElement* shadowRoot, SVGElement* target, SVGElementInstance* targetInstance)
799 {
800     // For instance <use> on <foreignObject> (direct case).
801     if (isDisallowedElement(target))
802         return;
803 
804     RefPtr<Element> newChild = targetInstance->correspondingElement()->cloneElementWithChildren();
805 
806     // We don't walk the target tree element-by-element, and clone each element,
807     // but instead use cloneElementWithChildren(). This is an optimization for the common
808     // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
809     // Though if there are disallowed elements in the subtree, we have to remove them.
810     // For instance: <use> on <g> containing <foreignObject> (indirect case).
811     if (subtreeContainsDisallowedElement(newChild.get()))
812         removeDisallowedElementsFromSubtree(newChild.get());
813 
814     SVGElement* newChildPtr = 0;
815     if (newChild->isSVGElement())
816         newChildPtr = static_cast<SVGElement*>(newChild.get());
817     ASSERT(newChildPtr);
818 
819     ExceptionCode ec = 0;
820     shadowRoot->appendChild(newChild.release(), ec);
821     ASSERT(!ec);
822 }
823 
824 #if ENABLE(SVG) && ENABLE(SVG_USE)
expandUseElementsInShadowTree(Node * element)825 void SVGUseElement::expandUseElementsInShadowTree(Node* element)
826 {
827     // Why expand the <use> elements in the shadow tree here, and not just
828     // do this directly in buildShadowTree, if we encounter a <use> element?
829     //
830     // Short answer: Because we may miss to expand some elements. Ie. if a <symbol>
831     // contains <use> tags, we'd miss them. So once we're done with settin' up the
832     // actual shadow tree (after the special case modification for svg/symbol) we have
833     // to walk it completely and expand all <use> elements.
834     if (element->hasTagName(SVGNames::useTag)) {
835         SVGUseElement* use = static_cast<SVGUseElement*>(element);
836 
837         String id = SVGURIReference::getTarget(use->href());
838         Element* targetElement = document()->getElementById(id);
839         SVGElement* target = 0;
840         if (targetElement && targetElement->isSVGElement())
841             target = static_cast<SVGElement*>(targetElement);
842 
843         // Don't ASSERT(target) here, it may be "pending", too.
844         // Setup sub-shadow tree root node
845         RefPtr<SVGShadowTreeContainerElement> cloneParent = SVGShadowTreeContainerElement::create(document());
846         use->cloneChildNodes(cloneParent.get());
847 
848         // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
849         // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
850         transferUseAttributesToReplacedElement(use, cloneParent.get());
851 
852         ExceptionCode ec = 0;
853         if (target && !isDisallowedElement(target)) {
854             RefPtr<Element> newChild = target->cloneElementWithChildren();
855 
856             SVGElement* newChildPtr = 0;
857             if (newChild->isSVGElement())
858                 newChildPtr = static_cast<SVGElement*>(newChild.get());
859             ASSERT(newChildPtr);
860 
861             cloneParent->appendChild(newChild.release(), ec);
862             ASSERT(!ec);
863         }
864 
865         // We don't walk the target tree element-by-element, and clone each element,
866         // but instead use cloneElementWithChildren(). This is an optimization for the common
867         // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
868         // Though if there are disallowed elements in the subtree, we have to remove them.
869         // For instance: <use> on <g> containing <foreignObject> (indirect case).
870         if (subtreeContainsDisallowedElement(cloneParent.get()))
871             removeDisallowedElementsFromSubtree(cloneParent.get());
872 
873         RefPtr<Node> replacingElement(cloneParent.get());
874 
875         // Replace <use> with referenced content.
876         ASSERT(use->parentNode());
877         use->parentNode()->replaceChild(cloneParent.release(), use, ec);
878         ASSERT(!ec);
879 
880         // Expand the siblings because the *element* is replaced and we will
881         // lose the sibling chain when we are back from recursion.
882         element = replacingElement.get();
883         for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling())
884             expandUseElementsInShadowTree(sibling.get());
885     }
886 
887     for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
888         expandUseElementsInShadowTree(child.get());
889 }
890 
expandSymbolElementsInShadowTree(Node * element)891 void SVGUseElement::expandSymbolElementsInShadowTree(Node* element)
892 {
893     if (element->hasTagName(SVGNames::symbolTag)) {
894         // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
895         // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
896         // always have explicit values for attributes width and height. If attributes width and/or
897         // height are provided on the 'use' element, then these attributes will be transferred to
898         // the generated 'svg'. If attributes width and/or height are not specified, the generated
899         // 'svg' element will use values of 100% for these attributes.
900         RefPtr<SVGSVGElement> svgElement = SVGSVGElement::create(SVGNames::svgTag, document());
901 
902         // Transfer all attributes from <symbol> to the new <svg> element
903         svgElement->attributes()->setAttributes(*element->attributes());
904 
905         // Only clone symbol children, and add them to the new <svg> element
906         ExceptionCode ec = 0;
907         for (Node* child = element->firstChild(); child; child = child->nextSibling()) {
908             RefPtr<Node> newChild = child->cloneNode(true);
909             svgElement->appendChild(newChild.release(), ec);
910             ASSERT(!ec);
911         }
912 
913         // We don't walk the target tree element-by-element, and clone each element,
914         // but instead use cloneNode(deep=true). This is an optimization for the common
915         // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
916         // Though if there are disallowed elements in the subtree, we have to remove them.
917         // For instance: <use> on <g> containing <foreignObject> (indirect case).
918         if (subtreeContainsDisallowedElement(svgElement.get()))
919             removeDisallowedElementsFromSubtree(svgElement.get());
920 
921         RefPtr<Node> replacingElement(svgElement.get());
922 
923         // Replace <symbol> with <svg>.
924         ASSERT(element->parentNode());
925         element->parentNode()->replaceChild(svgElement.release(), element, ec);
926         ASSERT(!ec);
927 
928         // Expand the siblings because the *element* is replaced and we will
929         // lose the sibling chain when we are back from recursion.
930         element = replacingElement.get();
931         for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling())
932             expandSymbolElementsInShadowTree(sibling.get());
933     }
934 
935     for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling())
936         expandSymbolElementsInShadowTree(child.get());
937 }
938 
939 #endif
940 
transferEventListenersToShadowTree(SVGElementInstance * target)941 void SVGUseElement::transferEventListenersToShadowTree(SVGElementInstance* target)
942 {
943     if (!target)
944         return;
945 
946     SVGElement* originalElement = target->correspondingElement();
947     ASSERT(originalElement);
948 
949     if (SVGElement* shadowTreeElement = target->shadowTreeElement()) {
950         if (EventTargetData* d = originalElement->eventTargetData()) {
951             EventListenerMap& map = d->eventListenerMap;
952             EventListenerMap::iterator end = map.end();
953             for (EventListenerMap::iterator it = map.begin(); it != end; ++it) {
954                 EventListenerVector& entry = *it->second;
955                 for (size_t i = 0; i < entry.size(); ++i) {
956                     // Event listeners created from markup have already been transfered to the shadow tree during cloning.
957                     if (entry[i].listener->wasCreatedFromMarkup())
958                         continue;
959                     shadowTreeElement->addEventListener(it->first, entry[i].listener, entry[i].useCapture);
960                 }
961             }
962         }
963     }
964 
965     for (SVGElementInstance* instance = target->firstChild(); instance; instance = instance->nextSibling())
966         transferEventListenersToShadowTree(instance);
967 }
968 
associateInstancesWithShadowTreeElements(Node * target,SVGElementInstance * targetInstance)969 void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance)
970 {
971     if (!target || !targetInstance)
972         return;
973 
974     SVGElement* originalElement = targetInstance->correspondingElement();
975 
976     if (originalElement->hasTagName(SVGNames::useTag)) {
977 #if ENABLE(SVG) && ENABLE(SVG_USE)
978         // <use> gets replaced by <g>
979         ASSERT(target->nodeName() == SVGNames::gTag);
980 #else
981         ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag);
982 #endif
983     } else if (originalElement->hasTagName(SVGNames::symbolTag)) {
984         // <symbol> gets replaced by <svg>
985 #if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT)
986         ASSERT(target->nodeName() == SVGNames::svgTag);
987 #endif
988     } else
989         ASSERT(target->nodeName() == originalElement->nodeName());
990 
991     SVGElement* element = 0;
992     if (target->isSVGElement())
993         element = static_cast<SVGElement*>(target);
994 
995     ASSERT(!targetInstance->shadowTreeElement());
996     targetInstance->setShadowTreeElement(element);
997 
998     Node* node = target->firstChild();
999     for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) {
1000         // Skip any non-svg elements in shadow tree
1001         while (node && !node->isSVGElement())
1002            node = node->nextSibling();
1003 
1004         if (!node)
1005             break;
1006 
1007         associateInstancesWithShadowTreeElements(node, instance);
1008         node = node->nextSibling();
1009     }
1010 }
1011 
instanceForShadowTreeElement(Node * element) const1012 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element) const
1013 {
1014     if (!m_targetElementInstance) {
1015         ASSERT(!inDocument());
1016         return 0;
1017     }
1018 
1019     return instanceForShadowTreeElement(element, m_targetElementInstance.get());
1020 }
1021 
instanceForShadowTreeElement(Node * element,SVGElementInstance * instance) const1022 SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const
1023 {
1024     ASSERT(element);
1025     ASSERT(instance);
1026 
1027     // We're dispatching a mutation event during shadow tree construction
1028     // this instance hasn't yet been associated to a shadowTree element.
1029     if (!instance->shadowTreeElement())
1030         return 0;
1031 
1032     if (element == instance->shadowTreeElement())
1033         return instance;
1034 
1035     for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) {
1036         if (SVGElementInstance* search = instanceForShadowTreeElement(element, current))
1037             return search;
1038     }
1039 
1040     return 0;
1041 }
1042 
invalidateShadowTree()1043 void SVGUseElement::invalidateShadowTree()
1044 {
1045     // Don't mutate the shadow tree while we're building it.
1046     if (m_updatesBlocked)
1047         return;
1048 
1049     m_needsShadowTreeRecreation = true;
1050     setNeedsStyleRecalc();
1051 }
1052 
transferUseAttributesToReplacedElement(SVGElement * from,SVGElement * to) const1053 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const
1054 {
1055     ASSERT(from);
1056     ASSERT(to);
1057 
1058     to->attributes()->setAttributes(*from->attributes());
1059 
1060     ExceptionCode ec = 0;
1061 
1062     to->removeAttribute(SVGNames::xAttr, ec);
1063     ASSERT(!ec);
1064 
1065     to->removeAttribute(SVGNames::yAttr, ec);
1066     ASSERT(!ec);
1067 
1068     to->removeAttribute(SVGNames::widthAttr, ec);
1069     ASSERT(!ec);
1070 
1071     to->removeAttribute(SVGNames::heightAttr, ec);
1072     ASSERT(!ec);
1073 
1074     to->removeAttribute(XLinkNames::hrefAttr, ec);
1075     ASSERT(!ec);
1076 }
1077 
selfHasRelativeLengths() const1078 bool SVGUseElement::selfHasRelativeLengths() const
1079 {
1080     if (x().isRelative()
1081      || y().isRelative()
1082      || width().isRelative()
1083      || height().isRelative())
1084         return true;
1085 
1086     if (!m_targetElementInstance)
1087         return false;
1088 
1089     SVGElement* element = m_targetElementInstance->correspondingElement();
1090     if (!element || !element->isStyled())
1091         return false;
1092 
1093     return static_cast<SVGStyledElement*>(element)->hasRelativeLengths();
1094 }
1095 
1096 }
1097 
1098 #endif // ENABLE(SVG)
1099