tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SVGTests.cpp (8186B)


      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/. */
      6 
      7 #include "mozilla/dom/SVGTests.h"
      8 
      9 #include "DOMSVGStringList.h"
     10 #include "mozilla/dom/SVGSwitchElement.h"
     11 #include "mozilla/intl/LocaleService.h"
     12 #include "mozilla/intl/oxilangtag_ffi_generated.h"
     13 #include "nsCharSeparatedTokenizer.h"
     14 #include "nsIContent.h"
     15 #include "nsIContentInlines.h"
     16 
     17 namespace mozilla::dom {
     18 
     19 nsStaticAtom* const SVGTests::sStringListNames[2] = {
     20    nsGkAtoms::requiredExtensions,
     21    nsGkAtoms::systemLanguage,
     22 };
     23 
     24 SVGTests::SVGTests() {
     25  mStringListAttributes[LANGUAGE].SetIsCommaSeparated(true);
     26 }
     27 
     28 already_AddRefed<DOMSVGStringList> SVGTests::RequiredExtensions() {
     29  return DOMSVGStringList::GetDOMWrapper(&mStringListAttributes[EXTENSIONS],
     30                                         AsSVGElement(), true, EXTENSIONS);
     31 }
     32 
     33 already_AddRefed<DOMSVGStringList> SVGTests::SystemLanguage() {
     34  return DOMSVGStringList::GetDOMWrapper(&mStringListAttributes[LANGUAGE],
     35                                         AsSVGElement(), true, LANGUAGE);
     36 }
     37 
     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")
     46  }
     47 #undef SVG_SUPPORTED_EXTENSION
     48 
     49  return false;
     50 }
     51 
     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;
     57    }
     58  }
     59  return false;
     60 }
     61 
     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 (aDoc->ShouldResistFingerprinting(RFPTarget::JSLocale)) {
     68    reqLangs.AppendElements(Span(std::array{"en-US", "en"}));
     69  } else {
     70    nsCString acceptLangs;
     71    intl::LocaleService::GetInstance()->GetAcceptLanguages(acceptLangs);
     72    nsCCharSeparatedTokenizer languageTokenizer(acceptLangs, ',');
     73    while (languageTokenizer.hasMoreTokens()) {
     74      reqLangs.AppendElement(languageTokenizer.nextToken());
     75    }
     76  }
     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;
     83      }
     84      using namespace intl::ffi;
     85      struct LangTagDelete {
     86        void operator()(LangTag* aLangTag) const { lang_tag_destroy(aLangTag); }
     87      };
     88      UniquePtr<LangTag, LangTagDelete> langTag(lang_tag_new(&avail));
     89      if (langTag && lang_tag_matches(langTag.get(), &req)) {
     90        return &avail - &aAvailLangs[0];
     91      }
     92    }
     93  }
     94  return -1;
     95 }
     96 
     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;
    106    }
    107    nsCOMPtr<SVGTests> tests(do_QueryInterface(child));
    108    if (tests) {
    109      if (!tests->mPassesConditionalProcessingTests.valueOr(true) ||
    110          !tests->PassesRequiredExtensionsTests()) {
    111        continue;
    112      }
    113      const auto& languages = tests->mStringListAttributes[LANGUAGE];
    114      if (!languages.IsExplicitlySet()) {
    115        if (!defaultChild) {
    116          defaultChild = child;
    117        }
    118        continue;
    119      }
    120      for (uint32_t i = 0; i < languages.Length(); i++) {
    121        children.AppendElement(child);
    122        availLocales.AppendElement(NS_ConvertUTF16toUTF8(languages[i]));
    123      }
    124    }
    125  }
    126 
    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());
    130 
    131  if (availLocales.IsEmpty()) {
    132    return defaultChild;
    133  }
    134 
    135  int32_t index = FindBestLanguage(availLocales, aSwitch->OwnerDoc());
    136  if (index >= 0) {
    137    return children[index];
    138  }
    139 
    140  return defaultChild;
    141 }
    142 
    143 bool SVGTests::PassesRequiredExtensionsTests() const {
    144  // Required Extensions
    145  //
    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;
    156    }
    157    for (uint32_t i = 0; i < extensions.Length(); i++) {
    158      if (!HasExtension(extensions[i])) {
    159        mPassesConditionalProcessingTests = Some(false);
    160        return false;
    161      }
    162    }
    163  }
    164  return true;
    165 }
    166 
    167 bool SVGTests::PassesConditionalProcessingTests() const {
    168  if (mPassesConditionalProcessingTests) {
    169    return mPassesConditionalProcessingTests.value();
    170  }
    171  if (!PassesRequiredExtensionsTests()) {
    172    return false;
    173  }
    174 
    175  // systemLanguage
    176  //
    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;
    185    }
    186 
    187    AutoTArray<nsCString, 4> availLocales;
    188    for (uint32_t i = 0; i < languages.Length(); i++) {
    189      availLocales.AppendElement(NS_ConvertUTF16toUTF8(languages[i]));
    190    }
    191 
    192    mPassesConditionalProcessingTests =
    193        Some(FindBestLanguage(availLocales, AsSVGElement()->OwnerDoc()) >= 0);
    194    return mPassesConditionalProcessingTests.value();
    195  }
    196 
    197  mPassesConditionalProcessingTests = Some(true);
    198  return true;
    199 }
    200 
    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();
    209      }
    210      mPassesConditionalProcessingTests = Nothing();
    211      MaybeInvalidate();
    212      return true;
    213    }
    214  }
    215  return false;
    216 }
    217 
    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;
    225    }
    226  }
    227 }
    228 
    229 nsStaticAtom* SVGTests::GetAttrName(uint8_t aAttrEnum) const {
    230  return sStringListNames[aAttrEnum];
    231 }
    232 
    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);
    236 }
    237 
    238 void SVGTests::MaybeInvalidate() {
    239  nsIContent* parent = AsSVGElement()->GetFlattenedTreeParent();
    240 
    241  if (auto* svgSwitch = SVGSwitchElement::FromNodeOrNull(parent)) {
    242    svgSwitch->MaybeInvalidate();
    243  }
    244 }
    245 
    246 }  // namespace mozilla::dom