Remove unused variable movedSectionLogicalTop from table layout code.
[chromium-blink-merge.git] / third_party / WebKit / Source / core / svg / SVGUseElement.cpp
blobbcdc4ba03b4403d2e60b5fb346318521d40168b2
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 * Copyright (C) 2012 University of Szeged
7 * Copyright (C) 2012 Renata Hodovan <reni@webkit.org>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
25 #include "config.h"
27 #include "core/svg/SVGUseElement.h"
29 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
30 #include "core/SVGNames.h"
31 #include "core/XLinkNames.h"
32 #include "core/dom/Document.h"
33 #include "core/dom/ElementTraversal.h"
34 #include "core/events/Event.h"
35 #include "core/dom/shadow/ElementShadow.h"
36 #include "core/dom/shadow/ShadowRoot.h"
37 #include "core/fetch/FetchRequest.h"
38 #include "core/fetch/ResourceFetcher.h"
39 #include "core/layout/svg/LayoutSVGTransformableContainer.h"
40 #include "core/svg/SVGGElement.h"
41 #include "core/svg/SVGLengthContext.h"
42 #include "core/svg/SVGSVGElement.h"
43 #include "core/xml/parser/XMLDocumentParser.h"
45 namespace blink {
47 static SVGUseEventSender& svgUseLoadEventSender()
49 DEFINE_STATIC_LOCAL(SVGUseEventSender, sharedLoadEventSender, (EventTypeNames::load));
50 return sharedLoadEventSender;
53 inline SVGUseElement::SVGUseElement(Document& document)
54 : SVGGraphicsElement(SVGNames::useTag, document)
55 , SVGURIReference(this)
56 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(SVGLengthMode::Width), AllowNegativeLengths))
57 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(SVGLengthMode::Height), AllowNegativeLengths))
58 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(SVGLengthMode::Width), ForbidNegativeLengths))
59 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(SVGLengthMode::Height), ForbidNegativeLengths))
60 , m_haveFiredLoadEvent(false)
61 , m_needsShadowTreeRecreation(false)
63 ASSERT(hasCustomStyleCallbacks());
65 addToPropertyMap(m_x);
66 addToPropertyMap(m_y);
67 addToPropertyMap(m_width);
68 addToPropertyMap(m_height);
71 PassRefPtrWillBeRawPtr<SVGUseElement> SVGUseElement::create(Document& document)
73 // Always build a user agent #shadow-root for SVGUseElement.
74 RefPtrWillBeRawPtr<SVGUseElement> use = adoptRefWillBeNoop(new SVGUseElement(document));
75 use->ensureUserAgentShadowRoot();
76 return use.release();
79 SVGUseElement::~SVGUseElement()
81 setDocumentResource(0);
82 #if !ENABLE(OILPAN)
83 clearShadowTree();
84 cancelShadowTreeRecreation();
85 #endif
86 svgUseLoadEventSender().cancelEvent(this);
89 DEFINE_TRACE(SVGUseElement)
91 visitor->trace(m_x);
92 visitor->trace(m_y);
93 visitor->trace(m_width);
94 visitor->trace(m_height);
95 visitor->trace(m_targetElementInstance);
96 SVGGraphicsElement::trace(visitor);
97 SVGURIReference::trace(visitor);
100 #if ENABLE(ASSERT)
101 static inline bool isWellFormedDocument(Document* document)
103 if (document->isXMLDocument())
104 return static_cast<XMLDocumentParser*>(document->parser())->wellFormed();
105 return true;
107 #endif
109 Node::InsertionNotificationRequest SVGUseElement::insertedInto(ContainerNode* rootParent)
111 // This functions exists to assure assumptions made in the code regarding SVGElementInstance creation/destruction are satisfied.
112 SVGGraphicsElement::insertedInto(rootParent);
113 if (!rootParent->inDocument())
114 return InsertionDone;
115 ASSERT(!m_targetElementInstance || !isWellFormedDocument(&document()));
116 ASSERT(!hasPendingResources() || !isWellFormedDocument(&document()));
117 invalidateShadowTree();
118 return InsertionDone;
121 void SVGUseElement::removedFrom(ContainerNode* rootParent)
123 SVGGraphicsElement::removedFrom(rootParent);
124 if (rootParent->inDocument()) {
125 clearShadowTree();
126 cancelShadowTreeRecreation();
130 TreeScope* SVGUseElement::referencedScope() const
132 if (isStructurallyExternal())
133 return externalDocument();
134 return &treeScope();
137 Document* SVGUseElement::externalDocument() const
139 if (m_resource && m_resource->isLoaded()) {
140 // Gracefully handle error condition.
141 if (m_resource->errorOccurred())
142 return nullptr;
143 ASSERT(m_resource->document());
144 return m_resource->document();
146 return nullptr;
149 void transferUseWidthAndHeightIfNeeded(const SVGUseElement& use, SVGElement* shadowElement, const SVGElement& originalElement)
151 DEFINE_STATIC_LOCAL(const AtomicString, hundredPercentString, ("100%", AtomicString::ConstructFromLiteral));
152 ASSERT(shadowElement);
153 if (isSVGSymbolElement(*shadowElement)) {
154 // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height.
155 // If attributes width and/or height are provided on the 'use' element, then these attributes
156 // will be transferred to the generated 'svg'. If attributes width and/or height are not specified,
157 // the generated 'svg' element will use values of 100% for these attributes.
158 shadowElement->setAttribute(SVGNames::widthAttr, use.width()->isSpecified() ? AtomicString(use.width()->currentValue()->valueAsString()) : hundredPercentString);
159 shadowElement->setAttribute(SVGNames::heightAttr, use.height()->isSpecified() ? AtomicString(use.height()->currentValue()->valueAsString()) : hundredPercentString);
160 } else if (isSVGSVGElement(*shadowElement)) {
161 // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these
162 // values will override the corresponding attributes on the 'svg' in the generated tree.
163 if (use.width()->isSpecified())
164 shadowElement->setAttribute(SVGNames::widthAttr, AtomicString(use.width()->currentValue()->valueAsString()));
165 else
166 shadowElement->setAttribute(SVGNames::widthAttr, originalElement.getAttribute(SVGNames::widthAttr));
167 if (use.height()->isSpecified())
168 shadowElement->setAttribute(SVGNames::heightAttr, AtomicString(use.height()->currentValue()->valueAsString()));
169 else
170 shadowElement->setAttribute(SVGNames::heightAttr, originalElement.getAttribute(SVGNames::heightAttr));
174 bool SVGUseElement::isPresentationAttribute(const QualifiedName& attrName) const
176 if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr)
177 return true;
178 return SVGGraphicsElement::isPresentationAttribute(attrName);
181 bool SVGUseElement::isPresentationAttributeWithSVGDOM(const QualifiedName& attrName) const
183 if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr)
184 return true;
185 return SVGGraphicsElement::isPresentationAttributeWithSVGDOM(attrName);
188 void SVGUseElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
190 RefPtrWillBeRawPtr<SVGAnimatedPropertyBase> property = propertyFromAttribute(name);
191 if (property == m_x)
192 addSVGLengthPropertyToPresentationAttributeStyle(style, CSSPropertyX, *m_x->currentValue());
193 else if (property == m_y)
194 addSVGLengthPropertyToPresentationAttributeStyle(style, CSSPropertyY, *m_y->currentValue());
195 else
196 SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
199 void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
201 if (attrName == SVGNames::xAttr
202 || attrName == SVGNames::yAttr
203 || attrName == SVGNames::widthAttr
204 || attrName == SVGNames::heightAttr) {
205 SVGElement::InvalidationGuard invalidationGuard(this);
207 if (attrName == SVGNames::xAttr
208 || attrName == SVGNames::yAttr) {
209 invalidateSVGPresentationAttributeStyle();
210 setNeedsStyleRecalc(LocalStyleChange,
211 StyleChangeReasonForTracing::fromAttribute(attrName));
214 updateRelativeLengthsInformation();
215 if (m_targetElementInstance) {
216 ASSERT(m_targetElementInstance->correspondingElement());
217 transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement());
220 LayoutObject* object = this->layoutObject();
221 if (object)
222 markForLayoutAndParentResourceInvalidation(object);
223 return;
226 if (SVGURIReference::isKnownAttribute(attrName)) {
227 SVGElement::InvalidationGuard invalidationGuard(this);
228 if (isStructurallyExternal()) {
229 KURL url = document().completeURL(hrefString());
230 if (url.hasFragmentIdentifier()) {
231 FetchRequest request(ResourceRequest(url), localName());
232 setDocumentResource(DocumentResource::fetchSVGDocument(request, document().fetcher()));
234 } else {
235 setDocumentResource(0);
238 invalidateShadowTree();
240 return;
243 SVGGraphicsElement::svgAttributeChanged(attrName);
246 static bool isDisallowedElement(Node* node)
248 // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used
249 // (i.e., "instanced") in the SVG document via a 'use' element."
250 // "Graphics Element" is defined as 'circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text'
251 // Excluded are anything that is used by reference or that only make sense to appear once in a document.
252 // We must also allow the shadow roots of other use elements.
253 if (node->isShadowRoot() || node->isTextNode())
254 return false;
256 if (!node->isSVGElement())
257 return true;
259 Element* element = toElement(node);
261 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, allowedElementTags, ());
262 if (allowedElementTags.isEmpty()) {
263 allowedElementTags.add(SVGNames::aTag);
264 allowedElementTags.add(SVGNames::circleTag);
265 allowedElementTags.add(SVGNames::descTag);
266 allowedElementTags.add(SVGNames::ellipseTag);
267 allowedElementTags.add(SVGNames::gTag);
268 allowedElementTags.add(SVGNames::imageTag);
269 allowedElementTags.add(SVGNames::lineTag);
270 allowedElementTags.add(SVGNames::metadataTag);
271 allowedElementTags.add(SVGNames::pathTag);
272 allowedElementTags.add(SVGNames::polygonTag);
273 allowedElementTags.add(SVGNames::polylineTag);
274 allowedElementTags.add(SVGNames::rectTag);
275 allowedElementTags.add(SVGNames::svgTag);
276 allowedElementTags.add(SVGNames::switchTag);
277 allowedElementTags.add(SVGNames::symbolTag);
278 allowedElementTags.add(SVGNames::textTag);
279 allowedElementTags.add(SVGNames::textPathTag);
280 allowedElementTags.add(SVGNames::titleTag);
281 allowedElementTags.add(SVGNames::tspanTag);
282 allowedElementTags.add(SVGNames::useTag);
284 return !allowedElementTags.contains<SVGAttributeHashTranslator>(element->tagQName());
287 static bool subtreeContainsDisallowedElement(Node* start)
289 if (isDisallowedElement(start))
290 return true;
292 for (Node* cur = start->firstChild(); cur; cur = cur->nextSibling()) {
293 if (subtreeContainsDisallowedElement(cur))
294 return true;
297 return false;
300 void SVGUseElement::scheduleShadowTreeRecreation()
302 if (inUseShadowTree())
303 return;
304 m_needsShadowTreeRecreation = true;
305 document().scheduleUseShadowTreeUpdate(*this);
308 void SVGUseElement::cancelShadowTreeRecreation()
310 m_needsShadowTreeRecreation = false;
311 document().unscheduleUseShadowTreeUpdate(*this);
314 void SVGUseElement::clearInstanceRoot()
316 if (m_targetElementInstance)
317 m_targetElementInstance = nullptr;
320 void SVGUseElement::clearShadowTree()
322 clearInstanceRoot();
324 // FIXME: We should try to optimize this, to at least allow partial reclones.
325 if (ShadowRoot* shadowTreeRootElement = userAgentShadowRoot())
326 shadowTreeRootElement->removeChildren(OmitSubtreeModifiedEvent);
328 removeAllOutgoingReferences();
331 void SVGUseElement::buildPendingResource()
333 if (inUseShadowTree())
334 return;
335 clearShadowTree();
336 cancelShadowTreeRecreation();
337 if (!referencedScope() || !inDocument())
338 return;
340 AtomicString id;
341 Element* target = SVGURIReference::targetElementFromIRIString(hrefString(), treeScope(), &id, externalDocument());
342 if (!target || !target->inDocument()) {
343 // If we can't find the target of an external element, just give up.
344 // We can't observe if the target somewhen enters the external document, nor should we do it.
345 if (externalDocument())
346 return;
347 if (id.isEmpty())
348 return;
350 referencedScope()->document().accessSVGExtensions().addPendingResource(id, this);
351 ASSERT(hasPendingResources());
352 return;
355 if (target->isSVGElement()) {
356 buildShadowAndInstanceTree(toSVGElement(target));
357 invalidateDependentShadowTrees();
360 ASSERT(!m_needsShadowTreeRecreation);
363 static PassRefPtrWillBeRawPtr<Node> cloneNodeAndAssociate(Node& toClone)
365 RefPtrWillBeRawPtr<Node> clone = toClone.cloneNode(false);
366 if (!clone->isSVGElement())
367 return clone.release();
369 SVGElement& svgElement = toSVGElement(toClone);
370 ASSERT(!svgElement.correspondingElement());
371 toSVGElement(clone.get())->setCorrespondingElement(&svgElement);
372 if (EventTargetData* data = toClone.eventTargetData())
373 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(clone.get());
374 TrackExceptionState exceptionState;
375 for (Node* node = toClone.firstChild(); node && !exceptionState.hadException(); node = node->nextSibling())
376 clone->appendChild(cloneNodeAndAssociate(*node), exceptionState);
377 return clone.release();
380 void SVGUseElement::buildShadowAndInstanceTree(SVGElement* target)
382 ASSERT(!m_targetElementInstance);
383 ASSERT(!m_needsShadowTreeRecreation);
385 // <use> creates a "user agent" shadow root. Do not build the shadow/instance tree for <use>
386 // elements living in a user agent shadow tree because they will get expanded in a second
387 // pass -- see expandUseElementsInShadowTree().
388 if (inUseShadowTree())
389 return;
391 // Do not allow self-referencing.
392 // 'target' may be null, if it's a non SVG namespaced element.
393 if (!target || target == this || isDisallowedElement(target))
394 return;
396 // Set up root SVG element in shadow tree.
397 RefPtrWillBeRawPtr<Element> newChild = target->cloneElementWithoutChildren();
398 m_targetElementInstance = toSVGElement(newChild.get());
399 ShadowRoot* shadowTreeRootElement = userAgentShadowRoot();
400 shadowTreeRootElement->appendChild(newChild.release());
402 // Clone the target subtree into the shadow tree, not handling <use> and <symbol> yet.
404 // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it!
405 // Non-appearing <use> content is easier to debug, then half-appearing content.
406 if (!buildShadowTree(target, m_targetElementInstance.get(), false)) {
407 clearShadowTree();
408 return;
411 if (instanceTreeIsLoading(m_targetElementInstance.get()))
412 return;
414 // Assure shadow tree building was successfull
415 ASSERT(m_targetElementInstance);
416 ASSERT(m_targetElementInstance->correspondingUseElement() == this);
417 ASSERT(m_targetElementInstance->correspondingElement() == target);
419 // Expand all <use> elements in the shadow tree.
420 // Expand means: replace the actual <use> element by what it references.
421 if (!expandUseElementsInShadowTree(m_targetElementInstance.get())) {
422 clearShadowTree();
423 return;
426 // Expand all <symbol> elements in the shadow tree.
427 // Expand means: replace the actual <symbol> element by the <svg> element.
428 expandSymbolElementsInShadowTree(toSVGElement(shadowTreeRootElement->firstChild()));
430 m_targetElementInstance = toSVGElement(shadowTreeRootElement->firstChild());
431 transferUseWidthAndHeightIfNeeded(*this, m_targetElementInstance.get(), *m_targetElementInstance->correspondingElement());
433 ASSERT(m_targetElementInstance->parentNode() == shadowTreeRootElement);
435 // Update relative length information.
436 updateRelativeLengthsInformation();
439 LayoutObject* SVGUseElement::createLayoutObject(const ComputedStyle&)
441 return new LayoutSVGTransformableContainer(this);
444 static bool isDirectReference(const SVGElement& element)
446 return isSVGPathElement(element)
447 || isSVGRectElement(element)
448 || isSVGCircleElement(element)
449 || isSVGEllipseElement(element)
450 || isSVGPolygonElement(element)
451 || isSVGPolylineElement(element)
452 || isSVGTextElement(element);
455 void SVGUseElement::toClipPath(Path& path) const
457 ASSERT(path.isEmpty());
459 const SVGGraphicsElement* element = targetGraphicsElementForClipping();
461 if (!element)
462 return;
464 if (element->isSVGGeometryElement()) {
465 toSVGGeometryElement(*element).toClipPath(path);
466 // FIXME: Avoid manual resolution of x/y here. Its potentially harmful.
467 SVGLengthContext lengthContext(this);
468 path.translate(FloatSize(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext)));
469 path.transform(calculateAnimatedLocalTransform());
473 SVGGraphicsElement* SVGUseElement::targetGraphicsElementForClipping() const
475 Node* n = userAgentShadowRoot()->firstChild();
476 if (!n || !n->isSVGElement())
477 return nullptr;
479 SVGElement& element = toSVGElement(*n);
481 if (!element.isSVGGraphicsElement())
482 return nullptr;
484 // Spec: "If a <use> element is a child of a clipPath element, it must directly
485 // reference <path>, <text> or basic shapes elements. Indirect references are an
486 // error and the clipPath element must be ignored."
487 // http://dev.w3.org/fxtf/css-masking-1/#the-clip-path
488 if (!isDirectReference(element)) {
489 // Spec: Indirect references are an error (14.3.5)
490 document().accessSVGExtensions().reportError("Not allowed to use indirect reference in <clip-path>");
491 return nullptr;
494 return &toSVGGraphicsElement(element);
497 bool SVGUseElement::buildShadowTree(SVGElement* target, SVGElement* targetInstance, bool foundUse)
499 ASSERT(target);
500 ASSERT(targetInstance);
502 // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced
503 // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree.
504 if (isSVGUseElement(*target)) {
505 // We only need to track first degree <use> dependencies. Indirect references are handled
506 // as the invalidation bubbles up the dependency chain.
507 if (!foundUse && !isStructurallyExternal()) {
508 addReferenceTo(target);
509 foundUse = true;
511 } else if (isDisallowedElement(target)) {
512 return false;
515 targetInstance->setCorrespondingElement(target);
516 if (EventTargetData* data = target->eventTargetData())
517 data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(targetInstance);
519 for (Node* child = target->firstChild(); child; child = child->nextSibling()) {
520 // Skip any disallowed element.
521 if (isDisallowedElement(child))
522 continue;
524 RefPtrWillBeRawPtr<Node> newChild = child->cloneNode(false);
525 targetInstance->appendChild(newChild.get());
526 if (newChild->isSVGElement()) {
527 // Enter recursion, appending new instance tree nodes to the "instance" object.
528 if (!buildShadowTree(toSVGElement(child), toSVGElement(newChild), foundUse))
529 return false;
532 return true;
535 bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, ContainerNode* targetInstance, SVGElement*& newTarget)
537 ASSERT(referencedScope());
538 Element* targetElement = SVGURIReference::targetElementFromIRIString(use->hrefString(), *referencedScope());
539 newTarget = 0;
540 if (targetElement && targetElement->isSVGElement())
541 newTarget = toSVGElement(targetElement);
543 if (!newTarget)
544 return false;
546 // Shortcut for self-references
547 if (newTarget == this)
548 return true;
550 AtomicString targetId = newTarget->getIdAttribute();
551 ContainerNode* instance = targetInstance->parentNode();
552 while (instance && instance->isSVGElement()) {
553 SVGElement* element = toSVGElement(instance);
554 if (element->hasID() && element->getIdAttribute() == targetId && element->document() == newTarget->document())
555 return true;
557 instance = instance->parentNode();
559 return false;
562 static inline void removeDisallowedElementsFromSubtree(Element& subtree)
564 ASSERT(!subtree.inDocument());
565 Element* element = ElementTraversal::firstWithin(subtree);
566 while (element) {
567 if (isDisallowedElement(element)) {
568 Element* next = ElementTraversal::nextSkippingChildren(*element, &subtree);
569 // The subtree is not in document so this won't generate events that could mutate the tree.
570 element->parentNode()->removeChild(element);
571 element = next;
572 } else {
573 element = ElementTraversal::next(*element, &subtree);
578 bool SVGUseElement::expandUseElementsInShadowTree(SVGElement* element)
580 ASSERT(element);
581 // Why expand the <use> elements in the shadow tree here, and not just
582 // do this directly in buildShadowTree, if we encounter a <use> element?
584 // Short answer: Because we may miss to expand some elements. For example, if a <symbol>
585 // contains <use> tags, we'd miss them. So once we're done with setting up the
586 // actual shadow tree (after the special case modification for svg/symbol) we have
587 // to walk it completely and expand all <use> elements.
588 if (isSVGUseElement(*element)) {
589 SVGUseElement* use = toSVGUseElement(element);
590 ASSERT(!use->resourceIsStillLoading());
592 SVGElement* target = 0;
593 if (hasCycleUseReferencing(toSVGUseElement(use->correspondingElement()), use, target))
594 return false;
596 if (target && isDisallowedElement(target))
597 return false;
598 // Don't ASSERT(target) here, it may be "pending", too.
599 // Setup sub-shadow tree root node
600 RefPtrWillBeRawPtr<SVGGElement> cloneParent = SVGGElement::create(referencedScope()->document());
601 cloneParent->setCorrespondingElement(use->correspondingElement());
603 // Move already cloned elements to the new <g> element
604 for (Node* child = use->firstChild(); child; ) {
605 Node* nextChild = child->nextSibling();
606 cloneParent->appendChild(child);
607 child = nextChild;
610 // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
611 // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
612 transferUseAttributesToReplacedElement(use, cloneParent.get());
614 if (target) {
615 RefPtrWillBeRawPtr<Node> newChild = cloneNodeAndAssociate(*target);
616 ASSERT(newChild->isSVGElement());
617 transferUseWidthAndHeightIfNeeded(*use, toSVGElement(newChild.get()), *target);
618 cloneParent->appendChild(newChild.release());
621 // We don't walk the target tree element-by-element, and clone each element,
622 // but instead use cloneElementWithChildren(). This is an optimization for the common
623 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
624 // Though if there are disallowed elements in the subtree, we have to remove them.
625 // For instance: <use> on <g> containing <foreignObject> (indirect case).
626 if (subtreeContainsDisallowedElement(cloneParent.get()))
627 removeDisallowedElementsFromSubtree(*cloneParent);
629 RefPtrWillBeRawPtr<SVGElement> replacingElement(cloneParent.get());
631 // Replace <use> with referenced content.
632 ASSERT(use->parentNode());
633 use->parentNode()->replaceChild(cloneParent.release(), use);
635 // Expand the siblings because the *element* is replaced and we will
636 // lose the sibling chain when we are back from recursion.
637 element = replacingElement.get();
638 for (RefPtrWillBeRawPtr<SVGElement> sibling = Traversal<SVGElement>::nextSibling(*element); sibling; sibling = Traversal<SVGElement>::nextSibling(*sibling)) {
639 if (!expandUseElementsInShadowTree(sibling.get()))
640 return false;
644 for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child)) {
645 if (!expandUseElementsInShadowTree(child.get()))
646 return false;
648 return true;
651 void SVGUseElement::expandSymbolElementsInShadowTree(SVGElement* element)
653 ASSERT(element);
654 if (isSVGSymbolElement(*element)) {
655 // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
656 // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
657 // always have explicit values for attributes width and height. If attributes width and/or
658 // height are provided on the 'use' element, then these attributes will be transferred to
659 // the generated 'svg'. If attributes width and/or height are not specified, the generated
660 // 'svg' element will use values of 100% for these attributes.
661 ASSERT(referencedScope());
662 RefPtrWillBeRawPtr<SVGSVGElement> svgElement = SVGSVGElement::create(referencedScope()->document());
663 // Transfer all data (attributes, etc.) from <symbol> to the new <svg> element.
664 svgElement->cloneDataFromElement(*element);
665 svgElement->setCorrespondingElement(element->correspondingElement());
667 // Move already cloned elements to the new <svg> element
668 for (Node* child = element->firstChild(); child; ) {
669 Node* nextChild = child->nextSibling();
670 svgElement->appendChild(child);
671 child = nextChild;
674 // We don't walk the target tree element-by-element, and clone each element,
675 // but instead use cloneNode(deep=true). This is an optimization for the common
676 // case where <use> doesn't contain disallowed elements (ie. <foreignObject>).
677 // Though if there are disallowed elements in the subtree, we have to remove them.
678 // For instance: <use> on <g> containing <foreignObject> (indirect case).
679 if (subtreeContainsDisallowedElement(svgElement.get()))
680 removeDisallowedElementsFromSubtree(*svgElement);
682 RefPtrWillBeRawPtr<SVGElement> replacingElement(svgElement.get());
684 // Replace <symbol> with <svg>.
685 ASSERT(element->parentNode());
686 element->parentNode()->replaceChild(svgElement.release(), element);
688 // Expand the siblings because the *element* is replaced and we will
689 // lose the sibling chain when we are back from recursion.
690 element = replacingElement.get();
693 for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child))
694 expandSymbolElementsInShadowTree(child.get());
697 void SVGUseElement::invalidateShadowTree()
699 if (!inActiveDocument() || m_needsShadowTreeRecreation)
700 return;
701 clearInstanceRoot();
702 scheduleShadowTreeRecreation();
703 invalidateDependentShadowTrees();
706 void SVGUseElement::invalidateDependentShadowTrees()
708 // Recursively invalidate dependent <use> shadow trees
709 const WillBeHeapHashSet<RawPtrWillBeWeakMember<SVGElement>>& instances = instancesForElement();
710 for (SVGElement* instance : instances) {
711 if (SVGUseElement* element = instance->correspondingUseElement()) {
712 ASSERT(element->inDocument());
713 element->invalidateShadowTree();
718 void SVGUseElement::transferUseAttributesToReplacedElement(SVGElement* from, SVGElement* to) const
720 ASSERT(from);
721 ASSERT(to);
723 to->cloneDataFromElement(*from);
725 to->removeAttribute(SVGNames::xAttr);
726 to->removeAttribute(SVGNames::yAttr);
727 to->removeAttribute(SVGNames::widthAttr);
728 to->removeAttribute(SVGNames::heightAttr);
729 to->removeAttribute(XLinkNames::hrefAttr);
732 bool SVGUseElement::selfHasRelativeLengths() const
734 if (m_x->currentValue()->isRelative()
735 || m_y->currentValue()->isRelative()
736 || m_width->currentValue()->isRelative()
737 || m_height->currentValue()->isRelative())
738 return true;
740 if (!m_targetElementInstance)
741 return false;
743 return m_targetElementInstance->hasRelativeLengths();
746 FloatRect SVGUseElement::getBBox()
748 document().updateLayoutIgnorePendingStylesheets();
750 if (!layoutObject())
751 return FloatRect();
753 LayoutSVGTransformableContainer& transformableContainer = toLayoutSVGTransformableContainer(*layoutObject());
754 // Don't apply the additional translation if the oBB is invalid.
755 if (!transformableContainer.isObjectBoundingBoxValid())
756 return FloatRect();
758 // TODO(fs): Preferably this would just use objectBoundingBox() (and hence
759 // don't need to override SVGGraphicsElement::getBBox at all) and be
760 // correct without additional work. That will not work out ATM without
761 // additional quirks. The problem stems from including the additional
762 // translation directly on the LayoutObject corresponding to the
763 // SVGUseElement.
764 FloatRect bbox = transformableContainer.objectBoundingBox();
765 bbox.move(transformableContainer.additionalTranslation());
766 return bbox;
769 void SVGUseElement::dispatchPendingEvent(SVGUseEventSender* eventSender)
771 ASSERT_UNUSED(eventSender, eventSender == &svgUseLoadEventSender());
772 ASSERT(isStructurallyExternal() && m_haveFiredLoadEvent);
773 dispatchEvent(Event::create(EventTypeNames::load));
776 void SVGUseElement::notifyFinished(Resource* resource)
778 if (!inDocument())
779 return;
781 invalidateShadowTree();
782 if (resource->errorOccurred())
783 dispatchEvent(Event::create(EventTypeNames::error));
784 else if (!resource->wasCanceled()) {
785 if (m_haveFiredLoadEvent)
786 return;
787 if (!isStructurallyExternal())
788 return;
789 ASSERT(!m_haveFiredLoadEvent);
790 m_haveFiredLoadEvent = true;
791 svgUseLoadEventSender().dispatchEventSoon(this);
795 bool SVGUseElement::resourceIsStillLoading()
797 if (m_resource && m_resource->isLoading())
798 return true;
799 return false;
802 bool SVGUseElement::instanceTreeIsLoading(SVGElement* targetInstance)
804 for (SVGElement* element = Traversal<SVGElement>::firstChild(*targetInstance); element; element = Traversal<SVGElement>::nextSibling(*element)) {
805 if (SVGUseElement* use = element->correspondingUseElement()) {
806 if (use->resourceIsStillLoading())
807 return true;
809 if (element->hasChildren() && instanceTreeIsLoading(element))
810 return true;
812 return false;
815 void SVGUseElement::setDocumentResource(ResourcePtr<DocumentResource> resource)
817 if (m_resource == resource)
818 return;
820 if (m_resource)
821 m_resource->removeClient(this);
823 m_resource = resource;
824 if (m_resource)
825 m_resource->addClient(this);