Backed out changeset 804f26b2c6b8 (bug 1428698) for xpcshell failure on toolkit/mozapps/extensions/test/xpcshell/test_bug397778.js on a CLOSED TREE

--HG--
extra : amend_source : 0a86497ef3fed6c36a638ccc640d1eccbe14bce6
This commit is contained in:
Dorel Luca 2018-01-26 03:43:51 +02:00
parent 49993bfe85
commit f0dfa23b9e
7 changed files with 172 additions and 320 deletions

View File

@ -446,7 +446,7 @@ LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
// aRetVal, so that no available locale will be found more than once.
AutoTArray<Locale, 100> availLocales;
for (auto& avail : aAvailable) {
availLocales.AppendElement(Locale(avail));
availLocales.AppendElement(Locale(avail, true));
}
// Helper to erase an entry from availLocales once we have copied it to
@ -460,12 +460,9 @@ LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
};
for (auto& requested : aRequested) {
if (requested.IsEmpty()) {
continue;
}
// 1) Try to find a simple (case-insensitive) string match for the request.
auto matchesExactly = [&](Locale& aLoc) {
auto matchesExactly = [&](const Locale& aLoc) {
return requested.Equals(aLoc.AsString(),
nsCaseInsensitiveCStringComparator());
};
@ -481,9 +478,9 @@ LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
}
// 2) Try to match against the available locales treated as ranges.
auto findRangeMatches = [&](Locale& aReq, bool aAvailRange, bool aReqRange) {
auto matchesRange = [&](Locale& aLoc) {
return aLoc.Matches(aReq, aAvailRange, aReqRange);
auto findRangeMatches = [&](const Locale& aReq) {
auto matchesRange = [&](const Locale& aLoc) {
return aReq.Matches(aLoc);
};
bool foundMatch = false;
auto match = availLocales.begin();
@ -499,35 +496,35 @@ LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
return foundMatch;
};
Locale requestedLocale = Locale(requested);
if (findRangeMatches(requestedLocale, true, false)) {
Locale requestedLocale = Locale(requested, false);
if (findRangeMatches(requestedLocale)) {
HANDLE_STRATEGY;
}
// 3) Try to match against a maximized version of the requested locale
if (requestedLocale.AddLikelySubtags()) {
if (findRangeMatches(requestedLocale, true, false)) {
if (findRangeMatches(requestedLocale)) {
HANDLE_STRATEGY;
}
}
// 4) Try to match against a variant as a range
requestedLocale.ClearVariants();
if (findRangeMatches(requestedLocale, true, true)) {
requestedLocale.SetVariantRange();
if (findRangeMatches(requestedLocale)) {
HANDLE_STRATEGY;
}
// 5) Try to match against the likely subtag without region
requestedLocale.ClearRegion();
if (requestedLocale.AddLikelySubtags()) {
if (findRangeMatches(requestedLocale, true, false)) {
if (requestedLocale.AddLikelySubtagsWithoutRegion()) {
if (findRangeMatches(requestedLocale)) {
HANDLE_STRATEGY;
}
}
// 6) Try to match against a region as a range
requestedLocale.ClearRegion();
if (findRangeMatches(requestedLocale, true, true)) {
requestedLocale.SetRegionRange();
if (findRangeMatches(requestedLocale)) {
HANDLE_STRATEGY;
}
}
@ -595,12 +592,10 @@ LocaleService::Observe(nsISupports *aSubject, const char *aTopic,
}
bool
LocaleService::LanguagesMatch(const nsACString& aRequested,
const nsACString& aAvailable)
LocaleService::LanguagesMatch(const nsCString& aRequested,
const nsCString& aAvailable)
{
Locale requested = Locale(aRequested);
Locale available = Locale(aAvailable);
return requested.GetLanguage().Equals(available.GetLanguage());
return Locale(aRequested, true).LanguageMatches(Locale(aAvailable, true));
}

View File

@ -247,8 +247,8 @@ public:
*/
bool IsAppLocaleRTL();
static bool LanguagesMatch(const nsACString& aRequested,
const nsACString& aAvailable);
static bool LanguagesMatch(const nsCString& aRequested,
const nsCString& aAvailable);
bool IsServer();

View File

@ -5,9 +5,6 @@
#include "mozilla/intl/MozLocale.h"
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
#include "unicode/uloc.h"
using namespace mozilla::intl;
@ -16,174 +13,134 @@ using namespace mozilla::intl;
* Note: The file name is `MozLocale` to avoid compilation problems on case-insensitive
* Windows. The class name is `Locale`.
*/
Locale::Locale(const nsACString& aLocale)
Locale::Locale(const nsCString& aLocale, bool aRange)
: mLocaleStr(aLocale)
{
int32_t position = 0;
if (!IsASCII(aLocale)) {
mIsValid = false;
return;
}
int32_t partNum = 0;
nsAutoCString normLocale(aLocale);
normLocale.ReplaceChar('_', '-');
/**
* BCP47 language tag:
*
* langtag = language 2*3ALPHA
* ["-" extlang] 3ALPHA *2("-" 3ALPHA)
* ["-" script] 4ALPHA
* ["-" region] 2ALPHA / 3DIGIT
* *("-" variant) 5*8alphanum / (DIGIT 3alphanum)
* *("-" extension) [0-9a-wy-z] 1*("-" (1*8alphanum))
* ["-" privateuse] x 1*("-" (1*8alphanum))
*
* This class currently supports a subset of the full BCP47 language tag
* with a single extension of allowing variants to be 3ALPHA to support
* `ja-JP-mac` code:
*
* langtag = language 2*3ALPHA
* ["-" script] 4ALPHA
* ["-" region] 2ALPHA
* *("-" variant) 3*8alphanum
*
* The `position` variable represents the currently expected section of the tag
* and intentionally skips positions (like `extlang`) which may be added later.
*/
for (const nsACString& subTag : normLocale.Split('-')) {
auto slen = subTag.Length();
if (position == 0) {
if (slen < 2 || slen > 3) {
mIsValid = false;
return;
}
mLanguage = subTag;
ToLowerCase(mLanguage);
position = 2;
} else if (position <= 2 && slen == 4) {
mScript = subTag;
ToLowerCase(mScript);
mScript.Replace(0, 1, ToUpperCase(mScript[0]));
position = 3;
} else if (position <= 3 && slen == 2) {
mRegion = subTag;
ToUpperCase(mRegion);
position = 4;
} else if (position <= 4 && slen >= 3 && slen <= 8) {
// we're quirky here because we allow for variant to be 3 char long.
// BCP47 requires variants to be 5-8 char long at lest.
//
// We do this to support the `ja-JP-mac` quirk that we have.
nsAutoCString lcSubTag(subTag);
ToLowerCase(lcSubTag);
mVariants.InsertElementSorted(lcSubTag);
position = 4;
for (const nsACString& part : normLocale.Split('-')) {
switch (partNum) {
case 0:
if (part.EqualsLiteral("*") ||
part.Length() == 2 || part.Length() == 3) {
mLanguage.Assign(part);
}
break;
case 1:
if (part.EqualsLiteral("*") || part.Length() == 4) {
mScript.Assign(part);
break;
}
// fallover to region case
partNum++;
MOZ_FALLTHROUGH;
case 2:
if (part.EqualsLiteral("*") || part.Length() == 2) {
mRegion.Assign(part);
}
break;
case 3:
if (part.EqualsLiteral("*") || (part.Length() >= 3 && part.Length() <= 8)) {
mVariant.Assign(part);
}
break;
}
partNum++;
}
if (aRange) {
if (mLanguage.IsEmpty()) {
mLanguage.AssignLiteral("*");
}
if (mScript.IsEmpty()) {
mScript.AssignLiteral("*");
}
if (mRegion.IsEmpty()) {
mRegion.AssignLiteral("*");
}
if (mVariant.IsEmpty()) {
mVariant.AssignLiteral("*");
}
}
}
bool
Locale::IsValid()
static bool
SubtagMatches(const nsCString& aSubtag1, const nsCString& aSubtag2)
{
return mIsValid;
}
const nsCString
Locale::AsString()
{
nsCString tag;
if (!mIsValid) {
tag.AppendLiteral("und");
return tag;
}
tag.Append(mLanguage);
if (!mScript.IsEmpty()) {
tag.AppendLiteral("-");
tag.Append(mScript);
}
if (!mRegion.IsEmpty()) {
tag.AppendLiteral("-");
tag.Append(mRegion);
}
for (const auto& variant : mVariants) {
tag.AppendLiteral("-");
tag.Append(variant);
}
return tag;
}
const nsACString&
Locale::GetLanguage() const
{
return mLanguage;
}
const nsACString&
Locale::GetScript() const
{
return mScript;
}
const nsACString&
Locale::GetRegion() const
{
return mRegion;
}
const nsTArray<nsCString>&
Locale::GetVariants() const
{
return mVariants;
return aSubtag1.EqualsLiteral("*") ||
aSubtag2.EqualsLiteral("*") ||
aSubtag1.Equals(aSubtag2, nsCaseInsensitiveCStringComparator());
}
bool
Locale::Matches(const Locale& aOther, bool aThisRange, bool aOtherRange) const
Locale::Matches(const Locale& aLocale) const
{
if ((!aThisRange || !mLanguage.IsEmpty()) &&
(!aOtherRange || !aOther.mLanguage.IsEmpty()) &&
!mLanguage.Equals(aOther.mLanguage)) {
return false;
}
return SubtagMatches(mLanguage, aLocale.mLanguage) &&
SubtagMatches(mScript, aLocale.mScript) &&
SubtagMatches(mRegion, aLocale.mRegion) &&
SubtagMatches(mVariant, aLocale.mVariant);
}
if ((!aThisRange || !mScript.IsEmpty()) &&
(!aOtherRange || !aOther.mScript.IsEmpty()) &&
!mScript.Equals(aOther.mScript)) {
return false;
}
if ((!aThisRange || !mRegion.IsEmpty()) &&
(!aOtherRange || !aOther.mRegion.IsEmpty()) &&
!mRegion.Equals(aOther.mRegion)) {
return false;
}
if ((!aThisRange || !mVariants.IsEmpty()) &&
(!aOtherRange || !aOther.mVariants.IsEmpty()) &&
mVariants != aOther.mVariants) {
return false;
}
return true;
bool
Locale::LanguageMatches(const Locale& aLocale) const
{
return SubtagMatches(mLanguage, aLocale.mLanguage) &&
SubtagMatches(mScript, aLocale.mScript);
}
void
Locale::SetVariantRange()
{
mVariant.AssignLiteral("*");
}
void
Locale::SetRegionRange()
{
mRegion.AssignLiteral("*");
}
bool
Locale::AddLikelySubtags()
{
return AddLikelySubtagsForLocale(mLocaleStr);
}
bool
Locale::AddLikelySubtagsWithoutRegion()
{
nsAutoCString locale(mLanguage);
if (!mScript.IsEmpty()) {
locale.Append("-");
locale.Append(mScript);
}
// We don't add variant here because likelySubtag doesn't care about it.
return AddLikelySubtagsForLocale(locale);
}
bool
Locale::AddLikelySubtagsForLocale(const nsACString& aLocale)
{
const int32_t kLocaleMax = 160;
char maxLocale[kLocaleMax];
nsAutoCString locale(aLocale);
UErrorCode status = U_ZERO_ERROR;
uloc_addLikelySubtags(AsString().get(), maxLocale, kLocaleMax, &status);
uloc_addLikelySubtags(locale.get(), maxLocale, kLocaleMax, &status);
if (U_FAILURE(status)) {
return false;
}
nsDependentCString maxLocStr(maxLocale);
Locale loc = Locale(maxLocStr);
Locale loc = Locale(maxLocStr, false);
if (loc == *this) {
return false;
@ -198,15 +155,3 @@ Locale::AddLikelySubtags()
return true;
}
void
Locale::ClearVariants()
{
mVariants.Clear();
}
void
Locale::ClearRegion()
{
mRegion.Truncate();
}

View File

@ -3,91 +3,62 @@
* 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/. */
#ifndef mozilla_intl_MozLocale_h__
#define mozilla_intl_MozLocale_h__
#ifndef mozilla_intl_Locale_h__
#define mozilla_intl_Locale_h__
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
namespace intl {
/**
* Locale class is a core representation of a single locale.
*
* A locale is a data object representing a combination of language, script,
* region, variant and a set of regional extension preferences that may further specify
* particular user choices like calendar, numbering system, etc.
*
* A locale can be expressed as a language tag string, like a simple "fr" for French,
* or a more specific "sr-Cyrl-RS-u-hc-h12" for Serbian in Russia with a Cyrylic script,
* and hour cycle selected to be `h12`.
*
* The format of the language tag follows BCP47 standard and implements a subset of it.
* In the future we expect to extend this class to cover more subtags and extensions.
*
* BCP47: https://tools.ietf.org/html/bcp47
*
* The aim of this class it aid in validation, parsing and canonicalization of the
* string.
*
* It allows the user to input any well-formed BCP47 language tag and operate
* on its subtags in a canonicalized form.
*
* It should be used for all operations on language tags, and together with
* LocaleService::NegotiateLanguages for language negotiation.
*
* Example:
*
* Locale loc = Locale("de-at");
*
* ASSERT_TRUE(loc.GetLanguage().Equals("de"));
* ASSERT_TRUE(loc.GetScript().IsEmpty());
* ASSERT_TRUE(loc.GetRegion().Equals("AT")); // canonicalized to upper case
* Locale object, a BCP47-style tag decomposed into subtags for
* matching purposes.
*
* If constructed with aRange = true, any missing subtags will be
* set to "*".
*
* Note: The file name is `MozLocale` to avoid compilation problems on case-insensitive
* Windows. The class name is `Locale`.
*/
class Locale {
public:
explicit Locale(const nsACString& aLocale);
explicit Locale(const char* aLocale)
: Locale(nsDependentCString(aLocale))
{ };
Locale(const nsCString& aLocale, bool aRange);
const nsACString& GetLanguage() const;
const nsACString& GetScript() const;
const nsACString& GetRegion() const;
const nsTArray<nsCString>& GetVariants() const;
bool Matches(const Locale& aLocale) const;
bool LanguageMatches(const Locale& aLocale) const;
bool IsValid();
const nsCString AsString();
bool Matches(const Locale& aOther, bool aThisRange, bool aOtherRange) const;
void SetVariantRange();
void SetRegionRange();
// returns false if nothing changed
bool AddLikelySubtags();
void ClearVariants();
void ClearRegion();
bool AddLikelySubtagsWithoutRegion();
const nsCString& AsString() const {
return mLocaleStr;
}
bool operator== (const Locale& aOther) {
return mLanguage.Equals(aOther.mLanguage) &&
mScript.Equals(aOther.mScript) &&
mRegion.Equals(aOther.mRegion) &&
mVariants == aOther.mVariants;
const auto& cmp = nsCaseInsensitiveCStringComparator();
return mLanguage.Equals(aOther.mLanguage, cmp) &&
mScript.Equals(aOther.mScript, cmp) &&
mRegion.Equals(aOther.mRegion, cmp) &&
mVariant.Equals(aOther.mVariant, cmp);
}
private:
nsAutoCStringN<3> mLanguage;
nsAutoCStringN<4> mScript;
nsAutoCStringN<2> mRegion;
nsTArray<nsCString> mVariants;
bool mIsValid = true;
const nsCString& mLocaleStr;
nsCString mLanguage;
nsCString mScript;
nsCString mRegion;
nsCString mVariant;
bool AddLikelySubtagsForLocale(const nsACString& aLocale);
};
} // intl
} // namespace mozilla
DECLARE_USE_COPY_CONSTRUCTORS(mozilla::intl::Locale)
#endif /* mozilla_intl_MozLocale_h__ */
#endif /* mozilla_intl_Locale_h__ */

View File

@ -1,64 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "gtest/gtest.h"
#include "mozilla/intl/MozLocale.h"
using namespace mozilla::intl;
TEST(Intl_Locale_Locale, Locale) {
Locale loc = Locale("en-US");
ASSERT_TRUE(loc.GetLanguage().Equals("en"));
ASSERT_TRUE(loc.GetRegion().Equals("US"));
}
TEST(Intl_Locale_Locale, AsString) {
Locale loc = Locale("ja-jp-windows");
ASSERT_TRUE(loc.AsString().Equals("ja-JP-windows"));
}
TEST(Intl_Locale_Locale, GetSubTags) {
Locale loc = Locale("en-latn-us-macos");
ASSERT_TRUE(loc.GetLanguage().Equals("en"));
ASSERT_TRUE(loc.GetScript().Equals("Latn"));
ASSERT_TRUE(loc.GetRegion().Equals("US"));
ASSERT_TRUE(loc.GetVariants().Length() == 1);
ASSERT_TRUE(loc.GetVariants()[0].Equals("macos"));
}
TEST(Intl_Locale_Locale, Matches) {
Locale loc = Locale("en-US");
Locale loc2 = Locale("en-GB");
ASSERT_FALSE(loc == loc2);
Locale loc3 = Locale("en-US");
ASSERT_TRUE(loc == loc3);
Locale loc4 = Locale("En_us");
ASSERT_TRUE(loc == loc4);
}
TEST(Intl_Locale_Locale, MatchesRange) {
Locale loc = Locale("en-US");
Locale loc2 = Locale("en-Latn-US");
ASSERT_FALSE(loc == loc2);
ASSERT_TRUE(loc.Matches(loc2, true, false));
ASSERT_FALSE(loc.Matches(loc2, false, true));
ASSERT_FALSE(loc.Matches(loc2, false, false));
ASSERT_TRUE(loc.Matches(loc2, true, true));
Locale loc3 = Locale("en");
ASSERT_FALSE(loc == loc3);
ASSERT_TRUE(loc.Matches(loc3, false, true));
ASSERT_FALSE(loc.Matches(loc3, true, false));
ASSERT_FALSE(loc.Matches(loc3, false, false));
ASSERT_TRUE(loc.Matches(loc3, true, true));
}

View File

@ -9,7 +9,6 @@ UNIFIED_SOURCES += [
'TestDateTimeFormat.cpp',
'TestLocaleService.cpp',
'TestLocaleServiceNegotiate.cpp',
'TestMozLocale.cpp',
'TestOSPreferences.cpp',
]

View File

@ -12,16 +12,16 @@ const data = {
[["en"], ["en"], ["en"]],
[["en-US"], ["en-US"], ["en-US"]],
[["en-Latn-US"], ["en-Latn-US"], ["en-Latn-US"]],
[["en-Latn-US-windows"], ["en-Latn-US-windows"], ["en-Latn-US-windows"]],
[["en-Latn-US-mac"], ["en-Latn-US-mac"], ["en-Latn-US-mac"]],
[["fr-FR"], ["de", "it", "fr-FR"], ["fr-FR"]],
[["fr", "pl", "de-DE"], ["pl", "en-US", "de-DE"], ["pl", "de-DE"]],
],
"available as range": [
[["en-US"], ["en"], ["en"]],
[["en-Latn-US"], ["en-US"], ["en-US"]],
[["en-US-windows"], ["en-US"], ["en-US"]],
[["en-US-mac"], ["en-US"], ["en-US"]],
[["fr-CA", "de-DE"], ["fr", "it", "de"], ["fr", "de"]],
[["ja-JP-windows"], ["ja"], ["ja"]],
[["ja-JP-mac"], ["ja"], ["ja"]],
[["en-Latn-GB", "en-Latn-IN"], ["en-IN", "en-GB"], ["en-GB", "en-IN"]],
],
"should match on likely subtag": [
@ -38,27 +38,36 @@ const data = {
"should match likelySubtag region over other regions": [
[["en-CA"], ["en-ZA", "en-GB", "en-US"], ["en-US", "en-ZA", "en-GB"]],
],
"should match on a requested locale as a range": [
[["en-*-US"], ["en-US"], ["en-US"]],
[["en-Latn-US-*"], ["en-Latn-US"], ["en-Latn-US"]],
[["en-*-US-*"], ["en-US"], ["en-US"]],
],
"should match cross-region": [
[["en"], ["en-US"], ["en-US"]],
[["en-US"], ["en-GB"], ["en-GB"]],
[["en-Latn-US"], ["en-Latn-GB"], ["en-Latn-GB"]],
// This is a cross-region check, because the requested Locale
// is really lang: en, script: *, region: undefined
[["en-*"], ["en-US"], ["en-US"]],
],
"should match cross-variant": [
[["en-US-linux"], ["en-US-windows"], ["en-US-windows"]],
[["en-US-mac"], ["en-US-win"], ["en-US-win"]],
],
"should prioritize properly": [
// exact match first
[["en-US"], ["en-US-windows", "en", "en-US"], ["en-US", "en", "en-US-windows"]],
[["en-US"], ["en-US-mac", "en", "en-US"], ["en-US", "en", "en-US-mac"]],
// available as range second
[["en-Latn-US"], ["en-GB", "en-US"], ["en-US", "en-GB"]],
// likely subtags third
[["en"], ["en-Cyrl-US", "en-Latn-US"], ["en-Latn-US"]],
// variant range fourth
[["en-US-macos"], ["en-US-windows", "en-GB-macos"], ["en-US-windows", "en-GB-macos"]],
[["en-US-mac"], ["en-US-win", "en-GB-mac"], ["en-US-win", "en-GB-mac"]],
// regional range fifth
[["en-US-macos"], ["en-GB-windows"], ["en-GB-windows"]],
[["en-US-mac"], ["en-GB-win"], ["en-GB-win"]],
],
"should prioritize properly (extra tests)": [
[["en-US"], ["en-GB", "en"], ["en", "en-GB"]],
[["fr-CA-macos", "de-DE"], ["de-DE", "fr-FR-windows"], ["fr-FR-windows", "de-DE"]],
],
"should handle default locale properly": [
[["fr"], ["de", "it"], []],
@ -67,19 +76,16 @@ const data = {
[["fr", "de-DE"], ["de-DE", "fr-CA"], "en-US", ["fr-CA", "de-DE", "en-US"]],
],
"should handle all matches on the 1st higher than any on the 2nd": [
[["fr-CA-macos", "de-DE"], ["de-DE", "fr-FR-windows"], ["fr-FR-windows", "de-DE"]],
[["fr-CA-mac", "de-DE"], ["de-DE", "fr-FR-win"], ["fr-FR-win", "de-DE"]],
],
"should handle cases and underscores": [
[["fr_FR"], ["fr-FR"], ["fr-FR"]],
[["fr_fr"], ["fr-fr"], ["fr-FR"]],
[["fr_Fr"], ["fr-fR"], ["fr-FR"]],
[["fr_fr"], ["fr-fr"], ["fr-fr"]],
[["fr_Fr"], ["fr-fR"], ["fr-fR"]],
[["fr_lAtN_fr"], ["fr-Latn-FR"], ["fr-Latn-FR"]],
[["fr_FR"], ["fr_FR"], ["fr-FR"]],
[["fr-FR"], ["fr_FR"], ["fr-FR"]],
[["fr_Cyrl_FR_macos"], ["fr_Cyrl_fr-macos"], ["fr-Cyrl-FR-macos"]],
],
"should handle mozilla specific 3-letter variants": [
[["ja-JP-mac", "de-DE"], ["ja-JP-mac", "de-DE"], ["ja-JP-mac", "de-DE"]],
[["fr_FR"], ["fr_FR"], ["fr_FR"]],
[["fr-FR"], ["fr_FR"], ["fr_FR"]],
[["fr_Cyrl_FR_mac"], ["fr_Cyrl_fr-mac"], ["fr_Cyrl_fr-mac"]],
],
"should not crash on invalid input": [
[null, ["fr-FR"], []],