1/*
2 * Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
4 * Copyright (C) 2010 Dirk Schulze <krit@webkit.org>
5 * Copyright (C) 2018 Apple Inc. 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#include "SVGFEImageElement.h"
25
26#include "CachedImage.h"
27#include "CachedResourceLoader.h"
28#include "CachedResourceRequest.h"
29#include "Document.h"
30#include "Image.h"
31#include "RenderObject.h"
32#include "RenderSVGResource.h"
33#include "SVGNames.h"
34#include "SVGPreserveAspectRatioValue.h"
35#include <wtf/IsoMallocInlines.h>
36
37namespace WebCore {
38
39WTF_MAKE_ISO_ALLOCATED_IMPL(SVGFEImageElement);
40
41inline SVGFEImageElement::SVGFEImageElement(const QualifiedName& tagName, Document& document)
42 : SVGFilterPrimitiveStandardAttributes(tagName, document)
43 , SVGExternalResourcesRequired(this)
44 , SVGURIReference(this)
45{
46 ASSERT(hasTagName(SVGNames::feImageTag));
47
48 static std::once_flag onceFlag;
49 std::call_once(onceFlag, [] {
50 PropertyRegistry::registerProperty<SVGNames::preserveAspectRatioAttr, &SVGFEImageElement::m_preserveAspectRatio>();
51 });
52}
53
54Ref<SVGFEImageElement> SVGFEImageElement::create(const QualifiedName& tagName, Document& document)
55{
56 return adoptRef(*new SVGFEImageElement(tagName, document));
57}
58
59SVGFEImageElement::~SVGFEImageElement()
60{
61 clearResourceReferences();
62}
63
64bool SVGFEImageElement::hasSingleSecurityOrigin() const
65{
66 if (!m_cachedImage)
67 return true;
68 auto* image = m_cachedImage->image();
69 return !image || image->hasSingleSecurityOrigin();
70}
71
72void SVGFEImageElement::clearResourceReferences()
73{
74 if (m_cachedImage) {
75 m_cachedImage->removeClient(*this);
76 m_cachedImage = nullptr;
77 }
78
79 document().accessSVGExtensions().removeAllTargetReferencesForElement(*this);
80}
81
82void SVGFEImageElement::requestImageResource()
83{
84 ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
85 options.contentSecurityPolicyImposition = isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck;
86
87 CachedResourceRequest request(ResourceRequest(document().completeURL(href())), options);
88 request.setInitiator(*this);
89 m_cachedImage = document().cachedResourceLoader().requestImage(WTFMove(request)).value_or(nullptr);
90
91 if (m_cachedImage)
92 m_cachedImage->addClient(*this);
93}
94
95void SVGFEImageElement::buildPendingResource()
96{
97 clearResourceReferences();
98 if (!isConnected())
99 return;
100
101 auto target = SVGURIReference::targetElementFromIRIString(href(), treeScope());
102 if (!target.element) {
103 if (target.identifier.isEmpty())
104 requestImageResource();
105 else {
106 document().accessSVGExtensions().addPendingResource(target.identifier, *this);
107 ASSERT(hasPendingResources());
108 }
109 } else if (is<SVGElement>(*target.element)) {
110 // Register us with the target in the dependencies map. Any change of hrefElement
111 // that leads to relayout/repainting now informs us, so we can react to it.
112 document().accessSVGExtensions().addElementReferencingTarget(*this, downcast<SVGElement>(*target.element));
113 }
114
115 invalidate();
116}
117
118void SVGFEImageElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
119{
120 if (name == SVGNames::preserveAspectRatioAttr) {
121 SVGPreserveAspectRatioValue preserveAspectRatio;
122 preserveAspectRatio.parse(value);
123 m_preserveAspectRatio->setBaseValInternal(preserveAspectRatio);
124 return;
125 }
126
127 SVGFilterPrimitiveStandardAttributes::parseAttribute(name, value);
128 SVGURIReference::parseAttribute(name, value);
129 SVGExternalResourcesRequired::parseAttribute(name, value);
130}
131
132void SVGFEImageElement::svgAttributeChanged(const QualifiedName& attrName)
133{
134 if (attrName == SVGNames::preserveAspectRatioAttr) {
135 InstanceInvalidationGuard guard(*this);
136 invalidate();
137 return;
138 }
139
140 if (SVGURIReference::isKnownAttribute(attrName)) {
141 InstanceInvalidationGuard guard(*this);
142 buildPendingResource();
143 return;
144 }
145
146 SVGFilterPrimitiveStandardAttributes::svgAttributeChanged(attrName);
147}
148
149Node::InsertedIntoAncestorResult SVGFEImageElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
150{
151 SVGFilterPrimitiveStandardAttributes::insertedIntoAncestor(insertionType, parentOfInsertedTree);
152 return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
153}
154
155void SVGFEImageElement::didFinishInsertingNode()
156{
157 buildPendingResource();
158}
159
160void SVGFEImageElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
161{
162 SVGFilterPrimitiveStandardAttributes::removedFromAncestor(removalType, oldParentOfRemovedTree);
163 if (removalType.disconnectedFromDocument)
164 clearResourceReferences();
165}
166
167void SVGFEImageElement::notifyFinished(CachedResource&)
168{
169 if (!isConnected())
170 return;
171
172 auto parent = makeRefPtr(parentElement());
173
174 if (!parent || !parent->hasTagName(SVGNames::filterTag))
175 return;
176
177 RenderElement* parentRenderer = parent->renderer();
178 if (!parentRenderer)
179 return;
180
181 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*parentRenderer);
182}
183
184RefPtr<FilterEffect> SVGFEImageElement::build(SVGFilterBuilder*, Filter& filter) const
185{
186 if (m_cachedImage)
187 return FEImage::createWithImage(filter, m_cachedImage->imageForRenderer(renderer()), preserveAspectRatio());
188 return FEImage::createWithIRIReference(filter, treeScope(), href(), preserveAspectRatio());
189}
190
191void SVGFEImageElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
192{
193 SVGFilterPrimitiveStandardAttributes::addSubresourceAttributeURLs(urls);
194
195 addSubresourceURL(urls, document().completeURL(href()));
196}
197
198}
199