Forked from
The Tor Project / Applications / Mullvad Browser
80054 commits behind, 859 commits ahead of the upstream repository.
-
Pier Angelo Vendrame authored
Differential Revision: https://phabricator.services.mozilla.com/D198967
Pier Angelo Vendrame authoredDifferential Revision: https://phabricator.services.mozilla.com/D198967
ICUUtils.cpp 5.25 KiB
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef MOZILLA_INTERNAL_API
# include "mozilla/Assertions.h"
# include "mozilla/UniquePtr.h"
# include "ICUUtils.h"
# include "mozilla/StaticPrefs_dom.h"
# include "mozilla/intl/LocaleService.h"
# include "mozilla/intl/FormatBuffer.h"
# include "mozilla/intl/NumberFormat.h"
# include "mozilla/intl/NumberParser.h"
# include "nsIContent.h"
# include "mozilla/dom/Document.h"
# include "nsString.h"
using namespace mozilla;
using mozilla::intl::LocaleService;
void ICUUtils::LanguageTagIterForContent::GetNext(nsACString& aBCP47LangTag) {
if (mCurrentFallbackIndex < 0) {
mCurrentFallbackIndex = 0;
// Try the language specified by a 'lang'/'xml:lang' attribute on mContent
// or any ancestor, if such an attribute is specified:
nsAutoString lang;
mContent->GetLang(lang);
if (!lang.IsEmpty()) {
CopyUTF16toUTF8(lang, aBCP47LangTag);
return;
}
}
if (mCurrentFallbackIndex < 1) {
mCurrentFallbackIndex = 1;
// Else try the language specified by any Content-Language HTTP header or
// pragma directive:
nsAutoString lang;
mContent->OwnerDoc()->GetContentLanguage(lang);
if (!lang.IsEmpty()) {
CopyUTF16toUTF8(lang, aBCP47LangTag);
return;
}
}
if (mCurrentFallbackIndex < 2) {
mCurrentFallbackIndex = 2;
// Else take the app's locale (or en-US, if spoof English applies):
const bool spoofLocale = nsContentUtils::SpoofLocaleEnglish() &&
!mContent->OwnerDoc()->AllowsL10n();
if (spoofLocale) {
aBCP47LangTag.AssignLiteral("en-US");
return;
}
nsAutoCString appLocale;
LocaleService::GetInstance()->GetAppLocaleAsBCP47(aBCP47LangTag);
return;
}
// TODO: Probably not worth it, but maybe have a fourth fallback to using
// the OS locale?
aBCP47LangTag.Truncate(); // Signal iterator exhausted
}
/* static */
bool ICUUtils::LocalizeNumber(double aValue,
LanguageTagIterForContent& aLangTags,
nsAString& aLocalizedValue) {
MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
nsAutoCString langTag;
aLangTags.GetNext(langTag);
intl::NumberFormatOptions options;
if (StaticPrefs::dom_forms_number_grouping()) {
options.mGrouping = intl::NumberFormatOptions::Grouping::Always;
} else {
options.mGrouping = intl::NumberFormatOptions::Grouping::Never;
}
// ICU default is a maximum of 3 significant fractional digits. We don't
// want that limit, so we set it to the maximum that a double can represent
// (14-16 decimal fractional digits).
options.mFractionDigits = Some(std::make_pair(0, 16));
while (!langTag.IsEmpty()) {
auto result = intl::NumberFormat::TryCreate(langTag.get(), options);
if (result.isErr()) {
aLangTags.GetNext(langTag);
continue;
}
UniquePtr<intl::NumberFormat> nf = result.unwrap();
intl::nsTStringToBufferAdapter adapter(aLocalizedValue);
if (nf->format(aValue, adapter).isOk()) {
return true;
}
aLangTags.GetNext(langTag);
}
return false;
}
/* static */
double ICUUtils::ParseNumber(nsAString& aValue,
LanguageTagIterForContent& aLangTags) {
MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
if (aValue.IsEmpty()) {
return std::numeric_limits<float>::quiet_NaN();
}
uint32_t length = aValue.Length();
nsAutoCString langTag;
aLangTags.GetNext(langTag);
while (!langTag.IsEmpty()) {
auto createResult = intl::NumberParser::TryCreate(
langTag.get(), StaticPrefs::dom_forms_number_grouping());
if (createResult.isErr()) {
aLangTags.GetNext(langTag);
continue;
}
UniquePtr<intl::NumberParser> np = createResult.unwrap();
static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
"Unexpected character size - the following cast is unsafe");
auto parseResult = np->ParseDouble(
mozilla::Span<const char16_t>(PromiseFlatString(aValue).get(), length));
if (parseResult.isOk()) {
std::pair<double, int32_t> parsed = parseResult.unwrap();
if (parsed.second == static_cast<int32_t>(length)) {
return parsed.first;
}
}
aLangTags.GetNext(langTag);
}
return std::numeric_limits<float>::quiet_NaN();
}
/* static */
void ICUUtils::AssignUCharArrayToString(UChar* aICUString, int32_t aLength,
nsAString& aMozString) {
// Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
// cast here.
static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
"Unexpected character size - the following cast is unsafe");
aMozString.Assign((const nsAString::char_type*)aICUString, aLength);
NS_ASSERTION((int32_t)aMozString.Length() == aLength, "Conversion failed");
}
/* static */
nsresult ICUUtils::ICUErrorToNsResult(const intl::ICUError aError) {
switch (aError) {
case intl::ICUError::OutOfMemory:
return NS_ERROR_OUT_OF_MEMORY;
default:
return NS_ERROR_FAILURE;
}
}
#endif /* MOZILLA_INTERNAL_API */