1/*
2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) 2015-2018 Apple Inc. All right reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "SVGTests.h"
24
25#include "DOMImplementation.h"
26#include "HTMLNames.h"
27#include "SVGElement.h"
28#include "SVGNames.h"
29#include "SVGStringList.h"
30#include <wtf/Language.h>
31#include <wtf/NeverDestroyed.h>
32
33#if ENABLE(MATHML)
34#include "MathMLNames.h"
35#endif
36
37namespace WebCore {
38
39using namespace SVGNames;
40
41static const HashSet<String, ASCIICaseInsensitiveHash>& supportedSVGFeatures()
42{
43 static NeverDestroyed<HashSet<String, ASCIICaseInsensitiveHash>> features = [] {
44 static const char* const features10[] = {
45#if ENABLE(SVG_FONTS)
46 "dom",
47 "dom.svg",
48 "dom.svg.static",
49 "svg",
50 "svg.static",
51#endif
52 };
53 static const char* const features11[] = {
54 "animation",
55 "basegraphicsattribute",
56 "basicclip",
57 "basicfilter",
58 "basicpaintattribute",
59 "basicstructure",
60 "basictext",
61 "clip",
62 "conditionalprocessing",
63 "containerattribute",
64 "coreattribute",
65 "cursor",
66 "documenteventsattribute",
67 "extensibility",
68 "externalresourcesrequired",
69 "filter",
70 "gradient",
71 "graphicaleventsattribute",
72 "graphicsattribute",
73 "hyperlinking",
74 "image",
75 "marker",
76 "mask",
77 "opacityattribute",
78 "paintattribute",
79 "pattern",
80 "script",
81 "shape",
82 "structure",
83 "style",
84 "svg-animation",
85 "svgdom-animation",
86 "text",
87 "view",
88 "viewportattribute",
89 "xlinkattribute",
90#if ENABLE(SVG_FONTS)
91 "basicfont",
92 "font",
93 "svg",
94 "svg-static",
95 "svgdom",
96 "svgdom-static",
97#endif
98 };
99 HashSet<String, ASCIICaseInsensitiveHash> set;
100 for (auto& feature : features10)
101 set.add(makeString("org.w3c.", feature));
102 for (auto& feature : features11)
103 set.add(makeString("http://www.w3.org/tr/svg11/feature#", feature));
104 return set;
105 }();
106 return features;
107}
108
109SVGTests::SVGTests(SVGElement* contextElement)
110 : m_contextElement(*contextElement)
111 , m_requiredFeatures(SVGStringList::create(contextElement))
112 , m_requiredExtensions(SVGStringList::create(contextElement))
113 , m_systemLanguage(SVGStringList::create(contextElement))
114{
115 static std::once_flag onceFlag;
116 std::call_once(onceFlag, [] {
117 PropertyRegistry::registerProperty<SVGNames::requiredFeaturesAttr, &SVGTests::m_requiredFeatures>();
118 PropertyRegistry::registerProperty<SVGNames::requiredExtensionsAttr, &SVGTests::m_requiredExtensions>();
119 PropertyRegistry::registerProperty<SVGNames::systemLanguageAttr, &SVGTests::m_systemLanguage>();
120 });
121}
122
123bool SVGTests::hasExtension(const String& extension)
124{
125 // We recognize XHTML and MathML, as implemented in Gecko and suggested in the SVG Tiny recommendation (http://www.w3.org/TR/SVG11/struct.html#RequiredExtensionsAttribute).
126#if ENABLE(MATHML)
127 if (extension == MathMLNames::mathmlNamespaceURI)
128 return true;
129#endif
130 return extension == HTMLNames::xhtmlNamespaceURI;
131}
132
133bool SVGTests::isValid() const
134{
135 for (auto& feature : m_requiredFeatures->items()) {
136 if (feature.isEmpty() || !supportedSVGFeatures().contains(feature))
137 return false;
138 }
139 for (auto& language : m_systemLanguage->items()) {
140 if (language != defaultLanguage().substring(0, 2))
141 return false;
142 }
143 for (auto& extension : m_requiredExtensions->items()) {
144 if (!hasExtension(extension))
145 return false;
146 }
147 return true;
148}
149
150void SVGTests::parseAttribute(const QualifiedName& attributeName, const AtomicString& value)
151{
152 if (attributeName == requiredFeaturesAttr)
153 m_requiredFeatures->reset(value);
154 if (attributeName == requiredExtensionsAttr)
155 m_requiredExtensions->reset(value);
156 if (attributeName == systemLanguageAttr)
157 m_systemLanguage->reset(value);
158}
159
160void SVGTests::svgAttributeChanged(const QualifiedName& attrName)
161{
162 if (!PropertyRegistry::isKnownAttribute(attrName))
163 return;
164
165 if (!m_contextElement.isConnected())
166 return;
167 m_contextElement.invalidateStyleAndRenderersForSubtree();
168}
169
170void SVGTests::addSupportedAttributes(HashSet<QualifiedName>& supportedAttributes)
171{
172 supportedAttributes.add(requiredFeaturesAttr);
173 supportedAttributes.add(requiredExtensionsAttr);
174 supportedAttributes.add(systemLanguageAttr);
175}
176
177bool SVGTests::hasFeatureForLegacyBindings(const String& feature, const String& version)
178{
179 // FIXME: This function is here only to be exposed in the Objective-C and GObject bindings for both Node and DOMImplementation.
180 // It's likely that we can just remove this and instead have the bindings return true unconditionally.
181 // This is what the DOMImplementation function now does in JavaScript as is now suggested in the DOM specification.
182 // The behavior implemented below is quirky, but preserves what WebKit has done for at least the last few years.
183
184 bool hasSVG10FeaturePrefix = startsWithLettersIgnoringASCIICase(feature, "org.w3c.dom.svg") || startsWithLettersIgnoringASCIICase(feature, "org.w3c.svg");
185 bool hasSVG11FeaturePrefix = startsWithLettersIgnoringASCIICase(feature, "http://www.w3.org/tr/svg");
186
187 // We don't even try to handle feature names that don't look like the SVG ones, so just return true for all of those.
188 if (!(hasSVG10FeaturePrefix || hasSVG11FeaturePrefix))
189 return true;
190
191 // If the version number matches the style of the feature name, then use the set to see if the feature is supported.
192 if (version.isEmpty() || (hasSVG10FeaturePrefix && version == "1.0") || (hasSVG11FeaturePrefix && version == "1.1"))
193 return supportedSVGFeatures().contains(feature);
194
195 return false;
196}
197
198}
199