ad92384135fa28dd5389e8789644ef95b7e7f87d
[WebKit-https.git] / Source / WebCore / svg / SVGUseElement.cpp
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>
8  * Copyright (C) 2015-2019 Apple Inc. All rights reserved.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "SVGUseElement.h"
28
29 #include "CachedResourceLoader.h"
30 #include "CachedSVGDocument.h"
31 #include "ElementIterator.h"
32 #include "Event.h"
33 #include "EventNames.h"
34 #include "RenderSVGResource.h"
35 #include "RenderSVGTransformableContainer.h"
36 #include "SVGDocumentExtensions.h"
37 #include "SVGGElement.h"
38 #include "SVGSVGElement.h"
39 #include "SVGSymbolElement.h"
40 #include "ScriptDisallowedScope.h"
41 #include "ShadowRoot.h"
42 #include "XLinkNames.h"
43 #include <wtf/IsoMallocInlines.h>
44
45 namespace WebCore {
46
47 WTF_MAKE_ISO_ALLOCATED_IMPL(SVGUseElement);
48
49 inline SVGUseElement::SVGUseElement(const QualifiedName& tagName, Document& document)
50     : SVGGraphicsElement(tagName, document)
51     , SVGExternalResourcesRequired(this)
52     , SVGURIReference(this)
53     , m_svgLoadEventTimer(*this, &SVGElement::svgLoadEventTimerFired)
54 {
55     ASSERT(hasCustomStyleResolveCallbacks());
56     ASSERT(hasTagName(SVGNames::useTag));
57
58     static std::once_flag onceFlag;
59     std::call_once(onceFlag, [] {
60         PropertyRegistry::registerProperty<SVGNames::xAttr, &SVGUseElement::m_x>();
61         PropertyRegistry::registerProperty<SVGNames::yAttr, &SVGUseElement::m_y>();
62         PropertyRegistry::registerProperty<SVGNames::widthAttr, &SVGUseElement::m_width>();
63         PropertyRegistry::registerProperty<SVGNames::heightAttr, &SVGUseElement::m_height>();
64     });
65 }
66
67 Ref<SVGUseElement> SVGUseElement::create(const QualifiedName& tagName, Document& document)
68 {
69     return adoptRef(*new SVGUseElement(tagName, document));
70 }
71
72 SVGUseElement::~SVGUseElement()
73 {
74     if (m_externalDocument)
75         m_externalDocument->removeClient(*this);
76 }
77
78 void SVGUseElement::parseAttribute(const QualifiedName& name, const AtomString& value)
79 {
80     SVGParsingError parseError = NoError;
81
82     if (name == SVGNames::xAttr)
83         m_x->setBaseValInternal(SVGLengthValue::construct(SVGLengthMode::Width, value, parseError));
84     else if (name == SVGNames::yAttr)
85         m_y->setBaseValInternal(SVGLengthValue::construct(SVGLengthMode::Height, value, parseError));
86     else if (name == SVGNames::widthAttr)
87         m_width->setBaseValInternal(SVGLengthValue::construct(SVGLengthMode::Width, value, parseError, SVGLengthNegativeValuesMode::Forbid));
88     else if (name == SVGNames::heightAttr)
89         m_height->setBaseValInternal(SVGLengthValue::construct(SVGLengthMode::Height, value, parseError, SVGLengthNegativeValuesMode::Forbid));
90
91     reportAttributeParsingError(parseError, name, value);
92
93     SVGExternalResourcesRequired::parseAttribute(name, value);
94     SVGGraphicsElement::parseAttribute(name, value);
95     SVGURIReference::parseAttribute(name, value);
96 }
97
98 Node::InsertedIntoAncestorResult SVGUseElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
99 {
100     SVGGraphicsElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
101     if (insertionType.connectedToDocument) {
102         if (m_shadowTreeNeedsUpdate)
103             document().addSVGUseElement(*this);
104         SVGExternalResourcesRequired::insertedIntoDocument();
105         invalidateShadowTree();
106         // FIXME: Move back the call to updateExternalDocument() here once notifyFinished is made always async.
107         return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
108     }
109     return InsertedIntoAncestorResult::Done;
110 }
111
112 void SVGUseElement::didFinishInsertingNode()
113 {
114     updateExternalDocument();
115 }
116
117 void SVGUseElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
118 {
119     // Check m_shadowTreeNeedsUpdate before calling SVGElement::removedFromAncestor which calls SVGElement::invalidateInstances
120     // and SVGUseElement::updateExternalDocument which calls invalidateShadowTree().
121     if (removalType.disconnectedFromDocument) {
122         if (m_shadowTreeNeedsUpdate)
123             document().removeSVGUseElement(*this);
124     }
125     SVGGraphicsElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
126     if (removalType.disconnectedFromDocument) {
127         clearShadowTree();
128         updateExternalDocument();
129     }
130 }
131
132 inline Document* SVGUseElement::externalDocument() const
133 {
134     return m_externalDocument ? m_externalDocument->document() : nullptr;
135 }
136
137 void SVGUseElement::transferSizeAttributesToTargetClone(SVGElement& shadowElement) const
138 {
139     // FIXME: The check for valueInSpecifiedUnits being non-zero below is a workaround for the fact
140     // that we currently have no good way to tell whether a particular animatable attribute is a value
141     // indicating it was unspecified, or specified but could not be parsed. Would be nice to fix that some day.
142     if (is<SVGSymbolElement>(shadowElement)) {
143         // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height.
144         // If attributes width and/or height are provided on the 'use' element, then these attributes
145         // will be transferred to the generated 'svg'. If attributes width and/or height are not specified,
146         // the generated 'svg' element will use values of 100% for these attributes.
147         shadowElement.setAttribute(SVGNames::widthAttr, width().valueInSpecifiedUnits() ? AtomString(width().valueAsString()) : "100%");
148         shadowElement.setAttribute(SVGNames::heightAttr, height().valueInSpecifiedUnits() ? AtomString(height().valueAsString()) : "100%");
149     } else if (is<SVGSVGElement>(shadowElement)) {
150         // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these
151         // values will override the corresponding attributes on the 'svg' in the generated tree.
152         auto correspondingElement = makeRefPtr(shadowElement.correspondingElement());
153         shadowElement.setAttribute(SVGNames::widthAttr, width().valueInSpecifiedUnits() ? AtomString(width().valueAsString()) : (correspondingElement ? correspondingElement->getAttribute(SVGNames::widthAttr) : nullAtom()));
154         shadowElement.setAttribute(SVGNames::heightAttr, height().valueInSpecifiedUnits() ? AtomString(height().valueAsString()) : (correspondingElement ? correspondingElement->getAttribute(SVGNames::heightAttr) : nullAtom()));
155     }
156 }
157
158 void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
159 {
160     InstanceInvalidationGuard guard(*this);
161
162     if (PropertyRegistry::isKnownAttribute(attrName)) {
163         updateRelativeLengthsInformation();
164         if (attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr) {
165             // FIXME: It's unnecessarily inefficient to update both width and height each time either is changed.
166             if (auto targetClone = this->targetClone())
167                 transferSizeAttributesToTargetClone(*targetClone);
168         }
169         if (auto* renderer = this->renderer())
170             RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
171         return;
172     }
173
174     if (SVGURIReference::isKnownAttribute(attrName)) {
175         updateExternalDocument();
176         invalidateShadowTree();
177         return;
178     }
179
180     if (SVGLangSpace::isKnownAttribute(attrName) || SVGExternalResourcesRequired::isKnownAttribute(attrName))
181         invalidateShadowTree();
182
183     SVGGraphicsElement::svgAttributeChanged(attrName);
184     SVGExternalResourcesRequired::svgAttributeChanged(attrName);
185 }
186
187 static HashSet<AtomString> createAllowedElementSet()
188 {
189     // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used
190     // (i.e., "instanced") in the SVG document via a 'use' element."
191     // "Graphics Element" is defined as 'circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text'
192     // Excluded are anything that is used by reference or that only make sense to appear once in a document.
193     using namespace SVGNames;
194     HashSet<AtomString> set;
195     for (auto& tag : { aTag.get(), circleTag.get(), descTag.get(), ellipseTag.get(), gTag.get(), imageTag.get(), lineTag.get(), metadataTag.get(), pathTag.get(), polygonTag.get(), polylineTag.get(), rectTag.get(), svgTag.get(), switchTag.get(), symbolTag.get(), textTag.get(), textPathTag.get(), titleTag.get(), trefTag.get(), tspanTag.get(), useTag.get() })
196         set.add(tag.localName());
197     return set;
198 }
199
200 static inline bool isDisallowedElement(const SVGElement& element)
201 {
202     static NeverDestroyed<HashSet<AtomString>> set = createAllowedElementSet();
203     return !set.get().contains(element.localName());
204 }
205
206 static inline bool isDisallowedElement(const Element& element)
207 {
208     return !element.isSVGElement() || isDisallowedElement(downcast<SVGElement>(element));
209 }
210
211 void SVGUseElement::clearShadowTree()
212 {
213     if (auto root = userAgentShadowRoot()) {
214         // Safe because SVG use element's shadow tree is never used to fire synchronous events during layout or DOM mutations.
215         ScriptDisallowedScope::EventAllowedScope scope(*root);
216         root->removeChildren();
217     }
218 }
219
220 void SVGUseElement::buildPendingResource()
221 {
222     invalidateShadowTree();
223 }
224
225 void SVGUseElement::updateShadowTree()
226 {
227     m_shadowTreeNeedsUpdate = false;
228
229     // FIXME: It's expensive to re-clone the entire tree every time. We should find a more efficient way to handle this.
230     clearShadowTree();
231
232     if (!isConnected())
233         return;
234     document().removeSVGUseElement(*this);
235
236     String targetID;
237     auto* target = findTarget(&targetID);
238     if (!target) {
239         document().accessSVGExtensions().addPendingResource(targetID, *this);
240         return;
241     }
242
243     RELEASE_ASSERT(!isDescendantOf(target));
244     {
245         auto& shadowRoot = ensureUserAgentShadowRoot();
246         cloneTarget(shadowRoot, *target);
247         expandUseElementsInShadowTree();
248         expandSymbolElementsInShadowTree();
249         updateRelativeLengthsInformation();
250     }
251
252     transferEventListenersToShadowTree();
253
254     // When we invalidate the other shadow trees, it's important that we don't
255     // follow any cycles and invalidate ourselves. To avoid that, we temporarily
256     // set m_shadowTreeNeedsUpdate to true so invalidateShadowTree will
257     // quickly return and do nothing.
258     ASSERT(!m_shadowTreeNeedsUpdate);
259     m_shadowTreeNeedsUpdate = true;
260     invalidateDependentShadowTrees();
261     m_shadowTreeNeedsUpdate = false;
262 }
263
264 RefPtr<SVGElement> SVGUseElement::targetClone() const
265 {
266     auto root = userAgentShadowRoot();
267     if (!root)
268         return nullptr;
269     return childrenOfType<SVGElement>(*root).first();
270 }
271
272 RenderPtr<RenderElement> SVGUseElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
273 {
274     return createRenderer<RenderSVGTransformableContainer>(*this, WTFMove(style));
275 }
276
277 static bool isDirectReference(const SVGElement& element)
278 {
279     using namespace SVGNames;
280     return element.hasTagName(circleTag)
281         || element.hasTagName(ellipseTag)
282         || element.hasTagName(pathTag)
283         || element.hasTagName(polygonTag)
284         || element.hasTagName(polylineTag)
285         || element.hasTagName(rectTag)
286         || element.hasTagName(textTag);
287 }
288
289 Path SVGUseElement::toClipPath()
290 {
291     auto targetClone = this->targetClone();
292     if (!is<SVGGraphicsElement>(targetClone))
293         return { };
294
295     if (!isDirectReference(*targetClone)) {
296         // Spec: Indirect references are an error (14.3.5)
297         document().accessSVGExtensions().reportError("Not allowed to use indirect reference in <clip-path>"_s);
298         return { };
299     }
300
301     Path path = downcast<SVGGraphicsElement>(*targetClone).toClipPath();
302     SVGLengthContext lengthContext(this);
303     // FIXME: Find a way to do this without manual resolution of x/y here. It's potentially incorrect.
304     path.translate(FloatSize(x().value(lengthContext), y().value(lengthContext)));
305     path.transform(animatedLocalTransform());
306     return path;
307 }
308
309 RenderElement* SVGUseElement::rendererClipChild() const
310 {
311     auto targetClone = this->targetClone();
312     if (!targetClone)
313         return nullptr;
314     if (!isDirectReference(*targetClone))
315         return nullptr;
316     return targetClone->renderer();
317 }
318
319 static inline void disassociateAndRemoveClones(const Vector<Element*>& clones)
320 {
321     for (auto& clone : clones) {
322         for (auto& descendant : descendantsOfType<SVGElement>(*clone))
323             descendant.setCorrespondingElement(nullptr);
324         if (is<SVGElement>(clone))
325             downcast<SVGElement>(*clone).setCorrespondingElement(nullptr);
326         clone->parentNode()->removeChild(*clone);
327     }
328 }
329
330 static void removeDisallowedElementsFromSubtree(SVGElement& subtree)
331 {
332     // Remove disallowed elements after the fact rather than not cloning them in the first place.
333     // This optimizes for the normal case where none of those elements are present.
334
335     // This function is used only on elements in subtrees that are not yet in documents, so
336     // mutation events are not a factor; there are no event listeners to handle those events.
337     // Assert that it's not in a document to make sure callers are still using it this way.
338     ASSERT(!subtree.isConnected());
339
340     Vector<Element*> disallowedElements;
341     auto descendants = descendantsOfType<Element>(subtree);
342     for (auto it = descendants.begin(), end = descendants.end(); it != end; ) {
343         if (isDisallowedElement(*it)) {
344             disallowedElements.append(&*it);
345             it.traverseNextSkippingChildren();
346             continue;
347         }
348         ++it;
349     }
350
351     disassociateAndRemoveClones(disallowedElements);
352 }
353
354 static void removeSymbolElementsFromSubtree(SVGElement& subtree)
355 {
356     // Symbol elements inside the subtree should not be cloned for two reasons: 1) They are invisible and
357     // don't need to be cloned to get correct rendering. 2) expandSymbolElementsInShadowTree will turn them
358     // into <svg> elements, which is correct for symbol elements directly referenced by use elements,
359     // but incorrect for ones that just happen to be in a subtree.
360     Vector<Element*> symbolElements;
361     for (auto& descendant : descendantsOfType<SVGSymbolElement>(subtree))
362         symbolElements.append(&descendant);
363     disassociateAndRemoveClones(symbolElements);
364 }
365
366 static void associateClonesWithOriginals(SVGElement& clone, SVGElement& original)
367 {
368     // This assertion checks that we don't call this with the arguments backwards.
369     // The clone is new and so it's not installed in a parent yet.
370     ASSERT(!clone.parentNode());
371
372     // The loop below works because we are associating these clones immediately, before
373     // doing transformations like removing disallowed elements or expanding elements.
374     clone.setCorrespondingElement(&original);
375     for (auto pair : descendantsOfType<SVGElement>(clone, original))
376         pair.first.setCorrespondingElement(&pair.second);
377 }
378
379 static void associateReplacementCloneWithOriginal(SVGElement& replacementClone, SVGElement& originalClone)
380 {
381     auto* correspondingElement = originalClone.correspondingElement();
382     ASSERT(correspondingElement);
383     originalClone.setCorrespondingElement(nullptr);
384     replacementClone.setCorrespondingElement(correspondingElement);
385 }
386
387 static void associateReplacementClonesWithOriginals(SVGElement& replacementClone, SVGElement& originalClone)
388 {
389     // This assertion checks that we don't call this with the arguments backwards.
390     // The replacement clone is new and so it's not installed in a parent yet.
391     ASSERT(!replacementClone.parentNode());
392
393     // The loop below works because we are associating these clones immediately, before
394     // doing transformations like removing disallowed elements or expanding elements.
395     associateReplacementCloneWithOriginal(replacementClone, originalClone);
396     for (auto pair : descendantsOfType<SVGElement>(replacementClone, originalClone))
397         associateReplacementCloneWithOriginal(pair.first, pair.second);
398 }
399
400 SVGElement* SVGUseElement::findTarget(String* targetID) const
401 {
402     auto* correspondingElement = this->correspondingElement();
403     auto& original = correspondingElement ? downcast<SVGUseElement>(*correspondingElement) : *this;
404
405     auto targetResult = targetElementFromIRIString(original.href(), original.treeScope(), original.externalDocument());
406     if (targetID) {
407         *targetID = WTFMove(targetResult.identifier);
408         // If the reference is external, don't return the target ID to the caller.
409         // The caller would use the target ID to wait for a pending resource on the wrong document.
410         // If we ever want the change that and let the caller to wait on the external document,
411         // we should change this function so it returns the appropriate document to go with the ID.
412         if (!targetID->isNull() && isExternalURIReference(original.href(), original.document()))
413             *targetID = String { };
414     }
415     if (!is<SVGElement>(targetResult.element))
416         return nullptr;
417     auto& target = downcast<SVGElement>(*targetResult.element);
418
419     if (!target.isConnected() || isDisallowedElement(target))
420         return nullptr;
421
422     if (correspondingElement) {
423         for (auto& ancestor : lineageOfType<SVGElement>(*this)) {
424             if (ancestor.correspondingElement() == &target)
425                 return nullptr;
426         }
427     } else {
428         if (target.contains(this))
429             return nullptr;
430         // Target should only refer to a node in the same tree or a node in another document.
431         ASSERT(!isDescendantOrShadowDescendantOf(&target));
432     }
433
434     return &target;
435 }
436
437 void SVGUseElement::cloneTarget(ContainerNode& container, SVGElement& target) const
438 {
439     Ref<SVGElement> targetClone = static_cast<SVGElement&>(target.cloneElementWithChildren(document()).get());
440     associateClonesWithOriginals(targetClone.get(), target);
441     removeDisallowedElementsFromSubtree(targetClone.get());
442     removeSymbolElementsFromSubtree(targetClone.get());
443     transferSizeAttributesToTargetClone(targetClone.get());
444     container.appendChild(targetClone);
445 }
446
447 static void cloneDataAndChildren(SVGElement& replacementClone, SVGElement& originalClone)
448 {
449     // This assertion checks that we don't call this with the arguments backwards.
450     // The replacement clone is new and so it's not installed in a parent yet.
451     ASSERT(!replacementClone.parentNode());
452
453     replacementClone.cloneDataFromElement(originalClone);
454     originalClone.cloneChildNodes(replacementClone);
455     associateReplacementClonesWithOriginals(replacementClone, originalClone);
456     removeDisallowedElementsFromSubtree(replacementClone);
457 }
458
459 void SVGUseElement::expandUseElementsInShadowTree() const
460 {
461     auto descendants = descendantsOfType<SVGUseElement>(*userAgentShadowRoot());
462     for (auto it = descendants.begin(), end = descendants.end(); it != end; ) {
463         SVGUseElement& originalClone = *it;
464         it = end; // Efficiently quiets assertions due to the outstanding iterator.
465
466         auto* target = originalClone.findTarget();
467
468         // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
469         // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
470
471         auto replacementClone = SVGGElement::create(document());
472
473         cloneDataAndChildren(replacementClone.get(), originalClone);
474
475         replacementClone->removeAttribute(SVGNames::xAttr);
476         replacementClone->removeAttribute(SVGNames::yAttr);
477         replacementClone->removeAttribute(SVGNames::widthAttr);
478         replacementClone->removeAttribute(SVGNames::heightAttr);
479         replacementClone->removeAttribute(SVGNames::hrefAttr);
480         replacementClone->removeAttribute(XLinkNames::hrefAttr);
481
482         if (target)
483             originalClone.cloneTarget(replacementClone.get(), *target);
484
485         originalClone.parentNode()->replaceChild(replacementClone, originalClone);
486
487         // Resume iterating, starting just inside the replacement clone.
488         it = descendants.from(replacementClone.get());
489     }
490 }
491
492 void SVGUseElement::expandSymbolElementsInShadowTree() const
493 {
494     auto descendants = descendantsOfType<SVGSymbolElement>(*userAgentShadowRoot());
495     for (auto it = descendants.begin(), end = descendants.end(); it != end; ) {
496         SVGSymbolElement& originalClone = *it;
497         it = end; // Efficiently quiets assertions due to the outstanding iterator.
498
499         // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
500         // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
501         // always have explicit values for attributes width and height. If attributes width and/or
502         // height are provided on the 'use' element, then these attributes will be transferred to
503         // the generated 'svg'. If attributes width and/or height are not specified, the generated
504         // 'svg' element will use values of 100% for these attributes.
505
506         auto replacementClone = SVGSVGElement::create(document());
507         cloneDataAndChildren(replacementClone.get(), originalClone);
508
509         originalClone.parentNode()->replaceChild(replacementClone, originalClone);
510
511         // Resume iterating, starting just inside the replacement clone.
512         it = descendants.from(replacementClone.get());
513     }
514 }
515
516 void SVGUseElement::transferEventListenersToShadowTree() const
517 {
518     // FIXME: Don't directly add event listeners on each descendant. Copy event listeners on the use element instead.
519     for (auto& descendant : descendantsOfType<SVGElement>(*userAgentShadowRoot())) {
520         if (EventTargetData* data = descendant.correspondingElement()->eventTargetData())
521             data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(&descendant);
522     }
523 }
524
525 void SVGUseElement::invalidateShadowTree()
526 {
527     if (m_shadowTreeNeedsUpdate)
528         return;
529     m_shadowTreeNeedsUpdate = true;
530     invalidateStyleAndRenderersForSubtree();
531     invalidateDependentShadowTrees();
532     if (isConnected())
533         document().addSVGUseElement(*this);
534 }
535
536 void SVGUseElement::invalidateDependentShadowTrees()
537 {
538     for (auto* instance : instances()) {
539         if (auto element = instance->correspondingUseElement())
540             element->invalidateShadowTree();
541     }
542 }
543
544 bool SVGUseElement::selfHasRelativeLengths() const
545 {
546     if (x().isRelative() || y().isRelative() || width().isRelative() || height().isRelative())
547         return true;
548
549     auto targetClone = this->targetClone();
550     return targetClone && targetClone->hasRelativeLengths();
551 }
552
553 void SVGUseElement::notifyFinished(CachedResource& resource)
554 {
555     ASSERT(ScriptDisallowedScope::InMainThread::isScriptAllowed());
556     invalidateShadowTree();
557     if (resource.errorOccurred())
558         dispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
559     else if (!resource.wasCanceled())
560         SVGExternalResourcesRequired::dispatchLoadEvent();
561 }
562
563 void SVGUseElement::finishParsingChildren()
564 {
565     SVGGraphicsElement::finishParsingChildren();
566     SVGExternalResourcesRequired::finishParsingChildren();
567 }
568
569 void SVGUseElement::updateExternalDocument()
570 {
571     URL externalDocumentURL;
572     if (isConnected() && isExternalURIReference(href(), document())) {
573         externalDocumentURL = document().completeURL(href());
574         if (!externalDocumentURL.hasFragmentIdentifier())
575             externalDocumentURL = URL();
576     }
577
578     if (externalDocumentURL == (m_externalDocument ? m_externalDocument->url() : URL()))
579         return;
580
581     if (m_externalDocument)
582         m_externalDocument->removeClient(*this);
583
584     if (externalDocumentURL.isNull())
585         m_externalDocument = nullptr;
586     else {
587         ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
588         options.contentSecurityPolicyImposition = isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck;
589         options.mode = FetchOptions::Mode::SameOrigin;
590         CachedResourceRequest request { ResourceRequest { externalDocumentURL }, options };
591         request.setInitiator(*this);
592         m_externalDocument = document().cachedResourceLoader().requestSVGDocument(WTFMove(request)).value_or(nullptr);
593         if (m_externalDocument)
594             m_externalDocument->addClient(*this);
595     }
596
597     invalidateShadowTree();
598 }
599
600 bool SVGUseElement::isValid() const
601 {
602     return SVGTests::isValid();
603 }
604
605 bool SVGUseElement::haveLoadedRequiredResources()
606 {
607     return SVGExternalResourcesRequired::haveLoadedRequiredResources();
608 }
609
610 void SVGUseElement::setHaveFiredLoadEvent(bool haveFiredLoadEvent)
611 {
612     m_haveFiredLoadEvent = haveFiredLoadEvent;
613 }
614
615 bool SVGUseElement::haveFiredLoadEvent() const
616 {
617     return m_haveFiredLoadEvent;
618 }
619
620 Timer* SVGUseElement::svgLoadEventTimer()
621 {
622     return &m_svgLoadEventTimer;
623 }
624
625 }