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