1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/SVGTests.h"
9 #include "DOMSVGStringList.h"
10 #include "nsCharSeparatedTokenizer.h"
11 #include "nsIContent.h"
12 #include "nsIContentInlines.h"
13 #include "mozilla/dom/SVGSwitchElement.h"
14 #include "mozilla/intl/oxilangtag_ffi_generated.h"
15 #include "mozilla/Preferences.h"
17 namespace mozilla::dom
{
19 nsStaticAtom
* const SVGTests::sStringListNames
[2] = {
20 nsGkAtoms::requiredExtensions
,
21 nsGkAtoms::systemLanguage
,
24 SVGTests::SVGTests() {
25 mStringListAttributes
[LANGUAGE
].SetIsCommaSeparated(true);
28 already_AddRefed
<DOMSVGStringList
> SVGTests::RequiredExtensions() {
29 return DOMSVGStringList::GetDOMWrapper(&mStringListAttributes
[EXTENSIONS
],
30 AsSVGElement(), true, EXTENSIONS
);
33 already_AddRefed
<DOMSVGStringList
> SVGTests::SystemLanguage() {
34 return DOMSVGStringList::GetDOMWrapper(&mStringListAttributes
[LANGUAGE
],
35 AsSVGElement(), true, LANGUAGE
);
38 bool SVGTests::HasExtension(const nsAString
& aExtension
) const {
39 #define SVG_SUPPORTED_EXTENSION(str) \
40 if (aExtension.EqualsLiteral(str)) return true;
41 SVG_SUPPORTED_EXTENSION("http://www.w3.org/1999/xhtml")
42 nsNameSpaceManager
* nameSpaceManager
= nsNameSpaceManager::GetInstance();
43 if (AsSVGElement()->IsInChromeDocument() ||
44 !nameSpaceManager
->mMathMLDisabled
) {
45 SVG_SUPPORTED_EXTENSION("http://www.w3.org/1998/Math/MathML")
47 #undef SVG_SUPPORTED_EXTENSION
52 bool SVGTests::IsConditionalProcessingAttribute(
53 const nsAtom
* aAttribute
) const {
54 for (uint32_t i
= 0; i
< std::size(sStringListNames
); i
++) {
55 if (aAttribute
== sStringListNames
[i
]) {
62 // Find the best match from aAvailLangs for the users accept-languages,
63 // returning the index in the aAvailLangs list, or -1 if no match.
64 static int32_t FindBestLanguage(const nsTArray
<nsCString
>& aAvailLangs
,
65 const Document
* aDoc
) {
66 AutoTArray
<nsCString
, 16> reqLangs
;
67 if (nsContentUtils::SpoofLocaleEnglish(aDoc
)) {
68 reqLangs
.AppendElements(Span(std::array
{"en-US", "en"}));
70 nsCString acceptLangs
;
71 Preferences::GetLocalizedCString("intl.accept_languages", acceptLangs
);
72 nsCCharSeparatedTokenizer
languageTokenizer(acceptLangs
, ',');
73 while (languageTokenizer
.hasMoreTokens()) {
74 reqLangs
.AppendElement(languageTokenizer
.nextToken());
77 for (const auto& req
: reqLangs
) {
78 for (const auto& avail
: aAvailLangs
) {
79 if (avail
.Length() > req
.Length()) {
80 // Ensure that en does not match en-us, i.e. you need to have en in
81 // intl.accept_languages to match en in markup.
84 using namespace intl::ffi
;
85 struct LangTagDelete
{
86 void operator()(LangTag
* aLangTag
) const { lang_tag_destroy(aLangTag
); }
88 UniquePtr
<LangTag
, LangTagDelete
> langTag(lang_tag_new(&avail
));
89 if (langTag
&& lang_tag_matches(langTag
.get(), &req
)) {
90 return &avail
- &aAvailLangs
[0];
97 nsIContent
* SVGTests::FindActiveSwitchChild(
98 const dom::SVGSwitchElement
* aSwitch
) {
99 AutoTArray
<nsCString
, 16> availLocales
;
100 AutoTArray
<nsIContent
*, 16> children
;
101 nsIContent
* defaultChild
= nullptr;
102 for (auto* child
= aSwitch
->GetFirstChild(); child
;
103 child
= child
->GetNextSibling()) {
104 if (!child
->IsElement()) {
107 nsCOMPtr
<SVGTests
> tests(do_QueryInterface(child
));
109 if (!tests
->mPassesConditionalProcessingTests
.valueOr(true) ||
110 !tests
->PassesRequiredExtensionsTests()) {
113 const auto& languages
= tests
->mStringListAttributes
[LANGUAGE
];
114 if (!languages
.IsExplicitlySet()) {
116 defaultChild
= child
;
120 for (uint32_t i
= 0; i
< languages
.Length(); i
++) {
121 children
.AppendElement(child
);
122 availLocales
.AppendElement(NS_ConvertUTF16toUTF8(languages
[i
]));
127 // For each entry in availLocales, we expect to have a corresponding entry
128 // in children that provides the child node associated with that locale.
129 MOZ_ASSERT(children
.Length() == availLocales
.Length());
131 if (availLocales
.IsEmpty()) {
135 int32_t index
= FindBestLanguage(availLocales
, aSwitch
->OwnerDoc());
137 return children
[index
];
143 bool SVGTests::PassesRequiredExtensionsTests() const {
144 // Required Extensions
146 // The requiredExtensions attribute defines a list of required language
147 // extensions. Language extensions are capabilities within a user agent that
148 // go beyond the feature set defined in the SVG specification.
149 // Each extension is identified by a URI reference.
150 // For now, claim that mozilla's SVG implementation supports XHTML and MathML.
151 const auto& extensions
= mStringListAttributes
[EXTENSIONS
];
152 if (extensions
.IsExplicitlySet()) {
153 if (extensions
.IsEmpty()) {
154 mPassesConditionalProcessingTests
= Some(false);
157 for (uint32_t i
= 0; i
< extensions
.Length(); i
++) {
158 if (!HasExtension(extensions
[i
])) {
159 mPassesConditionalProcessingTests
= Some(false);
167 bool SVGTests::PassesConditionalProcessingTests() const {
168 if (mPassesConditionalProcessingTests
) {
169 return mPassesConditionalProcessingTests
.value();
171 if (!PassesRequiredExtensionsTests()) {
177 // Evaluates to true if there's a BCP 47 match for the one of the user
178 // preference languages with one of the languages given in the value of
180 const auto& languages
= mStringListAttributes
[LANGUAGE
];
181 if (languages
.IsExplicitlySet()) {
182 if (languages
.IsEmpty()) {
183 mPassesConditionalProcessingTests
= Some(false);
187 AutoTArray
<nsCString
, 4> availLocales
;
188 for (uint32_t i
= 0; i
< languages
.Length(); i
++) {
189 availLocales
.AppendElement(NS_ConvertUTF16toUTF8(languages
[i
]));
192 mPassesConditionalProcessingTests
=
193 Some(FindBestLanguage(availLocales
, AsSVGElement()->OwnerDoc()) >= 0);
194 return mPassesConditionalProcessingTests
.value();
197 mPassesConditionalProcessingTests
= Some(true);
201 bool SVGTests::ParseConditionalProcessingAttribute(nsAtom
* aAttribute
,
202 const nsAString
& aValue
,
203 nsAttrValue
& aResult
) {
204 for (uint32_t i
= 0; i
< std::size(sStringListNames
); i
++) {
205 if (aAttribute
== sStringListNames
[i
]) {
206 nsresult rv
= mStringListAttributes
[i
].SetValue(aValue
);
208 mStringListAttributes
[i
].Clear();
210 mPassesConditionalProcessingTests
= Nothing();
218 void SVGTests::UnsetAttr(const nsAtom
* aAttribute
) {
219 for (uint32_t i
= 0; i
< std::size(sStringListNames
); i
++) {
220 if (aAttribute
== sStringListNames
[i
]) {
221 mStringListAttributes
[i
].Clear();
222 mPassesConditionalProcessingTests
= Nothing();
229 nsStaticAtom
* SVGTests::GetAttrName(uint8_t aAttrEnum
) const {
230 return sStringListNames
[aAttrEnum
];
233 void SVGTests::GetAttrValue(uint8_t aAttrEnum
, nsAttrValue
& aValue
) const {
234 MOZ_ASSERT(aAttrEnum
< std::size(sStringListNames
), "aAttrEnum out of range");
235 aValue
.SetTo(mStringListAttributes
[aAttrEnum
], nullptr);
238 void SVGTests::MaybeInvalidate() {
239 nsIContent
* parent
= AsSVGElement()->GetFlattenedTreeParent();
241 if (auto* svgSwitch
= SVGSwitchElement::FromNodeOrNull(parent
)) {
242 svgSwitch
->MaybeInvalidate();
246 } // namespace mozilla::dom