Load event must be fired only for the SVG structurally external elements and the...
[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_loadEventTimer(*this, &SVGElement::loadEventTimerFired)
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         invalidateShadowTree();
105         // FIXME: Move back the call to updateExternalDocument() here once notifyFinished is made always async.
106         return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
107     }
108     return InsertedIntoAncestorResult::Done;
109 }
110
111 void SVGUseElement::didFinishInsertingNode()
112 {
113     updateExternalDocument();
114 }
115
116 void SVGUseElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
117 {
118     // Check m_shadowTreeNeedsUpdate before calling SVGElement::removedFromAncestor which calls SVGElement::invalidateInstances
119     // and SVGUseElement::updateExternalDocument which calls invalidateShadowTree().
120     if (removalType.disconnectedFromDocument) {
121         if (m_shadowTreeNeedsUpdate)
122             document().removeSVGUseElement(*this);
123     }
124     SVGGraphicsElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
125     if (removalType.disconnectedFromDocument) {
126         clearShadowTree();
127         updateExternalDocument();
128     }
129 }
130
131 inline Document* SVGUseElement::externalDocument() const
132 {
133     return m_externalDocument ? m_externalDocument->document() : nullptr;
134 }
135
136 void SVGUseElement::transferSizeAttributesToTargetClone(SVGElement& shadowElement) const
137 {
138     // FIXME: The check for valueInSpecifiedUnits being non-zero below is a workaround for the fact
139     // that we currently have no good way to tell whether a particular animatable attribute is a value
140     // indicating it was unspecified, or specified but could not be parsed. Would be nice to fix that some day.
141     if (is<SVGSymbolElement>(shadowElement)) {
142         // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height.
143         // If attributes width and/or height are provided on the 'use' element, then these attributes
144         // will be transferred to the generated 'svg'. If attributes width and/or height are not specified,
145         // the generated 'svg' element will use values of 100% for these attributes.
146         shadowElement.setAttribute(SVGNames::widthAttr, width().valueInSpecifiedUnits() ? AtomString(width().valueAsString()) : "100%");
147         shadowElement.setAttribute(SVGNames::heightAttr, height().valueInSpecifiedUnits() ? AtomString(height().valueAsString()) : "100%");
148     } else if (is<SVGSVGElement>(shadowElement)) {
149         // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these
150         // values will override the corresponding attributes on the 'svg' in the generated tree.
151         auto correspondingElement = makeRefPtr(shadowElement.correspondingElement());
152         shadowElement.setAttribute(SVGNames::widthAttr, width().valueInSpecifiedUnits() ? AtomString(width().valueAsString()) : (correspondingElement ? correspondingElement->getAttribute(SVGNames::widthAttr) : nullAtom()));
153         shadowElement.setAttribute(SVGNames::heightAttr, height().valueInSpecifiedUnits() ? AtomString(height().valueAsString()) : (correspondingElement ? correspondingElement->getAttribute(SVGNames::heightAttr) : nullAtom()));
154     }
155 }
156
157 void SVGUseElement::svgAttributeChanged(const QualifiedName& attrName)
158 {
159     InstanceInvalidationGuard guard(*this);
160
161     if (PropertyRegistry::isKnownAttribute(attrName)) {
162         updateRelativeLengthsInformation();
163         if (attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr) {
164             // FIXME: It's unnecessarily inefficient to update both width and height each time either is changed.
165             if (auto targetClone = this->targetClone())
166                 transferSizeAttributesToTargetClone(*targetClone);
167         }
168         if (auto* renderer = this->renderer())
169             RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
170         return;
171     }
172
173     if (SVGURIReference::isKnownAttribute(attrName)) {
174         updateExternalDocument();
175         invalidateShadowTree();
176         return;
177     }
178
179     if (SVGLangSpace::isKnownAttribute(attrName) || SVGExternalResourcesRequired::isKnownAttribute(attrName))
180         invalidateShadowTree();
181
182     SVGGraphicsElement::svgAttributeChanged(attrName);
183     SVGExternalResourcesRequired::svgAttributeChanged(attrName);
184 }
185
186 static HashSet<AtomString> createAllowedElementSet()
187 {
188     // Spec: "Any 'svg', 'symbol', 'g', graphics element or other 'use' is potentially a template object that can be re-used
189     // (i.e., "instanced") in the SVG document via a 'use' element."
190     // "Graphics Element" is defined as 'circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text'
191     // Excluded are anything that is used by reference or that only make sense to appear once in a document.
192     using namespace SVGNames;
193     HashSet<AtomString> set;
194     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() })
195         set.add(tag.localName());
196     return set;
197 }
198
199 static inline bool isDisallowedElement(const SVGElement& element)
200 {
201     static NeverDestroyed<HashSet<AtomString>> set = createAllowedElementSet();
202     return !set.get().contains(element.localName());
203 }
204
205 static inline bool isDisallowedElement(const Element& element)
206 {
207     return !element.isSVGElement() || isDisallowedElement(downcast<SVGElement>(element));
208 }
209
210 void SVGUseElement::clearShadowTree()
211 {
212     if (auto root = userAgentShadowRoot()) {
213         // Safe because SVG use element's shadow tree is never used to fire synchronous events during layout or DOM mutations.
214         ScriptDisallowedScope::EventAllowedScope scope(*root);
215         root->removeChildren();
216     }
217 }
218
219 void SVGUseElement::buildPendingResource()
220 {
221     invalidateShadowTree();
222 }
223
224 void SVGUseElement::updateShadowTree()
225 {
226     m_shadowTreeNeedsUpdate = false;
227
228     // FIXME: It's expensive to re-clone the entire tree every time. We should find a more efficient way to handle this.
229     clearShadowTree();
230
231     if (!isConnected())
232         return;
233     document().removeSVGUseElement(*this);
234
235     String targetID;
236     auto* target = findTarget(&targetID);
237     if (!target) {
238         document().accessSVGExtensions().addPendingResource(targetID, *this);
239         return;
240     }
241
242     RELEASE_ASSERT(!isDescendantOf(target));
243     {
244         auto& shadowRoot = ensureUserAgentShadowRoot();
245         cloneTarget(shadowRoot, *target);
246         expandUseElementsInShadowTree();
247         expandSymbolElementsInShadowTree();
248         updateRelativeLengthsInformation();
249     }
250
251     transferEventListenersToShadowTree();
252
253     // When we invalidate the other shadow trees, it's important that we don't
254     // follow any cycles and invalidate ourselves. To avoid that, we temporarily
255     // set m_shadowTreeNeedsUpdate to true so invalidateShadowTree will
256     // quickly return and do nothing.
257     ASSERT(!m_shadowTreeNeedsUpdate);
258     m_shadowTreeNeedsUpdate = true;
259     invalidateDependentShadowTrees();
260     m_shadowTreeNeedsUpdate = false;
261 }
262
263 RefPtr<SVGElement> SVGUseElement::targetClone() const
264 {
265     auto root = userAgentShadowRoot();
266     if (!root)
267         return nullptr;
268     return childrenOfType<SVGElement>(*root).first();
269 }
270
271 RenderPtr<RenderElement> SVGUseElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
272 {
273     return createRenderer<RenderSVGTransformableContainer>(*this, WTFMove(style));
274 }
275
276 static bool isDirectReference(const SVGElement& element)
277 {
278     using namespace SVGNames;
279     return element.hasTagName(circleTag)
280         || element.hasTagName(ellipseTag)
281         || element.hasTagName(pathTag)
282         || element.hasTagName(polygonTag)
283         || element.hasTagName(polylineTag)
284         || element.hasTagName(rectTag)
285         || element.hasTagName(textTag);
286 }
287
288 Path SVGUseElement::toClipPath()
289 {
290     auto targetClone = this->targetClone();
291     if (!is<SVGGraphicsElement>(targetClone))
292         return { };
293
294     if (!isDirectReference(*targetClone)) {
295         // Spec: Indirect references are an error (14.3.5)
296         document().accessSVGExtensions().reportError("Not allowed to use indirect reference in <clip-path>"_s);
297         return { };
298     }
299
300     Path path = downcast<SVGGraphicsElement>(*targetClone).toClipPath();
301     SVGLengthContext lengthContext(this);
302     // FIXME: Find a way to do this without manual resolution of x/y here. It's potentially incorrect.
303     path.translate(FloatSize(x().value(lengthContext), y().value(lengthContext)));
304     path.transform(animatedLocalTransform());
305     return path;
306 }
307
308 RenderElement* SVGUseElement::rendererClipChild() const
309 {
310     auto targetClone = this->targetClone();
311     if (!targetClone)
312         return nullptr;
313     if (!isDirectReference(*targetClone))
314         return nullptr;
315     return targetClone->renderer();
316 }
317
318 static inline void disassociateAndRemoveClones(const Vector<Element*>& clones)
319 {
320     for (auto& clone : clones) {
321         for (auto& descendant : descendantsOfType<SVGElement>(*clone))
322             descendant.setCorrespondingElement(nullptr);
323         if (is<SVGElement>(clone))
324             downcast<SVGElement>(*clone).setCorrespondingElement(nullptr);
325         clone->parentNode()->removeChild(*clone);
326     }
327 }
328
329 static void removeDisallowedElementsFromSubtree(SVGElement& subtree)
330 {
331     // Remove disallowed elements after the fact rather than not cloning them in the first place.
332     // This optimizes for the normal case where none of those elements are present.
333
334     // This function is used only on elements in subtrees that are not yet in documents, so
335     // mutation events are not a factor; there are no event listeners to handle those events.
336     // Assert that it's not in a document to make sure callers are still using it this way.
337     ASSERT(!subtree.isConnected());
338
339     Vector<Element*> disallowedElements;
340     auto descendants = descendantsOfType<Element>(subtree);
341     for (auto it = descendants.begin(), end = descendants.end(); it != end; ) {
342         if (isDisallowedElement(*it)) {
343             disallowedElements.append(&*it);
344             it.traverseNextSkippingChildren();
345             continue;
346         }
347         ++it;
348     }
349
350     disassociateAndRemoveClones(disallowedElements);
351 }
352
353 static void removeSymbolElementsFromSubtree(SVGElement& subtree)
354 {
355     // Symbol elements inside the subtree should not be cloned for two reasons: 1) They are invisible and
356     // don't need to be cloned to get correct rendering. 2) expandSymbolElementsInShadowTree will turn them
357     // into <svg> elements, which is correct for symbol elements directly referenced by use elements,
358     // but incorrect for ones that just happen to be in a subtree.
359     Vector<Element*> symbolElements;
360     for (auto& descendant : descendantsOfType<SVGSymbolElement>(subtree))
361         symbolElements.append(&descendant);
362     disassociateAndRemoveClones(symbolElements);
363 }
364
365 static void associateClonesWithOriginals(SVGElement& clone, SVGElement& original)
366 {
367     // This assertion checks that we don't call this with the arguments backwards.
368     // The clone is new and so it's not installed in a parent yet.
369     ASSERT(!clone.parentNode());
370
371     // The loop below works because we are associating these clones immediately, before
372     // doing transformations like removing disallowed elements or expanding elements.
373     clone.setCorrespondingElement(&original);
374     for (auto pair : descendantsOfType<SVGElement>(clone, original))
375         pair.first.setCorrespondingElement(&pair.second);
376 }
377
378 static void associateReplacementCloneWithOriginal(SVGElement& replacementClone, SVGElement& originalClone)
379 {
380     auto* correspondingElement = originalClone.correspondingElement();
381     ASSERT(correspondingElement);
382     originalClone.setCorrespondingElement(nullptr);
383     replacementClone.setCorrespondingElement(correspondingElement);
384 }
385
386 static void associateReplacementClonesWithOriginals(SVGElement& replacementClone, SVGElement& originalClone)
387 {
388     // This assertion checks that we don't call this with the arguments backwards.
389     // The replacement clone is new and so it's not installed in a parent yet.
390     ASSERT(!replacementClone.parentNode());
391
392     // The loop below works because we are associating these clones immediately, before
393     // doing transformations like removing disallowed elements or expanding elements.
394     associateReplacementCloneWithOriginal(replacementClone, originalClone);
395     for (auto pair : descendantsOfType<SVGElement>(replacementClone, originalClone))
396         associateReplacementCloneWithOriginal(pair.first, pair.second);
397 }
398
399 SVGElement* SVGUseElement::findTarget(String* targetID) const
400 {
401     auto* correspondingElement = this->correspondingElement();
402     auto& original = correspondingElement ? downcast<SVGUseElement>(*correspondingElement) : *this;
403
404     auto targetResult = targetElementFromIRIString(original.href(), original.treeScope(), original.externalDocument());
405     if (targetID) {
406         *targetID = WTFMove(targetResult.identifier);
407         // If the reference is external, don't return the target ID to the caller.
408         // The caller would use the target ID to wait for a pending resource on the wrong document.
409         // If we ever want the change that and let the caller to wait on the external document,
410         // we should change this function so it returns the appropriate document to go with the ID.
411         if (!targetID->isNull() && isExternalURIReference(original.href(), original.document()))
412             *targetID = String { };
413     }
414     if (!is<SVGElement>(targetResult.element))
415         return nullptr;
416     auto& target = downcast<SVGElement>(*targetResult.element);
417
418     if (!target.isConnected() || isDisallowedElement(target))
419         return nullptr;
420
421     if (correspondingElement) {
422         for (auto& ancestor : lineageOfType<SVGElement>(*this)) {
423             if (ancestor.correspondingElement() == &target)
424                 return nullptr;
425         }
426     } else {
427         if (target.contains(this))
428             return nullptr;
429         // Target should only refer to a node in the same tree or a node in another document.
430         ASSERT(!isDescendantOrShadowDescendantOf(&target));
431     }
432
433     return &target;
434 }
435
436 void SVGUseElement::cloneTarget(ContainerNode& container, SVGElement& target) const
437 {
438     Ref<SVGElement> targetClone = static_cast<SVGElement&>(target.cloneElementWithChildren(document()).get());
439     associateClonesWithOriginals(targetClone.get(), target);
440     removeDisallowedElementsFromSubtree(targetClone.get());
441     removeSymbolElementsFromSubtree(targetClone.get());
442     transferSizeAttributesToTargetClone(targetClone.get());
443     container.appendChild(targetClone);
444 }
445
446 static void cloneDataAndChildren(SVGElement& replacementClone, SVGElement& originalClone)
447 {
448     // This assertion checks that we don't call this with the arguments backwards.
449     // The replacement clone is new and so it's not installed in a parent yet.
450     ASSERT(!replacementClone.parentNode());
451
452     replacementClone.cloneDataFromElement(originalClone);
453     originalClone.cloneChildNodes(replacementClone);
454     associateReplacementClonesWithOriginals(replacementClone, originalClone);
455     removeDisallowedElementsFromSubtree(replacementClone);
456 }
457
458 void SVGUseElement::expandUseElementsInShadowTree() const
459 {
460     auto descendants = descendantsOfType<SVGUseElement>(*userAgentShadowRoot());
461     for (auto it = descendants.begin(), end = descendants.end(); it != end; ) {
462         SVGUseElement& originalClone = *it;
463         it = end; // Efficiently quiets assertions due to the outstanding iterator.
464
465         auto* target = originalClone.findTarget();
466
467         // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the
468         // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element.
469
470         auto replacementClone = SVGGElement::create(document());
471
472         cloneDataAndChildren(replacementClone.get(), originalClone);
473
474         replacementClone->removeAttribute(SVGNames::xAttr);
475         replacementClone->removeAttribute(SVGNames::yAttr);
476         replacementClone->removeAttribute(SVGNames::widthAttr);
477         replacementClone->removeAttribute(SVGNames::heightAttr);
478         replacementClone->removeAttribute(SVGNames::hrefAttr);
479         replacementClone->removeAttribute(XLinkNames::hrefAttr);
480
481         if (target)
482             originalClone.cloneTarget(replacementClone.get(), *target);
483
484         originalClone.parentNode()->replaceChild(replacementClone, originalClone);
485
486         // Resume iterating, starting just inside the replacement clone.
487         it = descendants.from(replacementClone.get());
488     }
489 }
490
491 void SVGUseElement::expandSymbolElementsInShadowTree() const
492 {
493     auto descendants = descendantsOfType<SVGSymbolElement>(*userAgentShadowRoot());
494     for (auto it = descendants.begin(), end = descendants.end(); it != end; ) {
495         SVGSymbolElement& originalClone = *it;
496         it = end; // Efficiently quiets assertions due to the outstanding iterator.
497
498         // Spec: The referenced 'symbol' and its contents are deep-cloned into the generated tree,
499         // with the exception that the 'symbol' is replaced by an 'svg'. This generated 'svg' will
500         // always have explicit values for attributes width and height. If attributes width and/or
501         // height are provided on the 'use' element, then these attributes will be transferred to
502         // the generated 'svg'. If attributes width and/or height are not specified, the generated
503         // 'svg' element will use values of 100% for these attributes.
504
505         auto replacementClone = SVGSVGElement::create(document());
506         cloneDataAndChildren(replacementClone.get(), originalClone);
507
508         originalClone.parentNode()->replaceChild(replacementClone, originalClone);
509
510         // Resume iterating, starting just inside the replacement clone.
511         it = descendants.from(replacementClone.get());
512     }
513 }
514
515 void SVGUseElement::transferEventListenersToShadowTree() const
516 {
517     // FIXME: Don't directly add event listeners on each descendant. Copy event listeners on the use element instead.
518     for (auto& descendant : descendantsOfType<SVGElement>(*userAgentShadowRoot())) {
519         if (EventTargetData* data = descendant.correspondingElement()->eventTargetData())
520             data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(&descendant);
521     }
522 }
523
524 void SVGUseElement::invalidateShadowTree()
525 {
526     if (m_shadowTreeNeedsUpdate)
527         return;
528     m_shadowTreeNeedsUpdate = true;
529     invalidateStyleAndRenderersForSubtree();
530     invalidateDependentShadowTrees();
531     if (isConnected())
532         document().addSVGUseElement(*this);
533 }
534
535 void SVGUseElement::invalidateDependentShadowTrees()
536 {
537     for (auto* instance : instances()) {
538         if (auto element = instance->correspondingUseElement())
539             element->invalidateShadowTree();
540     }
541 }
542
543 bool SVGUseElement::selfHasRelativeLengths() const
544 {
545     if (x().isRelative() || y().isRelative() || width().isRelative() || height().isRelative())
546         return true;
547
548     auto targetClone = this->targetClone();
549     return targetClone && targetClone->hasRelativeLengths();
550 }
551
552 void SVGUseElement::notifyFinished(CachedResource& resource)
553 {
554     ASSERT(ScriptDisallowedScope::InMainThread::isScriptAllowed());
555     invalidateShadowTree();
556     if (resource.errorOccurred()) {
557         setErrorOccurred(true);
558         dispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
559     } else if (!resource.wasCanceled())
560         SVGURIReference::dispatchLoadEvent();
561 }
562
563 void SVGUseElement::updateExternalDocument()
564 {
565     URL externalDocumentURL;
566     if (isConnected() && isExternalURIReference(href(), document())) {
567         externalDocumentURL = document().completeURL(href());
568         if (!externalDocumentURL.hasFragmentIdentifier())
569             externalDocumentURL = URL();
570     }
571
572     if (externalDocumentURL == (m_externalDocument ? m_externalDocument->url() : URL()))
573         return;
574
575     if (m_externalDocument)
576         m_externalDocument->removeClient(*this);
577
578     if (externalDocumentURL.isNull())
579         m_externalDocument = nullptr;
580     else {
581         ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
582         options.contentSecurityPolicyImposition = isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck;
583         options.mode = FetchOptions::Mode::SameOrigin;
584         CachedResourceRequest request { ResourceRequest { externalDocumentURL }, options };
585         request.setInitiator(*this);
586         m_externalDocument = document().cachedResourceLoader().requestSVGDocument(WTFMove(request)).value_or(nullptr);
587         if (m_externalDocument)
588             m_externalDocument->addClient(*this);
589     }
590
591     invalidateShadowTree();
592 }
593
594 }