Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / SVGTests.cpp
blob6d6cd7203798e5140f36b2f0a9c98bced92c7e7d
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
49 return false;
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]) {
56 return true;
59 return false;
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"}));
69 } else {
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.
82 continue;
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];
94 return -1;
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()) {
105 continue;
107 nsCOMPtr<SVGTests> tests(do_QueryInterface(child));
108 if (tests) {
109 if (!tests->mPassesConditionalProcessingTests.valueOr(true) ||
110 !tests->PassesRequiredExtensionsTests()) {
111 continue;
113 const auto& languages = tests->mStringListAttributes[LANGUAGE];
114 if (!languages.IsExplicitlySet()) {
115 if (!defaultChild) {
116 defaultChild = child;
118 continue;
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()) {
132 return defaultChild;
135 int32_t index = FindBestLanguage(availLocales, aSwitch->OwnerDoc());
136 if (index >= 0) {
137 return children[index];
140 return defaultChild;
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);
155 return false;
157 for (uint32_t i = 0; i < extensions.Length(); i++) {
158 if (!HasExtension(extensions[i])) {
159 mPassesConditionalProcessingTests = Some(false);
160 return false;
164 return true;
167 bool SVGTests::PassesConditionalProcessingTests() const {
168 if (mPassesConditionalProcessingTests) {
169 return mPassesConditionalProcessingTests.value();
171 if (!PassesRequiredExtensionsTests()) {
172 return false;
175 // systemLanguage
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
179 // this parameter.
180 const auto& languages = mStringListAttributes[LANGUAGE];
181 if (languages.IsExplicitlySet()) {
182 if (languages.IsEmpty()) {
183 mPassesConditionalProcessingTests = Some(false);
184 return 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);
198 return 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);
207 if (NS_FAILED(rv)) {
208 mStringListAttributes[i].Clear();
210 mPassesConditionalProcessingTests = Nothing();
211 MaybeInvalidate();
212 return true;
215 return false;
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();
223 MaybeInvalidate();
224 return;
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