1/*
2 * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Rob Buis <buis@kde.org>
4 * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
5 * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved.
6 * Copyright (C) 2018-2019 Apple Inc. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "config.h"
25#include "SVGImageElement.h"
26
27#include "CSSPropertyNames.h"
28#include "RenderImageResource.h"
29#include "RenderSVGImage.h"
30#include "RenderSVGResource.h"
31#include "SVGNames.h"
32#include "XLinkNames.h"
33#include <wtf/IsoMallocInlines.h>
34#include <wtf/NeverDestroyed.h>
35
36namespace WebCore {
37
38WTF_MAKE_ISO_ALLOCATED_IMPL(SVGImageElement);
39
40inline SVGImageElement::SVGImageElement(const QualifiedName& tagName, Document& document)
41 : SVGGraphicsElement(tagName, document)
42 , SVGExternalResourcesRequired(this)
43 , SVGURIReference(this)
44 , m_imageLoader(*this)
45{
46 static std::once_flag onceFlag;
47 std::call_once(onceFlag, [] {
48 PropertyRegistry::registerProperty<SVGNames::xAttr, &SVGImageElement::m_x>();
49 PropertyRegistry::registerProperty<SVGNames::yAttr, &SVGImageElement::m_y>();
50 PropertyRegistry::registerProperty<SVGNames::widthAttr, &SVGImageElement::m_width>();
51 PropertyRegistry::registerProperty<SVGNames::heightAttr, &SVGImageElement::m_height>();
52 PropertyRegistry::registerProperty<SVGNames::preserveAspectRatioAttr, &SVGImageElement::m_preserveAspectRatio>();
53 });
54}
55
56Ref<SVGImageElement> SVGImageElement::create(const QualifiedName& tagName, Document& document)
57{
58 return adoptRef(*new SVGImageElement(tagName, document));
59}
60
61bool SVGImageElement::hasSingleSecurityOrigin() const
62{
63 auto* renderer = downcast<RenderSVGImage>(this->renderer());
64 if (!renderer || !renderer->imageResource().cachedImage())
65 return true;
66 auto* image = renderer->imageResource().cachedImage()->image();
67 return !image || image->hasSingleSecurityOrigin();
68}
69
70void SVGImageElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
71{
72 if (name == SVGNames::preserveAspectRatioAttr) {
73 SVGPreserveAspectRatioValue preserveAspectRatio;
74 preserveAspectRatio.parse(value);
75 m_preserveAspectRatio->setBaseValInternal(preserveAspectRatio);
76 return;
77 }
78
79 SVGParsingError parseError = NoError;
80
81 if (name == SVGNames::xAttr)
82 m_x->setBaseValInternal(SVGLengthValue::construct(LengthModeWidth, value, parseError));
83 else if (name == SVGNames::yAttr)
84 m_y->setBaseValInternal(SVGLengthValue::construct(LengthModeHeight, value, parseError));
85 else if (name == SVGNames::widthAttr)
86 m_width->setBaseValInternal(SVGLengthValue::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths));
87 else if (name == SVGNames::heightAttr)
88 m_height->setBaseValInternal(SVGLengthValue::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths));
89
90 reportAttributeParsingError(parseError, name, value);
91
92 SVGGraphicsElement::parseAttribute(name, value);
93 SVGExternalResourcesRequired::parseAttribute(name, value);
94 SVGURIReference::parseAttribute(name, value);
95}
96
97void SVGImageElement::svgAttributeChanged(const QualifiedName& attrName)
98{
99 if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr) {
100 InstanceInvalidationGuard guard(*this);
101 updateRelativeLengthsInformation();
102
103 if (auto* renderer = this->renderer()) {
104 if (downcast<RenderSVGImage>(*renderer).updateImageViewport())
105 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
106 }
107 return;
108 }
109
110 if (attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr) {
111 InstanceInvalidationGuard guard(*this);
112 invalidateSVGPresentationAttributeStyle();
113 return;
114 }
115
116 if (attrName == SVGNames::preserveAspectRatioAttr) {
117 InstanceInvalidationGuard guard(*this);
118 if (auto* renderer = this->renderer())
119 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
120 return;
121 }
122
123 if (SVGURIReference::isKnownAttribute(attrName)) {
124 m_imageLoader.updateFromElementIgnoringPreviousError();
125 return;
126 }
127
128 SVGGraphicsElement::svgAttributeChanged(attrName);
129 SVGExternalResourcesRequired::svgAttributeChanged(attrName);
130}
131
132RenderPtr<RenderElement> SVGImageElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
133{
134 return createRenderer<RenderSVGImage>(*this, WTFMove(style));
135}
136
137bool SVGImageElement::haveLoadedRequiredResources()
138{
139 return !externalResourcesRequired() || !m_imageLoader.hasPendingActivity();
140}
141
142void SVGImageElement::didAttachRenderers()
143{
144 if (auto* imageObj = downcast<RenderSVGImage>(renderer())) {
145 if (imageObj->imageResource().cachedImage())
146 return;
147
148 imageObj->imageResource().setCachedImage(m_imageLoader.image());
149 }
150}
151
152Node::InsertedIntoAncestorResult SVGImageElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
153{
154 SVGGraphicsElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
155 if (!insertionType.connectedToDocument)
156 return InsertedIntoAncestorResult::Done;
157 // Update image loader, as soon as we're living in the tree.
158 // We can only resolve base URIs properly, after that!
159 m_imageLoader.updateFromElement();
160 return InsertedIntoAncestorResult::Done;
161}
162
163const AtomicString& SVGImageElement::imageSourceURL() const
164{
165 return getAttribute(SVGNames::hrefAttr, XLinkNames::hrefAttr);
166}
167
168void SVGImageElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
169{
170 SVGGraphicsElement::addSubresourceAttributeURLs(urls);
171
172 addSubresourceURL(urls, document().completeURL(href()));
173}
174
175void SVGImageElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
176{
177 m_imageLoader.elementDidMoveToNewDocument();
178 SVGGraphicsElement::didMoveToNewDocument(oldDocument, newDocument);
179}
180
181}
182